diff --git a/builtin-functions/kphp-light/stdlib/rpc.txt b/builtin-functions/kphp-light/stdlib/rpc.txt index 90211d0d7f..9b64e6035f 100644 --- a/builtin-functions/kphp-light/stdlib/rpc.txt +++ b/builtin-functions/kphp-light/stdlib/rpc.txt @@ -113,6 +113,8 @@ function rpc_queue_push ($queue_id ::: int, $request_id ::: int) ::: void; function rpc_queue_empty ($queue_id ::: int) ::: bool; +function rpc_parse ($data) ::: bool; + /** @kphp-extern-func-info interruptible */ function rpc_queue_next ($queue_id ::: int, $timeout ::: float = -1.0) ::: int | false; @@ -121,9 +123,6 @@ function rpc_queue_next ($queue_id ::: int, $timeout ::: float = -1.0) ::: int | /** @kphp-extern-func-info interruptible stub generation-required */ function new_rpc_connection ($str ::: string, $port ::: int, $actor_id ::: mixed = 0, $timeout ::: float = 0.3, $connect_timeout ::: float = 0.3, $reconnect_timeout ::: float = 17.0) ::: \RpcConnection; // TODO: make actor_id int -/** @kphp-extern-func-info stub */ -function rpc_parse ($data) ::: bool; - /** @kphp-extern-func-info interruptible stub generation-required */ function store_finish() ::: bool; diff --git a/builtin-functions/kphp-light/stdlib/serialize-functions.txt b/builtin-functions/kphp-light/stdlib/serialize-functions.txt index e39aa0acb5..8e429a964a 100644 --- a/builtin-functions/kphp-light/stdlib/serialize-functions.txt +++ b/builtin-functions/kphp-light/stdlib/serialize-functions.txt @@ -48,13 +48,15 @@ function msgpack_serialize($v ::: mixed) ::: string | null; function msgpack_deserialize($v ::: string) ::: mixed; +/** @kphp-extern-func-info can_throw */ +function instance_serialize_safe(object $instance) ::: string; + +/** @kphp-extern-func-info cpp_template_call can_throw */ +function instance_deserialize_safe($serialized ::: string, $to_type ::: string) ::: instance<^2>; + // ===== UNSUPPORTED ===== /** @kphp-extern-func-info can_throw stub generation-required */ function msgpack_serialize_safe($v ::: mixed) ::: string; /** @kphp-extern-func-info can_throw stub generation-required */ function msgpack_deserialize_safe($v ::: string) ::: mixed; -/** @kphp-extern-func-info can_throw stub */ -function instance_serialize_safe(object $instance) ::: string; -/** @kphp-extern-func-info cpp_template_call can_throw stub */ -function instance_deserialize_safe($serialized ::: string, $to_type ::: string) ::: instance<^2>; diff --git a/builtin-functions/kphp-light/stdlib/server-functions.txt b/builtin-functions/kphp-light/stdlib/server-functions.txt index 6318fd793b..5a62421a11 100644 --- a/builtin-functions/kphp-light/stdlib/server-functions.txt +++ b/builtin-functions/kphp-light/stdlib/server-functions.txt @@ -65,6 +65,10 @@ function memory_get_usage ($real_usage ::: bool = false) ::: int; function memory_get_peak_usage ($real_usage ::: bool = false) ::: int; +function setlocale ($category ::: int, $locale ::: string) ::: string | false; + +function memory_get_detailed_stats() ::: int[]; + // ===== UNSUPPORTED ===== /** @kphp-extern-func-info stub */ @@ -94,8 +98,6 @@ function inet_pton ($address ::: string) ::: string | false; function memory_get_total_usage() ::: int; /** @kphp-extern-func-info stub generation-required */ function memory_get_static_usage() ::: int; -/** @kphp-extern-func-info stub generation-required */ -function memory_get_detailed_stats() ::: int[]; /** @kphp-extern-func-info stub */ function kphp_extended_instance_cache_metrics_init(callable(string $key):string $normalization_function) ::: void; @@ -118,9 +120,6 @@ define('LC_NUMERIC', 1); define('LC_TIME', 2); define('LC_MESSAGES', 5); -/** @kphp-extern-func-info stub generation-required */ -function setlocale ($category ::: int, $locale ::: string) ::: string | false; - function debug_backtrace() ::: string[][]; /** @kphp-extern-func-info stub generation-required */ diff --git a/runtime-light/k2-platform/k2-api.h b/runtime-light/k2-platform/k2-api.h index 165e00286c..8884086177 100644 --- a/runtime-light/k2-platform/k2-api.h +++ b/runtime-light/k2-platform/k2-api.h @@ -292,6 +292,18 @@ inline struct tm* localtime_r(const time_t* timer, struct tm* result) noexcept { return k2_localtime_r(timer, result); } +inline int32_t uselocale(int32_t category, std::string_view locale) noexcept { + return k2_uselocale(category, locale.data()); +} + +inline std::optional current_locale_name(int32_t category) noexcept { + auto* name{k2_current_locale_name(category)}; + if (name == nullptr) [[unlikely]] { + return std::nullopt; + } + return std::string_view{name}; +} + inline int32_t udp_connect(k2::descriptor* descriptor, const char* host, size_t host_len) noexcept { return k2_udp_connect(descriptor, host, host_len); } diff --git a/runtime-light/stdlib/diagnostics/exception-functions.h b/runtime-light/stdlib/diagnostics/exception-functions.h index fb2fda357a..5acb20662b 100644 --- a/runtime-light/stdlib/diagnostics/exception-functions.h +++ b/runtime-light/stdlib/diagnostics/exception-functions.h @@ -6,6 +6,8 @@ #include #include +#include +#include #include "runtime-common/core/runtime-core.h" #include "runtime-light/stdlib/diagnostics/exception-types.h" @@ -65,17 +67,22 @@ namespace kphp::exception { template T> -class_instance make_throwable(const string& file, int64_t line, int64_t code, const string& desc) noexcept { +class_instance make_throwable(string file, int64_t line, int64_t code, string desc) noexcept { auto instance{make_instance()}; auto* instance_ptr{instance.get()}; - instance_ptr->$file = file; + instance_ptr->$file = std::move(file); instance_ptr->$line = line; instance_ptr->$code = code; - instance_ptr->$message = desc; + instance_ptr->$message = std::move(desc); return instance; } +template T> +class_instance make_throwable(string err_msg, int64_t code = 0, std::source_location loc = std::source_location::current()) noexcept { + return make_throwable(string{loc.file_name()}, loc.line(), code, std::move(err_msg)); +} + } // namespace kphp::exception // ================================================================================================ @@ -87,6 +94,7 @@ T f$_exception_set_location(const T& e, const string& file, int64_t line) noexce return e; } -inline Exception f$err(const string& file, int64_t line, const string& code, const string& desc = {}) noexcept { - return kphp::exception::make_throwable(file, line, 0, (RuntimeContext::get().static_SB.clean() << "ERR_" << code << ": " << desc).str()); +inline Exception f$err(string file, int64_t line, const string& code, const string& desc = {}) noexcept { + return kphp::exception::make_throwable(std::move(file), line, 0, + (RuntimeContext::get().static_SB.clean() << "ERR_" << code << ": " << desc).str()); } diff --git a/runtime-light/stdlib/memory/memory-usage.h b/runtime-light/stdlib/memory/memory-usage.h index 61955b6b55..d43383c634 100644 --- a/runtime-light/stdlib/memory/memory-usage.h +++ b/runtime-light/stdlib/memory/memory-usage.h @@ -5,8 +5,10 @@ #pragma once #include +#include #include "runtime-common/core/allocator/runtime-allocator.h" +#include "runtime-common/core/runtime-core.h" inline int64_t f$memory_get_peak_usage(bool real_usage = false) noexcept { if (real_usage) { @@ -19,3 +21,16 @@ inline int64_t f$memory_get_peak_usage(bool real_usage = false) noexcept { inline int64_t f$memory_get_usage([[maybe_unused]] bool real_usage = false) noexcept { return static_cast(RuntimeAllocator::get().memory_resource.get_memory_stats().memory_used); } + +inline array f$memory_get_detailed_stats() noexcept { + const auto& stats{RuntimeAllocator::get().memory_resource.get_memory_stats()}; + return array({std::make_pair(string{"memory_limit"}, static_cast(stats.memory_limit)), + std::make_pair(string{"real_memory_used"}, static_cast(stats.real_memory_used)), + std::make_pair(string{"memory_used"}, static_cast(stats.memory_used)), + std::make_pair(string{"max_real_memory_used"}, static_cast(stats.max_real_memory_used)), + std::make_pair(string{"max_memory_used"}, static_cast(stats.max_memory_used)), + std::make_pair(string{"defragmentation_calls"}, static_cast(stats.defragmentation_calls)), + std::make_pair(string{"huge_memory_pieces"}, static_cast(stats.huge_memory_pieces)), + std::make_pair(string{"small_memory_pieces"}, static_cast(stats.small_memory_pieces)), + std::make_pair(string{"heap_memory_used"}, static_cast(0))}); +} diff --git a/runtime-light/stdlib/rpc/rpc-api.h b/runtime-light/stdlib/rpc/rpc-api.h index f7b5913ce3..f10da91c60 100644 --- a/runtime-light/stdlib/rpc/rpc-api.h +++ b/runtime-light/stdlib/rpc/rpc-api.h @@ -170,9 +170,33 @@ inline bool f$rpc_clean() noexcept { return true; } -template -bool f$rpc_parse(T /*unused*/) { - kphp::log::error("call to unsupported function"); +inline bool f$rpc_parse(const string& new_rpc_data) noexcept { + if (new_rpc_data.size() % sizeof(int32_t) != 0) { + kphp::log::warning("wrong parameter \"new_rpc_data\" of len {} passed to function rpc_parse", new_rpc_data.size()); + return false; + } + + const std::span spn{reinterpret_cast(new_rpc_data.c_str()), new_rpc_data.size()}; + RpcServerInstanceState::get().tl_storer.store_bytes(spn); + return true; +} + +inline bool f$rpc_parse(const mixed& new_rpc_data) noexcept { + if (!new_rpc_data.is_string()) { + kphp::log::warning("parameter 1 of function rpc_parse must be a string, {} is given", new_rpc_data.get_type_c_str()); + return false; + } + + return f$rpc_parse(new_rpc_data.to_string()); +} + +inline bool f$rpc_parse(bool new_rpc_data) noexcept { + return f$rpc_parse(mixed{new_rpc_data}); +} + +inline bool f$rpc_parse(const Optional& new_rpc_data) noexcept { + constexpr auto rpc_parse_lambda{[](const auto& v) noexcept { return f$rpc_parse(v); }}; + return call_fun_on_optional_value(rpc_parse_lambda, new_rpc_data); } // f$rpc_server_fetch_request() definition is generated into the tl/rpc_server_fetch_request.cpp file. diff --git a/runtime-light/stdlib/serialization/msgpack-functions.h b/runtime-light/stdlib/serialization/msgpack-functions.h index c378719019..472d57d90e 100644 --- a/runtime-light/stdlib/serialization/msgpack-functions.h +++ b/runtime-light/stdlib/serialization/msgpack-functions.h @@ -4,8 +4,13 @@ #pragma once +#include +#include + #include "runtime-common/core/runtime-core.h" #include "runtime-common/stdlib/msgpack/unpacker.h" +#include "runtime-common/stdlib/serialization/msgpack-functions.h" +#include "runtime-light/stdlib/diagnostics/exception-functions.h" #include "runtime-light/stdlib/diagnostics/logs.h" #include "runtime-light/stdlib/serialization/serialization-state.h" @@ -51,11 +56,24 @@ ResultClass f$instance_deserialize(const string& buffer, const string& /*unused* } template -string f$instance_serialize_safe(const class_instance& /*instance*/) noexcept { - kphp::log::error("call to unsupported function"); +string f$instance_serialize_safe(const class_instance& instance) noexcept { + string err_msg; + auto result{msgpack_functions_impl_::common_instance_serialize(instance, std::addressof(err_msg))}; + if (!err_msg.empty()) { + THROW_EXCEPTION(kphp::exception::make_throwable(std::move(err_msg))); + return {}; + } + kphp::log::assertion(result.has_value()); + return result.val(); } template -ResultClass f$instance_deserialize_safe(const string& /*buffer*/, const string& /*unused*/) noexcept { - kphp::log::error("call to unsupported function"); +ResultClass f$instance_deserialize_safe(const string& buffer, const string& /*unused*/) noexcept { + string err_msg; + auto res{f$msgpack_deserialize(buffer, std::addressof(err_msg))}; + if (!err_msg.empty()) { + THROW_EXCEPTION(kphp::exception::make_throwable(std::move(err_msg))); + return {}; + } + return res; } diff --git a/runtime-light/stdlib/string/string-functions.h b/runtime-light/stdlib/string/string-functions.h new file mode 100644 index 0000000000..28b7ad35c6 --- /dev/null +++ b/runtime-light/stdlib/string/string-functions.h @@ -0,0 +1,22 @@ +// Compiler for PHP (aka KPHP) +// Copyright (c) 2025 LLC «V Kontakte» +// Distributed under the GPL v3 License, see LICENSE.notice.txt + +#pragma once + +#include + +#include "runtime-common/core/runtime-core.h" +#include "runtime-light/k2-platform/k2-api.h" + +inline Optional f$setlocale(int64_t category, const string& locale) noexcept { + const int32_t i32category{static_cast(category)}; + if (k2::uselocale(i32category, {locale.c_str(), locale.size()}) != k2::errno_ok) { + return false; + } + const auto opt_locale_name{k2::current_locale_name(i32category)}; + if (!opt_locale_name.has_value()) [[unlikely]] { + return false; + } + return opt_locale_name->data(); +} diff --git a/runtime/rpc.cpp b/runtime/rpc.cpp index c9e947f0c3..ecfaf06853 100644 --- a/runtime/rpc.cpp +++ b/runtime/rpc.cpp @@ -138,7 +138,7 @@ void rpc_parse(const int32_t* new_rpc_data, int32_t new_rpc_data_len) { rpc_data_len = new_rpc_data_len; } -bool f$rpc_parse(const string& new_rpc_data) { +bool f$rpc_parse(const string& new_rpc_data) noexcept { if (new_rpc_data.size() % sizeof(int) != 0) { php_warning("Wrong parameter \"new_rpc_data\" of len %d passed to function rpc_parse", (int)new_rpc_data.size()); last_rpc_error = "Result's length is not divisible by 4"; @@ -157,7 +157,7 @@ bool f$rpc_parse(const string& new_rpc_data) { return true; } -bool f$rpc_parse(const mixed& new_rpc_data) { +bool f$rpc_parse(const mixed& new_rpc_data) noexcept { if (!new_rpc_data.is_string()) { php_warning("Parameter 1 of function rpc_parse must be a string, %s is given", new_rpc_data.get_type_c_str()); return false; @@ -166,12 +166,12 @@ bool f$rpc_parse(const mixed& new_rpc_data) { return f$rpc_parse(new_rpc_data.to_string()); } -bool f$rpc_parse(bool new_rpc_data) { +bool f$rpc_parse(bool new_rpc_data) noexcept { return f$rpc_parse(mixed{new_rpc_data}); } -bool f$rpc_parse(const Optional& new_rpc_data) { - auto rpc_parse_lambda = [](const auto& v) { return f$rpc_parse(v); }; +bool f$rpc_parse(const Optional& new_rpc_data) noexcept { + constexpr auto rpc_parse_lambda = [](const auto& v) noexcept { return f$rpc_parse(v); }; return call_fun_on_optional_value(rpc_parse_lambda, new_rpc_data); } diff --git a/runtime/rpc.h b/runtime/rpc.h index 740c314115..7fc2032c71 100644 --- a/runtime/rpc.h +++ b/runtime/rpc.h @@ -65,13 +65,13 @@ void last_rpc_error_reset(); void rpc_parse(const int32_t* new_rpc_data, int32_t new_rpc_data_len); -bool f$rpc_parse(const string& new_rpc_data); +bool f$rpc_parse(const string& new_rpc_data) noexcept; -bool f$rpc_parse(const mixed& new_rpc_data); +bool f$rpc_parse(const mixed& new_rpc_data) noexcept; -bool f$rpc_parse(bool new_rpc_data); +bool f$rpc_parse(bool new_rpc_data) noexcept; -bool f$rpc_parse(const Optional& new_rpc_data); +bool f$rpc_parse(const Optional& new_rpc_data) noexcept; int32_t rpc_get_pos();