From 1c9a2111e6dd56afbb835ba637d367da2fa7f205 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Tue, 29 Mar 2022 22:01:22 +0200 Subject: [PATCH 01/14] First steps towards marshal methods generation --- .gitmodules | 4 + Xamarin.Android.sln | 6 ++ external/magic_enum | 1 + src/monodroid/CMakeLists.txt | 52 ++++++++-- src/monodroid/jni/application_dso_stub.cc | 7 ++ src/monodroid/jni/generate-marshaling-data.cc | 97 +++++++++++++++++++ src/monodroid/jni/generated/marshal-types.cs | 21 ++++ src/monodroid/jni/monodroid-glue-internal.hh | 12 +++ src/monodroid/jni/monodroid-glue.cc | 4 + .../jni/xamarin-android-app-context.cc | 9 ++ .../jni/xamarin-app-marshaling-private.hh | 84 ++++++++++++++++ src/monodroid/jni/xamarin-app-marshaling.cc | 89 +++++++++++++++++ src/monodroid/jni/xamarin-app-marshaling.hh | 25 +++++ src/monodroid/jni/xamarin-app.hh | 7 ++ tools/mgen/Constants.cs.in | 14 +++ tools/mgen/build.sh | 5 + tools/mgen/main.cs | 72 ++++++++++++++ tools/mgen/mgen.csproj | 94 ++++++++++++++++++ tools/mgen/run.sh | 2 + 19 files changed, 599 insertions(+), 6 deletions(-) create mode 160000 external/magic_enum create mode 100644 src/monodroid/jni/generate-marshaling-data.cc create mode 100644 src/monodroid/jni/generated/marshal-types.cs create mode 100644 src/monodroid/jni/xamarin-android-app-context.cc create mode 100644 src/monodroid/jni/xamarin-app-marshaling-private.hh create mode 100644 src/monodroid/jni/xamarin-app-marshaling.cc create mode 100644 src/monodroid/jni/xamarin-app-marshaling.hh create mode 100644 tools/mgen/Constants.cs.in create mode 100755 tools/mgen/build.sh create mode 100644 tools/mgen/main.cs create mode 100644 tools/mgen/mgen.csproj create mode 100755 tools/mgen/run.sh diff --git a/.gitmodules b/.gitmodules index 8b2fa1df307..111117df8a8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -42,3 +42,7 @@ path = external/xamarin-android-tools url = https://github.com/xamarin/xamarin-android-tools branch = main +[submodule "external/magic_enum"] + path = external/magic_enum + url = https://github.com/Neargye/magic_enum.git + branch = master diff --git a/Xamarin.Android.sln b/Xamarin.Android.sln index 9f5ac1a3083..9ad4faf5513 100644 --- a/Xamarin.Android.sln +++ b/Xamarin.Android.sln @@ -155,6 +155,7 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "relnote-gen", "tools\relnote-gen\relnote-gen.csproj", "{D8E14B43-E929-4C18-9FA6-2C3DC47EFC17}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Java.Runtime.Environment", "external\Java.Interop\src\Java.Runtime.Environment\Java.Runtime.Environment.csproj", "{C0E44558-FEE3-4DD3-986A-3F46DD1BF41B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "mgen", "tools\mgen\mgen.csproj", "{1771A814-349B-4B0C-918D-0A989E78CB73}" EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution @@ -432,6 +433,10 @@ Global {C0E44558-FEE3-4DD3-986A-3F46DD1BF41B}.Debug|AnyCPU.Build.0 = Debug|Any CPU {C0E44558-FEE3-4DD3-986A-3F46DD1BF41B}.Release|AnyCPU.ActiveCfg = Release|Any CPU {C0E44558-FEE3-4DD3-986A-3F46DD1BF41B}.Release|AnyCPU.Build.0 = Release|Any CPU + {1771A814-349B-4B0C-918D-0A989E78CB73}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU + {1771A814-349B-4B0C-918D-0A989E78CB73}.Debug|AnyCPU.Build.0 = Debug|Any CPU + {1771A814-349B-4B0C-918D-0A989E78CB73}.Release|AnyCPU.ActiveCfg = Release|Any CPU + {1771A814-349B-4B0C-918D-0A989E78CB73}.Release|AnyCPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -503,6 +508,7 @@ Global {4EFCED6E-9A6B-453A-94E4-CE4B736EC684} = {864062D3-A415-4A6F-9324-5820237BA058} {D8E14B43-E929-4C18-9FA6-2C3DC47EFC17} = {864062D3-A415-4A6F-9324-5820237BA058} {C0E44558-FEE3-4DD3-986A-3F46DD1BF41B} = {04E3E11E-B47D-4599-8AFC-50515A95E715} + {1771A814-349B-4B0C-918D-0A989E78CB73} = {864062D3-A415-4A6F-9324-5820237BA058} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {53A1F287-EFB2-4D97-A4BB-4A5E145613F6} diff --git a/external/magic_enum b/external/magic_enum new file mode 160000 index 00000000000..ab2bf820e19 --- /dev/null +++ b/external/magic_enum @@ -0,0 +1 @@ +Subproject commit ab2bf820e19d38381aee6f99e326d64b4e8ca023 diff --git a/src/monodroid/CMakeLists.txt b/src/monodroid/CMakeLists.txt index 578311eedae..734a818ca86 100644 --- a/src/monodroid/CMakeLists.txt +++ b/src/monodroid/CMakeLists.txt @@ -19,6 +19,8 @@ project( LANGUAGES CXX C ) +option(COMPILER_DIAG_COLOR "Show compiler diagnostics/errors in color" ON) + set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) @@ -345,6 +347,12 @@ set(LOCAL_COMMON_COMPILER_ARGS -Wuninitialized ) +if(COMPILER_DIAG_COLOR) + list(APPEND LOCAL_COMMON_COMPILER_ARGS + -fdiagnostics-color=always + ) +endif() + set(LOCAL_COMMON_LINKER_ARGS "") if(ANDROID) if (ENABLE_CLANG_ASAN OR ENABLE_CLANG_UBSAN) @@ -460,6 +468,7 @@ endif() set(XAMARIN_INTERNAL_API_LIB xa-internal-api${CHECKED_BUILD_INFIX}) set(XAMARIN_DEBUG_APP_HELPER_LIB xamarin-debug-app-helper${CHECKED_BUILD_INFIX}) set(XAMARIN_APP_STUB_LIB xamarin-app) +set(XAMARIN_APP_MARSHALING_LIB xamarin-app-marshaling) string(TOLOWER ${CMAKE_BUILD_TYPE} XAMARIN_MONO_ANDROID_SUFFIX) set(XAMARIN_MONO_ANDROID_LIB "mono-android${CHECKED_BUILD_INFIX}.${XAMARIN_MONO_ANDROID_SUFFIX}") @@ -494,6 +503,12 @@ if(ANDROID) ${LZ4_SOURCES} ) + if(NOT DEBUG_BUILD) + list(APPEND XAMARIN_MONODROID_SOURCES + ${SOURCES_DIR}/xamarin-android-app-context.cc + ) + endif() + if(NOT USES_LIBSTDCPP) list(APPEND XAMARIN_MONODROID_SOURCES ${BIONIC_SOURCES_DIR}/cxa_guard.cc @@ -550,10 +565,20 @@ set(XAMARIN_DEBUG_APP_HELPER_SOURCES ${SOURCES_DIR}/shared-constants.cc ) +set(XAMARIN_APP_MARSHALING_LIB_SOURCES + ${SOURCES_DIR}/xamarin-app-marshaling.cc + ) + # Build configure_file(jni/host-config.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/host-config.h) -if(NOT (ANDROID AND ENABLE_NET)) +if(MINGW OR WIN32) + list(APPEND MONOSGEN_LIB_LINK -lmonosgen-2.0.dll) +else() + list(APPEND MONOSGEN_LIB_LINK -lmonosgen-2.0) +endif() + +if(NOT (ANDROID AND ENABLE_NET6)) add_library( ${XAMARIN_INTERNAL_API_LIB} SHARED @@ -583,6 +608,25 @@ target_link_options( ) if(ANDROID) + if(NOT DEBUG_BUILD) + # The marshaling lib is used only when building for devices + add_library( + ${XAMARIN_APP_MARSHALING_LIB} + STATIC + ${XAMARIN_APP_MARSHALING_LIB_SOURCES} + ) + + target_link_libraries( + ${XAMARIN_APP_MARSHALING_LIB} + ${MONOSGEN_LIB_LINK} + ) + + target_link_libraries( + ${XAMARIN_APP_STUB_LIB} + ${XAMARIN_APP_MARSHALING_LIB} + ) + endif() + # Only Android builds need to go in separate directories, desktop builds have the same ABI set_target_properties( ${XAMARIN_APP_STUB_LIB} @@ -669,11 +713,7 @@ if(APPLE) ) endif() -if(MINGW OR WIN32) - list(APPEND LINK_LIBS -lmonosgen-2.0.dll) -else() - list(APPEND LINK_LIBS -lmonosgen-2.0) -endif() +list(APPEND LINK_LIBS ${MONOSGEN_LIB_LINK}) if(NOT MINGW AND NOT WIN32) set(DEBUG_HELPER_LINK_LIBS "-ldl") diff --git a/src/monodroid/jni/application_dso_stub.cc b/src/monodroid/jni/application_dso_stub.cc index de2ee8445fc..8ddb6aef214 100644 --- a/src/monodroid/jni/application_dso_stub.cc +++ b/src/monodroid/jni/application_dso_stub.cc @@ -144,3 +144,10 @@ DSOCacheEntry dso_cache[] = { .handle = nullptr, }, }; + +#if defined (RELEASE) +void xamarin_app_init (xamarin::android::internal::AppContext *context) +{ + // Dummy +} +#endif // def RELEASE diff --git a/src/monodroid/jni/generate-marshaling-data.cc b/src/monodroid/jni/generate-marshaling-data.cc new file mode 100644 index 00000000000..eeab1c6b77c --- /dev/null +++ b/src/monodroid/jni/generate-marshaling-data.cc @@ -0,0 +1,97 @@ +// +// Generate constants that need to be shared between the C++ and the assembler sides +// +// Files are generated in the current directory +// +// Compile with: +// +// c++ -std=c++20 generate-marshaling-data.cc -o generate-marshaling-data +// +#include +#include +#include +#include +#include + +#define __FOR_GENERATOR_ONLY +#include "xamarin-app-marshaling-private.hh" +#include "../../../external/magic_enum/include/magic_enum.hpp" + +class invalid_operation_error : public std::runtime_error +{ +public: + explicit invalid_operation_error (const std::string& what_arg) + : runtime_error (what_arg) + {} + + explicit invalid_operation_error (const char* what_arg) + : runtime_error (what_arg) + {} +}; + +constexpr char marshal_types_basename[] = "marshal-types"; +constexpr char generated_subdir_name[] = "generated"; + +namespace fs = std::filesystem; + +static void output_header (std::ofstream &output) +{ + output << "/*\n" + << " * Autogenerated by Xamarin.Android using src/monodroid/jni/generate-marshaling-data\n" + << " * Do not edit\n" + << " */\n\n"; +} + +static fs::path get_output_file_path (std::string const& dir_name, std::string const& file_name, std::string const& file_extension) +{ + fs::path fpath { dir_name }; + + if (!fs::exists (fpath)) { + fs::create_directories (fpath); + } else if (!fs::is_directory (fpath)) { + std::string message { "Filesystem entry '" + fpath.make_preferred ().string () + "' already exists and is not a directory." }; + throw invalid_operation_error (message); + } + + fpath /= file_name + file_extension; + + return fpath; +} + +static void generate_marshal_type_names_for_csharp (std::string const& extension) +{ + fs::path fpath = get_output_file_path (generated_subdir_name, marshal_types_basename, extension); + std::ofstream output { fpath }; + + output_header (output); + + output << "namespace Xamarin.Android.Build.Tasks\n" + << "{\n" + << "\tstatic class JniMarshalingAssemblerUnboxMethodNames\n" + << "\t{\n"; + + auto lowercase_string = [](std::string& s) { + std::transform ( + s.begin (), + s.end (), + s.begin (), + [](unsigned char c) { return std::tolower(c); } + ); + + return s; + }; + + for (auto type : magic_enum::enum_entries ()) { + std::string type_name { type.second }; + output << "\t\tpublic const string " << type_name << " = \"monodroid_unbox_value_" << lowercase_string (type_name) << "\";\n"; + } + + output << "\t}\n" + << "}\n\n"; +} + +int main () +{ + generate_marshal_type_names_for_csharp (".cs"); + return 0; +} diff --git a/src/monodroid/jni/generated/marshal-types.cs b/src/monodroid/jni/generated/marshal-types.cs new file mode 100644 index 00000000000..d9a8304a6e7 --- /dev/null +++ b/src/monodroid/jni/generated/marshal-types.cs @@ -0,0 +1,21 @@ +/* + * Autogenerated by Xamarin.Android using src/monodroid/jni/generate-marshaling-data + * Do not edit + */ + +namespace Xamarin.Android.Build.Tasks +{ + static class JniMarshalingAssemblerUnboxMethodNames + { + public const string Boolean = "monodroid_unbox_value_boolean"; + public const string Byte = "monodroid_unbox_value_byte"; + public const string Char = "monodroid_unbox_value_char"; + public const string Double = "monodroid_unbox_value_double"; + public const string Float = "monodroid_unbox_value_float"; + public const string Int = "monodroid_unbox_value_int"; + public const string Long = "monodroid_unbox_value_long"; + public const string Pointer = "monodroid_unbox_value_pointer"; + public const string Short = "monodroid_unbox_value_short"; + } +} + diff --git a/src/monodroid/jni/monodroid-glue-internal.hh b/src/monodroid/jni/monodroid-glue-internal.hh index ed557fe0ab1..640a69988a6 100644 --- a/src/monodroid/jni/monodroid-glue-internal.hh +++ b/src/monodroid/jni/monodroid-glue-internal.hh @@ -9,6 +9,7 @@ #include "timing.hh" #include "cpp-util.hh" #include "xxhash.hh" +#include "xamarin-app-marshaling.hh" #include #include @@ -66,6 +67,14 @@ namespace xamarin::android::internal } }; +#if defined (RELEASE) && defined (ANDROID) + class XamarinAndroidAppContext : public AppContext + { + public: + virtual MonoImage *lookup_mono_image (uint8_t *module_uuid) override final; + }; +#endif // def RELEASE && def ANDROID + class MonodroidRuntime { #if defined (NET) @@ -413,6 +422,9 @@ namespace xamarin::android::internal static void *api_dso_handle; #endif // !def NET static std::mutex dso_handle_write_lock; +#if defined (RELEASE) && defined (ANDROID) + XamarinAndroidAppContext xa_app_context; +#endif // def RELEASE && def ANDROID }; } #endif diff --git a/src/monodroid/jni/monodroid-glue.cc b/src/monodroid/jni/monodroid-glue.cc index 53ef0a1bc61..50e12039e00 100644 --- a/src/monodroid/jni/monodroid-glue.cc +++ b/src/monodroid/jni/monodroid-glue.cc @@ -873,6 +873,10 @@ MonodroidRuntime::mono_runtime_init ([[maybe_unused]] dynamic_local_string +#include +#include + +#if !defined (__FOR_GENERATOR_ONLY) +#include +#include +#include +#include +#include + +#include "platform-compat.hh" +#include "xamarin-app-marshaling.hh" + +// +// Functions here must be in the global namespace and have C linkage since they are referenced from the generated native +// assembler code. +// +extern "C" { + // + // module_uuid: points to a 16-byte array with the UUID, used to look up in typemap + // module_id: unique module ID generated at build time, used for hashing together with type_token and method_token + // + MonoObject* monodroid_invoke_managed_method (uint8_t *module_uuid, uint32_t module_id, uint32_t type_token, uint32_t method_token, void **params); + jboolean monodroid_unbox_value_boolean (MonoObject *value); + jbyte monodroid_unbox_value_byte (MonoObject *value); + jchar monodroid_unbox_value_char (MonoObject *value); + jdouble monodroid_unbox_value_double (MonoObject *value); + jfloat monodroid_unbox_value_float (MonoObject *value); + jint monodroid_unbox_value_int (MonoObject *value); + jlong monodroid_unbox_value_long (MonoObject *value); + void* monodroid_unbox_value_pointer (MonoObject *value); + jshort monodroid_unbox_value_short (MonoObject *value); +} +#endif // ndef __FOR_GENERATOR_ONLY + +namespace xamarin::android::internal +{ + enum class MarshalingTypes + { + Boolean, + Byte, + Char, + Double, + Float, + Int, + Long, + Pointer, + Short, + }; + +#if !defined (__FOR_GENERATOR_ONLY) + class XamarinAppMarshaling + { + public: + void init (AppContext *context) noexcept; + + public: + template + force_inline + static TRet unbox_value (MonoObject *value) noexcept + { + void *ret = mono_object_unbox (value); + if (ret == nullptr) [[unlikely]] { + // TODO: log + abort (); + } + + return *reinterpret_cast(ret); + } + + MonoObject* invoke_managed_method (uint8_t *module_uuid, uint32_t module_id, uint32_t type_token, uint32_t method_token, void **params) const noexcept; + + private: + AppContext *context = nullptr; + }; +#endif // ndef __FOR_GENERATOR_ONLY +} + +#endif // ndef __XAMARIN_APP_MARSHALING_HH diff --git a/src/monodroid/jni/xamarin-app-marshaling.cc b/src/monodroid/jni/xamarin-app-marshaling.cc new file mode 100644 index 00000000000..4f7ff96eab2 --- /dev/null +++ b/src/monodroid/jni/xamarin-app-marshaling.cc @@ -0,0 +1,89 @@ +#include +#include + +#include +#include + +#include "xamarin-app-marshaling-private.hh" + +using namespace xamarin::android::internal; + +static XamarinAppMarshaling xam; + +void XamarinAppMarshaling::init (AppContext *ctx) noexcept +{ + context = ctx; +} + +force_inline +MonoObject* XamarinAppMarshaling::invoke_managed_method (uint8_t *module_uuid, uint32_t module_id, uint32_t type_token, uint32_t method_token, void **params) const noexcept +{ + // TODO: caching + // TODO: should we abort() if we can't find the image, class or method? + MonoImage *image = context->lookup_mono_image (module_uuid); + MonoClass *klass = mono_class_get (image, type_token); + MonoMethod *method = mono_get_method (image, method_token, klass); + MonoObject *exc = nullptr; + MonoObject *ret = mono_runtime_invoke (method, nullptr, params, &exc); + + if (exc != nullptr) { + // TODO: call AndroidEnvironment.UnhandledException(exc) + } + + return ret; +} + +MonoObject* monodroid_invoke_managed_method (uint8_t *module_uuid, uint32_t module_id, uint32_t type_token, uint32_t method_token, void **params) +{ + return xam.invoke_managed_method (module_uuid, module_id, type_token, method_token, params); +} + +jboolean monodroid_unbox_value_boolean (MonoObject *value) +{ + return xam.unbox_value (value); +} + +jbyte monodroid_unbox_value_byte (MonoObject *value) +{ + return xam.unbox_value (value); +} + +jchar monodroid_unbox_value_char (MonoObject *value) +{ + return xam.unbox_value (value); +} + +jdouble monodroid_unbox_value_double (MonoObject *value) +{ + return xam.unbox_value (value); +} + +jfloat monodroid_unbox_value_float (MonoObject *value) +{ + return xam.unbox_value (value); +} + +jint monodroid_unbox_value_int (MonoObject *value) +{ + return xam.unbox_value (value); +} + +jlong monodroid_unbox_value_long (MonoObject *value) +{ + return xam.unbox_value (value); +} + +void* monodroid_unbox_value_pointer (MonoObject *value) +{ + return xam.unbox_value (value); +} + +jshort monodroid_unbox_value_short (MonoObject *value) +{ + return xam.unbox_value (value); +} + +void xamarin_app_init (AppContext *context) +{ + xam.init (context); +} diff --git a/src/monodroid/jni/xamarin-app-marshaling.hh b/src/monodroid/jni/xamarin-app-marshaling.hh new file mode 100644 index 00000000000..b1f561a7333 --- /dev/null +++ b/src/monodroid/jni/xamarin-app-marshaling.hh @@ -0,0 +1,25 @@ +// Dear Emacs, this is a -*- C++ -*- header +#if !defined (__XAMARIN_APP_MARSHALING_HH) +#define __XAMARIN_APP_MARSHALING_HH + +#include +#include + +#include +#include + +namespace xamarin::android::internal +{ + class AppContext + { + public: + virtual MonoImage *lookup_mono_image (uint8_t *module_uuid) = 0; + }; +} + +// +// Called by libmonodroid.so on init +// +MONO_API_EXPORT void xamarin_app_init (xamarin::android::internal::AppContext *ctx); + +#endif // ndef __XAMARIN_APP_MARSHALING_HH diff --git a/src/monodroid/jni/xamarin-app.hh b/src/monodroid/jni/xamarin-app.hh index a13e64d61ef..863a97d3fd9 100644 --- a/src/monodroid/jni/xamarin-app.hh +++ b/src/monodroid/jni/xamarin-app.hh @@ -9,6 +9,10 @@ #include "monodroid.h" #include "xxhash.hh" +#if defined (RELEASE) +#include "xamarin-app-marshaling.hh" +#endif // def RELEASE + static constexpr uint64_t FORMAT_TAG = 0x015E6972616D58; static constexpr uint32_t COMPRESSED_DATA_MAGIC = 0x5A4C4158; // 'XALZ', little-endian static constexpr uint32_t ASSEMBLY_STORE_MAGIC = 0x41424158; // 'XABA', little-endian @@ -258,4 +262,7 @@ MONO_API MONO_API_EXPORT AssemblyStoreSingleAssemblyRuntimeData assembly_store_b MONO_API MONO_API_EXPORT AssemblyStoreRuntimeData assembly_stores[]; MONO_API MONO_API_EXPORT DSOCacheEntry dso_cache[]; + +MONO_API_EXPORT void xamarin_app_init (); + #endif // __XAMARIN_ANDROID_TYPEMAP_H diff --git a/tools/mgen/Constants.cs.in b/tools/mgen/Constants.cs.in new file mode 100644 index 00000000000..235cea1b60b --- /dev/null +++ b/tools/mgen/Constants.cs.in @@ -0,0 +1,14 @@ +using System; +using System.IO; + +namespace Xamarin.Android.Tools +{ + static class Constants + { + const string DotNetPath = @"@DotNetPreviewPath@"; + const string DotnetRuntimeVersion = "@MicrosoftNETCoreAppRefPackageVersion@"; + + public static readonly string FrameworkLibsDir = Path.Combine (DotNetPath, "packs", "Microsoft.NETCore.App.Runtime.Mono.android-arm64", DotnetRuntimeVersion, "runtimes", "android-arm64", "lib", "net6.0"); + public static readonly string FrameworkArchLibsDir = Path.Combine (DotNetPath, "packs", "Microsoft.NETCore.App.Runtime.Mono.android-arm64", DotnetRuntimeVersion, "runtimes", "android-arm64", "native"); + } +} diff --git a/tools/mgen/build.sh b/tools/mgen/build.sh new file mode 100755 index 00000000000..fcef9230617 --- /dev/null +++ b/tools/mgen/build.sh @@ -0,0 +1,5 @@ +#!/bin/bash -e +DOTNET="$(type -fP dotnet-preview || true)" +DOTNET="${DOTNET:-dotnet}" + +exec "${DOTNET}" build "$@" diff --git a/tools/mgen/main.cs b/tools/mgen/main.cs new file mode 100644 index 00000000000..13873cf6b0e --- /dev/null +++ b/tools/mgen/main.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using Mono.Cecil; + +using Java.Interop.Tools.Cecil; +using Java.Interop.Tools.JavaCallableWrappers; + +namespace Xamarin.Android.Tools +{ + class mgen + { + static void Usage () + { + Console.WriteLine ("Usage: mgen ASSEMBLY_PATH [ASSEMBLY_PATH...]"); + Console.WriteLine (); + } + + static void Die (string message, bool withUsage) + { + Console.WriteLine (message); + if (withUsage) { + Console.WriteLine (); + Usage (); + } + Environment.Exit (1); + } + + static void Logger (TraceLevel level, string message) + { + Console.WriteLine ($"[level] {message}"); + } + + static void ProcessAssemblies (string[] assemblyPaths) + { + var resolver = new DirectoryAssemblyResolver (Logger, loadDebugSymbols: true); + var assemblyDirs = new HashSet (StringComparer.OrdinalIgnoreCase); + foreach (string assemblyPath in assemblyPaths) { + string dir = Path.GetDirectoryName (assemblyPath); + if (assemblyDirs.Contains (dir)) { + continue; + } + + assemblyDirs.Add (dir); + resolver.SearchDirectories.Add (dir); + } + resolver.SearchDirectories.Add (Constants.FrameworkLibsDir); + resolver.SearchDirectories.Add (Constants.FrameworkArchLibsDir); + + var tdCache = new TypeDefinitionCache (); + var scanner = new JavaTypeScanner (Logger, tdCache); + List types = scanner.GetJavaTypes (assemblyPaths, resolver); + foreach (TypeDefinition type in types) { + if (JavaTypeScanner.ShouldSkipJavaCallableWrapperGeneration (type, tdCache)) { + continue; + } + Console.WriteLine ($"Interesting type: {type.FullName}"); + } + } + + static int Main (string[] args) + { + if (args.Length == 0) { + Die ("Not enough arguments", withUsage: true); + } + ProcessAssemblies (args); + + return 0; + } + } +} diff --git a/tools/mgen/mgen.csproj b/tools/mgen/mgen.csproj new file mode 100644 index 00000000000..4f3412acdc6 --- /dev/null +++ b/tools/mgen/mgen.csproj @@ -0,0 +1,94 @@ + + + + Exe + net6.0 + disable + true + enable + HAVE_CECIL + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Constants.cs.in + $(IntermediateOutputPath)/Constants.cs + + + + + + + + + + + + ../../external/Java.Interop/src + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/mgen/run.sh b/tools/mgen/run.sh new file mode 100755 index 00000000000..43ce0258c47 --- /dev/null +++ b/tools/mgen/run.sh @@ -0,0 +1,2 @@ +#!/bin/bash -e +exec bin/Debug/net6.0/mgen "$@" From dcf5ebc583ea143453165a0e3f4a5fb21a230f1d Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 5 May 2022 22:21:24 +0200 Subject: [PATCH 02/14] [WIP] MonoImage loader class Source modified for my test app, won't work for anyone else --- ...pplicationConfigNativeAssemblyGenerator.cs | 26 ++++ .../LlvmIrGenerator/LlvmIrGenerator.cs | 17 +-- ...peMappingReleaseNativeAssemblyGenerator.cs | 2 +- src/monodroid/jni/application_dso_stub.cc | 16 ++- src/monodroid/jni/embedded-assemblies.cc | 114 +++++------------ src/monodroid/jni/embedded-assemblies.hh | 33 +++-- src/monodroid/jni/mono-image-loader.hh | 115 ++++++++++++++++++ src/monodroid/jni/mono_android_Runtime.h | 3 + src/monodroid/jni/monodroid-glue.cc | 29 ++++- src/monodroid/jni/search.hh | 48 ++++++++ src/monodroid/jni/xamarin-app.hh | 5 + 11 files changed, 300 insertions(+), 108 deletions(-) create mode 100644 src/monodroid/jni/mono-image-loader.hh create mode 100644 src/monodroid/jni/search.hh diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs index ba2f35e8bad..83906073fcc 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs @@ -150,6 +150,7 @@ sealed class XamarinAndroidBundledAssembly StructureInfo? xamarinAndroidBundledAssemblyStructureInfo; StructureInfo assemblyStoreSingleAssemblyRuntimeDataStructureinfo; StructureInfo assemblyStoreRuntimeDataStructureInfo; + StructureInfo monoImage; public bool IsBundledApp { get; set; } public bool UsesMonoAOT { get; set; } @@ -305,6 +306,7 @@ protected override void MapStructures (LlvmIrGenerator generator) assemblyStoreRuntimeDataStructureInfo = generator.MapStructure (); xamarinAndroidBundledAssemblyStructureInfo = generator.MapStructure (); dsoCacheEntryStructureInfo = generator.MapStructure (); + monoImage = generator.MapStructure (); } protected override void Write (LlvmIrGenerator generator) @@ -320,6 +322,30 @@ protected override void Write (LlvmIrGenerator generator) WriteDSOCache (generator); WriteBundledAssemblies (generator); WriteAssemblyStoreAssemblies (generator); + + WriteAssemblyImageCache (generator); + } + + void WriteAssemblyImageCache (LlvmIrGenerator generator) + { + generator.WriteStructureArray (monoImage, (ulong)NumberOfAssembliesInApk, "assembly_image_cache", isArrayOfPointers: true); + + if (generator.Is64Bit) { + WriteHashes (); + } else { + WriteHashes (); + } + + void WriteHashes () where T: struct + { + // TODO: hash all assembly names here + var hashes = new List (); + for (int i = 0; i < NumberOfAssembliesInApk; i++) { + hashes.Add (default(T)); + } + + generator.WriteArray (hashes, LlvmIrVariableOptions.GlobalConstant, "assembly_image_cache_index"); + } } void WriteAssemblyStoreAssemblies (LlvmIrGenerator generator) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs index ec30d96f62a..c639fb1742d 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs @@ -423,11 +423,12 @@ bool WriteStructureArrayStart (StructureInfo info, IList (StructureInfo info, string? symbolName, ulong count, bool named, bool skipFinalComment = false, TextWriter? output = null) + void WriteStructureArrayEnd (StructureInfo info, string? symbolName, ulong count, bool named, bool skipFinalComment = false, TextWriter? output = null, bool isArrayOfPointers = false) { output = EnsureOutput (output); - output.Write ($", align {GetAggregateAlignment (info.MaxFieldAlignment, info.Size * count)}"); + int alignment = isArrayOfPointers ? PointerSize : GetAggregateAlignment (info.MaxFieldAlignment, info.Size * count); + output.Write ($", align {alignment}"); if (named && !skipFinalComment) { WriteEOL ($"end of '{symbolName!}' array", output); } else { @@ -438,21 +439,21 @@ void WriteStructureArrayEnd (StructureInfo info, string? symbolName, ulong /// /// Writes an array of zero-initialized entries. specifies the symbol attributes (visibility, writeability etc) /// - public void WriteStructureArray (StructureInfo info, ulong count, LlvmIrVariableOptions options, string? symbolName = null, bool writeFieldComment = true, string? initialComment = null) + public void WriteStructureArray (StructureInfo info, ulong count, LlvmIrVariableOptions options, string? symbolName = null, bool writeFieldComment = true, string? initialComment = null, bool isArrayOfPointers = false) { bool named = WriteStructureArrayStart (info, null, options, symbolName, initialComment); + string pointerAsterisk = isArrayOfPointers ? "*" : String.Empty; + Output.Write ($"[{count} x %struct.{info.Name}{pointerAsterisk}] zeroinitializer"); - Output.Write ($"[{count} x %struct.{info.Name}] zeroinitializer"); - - WriteStructureArrayEnd (info, symbolName, (ulong)count, named, skipFinalComment: true); + WriteStructureArrayEnd (info, symbolName, (ulong)count, named, skipFinalComment: true, isArrayOfPointers: isArrayOfPointers); } /// /// Writes an array of zero-initialized entries. The array will be generated as a local, writable symbol. /// - public void WriteStructureArray (StructureInfo info, ulong count, string? symbolName = null, bool writeFieldComment = true, string? initialComment = null) + public void WriteStructureArray (StructureInfo info, ulong count, string? symbolName = null, bool writeFieldComment = true, string? initialComment = null, bool isArrayOfPointers = false) { - WriteStructureArray (info, count, LlvmIrVariableOptions.Default, symbolName, writeFieldComment, initialComment); + WriteStructureArray (info, count, LlvmIrVariableOptions.Default, symbolName, writeFieldComment, initialComment, isArrayOfPointers); } /// diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGenerator.cs index 7804dfba5a7..40c923a6e56 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGenerator.cs @@ -67,7 +67,7 @@ public int Compare (StructureInstance a, StructureInstancedata_size, "", assembly_data, assembly_data_size); } -#if defined (NET) -MonoAssembly* -EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, MonoAssemblyLoadContextGCHandle alc_gchandle, [[maybe_unused]] MonoError *error) -{ - auto loader = [&] (uint8_t *assembly_data, uint32_t assembly_data_size, const char *name) -> MonoImage* { - return mono_image_open_from_data_alc ( - alc_gchandle, - reinterpret_cast(assembly_data), - assembly_data_size, - 0 /* need_copy */, - nullptr /* status */, - name - ); - }; - - return open_from_bundles (aname, loader, false /* ref_only */); -} -#endif // def NET - -MonoAssembly* -EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, bool ref_only) -{ - auto loader = [&] (uint8_t *assembly_data, uint32_t assembly_data_size, const char *name) -> MonoImage* { - return mono_image_open_from_data_with_name ( - reinterpret_cast(assembly_data), - assembly_data_size, - 0, - nullptr, - ref_only, - name - ); - }; - - return open_from_bundles (aname, loader, ref_only); -} - template force_inline void EmbeddedAssemblies::map_runtime_file (XamarinAndroidBundledAssembly& file) noexcept @@ -239,12 +205,13 @@ EmbeddedAssemblies::map_debug_data (XamarinAndroidBundledAssembly& file) noexcep map_runtime_file (file); } +template force_inline MonoAssembly* EmbeddedAssemblies::load_bundled_assembly ( XamarinAndroidBundledAssembly& assembly, dynamic_local_string const& name, dynamic_local_string const& abi_name, - std::function loader, + TLoaderData loader_data, bool ref_only) noexcept { if (assembly.name == nullptr || assembly.name[0] == '\0') { @@ -267,7 +234,7 @@ EmbeddedAssemblies::load_bundled_assembly ( uint32_t assembly_data_size; get_assembly_data (assembly, assembly_data, assembly_data_size); - MonoImage *image = loader (assembly_data, assembly_data_size, name.get ()); + MonoImage *image = MonoImageLoader::load (name, loader_data, assembly_data, assembly_data_size); if (image == nullptr) { return nullptr; } @@ -311,8 +278,9 @@ EmbeddedAssemblies::load_bundled_assembly ( return a; } +template force_inline MonoAssembly* -EmbeddedAssemblies::individual_assemblies_open_from_bundles (dynamic_local_string& name, std::function loader, bool ref_only) noexcept +EmbeddedAssemblies::individual_assemblies_open_from_bundles (dynamic_local_string& name, TLoaderData loader_data, bool ref_only) noexcept { if (!utils.ends_with (name, SharedConstants::DLL_EXTENSION)) { name.append (SharedConstants::DLL_EXTENSION); @@ -329,7 +297,7 @@ EmbeddedAssemblies::individual_assemblies_open_from_bundles (dynamic_local_strin MonoAssembly *a = nullptr; for (size_t i = 0; i < application_config.number_of_assemblies_in_apk; i++) { - a = load_bundled_assembly (bundled_assemblies [i], name, abi_name, loader, ref_only); + a = load_bundled_assembly (bundled_assemblies [i], name, abi_name, loader_data, ref_only); if (a != nullptr) { return a; } @@ -337,7 +305,7 @@ EmbeddedAssemblies::individual_assemblies_open_from_bundles (dynamic_local_strin if (extra_bundled_assemblies != nullptr) { for (XamarinAndroidBundledAssembly& assembly : *extra_bundled_assemblies) { - a = load_bundled_assembly (assembly, name, abi_name, loader, ref_only); + a = load_bundled_assembly (assembly, name, abi_name, loader_data, ref_only); if (a != nullptr) { return a; } @@ -377,8 +345,9 @@ EmbeddedAssemblies::find_assembly_store_entry ([[maybe_unused]] hash_t hash, [[m return nullptr; } +template force_inline MonoAssembly* -EmbeddedAssemblies::assembly_store_open_from_bundles (dynamic_local_string& name, std::function loader, bool ref_only) noexcept +EmbeddedAssemblies::assembly_store_open_from_bundles (dynamic_local_string& name, TLoaderData loader_data, bool ref_only) noexcept { size_t len = name.length (); bool have_dll_ext = utils.ends_with (name, SharedConstants::DLL_EXTENSION); @@ -411,6 +380,7 @@ EmbeddedAssemblies::assembly_store_open_from_bundles (dynamic_local_stringstore_id]; if (hash_entry->local_store_index >= rd.assembly_count) { log_fatal (LOG_ASSEMBLY, "Invalid index %u into local store assembly descriptor array", hash_entry->local_store_index); + abort (); } AssemblyStoreAssemblyDescriptor *bba = &rd.assemblies[hash_entry->local_store_index]; @@ -459,7 +429,7 @@ EmbeddedAssemblies::assembly_store_open_from_bundles (dynamic_local_string loader, bool ref_only) +template +force_inline MonoAssembly* +EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, TLoaderData loader_data, bool ref_only) noexcept { const char *culture = mono_assembly_name_get_culture (aname); const char *asmname = mono_assembly_name_get_name (aname); @@ -497,9 +468,9 @@ EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, std::function(n); - - while (right - left > 1) { - ssize_t middle = (left + right) >> 1; - if (arr[middle] < key) { - left = middle; - } else { - right = middle; - } - } - - return arr[right] == key ? right : -1; -} - -force_inline ptrdiff_t -EmbeddedAssemblies::binary_search_branchless (hash_t x, const hash_t *arr, uint32_t len) noexcept -{ - const hash_t *base = arr; - while (len > 1) { - uint32_t half = len >> 1; - // __builtin_prefetch(&base[(len - half) / 2]); - // __builtin_prefetch(&base[half + (len - half) / 2]); - base = (base[half] < x ? &base[half] : base); - len -= half; - } - - //return *(base + (*base < x)); - ptrdiff_t ret = (base + (*base < x)) - arr; - return arr[ret] == x ? ret : -1; -} - #if defined (RELEASE) && defined (ANDROID) force_inline const TypeMapModuleEntry* EmbeddedAssemblies::binary_search (uint32_t key, const TypeMapModuleEntry *arr, uint32_t n) noexcept @@ -719,7 +663,7 @@ EmbeddedAssemblies::typemap_java_to_managed (hash_t hash, const MonoString *java // In microbrenchmarks, `binary_search_branchless` is faster than `binary_search` but in "real" application tests, // the simple version appears to yield faster startup... Leaving both for now, for further investigation and // potential optimizations - ssize_t idx = binary_search (hash, map_java_hashes, java_type_count); + ssize_t idx = Search::binary_search (hash, map_java_hashes, java_type_count); //ptrdiff_t idx = binary_search_branchless (hash, map_java_hashes, java_type_count); TypeMapJava const* java_entry = idx >= 0 ? &map_java[idx] : nullptr; diff --git a/src/monodroid/jni/embedded-assemblies.hh b/src/monodroid/jni/embedded-assemblies.hh index 5e59a7cb1a0..92e54e1328f 100644 --- a/src/monodroid/jni/embedded-assemblies.hh +++ b/src/monodroid/jni/embedded-assemblies.hh @@ -29,6 +29,7 @@ #include "strings.hh" #include "xamarin-app.hh" #include "cpp-util.hh" +#include "mono-image-loader.hh" #include "shared-constants.hh" #include "xxhash.hh" @@ -52,8 +53,18 @@ namespace xamarin::android::internal { a.data (); requires std::same_as; }; + + template + concept LoaderData = requires (T a) { + requires std::same_as +#if defined (NET6) + || std::same_as +#endif + ; + }; #else #define ByteArrayContainer class +#define LoaderData typename #endif class EmbeddedAssemblies final @@ -171,23 +182,27 @@ namespace xamarin::android::internal { STATIC_IN_ANDROID_RELEASE MonoReflectionType* typemap_java_to_managed (hash_t hash, const MonoString *java_type_name) noexcept; 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 (NET) - MonoAssembly* open_from_bundles (MonoAssemblyName* aname, MonoAssemblyLoadContextGCHandle alc_gchandle, MonoError *error); -#endif // def NET - MonoAssembly* open_from_bundles (MonoAssemblyName* aname, bool ref_only); - MonoAssembly* individual_assemblies_open_from_bundles (dynamic_local_string& name, std::function loader, bool ref_only) noexcept; - MonoAssembly* assembly_store_open_from_bundles (dynamic_local_string& name, std::function loader, bool ref_only) noexcept; - MonoAssembly* open_from_bundles (MonoAssemblyName* aname, std::function loader, bool ref_only); + + template + MonoAssembly* individual_assemblies_open_from_bundles (dynamic_local_string& name, TLoaderData loader_data, bool ref_only) noexcept; + + template + MonoAssembly* assembly_store_open_from_bundles (dynamic_local_string& name, TLoaderData loader_data, bool ref_only) noexcept; + + template + MonoAssembly* open_from_bundles (MonoAssemblyName* aname, TLoaderData loader_data, bool ref_only) noexcept; template void map_runtime_file (XamarinAndroidBundledAssembly& file) noexcept; void map_assembly (XamarinAndroidBundledAssembly& file) noexcept; void map_debug_data (XamarinAndroidBundledAssembly& file) noexcept; + + template MonoAssembly* load_bundled_assembly ( XamarinAndroidBundledAssembly& assembly, dynamic_local_string const& name, dynamic_local_string const& abi_name, - std::function loader, + TLoaderData loader_data, bool ref_only) noexcept; #if defined (DEBUG) || !defined (ANDROID) @@ -279,8 +294,6 @@ namespace xamarin::android::internal { template static const Entry* binary_search (const Key *key, const Entry *base, size_t nmemb, size_t extra_size = 0) noexcept; - static ssize_t binary_search (hash_t key, const hash_t *arr, size_t n) noexcept; - static ptrdiff_t binary_search_branchless (hash_t x, const hash_t *arr, uint32_t len) noexcept; #if defined (DEBUG) || !defined (ANDROID) static int compare_type_name (const char *type_name, const TypeMapEntry *entry) noexcept; diff --git a/src/monodroid/jni/mono-image-loader.hh b/src/monodroid/jni/mono-image-loader.hh new file mode 100644 index 00000000000..8ccef3767f7 --- /dev/null +++ b/src/monodroid/jni/mono-image-loader.hh @@ -0,0 +1,115 @@ +// Dear Emacs, this is a -*- C++ -*- header +#if !defined (__MONO_IMAGE_LOADER_HH) +#define __MONO_IMAGE_LOADER_HH + +#include + +#include +#if defined (NET6) +#include +#endif +#include + +#include "platform-compat.hh" +#include "xamarin-app.hh" +#include "xxhash.hh" +#include "search.hh" +#include "strings.hh" + +namespace xamarin::android::internal { + enum class MonoImageLoaderContext + { + ALC, + AppDomain, + }; + + class MonoImageLoader final + { + public: +#if defined (RELEASE) && defined (ANDROID) && defined (NET6) + force_inline static MonoImage* get_from_index (size_t index) noexcept + { + return nullptr; + } + + force_inline static MonoImage* get_with_hash (hash_t hash) noexcept + { + ssize_t index = find_index (hash); + if (index < 0) { + return nullptr; + } + + return get_from_index (static_cast(index)); + } +#endif // def RELEASE && def ANDROID && def NET6 + +#if defined (NET6) + force_inline static MonoImage* load (dynamic_local_string const& name, MonoAssemblyLoadContextGCHandle alc_gchandle, hash_t name_hash, uint8_t *assembly_data, uint32_t assembly_data_size) noexcept + { + MonoImage *image = mono_image_open_from_data_alc ( + alc_gchandle, + reinterpret_cast(assembly_data), + assembly_data_size, + 0 /* need_copy */, + nullptr /* status */, + name.get () + ); + + return stash_and_return (image, name_hash); + } + + force_inline static MonoImage* load (dynamic_local_string const& name, MonoAssemblyLoadContextGCHandle alc_gchandle, uint8_t *assembly_data, uint32_t assembly_data_size) noexcept + { + return load (name, alc_gchandle, xxhash::hash (name.get (), name.length ()), assembly_data, assembly_data_size); + } +#endif + + force_inline static MonoImage* load (dynamic_local_string const& name, bool ref_only, hash_t name_hash, uint8_t *assembly_data, uint32_t assembly_data_size) noexcept + { + MonoImage *image = mono_image_open_from_data_with_name ( + reinterpret_cast(assembly_data), + assembly_data_size, + 0, + nullptr, + ref_only, + name.get () + ); + + return stash_and_return (image, name_hash); + } + + force_inline static MonoImage* load (dynamic_local_string const& name, bool ref_only, uint8_t *assembly_data, uint32_t assembly_data_size) noexcept + { + return load (name, ref_only, xxhash::hash (name.get (), name.length ()), assembly_data, assembly_data_size); + } + + private: + force_inline static ssize_t find_index (hash_t hash) noexcept + { +#if defined (RELEASE) && defined (ANDROID) + return Search::binary_search (hash, assembly_image_cache_index, application_config.number_of_assemblies_in_apk); +#else + return 0; +#endif // def RELEASE && def ANDROID + } + + force_inline static MonoImage* stash_and_return (MonoImage *image, [[maybe_unused]] hash_t hash) noexcept + { +#if defined (RELEASE) && defined (ANDROID) && defined (NET6) + ssize_t index = find_index (hash); + if (index < 0) { + // TODO: Warn? + return image; + } + + // We don't need to worry about locking here. Even if we're overwriting an entry just set from another + // thread, the image pointer is going to be the same (at least currently, it will change when we have + // support for unloadable Assembly Load Contexts) and the actual write operation to the destination is + // atomic + assembly_image_cache[index] = image; +#endif // def RELEASE && def ANDROID && def NET6 + return image; + } + }; +} +#endif // ndef __MONO_IMAGE_LOADER_HH diff --git a/src/monodroid/jni/mono_android_Runtime.h b/src/monodroid/jni/mono_android_Runtime.h index 2dfa0a6cf00..78e4e1aae68 100644 --- a/src/monodroid/jni/mono_android_Runtime.h +++ b/src/monodroid/jni/mono_android_Runtime.h @@ -87,6 +87,9 @@ JNIEXPORT void JNICALL Java_mono_android_Runtime_propagateUncaughtException JNIEXPORT void JNICALL Java_mono_android_Runtime_dumpTimingData (JNIEnv *, jclass); +JNIEXPORT void JNICALL Java_helloandroid_MainActivity_n_1onCreate__Landroid_os_Bundle_2 + (JNIEnv *, jclass, jobject); + #ifdef __cplusplus } #endif diff --git a/src/monodroid/jni/monodroid-glue.cc b/src/monodroid/jni/monodroid-glue.cc index 50e12039e00..9fd974698b8 100644 --- a/src/monodroid/jni/monodroid-glue.cc +++ b/src/monodroid/jni/monodroid-glue.cc @@ -875,7 +875,7 @@ MonodroidRuntime::mono_runtime_init ([[maybe_unused]] dynamic_local_stringstart_event (TimingEventKind::RuntimeRegister); } + const char *mt_ptr = env->GetStringUTFChars (managedType, nullptr); + log_info (LOG_DEFAULT, "[TESTING] Registering managed type: '%s'", mt_ptr); + bool ignore = strcmp (mt_ptr, "HelloAndroid.MainActivity, HelloAndroid") == 0; + env->ReleaseStringUTFChars (managedType, mt_ptr); + + if (ignore) { + log_info (LOG_DEFAULT, "[TESTING] This type's registration is ignored"); + return; + } + jsize managedType_len = env->GetStringLength (managedType); const jchar *managedType_ptr = env->GetStringChars (managedType, nullptr); int methods_len = env->GetStringLength (methods); @@ -2624,3 +2634,20 @@ JNICALL Java_mono_android_Runtime_propagateUncaughtException (JNIEnv *env, [[may monodroidRuntime.propagate_uncaught_exception (domain, env, javaThread, javaException); #endif // ndef NET } + +#if defined (RELEASE) && defined (ANDROID) && defined (NET6) +static void (*android_app_activity_on_create_bundle) (JNIEnv *env, jclass klass, jobject savedInstanceState) = nullptr; + +JNIEXPORT void +JNICALL Java_helloandroid_MainActivity_n_1onCreate__Landroid_os_Bundle_2 (JNIEnv *env, jclass klass, jobject savedInstanceState) +{ + log_info (LOG_DEFAULT, "%s (%p, %p, %p)", __PRETTY_FUNCTION__, env, klass, savedInstanceState); + + if (android_app_activity_on_create_bundle == nullptr) { + // MonoImage *image = mono_load; + } + + // TODO: obtain the function pointer for the symbol below + android_app_activity_on_create_bundle (env, klass, savedInstanceState); +} +#endif // def RELEASE && def ANDROID && def NET6 diff --git a/src/monodroid/jni/search.hh b/src/monodroid/jni/search.hh new file mode 100644 index 00000000000..554126d2bee --- /dev/null +++ b/src/monodroid/jni/search.hh @@ -0,0 +1,48 @@ +// Dear Emacs, this is a -*- C++ -*- header +#if !defined (__SEARCH_HH) +#define __SEARCH_HH + +#include + +#include "platform-compat.hh" +#include "xxhash.hh" + +namespace xamarin::android::internal { + class Search final + { + public: + force_inline static ssize_t binary_search (hash_t key, const hash_t *arr, size_t n) noexcept + { + ssize_t left = -1; + ssize_t right = static_cast(n); + + while (right - left > 1) { + ssize_t middle = (left + right) >> 1; + if (arr[middle] < key) { + left = middle; + } else { + right = middle; + } + } + + return arr[right] == key ? right : -1; + } + + force_inline static ptrdiff_t binary_search_branchless (hash_t x, const hash_t *arr, uint32_t len) noexcept + { + const hash_t *base = arr; + while (len > 1) { + uint32_t half = len >> 1; + // __builtin_prefetch(&base[(len - half) / 2]); + // __builtin_prefetch(&base[half + (len - half) / 2]); + base = (base[half] < x ? &base[half] : base); + len -= half; + } + + //return *(base + (*base < x)); + ptrdiff_t ret = (base + (*base < x)) - arr; + return arr[ret] == x ? ret : -1; + } + }; +} +#endif // ndef __SEARCH_HH diff --git a/src/monodroid/jni/xamarin-app.hh b/src/monodroid/jni/xamarin-app.hh index 863a97d3fd9..ea911b4225a 100644 --- a/src/monodroid/jni/xamarin-app.hh +++ b/src/monodroid/jni/xamarin-app.hh @@ -263,6 +263,11 @@ MONO_API MONO_API_EXPORT AssemblyStoreRuntimeData assembly_stores[]; MONO_API MONO_API_EXPORT DSOCacheEntry dso_cache[]; +#if defined (RELEASE) && defined (ANDROID) +MONO_API MONO_API_EXPORT MonoImage* assembly_image_cache[]; +MONO_API MONO_API_EXPORT xamarin::android::hash_t assembly_image_cache_index[]; + MONO_API_EXPORT void xamarin_app_init (); +#endif #endif // __XAMARIN_ANDROID_TYPEMAP_H From 83515efb6a55e545e9bbb458b0bc3db13ae575e1 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Fri, 6 May 2022 18:28:12 +0200 Subject: [PATCH 03/14] Don't silently cast `MonoError*` to `bool`, doh --- src/monodroid/jni/embedded-assemblies.cc | 15 +++++++++------ src/monodroid/jni/embedded-assemblies.hh | 2 +- src/monodroid/jni/mono-image-loader.hh | 17 ++++++++++++----- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/monodroid/jni/embedded-assemblies.cc b/src/monodroid/jni/embedded-assemblies.cc index a528bda16f2..9448d77d5cf 100644 --- a/src/monodroid/jni/embedded-assemblies.cc +++ b/src/monodroid/jni/embedded-assemblies.cc @@ -267,7 +267,8 @@ EmbeddedAssemblies::load_bundled_assembly ( MonoImageOpenStatus status; MonoAssembly *a = mono_assembly_load_from_full (image, name.get (), &status, ref_only); - if (a == nullptr) { + if (a == nullptr || status != MonoImageOpenStatus::MONO_IMAGE_OK) { + log_warn (LOG_ASSEMBLY, "Failed to load managed assembly '%s'. %s", name.get (), mono_image_strerror (status)); return nullptr; } @@ -442,7 +443,8 @@ EmbeddedAssemblies::assembly_store_open_from_bundles (dynamic_local_string force_inline MonoAssembly* -EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, TLoaderData loader_data, bool ref_only) noexcept +EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, TLoaderData loader_data, [[maybe_unused]] MonoError *error, bool ref_only) noexcept { const char *culture = mono_assembly_name_get_culture (aname); const char *asmname = mono_assembly_name_get_name (aname); @@ -484,7 +486,8 @@ EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, TLoaderData load MonoAssembly* EmbeddedAssemblies::open_from_bundles (MonoAssemblyLoadContextGCHandle alc_gchandle, MonoAssemblyName *aname, [[maybe_unused]] char **assemblies_path, [[maybe_unused]] void *user_data, MonoError *error) { - return embeddedAssemblies.open_from_bundles (aname, alc_gchandle, error); + constexpr bool ref_only = false; + return embeddedAssemblies.open_from_bundles (aname, alc_gchandle, error, ref_only); } #else // def NET @@ -496,7 +499,7 @@ EmbeddedAssemblies::open_from_bundles_refonly (MonoAssemblyName *aname, [[maybe_ { constexpr bool ref_only = false; - return embeddedAssemblies.open_from_bundles (aname, ref_only /* loader_data */, ref_only); + return embeddedAssemblies.open_from_bundles (aname, ref_only /* loader_data */, nullptr /* error */, ref_only); } #endif // ndef NET @@ -505,7 +508,7 @@ EmbeddedAssemblies::open_from_bundles_full (MonoAssemblyName *aname, [[maybe_unu { constexpr bool ref_only = false; - return embeddedAssemblies.open_from_bundles (aname, ref_only /* loader_data */, ref_only); + return embeddedAssemblies.open_from_bundles (aname, ref_only /* loader_data */, nullptr /* error */, ref_only); } void diff --git a/src/monodroid/jni/embedded-assemblies.hh b/src/monodroid/jni/embedded-assemblies.hh index 92e54e1328f..5f9ecb50dca 100644 --- a/src/monodroid/jni/embedded-assemblies.hh +++ b/src/monodroid/jni/embedded-assemblies.hh @@ -190,7 +190,7 @@ namespace xamarin::android::internal { MonoAssembly* assembly_store_open_from_bundles (dynamic_local_string& name, TLoaderData loader_data, bool ref_only) noexcept; template - MonoAssembly* open_from_bundles (MonoAssemblyName* aname, TLoaderData loader_data, bool ref_only) noexcept; + MonoAssembly* open_from_bundles (MonoAssemblyName* aname, TLoaderData loader_data, MonoError *error, bool ref_only) noexcept; template void map_runtime_file (XamarinAndroidBundledAssembly& file) noexcept; diff --git a/src/monodroid/jni/mono-image-loader.hh b/src/monodroid/jni/mono-image-loader.hh index 8ccef3767f7..0ff8d2ac56e 100644 --- a/src/monodroid/jni/mono-image-loader.hh +++ b/src/monodroid/jni/mono-image-loader.hh @@ -46,16 +46,17 @@ namespace xamarin::android::internal { #if defined (NET6) force_inline static MonoImage* load (dynamic_local_string const& name, MonoAssemblyLoadContextGCHandle alc_gchandle, hash_t name_hash, uint8_t *assembly_data, uint32_t assembly_data_size) noexcept { + MonoImageOpenStatus status; MonoImage *image = mono_image_open_from_data_alc ( alc_gchandle, reinterpret_cast(assembly_data), assembly_data_size, 0 /* need_copy */, - nullptr /* status */, + &status, name.get () ); - return stash_and_return (image, name_hash); + return stash_and_return (image, status, name_hash); } force_inline static MonoImage* load (dynamic_local_string const& name, MonoAssemblyLoadContextGCHandle alc_gchandle, uint8_t *assembly_data, uint32_t assembly_data_size) noexcept @@ -66,16 +67,17 @@ namespace xamarin::android::internal { force_inline static MonoImage* load (dynamic_local_string const& name, bool ref_only, hash_t name_hash, uint8_t *assembly_data, uint32_t assembly_data_size) noexcept { + MonoImageOpenStatus status; MonoImage *image = mono_image_open_from_data_with_name ( reinterpret_cast(assembly_data), assembly_data_size, 0, - nullptr, + &status, ref_only, name.get () ); - return stash_and_return (image, name_hash); + return stash_and_return (image, status, name_hash); } force_inline static MonoImage* load (dynamic_local_string const& name, bool ref_only, uint8_t *assembly_data, uint32_t assembly_data_size) noexcept @@ -93,8 +95,13 @@ namespace xamarin::android::internal { #endif // def RELEASE && def ANDROID } - force_inline static MonoImage* stash_and_return (MonoImage *image, [[maybe_unused]] hash_t hash) noexcept + force_inline static MonoImage* stash_and_return (MonoImage *image, MonoImageOpenStatus status, [[maybe_unused]] hash_t hash) noexcept { + if (image == nullptr || status != MonoImageOpenStatus::MONO_IMAGE_OK) { + log_warn (LOG_ASSEMBLY, "Failed to open assembly image for '%s'. %s", mono_image_strerror (status)); + return nullptr; + } + #if defined (RELEASE) && defined (ANDROID) && defined (NET6) ssize_t index = find_index (hash); if (index < 0) { From e16eb4030e69228776026f37d9542ce191f8ae0c Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 12 May 2022 13:57:27 +0200 Subject: [PATCH 04/14] Marshal methods POC works The code is tailored to my test app, so it won't work for anybody else yet --- .../Tasks/GeneratePackageManagerJava.cs | 9 ++- ...pplicationConfigNativeAssemblyGenerator.cs | 72 ++++++++++++++----- src/monodroid/CMakeLists.txt | 3 +- src/monodroid/jni/application_dso_stub.cc | 14 +++- src/monodroid/jni/embedded-assemblies.cc | 7 ++ src/monodroid/jni/mono-image-loader.cc | 5 ++ src/monodroid/jni/mono-image-loader.hh | 46 +++++++++--- src/monodroid/jni/mono_android_Runtime.h | 3 + src/monodroid/jni/monodroid-glue-internal.hh | 20 ++++-- src/monodroid/jni/monodroid-glue.cc | 43 +++++++++-- .../jni/xamarin-android-app-context.cc | 25 ++++++- src/monodroid/jni/xamarin-app-marshaling.cc | 15 +--- src/monodroid/jni/xamarin-app-marshaling.hh | 2 +- src/monodroid/jni/xamarin-app.hh | 21 +++++- 14 files changed, 222 insertions(+), 63 deletions(-) create mode 100644 src/monodroid/jni/mono-image-loader.cc diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs index 909cf9ddad6..fc62a079399 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs @@ -299,8 +299,15 @@ void AddEnvironment () int assemblyCount = 0; HashSet archAssemblyNames = null; + var uniqueAssemblyNames = new HashSet (StringComparer.OrdinalIgnoreCase); Action updateAssemblyCount = (ITaskItem assembly) => { + string assemblyName = Path.GetFileName (assembly.ItemSpec); + + if (!uniqueAssemblyNames.Contains (assemblyName)) { + uniqueAssemblyNames.Add (assemblyName); + } + if (!UseAssemblyStore) { assemblyCount++; return; @@ -316,7 +323,6 @@ void AddEnvironment () } else { archAssemblyNames ??= new HashSet (StringComparer.OrdinalIgnoreCase); - string assemblyName = Path.GetFileName (assembly.ItemSpec); if (!archAssemblyNames.Contains (assemblyName)) { assemblyCount++; archAssemblyNames.Add (assemblyName); @@ -429,6 +435,7 @@ void AddEnvironment () AndroidRuntimeJNIEnvToken = android_runtime_jnienv_class_token, JNIEnvInitializeToken = jnienv_initialize_method_token, JNIEnvRegisterJniNativesToken = jnienv_registerjninatives_method_token, + UniqueAssemblyNames = uniqueAssemblyNames, }; appConfigAsmGen.Init (); diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs index 83906073fcc..3e86901727d 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; using Java.Interop.Tools.TypeNameMappings; @@ -174,6 +175,7 @@ sealed class XamarinAndroidBundledAssembly public MonoComponent MonoComponents { get; set; } public PackageNamingPolicy PackageNamingPolicy { get; set; } public List NativeLibraries { get; set; } + public ICollection UniqueAssemblyNames { get; set; } public ApplicationConfigNativeAssemblyGenerator (IDictionary environmentVariables, IDictionary systemProperties, TaskLoggingHelper log) { @@ -328,9 +330,18 @@ protected override void Write (LlvmIrGenerator generator) void WriteAssemblyImageCache (LlvmIrGenerator generator) { + if (UniqueAssemblyNames == null) { + throw new InvalidOperationException ("Internal error: unique assembly names not provided"); + } + + if (UniqueAssemblyNames.Count != NumberOfAssembliesInApk) { + throw new InvalidOperationException ("Internal error: number of assemblies in the apk doesn't match the number of unique assembly names"); + } + + bool is64Bit = generator.Is64Bit; generator.WriteStructureArray (monoImage, (ulong)NumberOfAssembliesInApk, "assembly_image_cache", isArrayOfPointers: true); - if (generator.Is64Bit) { + if (is64Bit) { WriteHashes (); } else { WriteHashes (); @@ -338,13 +349,42 @@ void WriteAssemblyImageCache (LlvmIrGenerator generator) void WriteHashes () where T: struct { - // TODO: hash all assembly names here - var hashes = new List (); - for (int i = 0; i < NumberOfAssembliesInApk; i++) { - hashes.Add (default(T)); + var hashes = new Dictionary (); + uint index = 0; + foreach (string name in UniqueAssemblyNames) { + string clippedName = Path.GetFileNameWithoutExtension (name); + ulong hashFull = HashName (name, is64Bit); + ulong hashClipped = HashName (clippedName, is64Bit); + + // + // If the number of name forms changes, xamarin-app.hh MUST be updated to set value of the + // `number_of_assembly_name_forms_in_image_cache` constant to the number of forms. + // + hashes.Add ((T)Convert.ChangeType (hashFull, typeof(T)), (name, index)); + hashes.Add ((T)Convert.ChangeType (hashClipped, typeof(T)), (clippedName, index)); + + index++; } - - generator.WriteArray (hashes, LlvmIrVariableOptions.GlobalConstant, "assembly_image_cache_index"); + List keys = hashes.Keys.ToList (); + keys.Sort (); + + generator.WriteCommentLine ("Each entry maps hash of an assembly name to an index into the `assembly_image_cache` array"); + generator.WriteArray ( + keys, + LlvmIrVariableOptions.GlobalConstant, + "assembly_image_cache_hashes", + (int idx, T value) => $"{idx}: {hashes[value].name} => 0x{value:x} => {hashes[value].index}" + ); + + var indices = new List (); + for (int i = 0; i < keys.Count; i++) { + indices.Add (hashes[keys[i]].index); + } + generator.WriteArray ( + indices, + LlvmIrVariableOptions.GlobalConstant, + "assembly_image_cache_indices" + ); } } @@ -368,21 +408,21 @@ void WriteDSOCache (LlvmIrGenerator generator) // We need to hash here, because the hash is architecture-specific foreach (StructureInstance entry in dsoCache) { - entry.Obj.hash = HashName (entry.Obj.HashedName); + entry.Obj.hash = HashName (entry.Obj.HashedName, is64Bit); } dsoCache.Sort ((StructureInstance a, StructureInstance b) => a.Obj.hash.CompareTo (b.Obj.hash)); generator.WriteStructureArray (dsoCacheEntryStructureInfo, dsoCache, "dso_cache"); + } - ulong HashName (string name) - { - byte[] nameBytes = Encoding.UTF8.GetBytes (name); - if (is64Bit) { - return XXH64.DigestOf (nameBytes, 0, nameBytes.Length); - } - - return (ulong)XXH32.DigestOf (nameBytes, 0, nameBytes.Length); + ulong HashName (string name, bool is64Bit) + { + byte[] nameBytes = Encoding.UTF8.GetBytes (name); + if (is64Bit) { + return XXH64.DigestOf (nameBytes, 0, nameBytes.Length); } + + return (ulong)XXH32.DigestOf (nameBytes, 0, nameBytes.Length); } } } diff --git a/src/monodroid/CMakeLists.txt b/src/monodroid/CMakeLists.txt index 734a818ca86..39d3e718123 100644 --- a/src/monodroid/CMakeLists.txt +++ b/src/monodroid/CMakeLists.txt @@ -486,6 +486,7 @@ set(XAMARIN_MONODROID_SOURCES ${SOURCES_DIR}/globals.cc ${SOURCES_DIR}/logger.cc ${SOURCES_DIR}/monodroid-glue.cc + ${SOURCES_DIR}/mono-image-loader.cc ${SOURCES_DIR}/osbridge.cc ${SOURCES_DIR}/shared-constants.cc ${SOURCES_DIR}/timezones.cc @@ -503,7 +504,7 @@ if(ANDROID) ${LZ4_SOURCES} ) - if(NOT DEBUG_BUILD) + if(NOT DEBUG_BUILD AND ENABLE_NET6) list(APPEND XAMARIN_MONODROID_SOURCES ${SOURCES_DIR}/xamarin-android-app-context.cc ) diff --git a/src/monodroid/jni/application_dso_stub.cc b/src/monodroid/jni/application_dso_stub.cc index 1fd28d2b6d5..ca73eb5447b 100644 --- a/src/monodroid/jni/application_dso_stub.cc +++ b/src/monodroid/jni/application_dso_stub.cc @@ -151,9 +151,21 @@ MonoImage* assembly_image_cache[] = { nullptr, }; -MONO_API MONO_API_EXPORT xamarin::android::hash_t assembly_image_cache_index[] = { + +// Each element contains an index into `assembly_image_cache` +const uint32_t assembly_image_cache_indices[] = { 0, + 1, + 1, + 1, +}; + +// hashes point to indices in `assembly_image_cache_indices` +const xamarin::android::hash_t assembly_image_cache_hashes[] = { 0, + 1, + 2, + 3, }; void xamarin_app_init ([[maybe_unused]] xamarin::android::internal::AppContext *context) diff --git a/src/monodroid/jni/embedded-assemblies.cc b/src/monodroid/jni/embedded-assemblies.cc index 9448d77d5cf..3b2a621a8ad 100644 --- a/src/monodroid/jni/embedded-assemblies.cc +++ b/src/monodroid/jni/embedded-assemblies.cc @@ -357,6 +357,10 @@ EmbeddedAssemblies::assembly_store_open_from_bundles (dynamic_local_string force_inline MonoAssembly* EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, TLoaderData loader_data, [[maybe_unused]] MonoError *error, bool ref_only) noexcept diff --git a/src/monodroid/jni/mono-image-loader.cc b/src/monodroid/jni/mono-image-loader.cc new file mode 100644 index 00000000000..ea2cc39a822 --- /dev/null +++ b/src/monodroid/jni/mono-image-loader.cc @@ -0,0 +1,5 @@ +#include "mono-image-loader.hh" + +#if defined (USE_CACHE) +size_t xamarin::android::internal::MonoImageLoader::number_of_cache_index_entries = application_config.number_of_assemblies_in_apk * number_of_assembly_name_forms_in_image_cache; +#endif // def USE_CACHE diff --git a/src/monodroid/jni/mono-image-loader.hh b/src/monodroid/jni/mono-image-loader.hh index 0ff8d2ac56e..644d7811b69 100644 --- a/src/monodroid/jni/mono-image-loader.hh +++ b/src/monodroid/jni/mono-image-loader.hh @@ -3,6 +3,7 @@ #define __MONO_IMAGE_LOADER_HH #include +#include #include #if defined (NET6) @@ -16,6 +17,10 @@ #include "search.hh" #include "strings.hh" +#if defined (RELEASE) && defined (ANDROID) && defined (NET6) +#define USE_CACHE 1 +#endif + namespace xamarin::android::internal { enum class MonoImageLoaderContext { @@ -26,10 +31,14 @@ namespace xamarin::android::internal { class MonoImageLoader final { public: -#if defined (RELEASE) && defined (ANDROID) && defined (NET6) +#if defined (USE_CACHE) force_inline static MonoImage* get_from_index (size_t index) noexcept { - return nullptr; + if (index >= application_config.number_of_assemblies_in_apk) { + return nullptr; + } + + return get_from_index_quick (index); } force_inline static MonoImage* get_with_hash (hash_t hash) noexcept @@ -39,13 +48,14 @@ namespace xamarin::android::internal { return nullptr; } - return get_from_index (static_cast(index)); + return get_from_index_quick (static_cast(index)); } -#endif // def RELEASE && def ANDROID && def NET6 +#endif // def USE_CACHE #if defined (NET6) force_inline static MonoImage* load (dynamic_local_string const& name, MonoAssemblyLoadContextGCHandle alc_gchandle, hash_t name_hash, uint8_t *assembly_data, uint32_t assembly_data_size) noexcept { + log_info (LOG_DEFAULT, "Loading assembly %s; hash 0x%zx", name.get (), name_hash); MonoImageOpenStatus status; MonoImage *image = mono_image_open_from_data_alc ( alc_gchandle, @@ -61,6 +71,7 @@ namespace xamarin::android::internal { force_inline static MonoImage* load (dynamic_local_string const& name, MonoAssemblyLoadContextGCHandle alc_gchandle, uint8_t *assembly_data, uint32_t assembly_data_size) noexcept { + return load (name, alc_gchandle, xxhash::hash (name.get (), name.length ()), assembly_data, assembly_data_size); } #endif @@ -86,30 +97,39 @@ namespace xamarin::android::internal { } private: +#if defined (USE_CACHE) + // Performs NO BOUNDS CHECKING (intended!) + force_inline static MonoImage* get_from_index_quick (size_t index) noexcept + { + return assembly_image_cache[index]; + } + force_inline static ssize_t find_index (hash_t hash) noexcept { -#if defined (RELEASE) && defined (ANDROID) - return Search::binary_search (hash, assembly_image_cache_index, application_config.number_of_assemblies_in_apk); -#else - return 0; -#endif // def RELEASE && def ANDROID + ssize_t idx = Search::binary_search (hash, assembly_image_cache_hashes, number_of_cache_index_entries); + return idx >= 0 ? assembly_image_cache_indices[idx] : -1; + } +#endif // def USE_CACHE force_inline static MonoImage* stash_and_return (MonoImage *image, MonoImageOpenStatus status, [[maybe_unused]] hash_t hash) noexcept { + log_info (LOG_DEFAULT, "Stashing image %p; hash 0x%zx", image, hash); if (image == nullptr || status != MonoImageOpenStatus::MONO_IMAGE_OK) { log_warn (LOG_ASSEMBLY, "Failed to open assembly image for '%s'. %s", mono_image_strerror (status)); return nullptr; } -#if defined (RELEASE) && defined (ANDROID) && defined (NET6) +#if defined (USE_CACHE) ssize_t index = find_index (hash); + log_info (LOG_DEFAULT, "Index matching the hash == %zd", index); if (index < 0) { // TODO: Warn? return image; } - // We don't need to worry about locking here. Even if we're overwriting an entry just set from another + log_info (LOG_DEFAULT, "Stashing"); + // We don't need to worry about locking here. Even if we're overwriting an entry just set by another // thread, the image pointer is going to be the same (at least currently, it will change when we have // support for unloadable Assembly Load Contexts) and the actual write operation to the destination is // atomic @@ -117,6 +137,10 @@ namespace xamarin::android::internal { #endif // def RELEASE && def ANDROID && def NET6 return image; } + +#if defined (USE_CACHE) + static size_t number_of_cache_index_entries; +#endif // def USE_CACHE }; } #endif // ndef __MONO_IMAGE_LOADER_HH diff --git a/src/monodroid/jni/mono_android_Runtime.h b/src/monodroid/jni/mono_android_Runtime.h index 78e4e1aae68..7cc5c5230f9 100644 --- a/src/monodroid/jni/mono_android_Runtime.h +++ b/src/monodroid/jni/mono_android_Runtime.h @@ -90,6 +90,9 @@ JNIEXPORT void JNICALL Java_mono_android_Runtime_dumpTimingData JNIEXPORT void JNICALL Java_helloandroid_MainActivity_n_1onCreate__Landroid_os_Bundle_2 (JNIEnv *, jclass, jobject); +JNIEXPORT jobject JNICALL Java_helloandroid_MainActivity_n_1onCreateView__Landroid_view_View_2Ljava_lang_String_2Landroid_content_Context_2Landroid_util_AttributeSet_2 + (JNIEnv *env, jclass klass, jobject view, jstring name, jobject context, jobject attrs); + #ifdef __cplusplus } #endif diff --git a/src/monodroid/jni/monodroid-glue-internal.hh b/src/monodroid/jni/monodroid-glue-internal.hh index 640a69988a6..6179589b484 100644 --- a/src/monodroid/jni/monodroid-glue-internal.hh +++ b/src/monodroid/jni/monodroid-glue-internal.hh @@ -67,11 +67,11 @@ namespace xamarin::android::internal } }; -#if defined (RELEASE) && defined (ANDROID) - class XamarinAndroidAppContext : public AppContext +#if defined (RELEASE) && defined (ANDROID) && defined (NET6) + class XamarinAndroidAppContext final : public AppContext { public: - virtual MonoImage *lookup_mono_image (uint8_t *module_uuid) override final; + virtual void* get_function_pointer (uint32_t mono_image_index, uint32_t class_token, uint32_t method_token) override final; }; #endif // def RELEASE && def ANDROID @@ -258,6 +258,14 @@ namespace xamarin::android::internal char* get_java_class_name_for_TypeManager (jclass klass); +#if defined (RELEASE) && defined (ANDROID) && defined (NET6) + // TODO: remove after marshal methods are generated in libxamarin-app.so + static XamarinAndroidAppContext* get_app_context () noexcept + { + return &xa_app_context; + } +#endif + private: #if defined (ANDROID) static void mono_log_handler (const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data); @@ -422,9 +430,9 @@ namespace xamarin::android::internal static void *api_dso_handle; #endif // !def NET static std::mutex dso_handle_write_lock; -#if defined (RELEASE) && defined (ANDROID) - XamarinAndroidAppContext xa_app_context; -#endif // def RELEASE && def ANDROID +#if defined (RELEASE) && defined (ANDROID) && defined (NET6) + static XamarinAndroidAppContext xa_app_context; +#endif // def RELEASE && def ANDROID && def NET6 }; } #endif diff --git a/src/monodroid/jni/monodroid-glue.cc b/src/monodroid/jni/monodroid-glue.cc index 9fd974698b8..368239167e2 100644 --- a/src/monodroid/jni/monodroid-glue.cc +++ b/src/monodroid/jni/monodroid-glue.cc @@ -129,6 +129,10 @@ MonoCoreRuntimeProperties MonodroidRuntime::monovm_core_properties = { std::mutex MonodroidRuntime::dso_handle_write_lock; bool MonodroidRuntime::startup_in_progress = true; +#if defined (RELEASE) && defined (ANDROID) && defined (NET6) +XamarinAndroidAppContext MonodroidRuntime::xa_app_context; +#endif + #ifdef WINDOWS static const char* get_xamarin_android_msbuild_path (void); const char *BasicAndroidSystem::SYSTEM_LIB_PATH = get_xamarin_android_msbuild_path(); @@ -874,9 +878,9 @@ MonodroidRuntime::mono_runtime_init ([[maybe_unused]] dynamic_local_string 0) { ret = entries + (entry_count / 2); entry_hash = static_cast (ret->hash); - log_debug (LOG_ASSEMBLY, "dso_cache: entry_hash == 0x%zx", entry_hash); auto result = hash <=> entry_hash; if (result < 0) { @@ -2636,7 +2638,8 @@ JNICALL Java_mono_android_Runtime_propagateUncaughtException (JNIEnv *env, [[may } #if defined (RELEASE) && defined (ANDROID) && defined (NET6) -static void (*android_app_activity_on_create_bundle) (JNIEnv *env, jclass klass, jobject savedInstanceState) = nullptr; +using android_app_activity_on_create_bundle_fn = void (*) (JNIEnv *env, jclass klass, jobject savedInstanceState); +static android_app_activity_on_create_bundle_fn android_app_activity_on_create_bundle = nullptr; JNIEXPORT void JNICALL Java_helloandroid_MainActivity_n_1onCreate__Landroid_os_Bundle_2 (JNIEnv *env, jclass klass, jobject savedInstanceState) @@ -2644,10 +2647,36 @@ JNICALL Java_helloandroid_MainActivity_n_1onCreate__Landroid_os_Bundle_2 (JNIEnv log_info (LOG_DEFAULT, "%s (%p, %p, %p)", __PRETTY_FUNCTION__, env, klass, savedInstanceState); if (android_app_activity_on_create_bundle == nullptr) { - // MonoImage *image = mono_load; + void *fn = MonodroidRuntime::get_app_context ()->get_function_pointer ( + 16 /* Mono.Android.dll index */, + 0x020000AF /* Android.App.Activity token */, + 0x0600055B /* n_OnCreate_Landroid_os_Bundle_ */ + ); + + android_app_activity_on_create_bundle = reinterpret_cast(fn); } - // TODO: obtain the function pointer for the symbol below android_app_activity_on_create_bundle (env, klass, savedInstanceState); } + +using android_app_activity_on_create_view_fn = jobject (*) (JNIEnv *env, jclass klass, jobject view, jstring name, jobject context, jobject attrs); +static android_app_activity_on_create_view_fn android_app_activity_on_create_view = nullptr; + +JNIEXPORT jobject +JNICALL Java_helloandroid_MainActivity_n_1onCreateView__Landroid_view_View_2Ljava_lang_String_2Landroid_content_Context_2Landroid_util_AttributeSet_2 (JNIEnv *env, jclass klass, jobject view, jstring name, jobject context, jobject attrs) +{ + log_info (LOG_DEFAULT, "%s (%p, %p, %p, %p, %p, %p)", __PRETTY_FUNCTION__, env, klass, view, name, context, attrs); + + if (android_app_activity_on_create_view == nullptr) { + void *fn = MonodroidRuntime::get_app_context ()->get_function_pointer ( + 16 /* Mono.Android.dll index */, + 0x020000AF /* Android.App.Activity token */, + 0x06000564 /* n_OnCreateView_Landroid_view_View_Ljava_lang_String_Landroid_content_Context_Landroid_util_AttributeSet_ */ + ); + + android_app_activity_on_create_view = reinterpret_cast(fn); + } + + return android_app_activity_on_create_view (env, klass, view, name, context, attrs); +} #endif // def RELEASE && def ANDROID && def NET6 diff --git a/src/monodroid/jni/xamarin-android-app-context.cc b/src/monodroid/jni/xamarin-android-app-context.cc index 57022e45eba..92bfcee96e0 100644 --- a/src/monodroid/jni/xamarin-android-app-context.cc +++ b/src/monodroid/jni/xamarin-android-app-context.cc @@ -1,9 +1,28 @@ +#include + #include "monodroid-glue-internal.hh" +#include "mono-image-loader.hh" using namespace xamarin::android::internal; -MonoImage *XamarinAndroidAppContext::lookup_mono_image (uint8_t *module_uuid) +void* XamarinAndroidAppContext::get_function_pointer (uint32_t mono_image_index, uint32_t class_token, uint32_t method_token) { - // TODO: implement - return nullptr; + MonoImage *image = MonoImageLoader::get_from_index (mono_image_index); + + // TODO: implement MonoClassLoader with caching. Best to use indexes instead of keying on tokens. + MonoClass *method_klass = mono_class_get (image, class_token); + MonoMethod *method = mono_get_method (image, method_token, method_klass); + + MonoError error; + void *ret = mono_method_get_unmanaged_callers_only_ftnptr (method, &error); + if (ret == nullptr || error.error_code != MONO_ERROR_NONE) { + // TODO: make the error message friendlier somehow (class, method and assembly names) + log_fatal (LOG_DEFAULT, + "Failed to obtain function pointer to method with token 0x%x; class token: 0x%x; assembly index: %u", + method_token, class_token, mono_images_cleanup + ); + abort (); + } + + return ret; } diff --git a/src/monodroid/jni/xamarin-app-marshaling.cc b/src/monodroid/jni/xamarin-app-marshaling.cc index 4f7ff96eab2..648589031e5 100644 --- a/src/monodroid/jni/xamarin-app-marshaling.cc +++ b/src/monodroid/jni/xamarin-app-marshaling.cc @@ -2,6 +2,7 @@ #include #include +#include #include #include "xamarin-app-marshaling-private.hh" @@ -18,19 +19,7 @@ void XamarinAppMarshaling::init (AppContext *ctx) noexcept force_inline MonoObject* XamarinAppMarshaling::invoke_managed_method (uint8_t *module_uuid, uint32_t module_id, uint32_t type_token, uint32_t method_token, void **params) const noexcept { - // TODO: caching - // TODO: should we abort() if we can't find the image, class or method? - MonoImage *image = context->lookup_mono_image (module_uuid); - MonoClass *klass = mono_class_get (image, type_token); - MonoMethod *method = mono_get_method (image, method_token, klass); - MonoObject *exc = nullptr; - MonoObject *ret = mono_runtime_invoke (method, nullptr, params, &exc); - - if (exc != nullptr) { - // TODO: call AndroidEnvironment.UnhandledException(exc) - } - - return ret; + return nullptr; } MonoObject* monodroid_invoke_managed_method (uint8_t *module_uuid, uint32_t module_id, uint32_t type_token, uint32_t method_token, void **params) diff --git a/src/monodroid/jni/xamarin-app-marshaling.hh b/src/monodroid/jni/xamarin-app-marshaling.hh index b1f561a7333..256f26d3519 100644 --- a/src/monodroid/jni/xamarin-app-marshaling.hh +++ b/src/monodroid/jni/xamarin-app-marshaling.hh @@ -13,7 +13,7 @@ namespace xamarin::android::internal class AppContext { public: - virtual MonoImage *lookup_mono_image (uint8_t *module_uuid) = 0; + virtual void* get_function_pointer (uint32_t mono_image_index, uint32_t class_token, uint32_t method_token) = 0; }; } diff --git a/src/monodroid/jni/xamarin-app.hh b/src/monodroid/jni/xamarin-app.hh index ea911b4225a..5e256447cf4 100644 --- a/src/monodroid/jni/xamarin-app.hh +++ b/src/monodroid/jni/xamarin-app.hh @@ -263,11 +263,26 @@ MONO_API MONO_API_EXPORT AssemblyStoreRuntimeData assembly_stores[]; MONO_API MONO_API_EXPORT DSOCacheEntry dso_cache[]; -#if defined (RELEASE) && defined (ANDROID) +#if defined (RELEASE) && defined (ANDROID) && defined (NET6) + +// Number of assembly name forms for which we generate hashes (essentially file name mutations. For instance +// `HelloWorld.dll`, `HelloWorld`, `en-US/HelloWorld` etc). This is multiplied by the number of assemblies in the apk to +// obtain number of entries in the `assembly_image_cache_hashes` and `assembly_image_cache_indices` entries +constexpr uint32_t number_of_assembly_name_forms_in_image_cache = 2; + +// These 3 arrays constitute the cache used to store pointers to loaded managed assemblies. +// Three arrays are used so that we can have multiple hashes pointing to the same MonoImage*. +// +// This is done by the `assembly_image_cache_hashes` containing hases for all mutations of some +// assembly's name (e.g. with culture prefix, without extension etc) and position of that hash in +// `assembly_image_cache_hashes` is an index into `assembly_image_cache_indices` which, in turn, +// stores final index into the `assembly_image_cache` array. +// MONO_API MONO_API_EXPORT MonoImage* assembly_image_cache[]; -MONO_API MONO_API_EXPORT xamarin::android::hash_t assembly_image_cache_index[]; +MONO_API MONO_API_EXPORT const uint32_t assembly_image_cache_indices[]; +MONO_API MONO_API_EXPORT const xamarin::android::hash_t assembly_image_cache_hashes[]; MONO_API_EXPORT void xamarin_app_init (); -#endif +#endif // def RELEASE && def ANDROID && def NET6 #endif // __XAMARIN_ANDROID_TYPEMAP_H From 2cd2e707b11b63a66b7b383c2703d7b5e4bae0f2 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 12 May 2022 22:28:11 +0200 Subject: [PATCH 05/14] Baby steps --- src/monodroid/CMakeLists.txt | 16 ++-- src/monodroid/jni/application_dso_stub.cc | 2 +- src/monodroid/jni/monodroid-glue-internal.hh | 27 ++---- src/monodroid/jni/monodroid-glue.cc | 50 +---------- .../jni/xamarin-android-app-context.cc | 2 +- .../jni/xamarin-app-marshaling-private.hh | 84 ------------------ src/monodroid/jni/xamarin-app-marshaling.cc | 87 ++++++++----------- src/monodroid/jni/xamarin-app-marshaling.hh | 25 ------ src/monodroid/jni/xamarin-app.hh | 8 +- 9 files changed, 55 insertions(+), 246 deletions(-) delete mode 100644 src/monodroid/jni/xamarin-app-marshaling-private.hh delete mode 100644 src/monodroid/jni/xamarin-app-marshaling.hh diff --git a/src/monodroid/CMakeLists.txt b/src/monodroid/CMakeLists.txt index 39d3e718123..57c4b87fcc7 100644 --- a/src/monodroid/CMakeLists.txt +++ b/src/monodroid/CMakeLists.txt @@ -609,11 +609,11 @@ target_link_options( ) if(ANDROID) - if(NOT DEBUG_BUILD) + if(NOT DEBUG_BUILD AND ENABLE_NET6) # The marshaling lib is used only when building for devices add_library( ${XAMARIN_APP_MARSHALING_LIB} - STATIC + SHARED ${XAMARIN_APP_MARSHALING_LIB_SOURCES} ) @@ -621,11 +621,6 @@ if(ANDROID) ${XAMARIN_APP_MARSHALING_LIB} ${MONOSGEN_LIB_LINK} ) - - target_link_libraries( - ${XAMARIN_APP_STUB_LIB} - ${XAMARIN_APP_MARSHALING_LIB} - ) endif() # Only Android builds need to go in separate directories, desktop builds have the same ABI @@ -735,3 +730,10 @@ target_link_libraries( ${XAMARIN_MONO_ANDROID_LIB} ${LINK_LIBS} xamarin-app ) + +if((NOT DEBUG_BUILD) AND ANDROID AND ENABLE_NET6) + target_link_libraries( + ${XAMARIN_MONO_ANDROID_LIB} + ${XAMARIN_APP_MARSHALING_LIB} + ) +endif() diff --git a/src/monodroid/jni/application_dso_stub.cc b/src/monodroid/jni/application_dso_stub.cc index ca73eb5447b..2218568312e 100644 --- a/src/monodroid/jni/application_dso_stub.cc +++ b/src/monodroid/jni/application_dso_stub.cc @@ -168,7 +168,7 @@ const xamarin::android::hash_t assembly_image_cache_hashes[] = { 3, }; -void xamarin_app_init ([[maybe_unused]] xamarin::android::internal::AppContext *context) +void xamarin_app_init ([[maybe_unused]] get_function_pointer_fn fn) { // Dummy } diff --git a/src/monodroid/jni/monodroid-glue-internal.hh b/src/monodroid/jni/monodroid-glue-internal.hh index 6179589b484..b9a4f66d635 100644 --- a/src/monodroid/jni/monodroid-glue-internal.hh +++ b/src/monodroid/jni/monodroid-glue-internal.hh @@ -9,7 +9,6 @@ #include "timing.hh" #include "cpp-util.hh" #include "xxhash.hh" -#include "xamarin-app-marshaling.hh" #include #include @@ -67,14 +66,6 @@ namespace xamarin::android::internal } }; -#if defined (RELEASE) && defined (ANDROID) && defined (NET6) - class XamarinAndroidAppContext final : public AppContext - { - public: - virtual void* get_function_pointer (uint32_t mono_image_index, uint32_t class_token, uint32_t method_token) override final; - }; -#endif // def RELEASE && def ANDROID - class MonodroidRuntime { #if defined (NET) @@ -258,14 +249,6 @@ namespace xamarin::android::internal char* get_java_class_name_for_TypeManager (jclass klass); -#if defined (RELEASE) && defined (ANDROID) && defined (NET6) - // TODO: remove after marshal methods are generated in libxamarin-app.so - static XamarinAndroidAppContext* get_app_context () noexcept - { - return &xa_app_context; - } -#endif - private: #if defined (ANDROID) static void mono_log_handler (const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data); @@ -367,7 +350,12 @@ namespace xamarin::android::internal #if defined (NET) static void monodroid_debugger_unhandled_exception (MonoException *ex); -#endif + +#if defined (RELEASE) && defined (ANDROID) + static void* get_function_pointer (uint32_t mono_image_index, uint32_t class_token, uint32_t method_token) noexcept; +#endif // def RELEASE && def ANDROID +#endif // def NET6 + #if defined (DEBUG) void set_debug_env_vars (void); @@ -430,9 +418,6 @@ namespace xamarin::android::internal static void *api_dso_handle; #endif // !def NET static std::mutex dso_handle_write_lock; -#if defined (RELEASE) && defined (ANDROID) && defined (NET6) - static XamarinAndroidAppContext xa_app_context; -#endif // def RELEASE && def ANDROID && def NET6 }; } #endif diff --git a/src/monodroid/jni/monodroid-glue.cc b/src/monodroid/jni/monodroid-glue.cc index 368239167e2..dbe0f982422 100644 --- a/src/monodroid/jni/monodroid-glue.cc +++ b/src/monodroid/jni/monodroid-glue.cc @@ -129,10 +129,6 @@ MonoCoreRuntimeProperties MonodroidRuntime::monovm_core_properties = { std::mutex MonodroidRuntime::dso_handle_write_lock; bool MonodroidRuntime::startup_in_progress = true; -#if defined (RELEASE) && defined (ANDROID) && defined (NET6) -XamarinAndroidAppContext MonodroidRuntime::xa_app_context; -#endif - #ifdef WINDOWS static const char* get_xamarin_android_msbuild_path (void); const char *BasicAndroidSystem::SYSTEM_LIB_PATH = get_xamarin_android_msbuild_path(); @@ -879,7 +875,7 @@ MonodroidRuntime::mono_runtime_init ([[maybe_unused]] dynamic_local_stringget_function_pointer ( - 16 /* Mono.Android.dll index */, - 0x020000AF /* Android.App.Activity token */, - 0x0600055B /* n_OnCreate_Landroid_os_Bundle_ */ - ); - - android_app_activity_on_create_bundle = reinterpret_cast(fn); - } - - android_app_activity_on_create_bundle (env, klass, savedInstanceState); -} - -using android_app_activity_on_create_view_fn = jobject (*) (JNIEnv *env, jclass klass, jobject view, jstring name, jobject context, jobject attrs); -static android_app_activity_on_create_view_fn android_app_activity_on_create_view = nullptr; - -JNIEXPORT jobject -JNICALL Java_helloandroid_MainActivity_n_1onCreateView__Landroid_view_View_2Ljava_lang_String_2Landroid_content_Context_2Landroid_util_AttributeSet_2 (JNIEnv *env, jclass klass, jobject view, jstring name, jobject context, jobject attrs) -{ - log_info (LOG_DEFAULT, "%s (%p, %p, %p, %p, %p, %p)", __PRETTY_FUNCTION__, env, klass, view, name, context, attrs); - - if (android_app_activity_on_create_view == nullptr) { - void *fn = MonodroidRuntime::get_app_context ()->get_function_pointer ( - 16 /* Mono.Android.dll index */, - 0x020000AF /* Android.App.Activity token */, - 0x06000564 /* n_OnCreateView_Landroid_view_View_Ljava_lang_String_Landroid_content_Context_Landroid_util_AttributeSet_ */ - ); - - android_app_activity_on_create_view = reinterpret_cast(fn); - } - - return android_app_activity_on_create_view (env, klass, view, name, context, attrs); -} -#endif // def RELEASE && def ANDROID && def NET6 diff --git a/src/monodroid/jni/xamarin-android-app-context.cc b/src/monodroid/jni/xamarin-android-app-context.cc index 92bfcee96e0..e904e03e806 100644 --- a/src/monodroid/jni/xamarin-android-app-context.cc +++ b/src/monodroid/jni/xamarin-android-app-context.cc @@ -5,7 +5,7 @@ using namespace xamarin::android::internal; -void* XamarinAndroidAppContext::get_function_pointer (uint32_t mono_image_index, uint32_t class_token, uint32_t method_token) +void* MonodroidRuntime::get_function_pointer (uint32_t mono_image_index, uint32_t class_token, uint32_t method_token) noexcept { MonoImage *image = MonoImageLoader::get_from_index (mono_image_index); diff --git a/src/monodroid/jni/xamarin-app-marshaling-private.hh b/src/monodroid/jni/xamarin-app-marshaling-private.hh deleted file mode 100644 index 3e2157c0af6..00000000000 --- a/src/monodroid/jni/xamarin-app-marshaling-private.hh +++ /dev/null @@ -1,84 +0,0 @@ -// Dear Emacs, this is a -*- C++ -*- header -#if !defined (__XAMARIN_APP_MARSHALING_PRIVATE_HH) -#define __XAMARIN_APP_MARSHALING_PRIVATE_HH - -#include -#include -#include - -#if !defined (__FOR_GENERATOR_ONLY) -#include -#include -#include -#include -#include - -#include "platform-compat.hh" -#include "xamarin-app-marshaling.hh" - -// -// Functions here must be in the global namespace and have C linkage since they are referenced from the generated native -// assembler code. -// -extern "C" { - // - // module_uuid: points to a 16-byte array with the UUID, used to look up in typemap - // module_id: unique module ID generated at build time, used for hashing together with type_token and method_token - // - MonoObject* monodroid_invoke_managed_method (uint8_t *module_uuid, uint32_t module_id, uint32_t type_token, uint32_t method_token, void **params); - jboolean monodroid_unbox_value_boolean (MonoObject *value); - jbyte monodroid_unbox_value_byte (MonoObject *value); - jchar monodroid_unbox_value_char (MonoObject *value); - jdouble monodroid_unbox_value_double (MonoObject *value); - jfloat monodroid_unbox_value_float (MonoObject *value); - jint monodroid_unbox_value_int (MonoObject *value); - jlong monodroid_unbox_value_long (MonoObject *value); - void* monodroid_unbox_value_pointer (MonoObject *value); - jshort monodroid_unbox_value_short (MonoObject *value); -} -#endif // ndef __FOR_GENERATOR_ONLY - -namespace xamarin::android::internal -{ - enum class MarshalingTypes - { - Boolean, - Byte, - Char, - Double, - Float, - Int, - Long, - Pointer, - Short, - }; - -#if !defined (__FOR_GENERATOR_ONLY) - class XamarinAppMarshaling - { - public: - void init (AppContext *context) noexcept; - - public: - template - force_inline - static TRet unbox_value (MonoObject *value) noexcept - { - void *ret = mono_object_unbox (value); - if (ret == nullptr) [[unlikely]] { - // TODO: log - abort (); - } - - return *reinterpret_cast(ret); - } - - MonoObject* invoke_managed_method (uint8_t *module_uuid, uint32_t module_id, uint32_t type_token, uint32_t method_token, void **params) const noexcept; - - private: - AppContext *context = nullptr; - }; -#endif // ndef __FOR_GENERATOR_ONLY -} - -#endif // ndef __XAMARIN_APP_MARSHALING_HH diff --git a/src/monodroid/jni/xamarin-app-marshaling.cc b/src/monodroid/jni/xamarin-app-marshaling.cc index 648589031e5..39e706e089d 100644 --- a/src/monodroid/jni/xamarin-app-marshaling.cc +++ b/src/monodroid/jni/xamarin-app-marshaling.cc @@ -1,78 +1,59 @@ #include #include +#include + #include #include #include -#include "xamarin-app-marshaling-private.hh" - -using namespace xamarin::android::internal; - -static XamarinAppMarshaling xam; - -void XamarinAppMarshaling::init (AppContext *ctx) noexcept -{ - context = ctx; -} +#include "xamarin-app.hh" -force_inline -MonoObject* XamarinAppMarshaling::invoke_managed_method (uint8_t *module_uuid, uint32_t module_id, uint32_t type_token, uint32_t method_token, void **params) const noexcept -{ - return nullptr; -} +static get_function_pointer_fn get_function_pointer; -MonoObject* monodroid_invoke_managed_method (uint8_t *module_uuid, uint32_t module_id, uint32_t type_token, uint32_t method_token, void **params) +void xamarin_app_init (get_function_pointer_fn fn) { - return xam.invoke_managed_method (module_uuid, module_id, type_token, method_token, params); + get_function_pointer = fn; } -jboolean monodroid_unbox_value_boolean (MonoObject *value) -{ - return xam.unbox_value (value); -} +using android_app_activity_on_create_bundle_fn = void (*) (JNIEnv *env, jclass klass, jobject savedInstanceState); +static android_app_activity_on_create_bundle_fn android_app_activity_on_create_bundle = nullptr; -jbyte monodroid_unbox_value_byte (MonoObject *value) +JNIEXPORT void +JNICALL Java_helloandroid_MainActivity_n_1onCreate__Landroid_os_Bundle_2 (JNIEnv *env, jclass klass, jobject savedInstanceState) { - return xam.unbox_value (value); -} + // log_info (LOG_DEFAULT, "%s (%p, %p, %p)", __PRETTY_FUNCTION__, env, klass, savedInstanceState); -jchar monodroid_unbox_value_char (MonoObject *value) -{ - return xam.unbox_value (value); -} + if (android_app_activity_on_create_bundle == nullptr) { + void *fn = get_function_pointer ( + 16 /* Mono.Android.dll index */, + 0x020000AF /* Android.App.Activity token */, + 0x0600055B /* n_OnCreate_Landroid_os_Bundle_ */ + ); -jdouble monodroid_unbox_value_double (MonoObject *value) -{ - return xam.unbox_value (value); -} + android_app_activity_on_create_bundle = reinterpret_cast(fn); + } -jfloat monodroid_unbox_value_float (MonoObject *value) -{ - return xam.unbox_value (value); + android_app_activity_on_create_bundle (env, klass, savedInstanceState); } -jint monodroid_unbox_value_int (MonoObject *value) -{ - return xam.unbox_value (value); -} +using android_app_activity_on_create_view_fn = jobject (*) (JNIEnv *env, jclass klass, jobject view, jstring name, jobject context, jobject attrs); +static android_app_activity_on_create_view_fn android_app_activity_on_create_view = nullptr; -jlong monodroid_unbox_value_long (MonoObject *value) +JNIEXPORT jobject +JNICALL Java_helloandroid_MainActivity_n_1onCreateView__Landroid_view_View_2Ljava_lang_String_2Landroid_content_Context_2Landroid_util_AttributeSet_2 (JNIEnv *env, jclass klass, jobject view, jstring name, jobject context, jobject attrs) { - return xam.unbox_value (value); -} + // log_info (LOG_DEFAULT, "%s (%p, %p, %p, %p, %p, %p)", __PRETTY_FUNCTION__, env, klass, view, name, context, attrs); -void* monodroid_unbox_value_pointer (MonoObject *value) -{ - return xam.unbox_value (value); -} + if (android_app_activity_on_create_view == nullptr) { + void *fn = get_function_pointer ( + 16 /* Mono.Android.dll index */, + 0x020000AF /* Android.App.Activity token */, + 0x06000564 /* n_OnCreateView_Landroid_view_View_Ljava_lang_String_Landroid_content_Context_Landroid_util_AttributeSet_ */ + ); -jshort monodroid_unbox_value_short (MonoObject *value) -{ - return xam.unbox_value (value); -} + android_app_activity_on_create_view = reinterpret_cast(fn); + } -void xamarin_app_init (AppContext *context) -{ - xam.init (context); + return android_app_activity_on_create_view (env, klass, view, name, context, attrs); } diff --git a/src/monodroid/jni/xamarin-app-marshaling.hh b/src/monodroid/jni/xamarin-app-marshaling.hh deleted file mode 100644 index 256f26d3519..00000000000 --- a/src/monodroid/jni/xamarin-app-marshaling.hh +++ /dev/null @@ -1,25 +0,0 @@ -// Dear Emacs, this is a -*- C++ -*- header -#if !defined (__XAMARIN_APP_MARSHALING_HH) -#define __XAMARIN_APP_MARSHALING_HH - -#include -#include - -#include -#include - -namespace xamarin::android::internal -{ - class AppContext - { - public: - virtual void* get_function_pointer (uint32_t mono_image_index, uint32_t class_token, uint32_t method_token) = 0; - }; -} - -// -// Called by libmonodroid.so on init -// -MONO_API_EXPORT void xamarin_app_init (xamarin::android::internal::AppContext *ctx); - -#endif // ndef __XAMARIN_APP_MARSHALING_HH diff --git a/src/monodroid/jni/xamarin-app.hh b/src/monodroid/jni/xamarin-app.hh index 5e256447cf4..53eae3b70c1 100644 --- a/src/monodroid/jni/xamarin-app.hh +++ b/src/monodroid/jni/xamarin-app.hh @@ -9,10 +9,6 @@ #include "monodroid.h" #include "xxhash.hh" -#if defined (RELEASE) -#include "xamarin-app-marshaling.hh" -#endif // def RELEASE - static constexpr uint64_t FORMAT_TAG = 0x015E6972616D58; static constexpr uint32_t COMPRESSED_DATA_MAGIC = 0x5A4C4158; // 'XALZ', little-endian static constexpr uint32_t ASSEMBLY_STORE_MAGIC = 0x41424158; // 'XABA', little-endian @@ -282,7 +278,9 @@ MONO_API MONO_API_EXPORT MonoImage* assembly_image_cache[]; MONO_API MONO_API_EXPORT const uint32_t assembly_image_cache_indices[]; MONO_API MONO_API_EXPORT const xamarin::android::hash_t assembly_image_cache_hashes[]; -MONO_API_EXPORT void xamarin_app_init (); +using get_function_pointer_fn = void*(*)(uint32_t mono_image_index, uint32_t class_token, uint32_t method_token); + +MONO_API_EXPORT void xamarin_app_init (get_function_pointer_fn fn); #endif // def RELEASE && def ANDROID && def NET6 #endif // __XAMARIN_ANDROID_TYPEMAP_H From 41c186db97c114681fddcb726b6a8e7716e86281 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Fri, 13 May 2022 22:52:32 +0200 Subject: [PATCH 06/14] More baby steps --- Java.Interop.diff | 61 +++++++++ .../Tasks/GenerateJavaStubs.cs | 6 + .../Tasks/GeneratePackageManagerJava.cs | 26 +++- .../Tasks/PrepareAbiItems.cs | 3 + ...pplicationConfigNativeAssemblyGenerator.cs | 78 ------------ .../LlvmIrGenerator/LlvmIrComposer.cs | 13 ++ .../MarshalMethodsNativeAssemblyGenerator.cs | 117 ++++++++++++++++++ .../Xamarin.Android.Common.targets | 15 ++- src/monodroid/jni/application_dso_stub.cc | 16 +++ src/monodroid/jni/xamarin-app.hh | 12 ++ 10 files changed, 262 insertions(+), 85 deletions(-) create mode 100644 Java.Interop.diff create mode 100644 src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.cs diff --git a/Java.Interop.diff b/Java.Interop.diff new file mode 100644 index 00000000000..7c7c345b40c --- /dev/null +++ b/Java.Interop.diff @@ -0,0 +1,61 @@ +diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs +index bcd073bd..43128177 100644 +--- a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs ++++ b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs +@@ -59,6 +59,7 @@ namespace Java.Interop.Tools.JavaCallableWrappers { + List methods = new List (); + List ctors = new List (); + List children; ++ List overriddenMethods; + readonly IMetadataResolver cache; + + [Obsolete ("Use the TypeDefinitionCache overload for better performance.")] +@@ -80,6 +81,7 @@ namespace Java.Interop.Tools.JavaCallableWrappers { + } + } + ++ public List OverriddenMethods => overriddenMethods; + public string ApplicationJavaClass { get; set; } + public JavaPeerStyle CodeGenerationTarget { get; set; } + +@@ -89,7 +91,7 @@ namespace Java.Interop.Tools.JavaCallableWrappers { + + /// + /// The Java source code to be included in Instrumentation.onCreate +- /// ++ /// + /// Originally came from MonoRuntimeProvider.java delimited by: + /// // Mono Runtime Initialization {{{ + /// // }}} +@@ -497,6 +499,7 @@ namespace Java.Interop.Tools.JavaCallableWrappers { + + public void Generate (TextWriter writer) + { ++ overriddenMethods = new List (); + if (!string.IsNullOrEmpty (package)) { + writer.WriteLine ("package " + package + ";"); + writer.WriteLine (); +@@ -530,6 +533,17 @@ namespace Java.Interop.Tools.JavaCallableWrappers { + } + + GenerateFooter (writer); ++ ++ string javaTypeName = $"{package}.{name}"; ++ AddOverridenMethods (methods); ++ AddOverridenMethods (ctors); ++ ++ void AddOverridenMethods (List list) ++ { ++ foreach (Signature sig in list) { ++ overriddenMethods.Add ($"{javaTypeName}:{sig.Method}"); ++ } ++ } + } + + public void Generate (string outputPath) +@@ -958,5 +972,3 @@ namespace Java.Interop.Tools.JavaCallableWrappers { + } + } + } +- +- diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs index 21e8cfa283b..ae0a0b21c08 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs @@ -26,6 +26,8 @@ namespace Xamarin.Android.Tasks public class GenerateJavaStubs : AndroidTask { + public const string MarshalMethodsRegisterTaskKey = ".:!MarshalMethods!:."; + public override string TaskPrefix => "GJS"; [Required] @@ -338,6 +340,7 @@ bool CreateJavaSources (IEnumerable javaTypes, TypeDefinitionCac string monoInit = GetMonoInitSource (AndroidSdkPlatform); bool hasExportReference = ResolvedAssemblies.Any (assembly => Path.GetFileName (assembly.ItemSpec) == "Mono.Android.Export.dll"); bool generateOnCreateOverrides = int.Parse (AndroidSdkPlatform) <= 10; + var overriddenMethods = new List (); bool ok = true; foreach (var t in javaTypes) { @@ -355,6 +358,7 @@ bool CreateJavaSources (IEnumerable javaTypes, TypeDefinitionCac }; jti.Generate (writer); + overriddenMethods.AddRange (jti.OverriddenMethods); writer.Flush (); var path = jti.GetDestinationPath (outputPath); @@ -388,6 +392,8 @@ bool CreateJavaSources (IEnumerable javaTypes, TypeDefinitionCac } } } + + BuildEngine4.RegisterTaskObjectAssemblyLocal (MarshalMethodsRegisterTaskKey, overriddenMethods, RegisteredTaskObjectLifetime.Build); return ok; } diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs index fc62a079399..2b53232ee05 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs @@ -435,18 +435,34 @@ void AddEnvironment () AndroidRuntimeJNIEnvToken = android_runtime_jnienv_class_token, JNIEnvInitializeToken = jnienv_initialize_method_token, JNIEnvRegisterJniNativesToken = jnienv_registerjninatives_method_token, - UniqueAssemblyNames = uniqueAssemblyNames, }; appConfigAsmGen.Init (); + var marshalMethodsAsmGen = new MarshalMethodsNativeAssemblyGenerator () { + NumberOfAssembliesInApk = assemblyCount, + UniqueAssemblyNames = uniqueAssemblyNames, + OverriddenMethods = BuildEngine4.GetRegisteredTaskObjectAssemblyLocal> (GenerateJavaStubs.MarshalMethodsRegisterTaskKey, RegisteredTaskObjectLifetime.Build) + }; + marshalMethodsAsmGen.Init (); + foreach (string abi in SupportedAbis) { - string baseAsmFilePath = Path.Combine (EnvironmentOutputDirectory, $"environment.{abi.ToLowerInvariant ()}"); - string llFilePath = $"{baseAsmFilePath}.ll"; + string targetAbi = abi.ToLowerInvariant (); + string environmentBaseAsmFilePath = Path.Combine (EnvironmentOutputDirectory, $"environment.{targetAbi}"); + string marshalMethodsBaseAsmFilePath = Path.Combine (EnvironmentOutputDirectory, $"marshal_methods.{targetAbi}"); + string environmentLlFilePath = $"{environmentBaseAsmFilePath}.ll"; + string marshalMethodsLlFilePath = $"{marshalMethodsBaseAsmFilePath}.ll"; + + AndroidTargetArch targetArch = GetAndroidTargetArchForAbi (abi); + using (var sw = MemoryStreamPool.Shared.CreateStreamWriter ()) { + appConfigAsmGen.Write (targetArch, sw, environmentLlFilePath); + sw.Flush (); + Files.CopyIfStreamChanged (sw.BaseStream, environmentLlFilePath); + } using (var sw = MemoryStreamPool.Shared.CreateStreamWriter ()) { - appConfigAsmGen.Write (GetAndroidTargetArchForAbi (abi), sw, llFilePath); + marshalMethodsAsmGen.Write (targetArch, sw, marshalMethodsLlFilePath); sw.Flush (); - Files.CopyIfStreamChanged (sw.BaseStream, llFilePath); + Files.CopyIfStreamChanged (sw.BaseStream, marshalMethodsLlFilePath); } } diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/PrepareAbiItems.cs b/src/Xamarin.Android.Build.Tasks/Tasks/PrepareAbiItems.cs index 23d501ab4c7..1b897d9daf4 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/PrepareAbiItems.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/PrepareAbiItems.cs @@ -14,6 +14,7 @@ public class PrepareAbiItems : AndroidTask const string TypeMapBase = "typemaps"; const string EnvBase = "environment"; const string CompressedAssembliesBase = "compressed_assemblies"; + const string MarshalMethodsBase = "marshal_methods"; public override string TaskPrefix => "PAI"; @@ -50,6 +51,8 @@ public override bool RunTask () baseName = EnvBase; } else if (String.Compare ("compressed", Mode, StringComparison.OrdinalIgnoreCase) == 0) { baseName = CompressedAssembliesBase; + } else if (String.Compare ("marshal_methods", Mode, StringComparison.OrdinalIgnoreCase) == 0) { + baseName = MarshalMethodsBase; } else { Log.LogError ($"Unknown mode: {Mode}"); return false; diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs index 3e86901727d..a8f24dd85a5 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs @@ -1,11 +1,8 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; using Java.Interop.Tools.TypeNameMappings; -using K4os.Hash.xxHash; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; @@ -151,7 +148,6 @@ sealed class XamarinAndroidBundledAssembly StructureInfo? xamarinAndroidBundledAssemblyStructureInfo; StructureInfo assemblyStoreSingleAssemblyRuntimeDataStructureinfo; StructureInfo assemblyStoreRuntimeDataStructureInfo; - StructureInfo monoImage; public bool IsBundledApp { get; set; } public bool UsesMonoAOT { get; set; } @@ -175,7 +171,6 @@ sealed class XamarinAndroidBundledAssembly public MonoComponent MonoComponents { get; set; } public PackageNamingPolicy PackageNamingPolicy { get; set; } public List NativeLibraries { get; set; } - public ICollection UniqueAssemblyNames { get; set; } public ApplicationConfigNativeAssemblyGenerator (IDictionary environmentVariables, IDictionary systemProperties, TaskLoggingHelper log) { @@ -308,7 +303,6 @@ protected override void MapStructures (LlvmIrGenerator generator) assemblyStoreRuntimeDataStructureInfo = generator.MapStructure (); xamarinAndroidBundledAssemblyStructureInfo = generator.MapStructure (); dsoCacheEntryStructureInfo = generator.MapStructure (); - monoImage = generator.MapStructure (); } protected override void Write (LlvmIrGenerator generator) @@ -324,68 +318,6 @@ protected override void Write (LlvmIrGenerator generator) WriteDSOCache (generator); WriteBundledAssemblies (generator); WriteAssemblyStoreAssemblies (generator); - - WriteAssemblyImageCache (generator); - } - - void WriteAssemblyImageCache (LlvmIrGenerator generator) - { - if (UniqueAssemblyNames == null) { - throw new InvalidOperationException ("Internal error: unique assembly names not provided"); - } - - if (UniqueAssemblyNames.Count != NumberOfAssembliesInApk) { - throw new InvalidOperationException ("Internal error: number of assemblies in the apk doesn't match the number of unique assembly names"); - } - - bool is64Bit = generator.Is64Bit; - generator.WriteStructureArray (monoImage, (ulong)NumberOfAssembliesInApk, "assembly_image_cache", isArrayOfPointers: true); - - if (is64Bit) { - WriteHashes (); - } else { - WriteHashes (); - } - - void WriteHashes () where T: struct - { - var hashes = new Dictionary (); - uint index = 0; - foreach (string name in UniqueAssemblyNames) { - string clippedName = Path.GetFileNameWithoutExtension (name); - ulong hashFull = HashName (name, is64Bit); - ulong hashClipped = HashName (clippedName, is64Bit); - - // - // If the number of name forms changes, xamarin-app.hh MUST be updated to set value of the - // `number_of_assembly_name_forms_in_image_cache` constant to the number of forms. - // - hashes.Add ((T)Convert.ChangeType (hashFull, typeof(T)), (name, index)); - hashes.Add ((T)Convert.ChangeType (hashClipped, typeof(T)), (clippedName, index)); - - index++; - } - List keys = hashes.Keys.ToList (); - keys.Sort (); - - generator.WriteCommentLine ("Each entry maps hash of an assembly name to an index into the `assembly_image_cache` array"); - generator.WriteArray ( - keys, - LlvmIrVariableOptions.GlobalConstant, - "assembly_image_cache_hashes", - (int idx, T value) => $"{idx}: {hashes[value].name} => 0x{value:x} => {hashes[value].index}" - ); - - var indices = new List (); - for (int i = 0; i < keys.Count; i++) { - indices.Add (hashes[keys[i]].index); - } - generator.WriteArray ( - indices, - LlvmIrVariableOptions.GlobalConstant, - "assembly_image_cache_indices" - ); - } } void WriteAssemblyStoreAssemblies (LlvmIrGenerator generator) @@ -414,15 +346,5 @@ void WriteDSOCache (LlvmIrGenerator generator) generator.WriteStructureArray (dsoCacheEntryStructureInfo, dsoCache, "dso_cache"); } - - ulong HashName (string name, bool is64Bit) - { - byte[] nameBytes = Encoding.UTF8.GetBytes (name); - if (is64Bit) { - return XXH64.DigestOf (nameBytes, 0, nameBytes.Length); - } - - return (ulong)XXH32.DigestOf (nameBytes, 0, nameBytes.Length); - } } } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrComposer.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrComposer.cs index 4f65a90ffac..5347a1912cf 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrComposer.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrComposer.cs @@ -1,5 +1,8 @@ using System; using System.IO; +using System.Text; + +using K4os.Hash.xxHash; using Xamarin.Android.Tools; @@ -37,6 +40,16 @@ protected static string GetAbiName (AndroidTargetArch arch) }; } + protected ulong HashName (string name, bool is64Bit) + { + byte[] nameBytes = Encoding.UTF8.GetBytes (name); + if (is64Bit) { + return XXH64.DigestOf (nameBytes, 0, nameBytes.Length); + } + + return (ulong)XXH32.DigestOf (nameBytes, 0, nameBytes.Length); + } + /// /// Initialize the composer. It needs to allocate and populate all the structures that /// are used by the composer, before they can be mapped by the generator. The code here diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.cs new file mode 100644 index 00000000000..53bebe2b4e1 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +using Java.Interop.Tools.TypeNameMappings; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +using Xamarin.Android.Tasks.LLVMIR; + +namespace Xamarin.Android.Tasks +{ + class MarshalMethodsNativeAssemblyGenerator : LlvmIrComposer + { + // This is here only to generate strongly-typed IR + internal sealed class MonoClass + {} + + struct MarshalMethodsManagedClass + { + uint token; + + [NativePointer (IsNull = true)] + MonoClass klass; + }; + + public ICollection UniqueAssemblyNames { get; set; } + public int NumberOfAssembliesInApk { get; set; } + public List OverriddenMethods { get; set; } + + StructureInfo monoImage; + StructureInfo monoClass; + + public override void Init () + { + if (OverriddenMethods != null) { + Console.WriteLine ("Overridden methods:"); + foreach (string om in OverriddenMethods) { + Console.WriteLine ($" {om}"); + } + } + } + + protected override void MapStructures (LlvmIrGenerator generator) + { + monoImage = generator.MapStructure (); + monoClass = generator.MapStructure (); + } + + protected override void Write (LlvmIrGenerator generator) + { + WriteAssemblyImageCache (generator); + } + + void WriteAssemblyImageCache (LlvmIrGenerator generator) + { + if (UniqueAssemblyNames == null) { + throw new InvalidOperationException ("Internal error: unique assembly names not provided"); + } + + if (UniqueAssemblyNames.Count != NumberOfAssembliesInApk) { + throw new InvalidOperationException ("Internal error: number of assemblies in the apk doesn't match the number of unique assembly names"); + } + + bool is64Bit = generator.Is64Bit; + generator.WriteStructureArray (monoImage, (ulong)NumberOfAssembliesInApk, "assembly_image_cache", isArrayOfPointers: true); + + if (is64Bit) { + WriteHashes (); + } else { + WriteHashes (); + } + + void WriteHashes () where T: struct + { + var hashes = new Dictionary (); + uint index = 0; + foreach (string name in UniqueAssemblyNames) { + string clippedName = Path.GetFileNameWithoutExtension (name); + ulong hashFull = HashName (name, is64Bit); + ulong hashClipped = HashName (clippedName, is64Bit); + + // + // If the number of name forms changes, xamarin-app.hh MUST be updated to set value of the + // `number_of_assembly_name_forms_in_image_cache` constant to the number of forms. + // + hashes.Add ((T)Convert.ChangeType (hashFull, typeof(T)), (name, index)); + hashes.Add ((T)Convert.ChangeType (hashClipped, typeof(T)), (clippedName, index)); + + index++; + } + List keys = hashes.Keys.ToList (); + keys.Sort (); + + generator.WriteCommentLine ("Each entry maps hash of an assembly name to an index into the `assembly_image_cache` array"); + generator.WriteArray ( + keys, + LlvmIrVariableOptions.GlobalConstant, + "assembly_image_cache_hashes", + (int idx, T value) => $"{idx}: {hashes[value].name} => 0x{value:x} => {hashes[value].index}" + ); + + var indices = new List (); + for (int i = 0; i < keys.Count; i++) { + indices.Add (hashes[keys[i]].index); + } + generator.WriteArray ( + indices, + LlvmIrVariableOptions.GlobalConstant, + "assembly_image_cache_indices" + ); + } + } + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index 97006ecbd88..67afdb0e095 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -1591,6 +1591,14 @@ because xbuild doesn't support framework reference assemblies. Mode="compressed"> + + + @@ -1901,6 +1909,9 @@ because xbuild doesn't support framework reference assemblies. <_CompressedNativeAssemblyTarget Include="@(_CompressedAssembliesAssemblySource->'$([System.IO.Path]::ChangeExtension('%(Identity)', '.o'))')"> %(_CompressedAssembliesAssemblySource.abi) + <_NativeAssemblyTarget Include="@(_MarshalMethodsAssemblySource->'$([System.IO.Path]::ChangeExtension('%(Identity)', '.o'))')"> + %(_MarshalMethodsAssemblySource.abi) + @@ -1920,10 +1931,10 @@ because xbuild doesn't support framework reference assemblies. Date: Mon, 16 May 2022 15:59:28 +0200 Subject: [PATCH 07/14] Prepare for PR Disable all marshal methods code by default, we don't want it in `main` yet --- .gitmodules | 4 - Directory.Build.props | 4 + Java.Interop.diff | 61 ------------ Xamarin.Android.sln | 6 -- external/Java.Interop | 2 +- external/magic_enum | 1 - .../Tasks/GenerateJavaStubs.cs | 13 ++- .../Tasks/GeneratePackageManagerJava.cs | 16 +-- .../Tasks/PrepareAbiItems.cs | 5 +- .../MarshalMethodsNativeAssemblyGenerator.cs | 15 ++- .../Xamarin.Android.Build.Tasks.csproj | 4 + .../Xamarin.Android.Common.targets | 1 + src/monodroid/CMakeLists.txt | 21 ++-- src/monodroid/jni/generate-marshaling-data.cc | 97 ------------------- src/monodroid/jni/mono-image-loader.hh | 4 +- src/monodroid/jni/monodroid-glue.cc | 2 +- tools/mgen/Constants.cs.in | 14 --- tools/mgen/build.sh | 5 - tools/mgen/main.cs | 72 -------------- tools/mgen/mgen.csproj | 94 ------------------ tools/mgen/run.sh | 2 - 21 files changed, 57 insertions(+), 386 deletions(-) delete mode 100644 Java.Interop.diff delete mode 160000 external/magic_enum delete mode 100644 src/monodroid/jni/generate-marshaling-data.cc delete mode 100644 tools/mgen/Constants.cs.in delete mode 100755 tools/mgen/build.sh delete mode 100644 tools/mgen/main.cs delete mode 100644 tools/mgen/mgen.csproj delete mode 100755 tools/mgen/run.sh diff --git a/.gitmodules b/.gitmodules index 111117df8a8..8b2fa1df307 100644 --- a/.gitmodules +++ b/.gitmodules @@ -42,7 +42,3 @@ path = external/xamarin-android-tools url = https://github.com/xamarin/xamarin-android-tools branch = main -[submodule "external/magic_enum"] - path = external/magic_enum - url = https://github.com/Neargye/magic_enum.git - branch = master diff --git a/Directory.Build.props b/Directory.Build.props index a345c9af72c..ad57f479781 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -23,6 +23,10 @@ Major + + <_EnableMarshalMethods>NoThanks + + 12.3.99