/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
v25.8.1
src/debug_utils-inl.h
278 строк
9 KB
Anna Henningsen
src: avoid C strings in more C++ exception throws
10 ноя 2025, 15:38
Не верифицирован
10 ноя 2025, 15:38
a6c2213
Код
Авторство
О чём код?
#ifndef SRC_DEBUG_UTILS_INL_H_ #define SRC_DEBUG_UTILS_INL_H_ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include "debug_utils.h" #include "env.h" #include "util-inl.h" #include <type_traits> namespace node { template <typename T> concept StringViewConvertible = requires(T a) { { a.ToStringView() } -> std::convertible_to<std::string_view>; }; template <typename T> concept StringConvertible = requires(T a) { { a.ToString() } -> std::convertible_to<std::string>; }; // For std::filesystem::path and similar types template <typename T> concept StringConvertibleFSPathLike = requires(T a) { { a.string() } -> std::convertible_to<std::string>; }; struct ToStringHelper { template <typename T> requires(StringConvertible<T>) && (!StringViewConvertible<T>) static std::string Convert(const T& value) { return value.ToString(); } template <typename T> requires(StringConvertibleFSPathLike<T>) && (!StringViewConvertible<T>) && (!StringConvertible<T>) static std::string Convert(const T& value) { return value.string(); } template <typename T> requires StringViewConvertible<T> static std::string_view Convert(const T& value) { return value.ToStringView(); } template <typename T, typename test_for_number = typename std:: enable_if_t<std::is_arithmetic_v<T> || std::is_enum_v<T>, bool>, typename dummy = bool> static std::string Convert(const T& value) { return std::to_string(value); } static std::string_view Convert(const char* value) { return value != nullptr ? value : "(null)"; } static std::string Convert(const std::string& value) { return value; } static std::string_view Convert(std::string_view value) { return value; } static std::string Convert(bool value) { return value ? "true" : "false"; } template <unsigned BASE_BITS, typename T, typename = std::enable_if_t<std::is_integral_v<T>>> static std::string BaseConvert(const T& value) { auto v = static_cast<uint64_t>(value); char ret[3 * sizeof(T)]; char* ptr = ret + 3 * sizeof(T) - 1; *ptr = '\0'; const char* digits = "0123456789abcdef"; do { unsigned digit = v & ((1 << BASE_BITS) - 1); *--ptr = (BASE_BITS < 4 ? static_cast<char>('0' + digit) : digits[digit]); } while ((v >>= BASE_BITS) != 0); return ptr; } template <unsigned BASE_BITS, typename T, typename = std::enable_if_t<!std::is_integral_v<T>>> static auto BaseConvert(T&& value) { return Convert(std::forward<T>(value)); } }; template <typename T> auto ToStringOrStringView(const T& value) { return ToStringHelper::Convert(value); } template <typename T> std::string ToString(const T& value) { return std::string(ToStringOrStringView(value)); } template <unsigned BASE_BITS, typename T> auto ToBaseString(const T& value) { return ToStringHelper::BaseConvert<BASE_BITS>(value); } inline std::string SPrintFImpl(std::string_view format) { auto offset = format.find('%'); if (offset == std::string_view::npos) return std::string(format); CHECK_LT(offset + 1, format.size()); CHECK_EQ(format[offset + 1], '%'); // Only '%%' allowed when there are no arguments. return std::string(format.substr(0, offset + 1)) + SPrintFImpl(format.substr(offset + 2)); } template <typename Arg, typename... Args> std::string COLD_NOINLINE SPrintFImpl( // NOLINT(runtime/string) std::string_view format, Arg&& arg, Args&&... args) { auto offset = format.find('%'); CHECK_NE(offset, std::string_view::npos); // If you hit this, you passed in // too many arguments. std::string ret(format.substr(0, offset)); // Ignore long / size_t modifiers while (++offset < format.size() && (format[offset] == 'l' || format[offset] == 'z')) { } switch (offset == format.size() ? '\0' : format[offset]) { case '%': { return ret + '%' + SPrintFImpl(format.substr(offset + 1), std::forward<Arg>(arg), std::forward<Args>(args)...); } default: { return ret + '%' + SPrintFImpl(format.substr(offset), std::forward<Arg>(arg), std::forward<Args>(args)...); } case 'd': case 'i': case 'u': case 's': ret += ToStringOrStringView(arg); break; case 'o': ret += ToBaseString<3>(arg); break; case 'x': ret += ToBaseString<4>(arg); break; case 'X': ret += node::ToUpper(ToBaseString<4>(arg)); break; case 'p': { CHECK(std::is_pointer_v<typename std::remove_reference_t<Arg>>); char out[20]; int n = snprintf( out, sizeof(out), "%p", *reinterpret_cast<const void* const*>(&arg)); CHECK_GE(n, 0); ret += out; break; } } return ret + SPrintFImpl(format.substr(offset + 1), std::forward<Args>(args)...); } template <typename... Args> std::string COLD_NOINLINE SPrintF( // NOLINT(runtime/string) std::string_view format, Args&&... args) { return SPrintFImpl(format, std::forward<Args>(args)...); } template <typename... Args> void COLD_NOINLINE FPrintF(FILE* file, std::string_view format, Args&&... args) { FWrite(file, SPrintF(format, std::forward<Args>(args)...)); } template <typename... Args> inline void FORCE_INLINE Debug(EnabledDebugList* list, DebugCategory cat, const char* format, Args&&... args) { if (!list->enabled(cat)) [[unlikely]] return; FPrintF(stderr, format, std::forward<Args>(args)...); } inline void FORCE_INLINE Debug(EnabledDebugList* list, DebugCategory cat, const char* message) { if (!list->enabled(cat)) [[unlikely]] return; FPrintF(stderr, "%s", message); } template <typename... Args> inline void FORCE_INLINE Debug(Environment* env, DebugCategory cat, const char* format, Args&&... args) { Debug(env->enabled_debug_list(), cat, format, std::forward<Args>(args)...); } inline void FORCE_INLINE Debug(Environment* env, DebugCategory cat, const char* message) { Debug(env->enabled_debug_list(), cat, message); } template <typename... Args> inline void Debug(Environment* env, DebugCategory cat, const std::string& format, Args&&... args) { Debug(env->enabled_debug_list(), cat, format.c_str(), std::forward<Args>(args)...); } // Used internally by the 'real' Debug(AsyncWrap*, ...) functions below, so that // the FORCE_INLINE flag on them doesn't apply to the contents of this function // as well. // We apply COLD_NOINLINE to tell the compiler that it's not worth optimizing // this function for speed and it should rather focus on keeping it out of // hot code paths. In particular, we want to keep the string concatenating code // out of the function containing the original `Debug()` call. template <typename... Args> void COLD_NOINLINE UnconditionalAsyncWrapDebug(AsyncWrap* async_wrap, const char* format, Args&&... args) { Debug(async_wrap->env(), static_cast<DebugCategory>(async_wrap->provider_type()), async_wrap->diagnostic_name() + " " + format + "\n", std::forward<Args>(args)...); } template <typename... Args> inline void FORCE_INLINE Debug(AsyncWrap* async_wrap, const char* format, Args&&... args) { DCHECK_NOT_NULL(async_wrap); if (auto cat = static_cast<DebugCategory>(async_wrap->provider_type()); !async_wrap->env()->enabled_debug_list()->enabled(cat)) [[unlikely]] { return; } UnconditionalAsyncWrapDebug(async_wrap, format, std::forward<Args>(args)...); } template <typename... Args> inline void FORCE_INLINE Debug(AsyncWrap* async_wrap, const std::string& format, Args&&... args) { Debug(async_wrap, format.c_str(), std::forward<Args>(args)...); } namespace per_process { template <typename... Args> inline void FORCE_INLINE Debug(DebugCategory cat, const char* format, Args&&... args) { Debug(&enabled_debug_list, cat, format, std::forward<Args>(args)...); } inline void FORCE_INLINE Debug(DebugCategory cat, const char* message) { Debug(&enabled_debug_list, cat, message); } } // namespace per_process } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #endif // SRC_DEBUG_UTILS_INL_H_