diff --git a/src/native/clr/include/runtime-base/android-system.hh b/src/native/clr/include/runtime-base/android-system.hh index 3ddaee861b6..3b4c0dedfb7 100644 --- a/src/native/clr/include/runtime-base/android-system.hh +++ b/src/native/clr/include/runtime-base/android-system.hh @@ -1,7 +1,9 @@ #pragma once #include +#include #include +#include #include #include #include @@ -18,6 +20,7 @@ struct BundledProperty; namespace xamarin::android { class AndroidSystem { +#if !defined (XA_HOST_NATIVEAOT) // This optimizes things a little bit. The array is allocated at build time, so we pay no cost for its // allocation and at run time it allows us to skip dynamic memory allocation. inline static std::array single_app_lib_directory{}; @@ -35,6 +38,7 @@ namespace xamarin::android { std::string_view { "x86_64" }, // CPU_KIND_X86_64 std::string_view { "riscv" }, // CPU_KIND_RISCV }; +#endif public: static auto get_gref_gc_threshold () noexcept -> long @@ -60,16 +64,28 @@ namespace xamarin::android { running_in_emulator = yesno; } +#if defined (XA_HOST_NATIVEAOT) + static auto get_primary_override_dir () noexcept -> const char* + { + return primary_override_dir; + } +#else static auto get_primary_override_dir () noexcept -> std::string const& { return primary_override_dir; } +#endif static void set_primary_override_dir (jstring_wrapper& home) noexcept { +#if defined (XA_HOST_NATIVEAOT) + determine_primary_override_dir (home, primary_override_dir, sizeof (primary_override_dir)); +#else primary_override_dir = determine_primary_override_dir (home); +#endif } +#if !defined (XA_HOST_NATIVEAOT) static auto get_app_code_cache_dir () noexcept -> std::string const& { return app_code_cache_dir; @@ -104,6 +120,7 @@ namespace xamarin::android { log_debug (LOG_DEFAULT, "Creating public update directory: `{}`", override_dir); Util::create_public_directory (override_dir); } +#endif static auto is_embedded_dso_mode_enabled () noexcept -> bool { @@ -139,6 +156,19 @@ namespace xamarin::android { embedded_dso_mode_enabled = yesno; } +#if defined (XA_HOST_NATIVEAOT) + static void determine_primary_override_dir (jstring_wrapper &home, char *buffer, size_t buffer_size) noexcept + { + dynamic_local_string name { home.get_cstr () }; + name.append ("/") + .append (Constants::OVERRIDE_DIRECTORY_NAME) + .append ("/") + .append (Constants::android_lib_abi); + + abort_unless (name.length () < buffer_size, "Primary override directory path is too long"); + memcpy (buffer, name.get (), name.length () + 1); + } +#else static auto determine_primary_override_dir (jstring_wrapper &home) noexcept -> std::string { dynamic_local_string name { home.get_cstr () }; @@ -149,17 +179,22 @@ namespace xamarin::android { return {name.get (), name.length ()}; } +#endif private: static inline long max_gref_count = 0; static inline bool running_in_emulator = false; static inline bool embedded_dso_mode_enabled = false; +#if defined (XA_HOST_NATIVEAOT) + static inline char primary_override_dir[Constants::SENSIBLE_PATH_MAX] {}; +#else static inline std::string primary_override_dir; static inline std::string native_libraries_dir; static inline std::string app_code_cache_dir; #if defined (DEBUG) static inline std::unordered_map bundled_properties; +#endif #endif }; } diff --git a/src/native/clr/runtime-base/logger.cc b/src/native/clr/runtime-base/logger.cc index a61b68d4a76..07edfa25d8d 100644 --- a/src/native/clr/runtime-base/logger.cc +++ b/src/native/clr/runtime-base/logger.cc @@ -4,7 +4,6 @@ #include #include #include -#include #include #include @@ -22,10 +21,26 @@ using namespace xamarin::android; using std::operator""sv; namespace { - std::string gref_file{}; - std::string lref_file{}; + char *gref_file = nullptr; + char *lref_file = nullptr; bool light_gref = false; bool light_lref = false; + + void set_log_file (char *&log_file, std::string_view path) noexcept + { + char *new_log_file = nullptr; + if (!path.empty ()) { + size_t allocation_size = Helpers::add_with_overflow_check (path.length (), 1uz); + new_log_file = static_cast (std::malloc (allocation_size)); + abort_unless (new_log_file != nullptr, "Failed to allocate reference log file path"); + + memcpy (new_log_file, path.data (), path.length ()); + new_log_file [path.length ()] = '\0'; + } + + std::free (log_file); + log_file = new_log_file; + } } [[gnu::always_inline]] @@ -62,30 +77,46 @@ auto Logger::open_file (LogCategories category, std::string_view const& custom_p return log_and_return (ret, custom_path); } - std::string p{}; Util::create_public_directory (override_dir); - p.assign (override_dir); - p.append ("/"); - p.append (fallback_filename); + dynamic_local_string p; + p.append (override_dir) + .append ("/") + .append (fallback_filename); - return log_and_return (open_file (p), p); + std::string_view path = p.as_string_view (); + return log_and_return (open_file (path), path); } void Logger::init_reference_logging (std::string_view const& override_dir) noexcept { if ((log_categories & LOG_GREF) != 0 && !light_gref) { - _gref_log = open_file (LOG_GREF, gref_file, override_dir, "grefs.txt"sv); + _gref_log = open_file ( + LOG_GREF, + gref_file == nullptr ? std::string_view {} : std::string_view { gref_file }, + override_dir, + "grefs.txt"sv + ); } if ((log_categories & LOG_LREF) != 0 && !light_lref) { // if both lref & gref have files specified, and they're the same path, reuse the FILE*. - if (!lref_file.empty () && strcmp (lref_file.c_str (), !gref_file.empty () ? gref_file.c_str () : "") == 0) { + if (lref_file != nullptr && strcmp (lref_file, gref_file != nullptr ? gref_file : "") == 0) { _lref_log = _gref_log; } else { - _lref_log = open_file (LOG_LREF, lref_file, override_dir, "lrefs.txt"sv); + _lref_log = open_file ( + LOG_LREF, + lref_file == nullptr ? std::string_view {} : std::string_view { lref_file }, + override_dir, + "lrefs.txt"sv + ); } } + + std::free (gref_file); + gref_file = nullptr; + std::free (lref_file); + lref_file = nullptr; } [[gnu::always_inline]] bool @@ -158,20 +189,20 @@ Logger::init_logging_categories () noexcept continue; } - auto get_log_file_name = [](std::string_view const& file_kind, string_segment const& segment, size_t offset) -> const char* { + auto get_log_file_name = [](std::string_view const& file_kind, string_segment const& segment, size_t offset) -> std::string_view { auto file_name = segment.at (offset); if (!file_name.has_value ()) { log_warn (LOG_DEFAULT, "Unable to set path to {} log file: {}", file_kind, to_string (file_name.error ())); - return nullptr; + return {}; } - return file_name.value (); + return { file_name.value (), segment.length () - offset }; }; constexpr std::string_view CAT_GREF_EQUALS { "gref=" }; if (set_category (CAT_GREF_EQUALS, param, LOG_GREF, true /* arg_starts_with_name */)) { - gref_file = get_log_file_name ("gref"sv, param, CAT_GREF_EQUALS.length ()); + set_log_file (gref_file, get_log_file_name ("gref"sv, param, CAT_GREF_EQUALS.length ())); continue; } @@ -187,7 +218,7 @@ Logger::init_logging_categories () noexcept constexpr std::string_view CAT_LREF_EQUALS { "lref=" }; if (set_category (CAT_LREF_EQUALS, param, LOG_LREF, true /* arg_starts_with_name */)) { - lref_file = get_log_file_name ("lref"sv, param, CAT_LREF_EQUALS.length ()); + set_log_file (lref_file, get_log_file_name ("lref"sv, param, CAT_LREF_EQUALS.length ())); continue; } diff --git a/src/native/common/include/shared/cpp-util.hh b/src/native/common/include/shared/cpp-util.hh index 6693ac69793..19755db4cd0 100644 --- a/src/native/common/include/shared/cpp-util.hh +++ b/src/native/common/include/shared/cpp-util.hh @@ -6,12 +6,9 @@ #include #include #include -#include #include -#include #include #include -#include #include #include @@ -33,49 +30,127 @@ namespace xamarin::android::detail { return ret == -1 ? "Out of memory" : message; } - [[gnu::always_inline]] - static inline std::string get_function_name (const char *signature) + static inline constexpr size_t find_function_parameter_list (std::string_view signature) noexcept { using std::operator""sv; - std::string_view sig { signature }; - if (sig.length () == 0) { - return ""; + size_t search_end = signature.find (" ["sv); + if (search_end == std::string_view::npos) { + search_end = signature.length (); } - auto splitSignature = sig | std::views::split ("::"sv) | std::ranges::to> (); + if (search_end == 0) { + return std::string_view::npos; + } - std::string ret; - if (splitSignature.size () > 1) { - ret.append (splitSignature [splitSignature.size () - 2]); - ret.append ("::"sv); + size_t close_pos = signature.rfind (')', search_end - 1); + if (close_pos == std::string_view::npos) { + return std::string_view::npos; } - std::string_view func_name { splitSignature[splitSignature.size () - 1] }; - std::string_view::size_type args_pos = func_name.find ('('); - std::string_view::size_type name_start_pos = func_name.find (' '); - - if (name_start_pos == std::string_view::npos) { - name_start_pos = 0; - } else { - name_start_pos++; // point to after the space which separates return type from name - if (name_start_pos >= func_name.length ()) [[unlikely]] { - name_start_pos = 0; + + size_t depth = 0; + for (size_t pos = close_pos + 1; pos > 0;) { + char ch = signature [--pos]; + if (ch == ')') { + depth++; + } else if (ch == '(') { + if (depth == 0) [[unlikely]] { + return std::string_view::npos; + } + + depth--; + if (depth == 0) { + return pos; + } } } - if (args_pos == std::string_view::npos) { - ret.append (func_name.substr (name_start_pos)); - } else { - // If there's a snafu with positions, start from 0 - if (name_start_pos >= args_pos || name_start_pos > func_name.length ()) [[unlikely]] { - name_start_pos = 0; + return std::string_view::npos; + } + + static inline constexpr size_t find_function_component_start (std::string_view signature, size_t component_end) noexcept + { + size_t nesting_depth = 0; + + for (size_t pos = component_end; pos > 0;) { + char ch = signature [--pos]; + if (ch == ')' || ch == '>' || ch == ']') { + nesting_depth++; + } else if (ch == '(' || ch == '<' || ch == '[') { + if (nesting_depth > 0) { + nesting_depth--; + } + } else if (ch == ' ' && nesting_depth == 0) { + return pos + 1; } + } - ret.append (func_name.substr (name_start_pos, args_pos - name_start_pos)); + return 0; + } + + static inline constexpr std::string_view get_function_name (const char *signature) noexcept + { + using std::operator""sv; + + if (signature == nullptr || *signature == '\0') { + return ""sv; } - return ret; + std::string_view sig { signature }; + size_t name_end = find_function_parameter_list (sig); + if (name_end == std::string_view::npos) { + name_end = sig.length (); + } + + size_t operator_pos = sig.rfind ("operator"sv, name_end); + if (operator_pos != std::string_view::npos) { + auto is_identifier_character = [](char ch) constexpr { + return (ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9') || + ch == '_'; + }; + + size_t operator_end = operator_pos + "operator"sv.length (); + if ((operator_pos > 0 && is_identifier_character (sig [operator_pos - 1])) || + (operator_end < name_end && is_identifier_character (sig [operator_end]))) { + operator_pos = std::string_view::npos; + } + } + + size_t scope_pos = sig.rfind ("::"sv, operator_pos == std::string_view::npos ? name_end : operator_pos); + bool have_scoped_operator = operator_pos != std::string_view::npos && + scope_pos != std::string_view::npos && + scope_pos + 2 == operator_pos; + + size_t qualified_name_start = find_function_component_start (sig, have_scoped_operator ? scope_pos : name_end); + size_t name_start = qualified_name_start; + if (operator_pos != std::string_view::npos && !have_scoped_operator) { + name_start = operator_pos; + } else if (scope_pos != std::string_view::npos && scope_pos >= qualified_name_start) { + size_t previous_scope_pos = scope_pos == 0 ? std::string_view::npos : sig.rfind ("::"sv, scope_pos - 1); + if (previous_scope_pos != std::string_view::npos && previous_scope_pos >= qualified_name_start) { + name_start = previous_scope_pos + 2; + } + } + + if (name_start >= name_end || name_start > sig.length ()) [[unlikely]] { + name_start = 0; + } + + return sig.substr (name_start, name_end - name_start); } + + static_assert (get_function_name ("void ordinary_function()") == std::string_view { "ordinary_function" }); + static_assert (get_function_name ("void example::Widget::method(int)") == std::string_view { "Widget::method" }); + static_assert (get_function_name ("void example::Widget::method()") == std::string_view { "Widget::method" }); + static_assert (get_function_name ("void example::Widget::method(U) [with T = int; U = long int]") == std::string_view { "Widget::method" }); + static_assert (get_function_name ("void example::Widget::method()::::operator()(int) const") == std::string_view { "::operator()" }); + static_assert (get_function_name ("bool operator==(const Value&, const Value&)") == std::string_view { "operator==" }); + static_assert (get_function_name ("bool example::Value::operator==(const Value&) const") == std::string_view { "Value::operator==" }); + static_assert (get_function_name ("void malformed(") == std::string_view { "malformed(" }); + static_assert (get_function_name (nullptr) == std::string_view { "" }); + static_assert (get_function_name ("") == std::string_view { "" }); } template F> @@ -110,9 +185,11 @@ abort_if_invalid_pointer_argument (T *ptr, const char *ptr_name, std::source_loc abort_unless ( ptr != nullptr, [&ptr_name, &sloc] { + std::string_view function_name = xamarin::android::detail::get_function_name (sloc.function_name ()); return xamarin::android::detail::_format_message ( - "%s: parameter '%s' must be a valid pointer", - xamarin::android::detail::get_function_name (sloc.function_name ()).c_str (), + "%.*s: parameter '%s' must be a valid pointer", + static_cast(function_name.length ()), + function_name.data (), ptr_name ); }, @@ -127,9 +204,11 @@ abort_if_negative_integer_argument (int arg, const char *arg_name, std::source_l abort_unless ( arg > 0, [&arg_name, &sloc] { + std::string_view function_name = xamarin::android::detail::get_function_name (sloc.function_name ()); return xamarin::android::detail::_format_message ( - "%s: parameter '%s' must be a positive integer", - xamarin::android::detail::get_function_name (sloc.function_name ()).c_str (), + "%.*s: parameter '%s' must be a positive integer", + static_cast(function_name.length ()), + function_name.data (), arg_name ); },