diff --git a/builtin-functions/kphp-full/_functions.txt b/builtin-functions/kphp-full/_functions.txt index 9c54afce6e..449ea8b7c5 100644 --- a/builtin-functions/kphp-full/_functions.txt +++ b/builtin-functions/kphp-full/_functions.txt @@ -991,9 +991,15 @@ function rpc_tl_query_result ($query_ids ::: array) ::: mixed[][]; function rpc_tl_query_result_synchronously ($query_ids ::: array) ::: mixed[][]; function rpc_tl_pending_queries_count () ::: int; -/** @kphp-tl-class */ +/** + * @kphp-required + * @kphp-tl-class + */ interface RpcFunction { + public function getTLFunctionMagic() : int; public function getTLFunctionName() : string; + public function typedStore() : @tl\RpcFunctionFetcher; + public function typedFetch() : @tl\RpcFunctionFetcher; } /** @kphp-tl-class */ diff --git a/builtin-functions/kphp-light/stdlib/rpc.txt b/builtin-functions/kphp-light/stdlib/rpc.txt index 21a070325c..9f17237909 100644 --- a/builtin-functions/kphp-light/stdlib/rpc.txt +++ b/builtin-functions/kphp-light/stdlib/rpc.txt @@ -4,9 +4,15 @@ require_once __DIR__ . '/rpc_fetcher.txt'; // ===== SUPPORTED ===== -/** @kphp-tl-class */ +/** + * @kphp-required + * @kphp-tl-class + */ interface RpcFunction { + public function getTLFunctionMagic() : int; public function getTLFunctionName() : string; + public function typedStore() : @tl\RpcFunctionFetcher; + public function typedFetch() : @tl\RpcFunctionFetcher; } /** @kphp-tl-class */ diff --git a/compiler/code-gen/files/tl2cpp/tl2cpp.cpp b/compiler/code-gen/files/tl2cpp/tl2cpp.cpp index 4f3c7ab5e7..f250d69f6b 100644 --- a/compiler/code-gen/files/tl2cpp/tl2cpp.cpp +++ b/compiler/code-gen/files/tl2cpp/tl2cpp.cpp @@ -96,7 +96,12 @@ void write_rpc_server_functions(CodeGenerator &W) { W << fmt_format("case {:#010x}: ", static_cast(f->id)) << BEGIN; W << get_php_runtime_type(f, true) << " request;" << NL << "request.alloc();" << NL + << "auto custom_fetcher = f$VK$TL$RpcFunction$$typedFetch(request);" << NL + << "if (custom_fetcher.is_null()) " << BEGIN << "CurrentRpcServerQuery::get().save(" << cpp_tl_struct_name("f_", f->name) << "::rpc_server_typed_fetch(request.get()));" << NL + << END << "else" << BEGIN + << "CurrentRpcServerQuery::get().save(make_tl_func_base_simple_wrapper(std::move(custom_fetcher)));" << NL + << END << NL << "return request;" << NL << END << NL; } diff --git a/runtime-light/stdlib/rpc/rpc-tl-kphp-request.h b/runtime-light/stdlib/rpc/rpc-tl-kphp-request.h index 19086d26ce..4bc9a2c157 100644 --- a/runtime-light/stdlib/rpc/rpc-tl-kphp-request.h +++ b/runtime-light/stdlib/rpc/rpc-tl-kphp-request.h @@ -15,6 +15,40 @@ #include "runtime-light/stdlib/rpc/rpc-tl-query.h" #include "runtime-light/stdlib/rpc/rpc-tl-request.h" +int64_t f$VK$TL$RpcFunction$$getTLFunctionMagic(class_instance const& arg) noexcept; +class_instance f$VK$TL$RpcFunction$$typedStore(class_instance const& arg) noexcept; +class_instance f$VK$TL$RpcFunction$$typedFetch(class_instance const& arg) noexcept; + +class_instance f$VK$TL$RpcFunctionFetcher$$typedFetch(class_instance const& fetcher) noexcept; +void f$VK$TL$RpcFunctionFetcher$$typedStore(class_instance const& fetcher, + class_instance const& result) noexcept; + +// should be in header, because C$VK$TL$* classes are unknown on runtime compilation +struct tl_func_base_simple_wrapper : public tl_func_base { + explicit tl_func_base_simple_wrapper(class_instance&& wrapped) + : wrapped_(std::move(wrapped)) {} + + virtual mixed fetch() { + php_critical_error("this function should never be called for typed RPC function."); + return mixed{}; + } + + virtual class_instance typed_fetch() { + return f$VK$TL$RpcFunctionFetcher$$typedFetch(wrapped_); + } + + virtual void rpc_server_typed_store(const class_instance& result) { + return f$VK$TL$RpcFunctionFetcher$$typedStore(wrapped_, result); + } + +private: + class_instance wrapped_; +}; + +inline std::unique_ptr make_tl_func_base_simple_wrapper(class_instance&& wrapped) { + return std::make_unique(std::move(wrapped)); +} + namespace kphp::rpc::rpc_impl { // use template, because t_ReqResult_ is unknown on runtime compilation template class t_ReqResult_> @@ -47,8 +81,15 @@ class KphpRpcRequest final : public RpcRequest { auto& cur_query{CurrentTlQuery::get()}; cur_query.set_current_tl_function(tl_function_name()); const vk::final_action finalizer{[&cur_query] noexcept { cur_query.reset(); }}; - - std::unique_ptr stored_fetcher{storing_function.get()->store()}; + std::unique_ptr stored_fetcher; + auto custom_fetcher = f$VK$TL$RpcFunction$$typedStore(storing_function); + if (custom_fetcher.is_null()) { + stored_fetcher = storing_function.get()->store(); + } else { + stored_fetcher = make_tl_func_base_simple_wrapper(std::move(custom_fetcher)); + auto magic = f$VK$TL$RpcFunction$$getTLFunctionMagic(storing_function); + CurrentTlQuery::get().set_last_stored_tl_function_magic(magic); + } CHECK_EXCEPTION(return {}); return make_unique_on_script_memory>(std::move(stored_fetcher)); } diff --git a/runtime/tl/rpc_request.h b/runtime/tl/rpc_request.h index cb01f58d1c..3038b99298 100644 --- a/runtime/tl/rpc_request.h +++ b/runtime/tl/rpc_request.h @@ -9,6 +9,15 @@ #include "runtime/tl/rpc_function.h" #include "runtime/tl/rpc_response.h" #include "runtime/tl/tl_builtins.h" +#include "runtime/tl/tl_func_base.h" + +int64_t f$VK$TL$RpcFunction$$getTLFunctionMagic(class_instance const& arg) noexcept; +class_instance f$VK$TL$RpcFunction$$typedStore(class_instance const& arg) noexcept; +class_instance f$VK$TL$RpcFunction$$typedFetch(class_instance const& arg) noexcept; + +class_instance f$VK$TL$RpcFunctionFetcher$$typedFetch(class_instance const& fetcher) noexcept; +void f$VK$TL$RpcFunctionFetcher$$typedStore(class_instance const& fetcher, + class_instance const& result) noexcept; class RpcRequestResult; @@ -79,6 +88,32 @@ class RpcRequestResultUntyped final : public RpcRequestResult { } }; +// should be in header, because C$VK$TL$* classes are unknown on runtime compilation +struct tl_func_base_simple_wrapper : public tl_func_base { + explicit tl_func_base_simple_wrapper(class_instance&& wrapped) + : wrapped_(std::move(wrapped)) {} + + virtual mixed fetch() { + php_critical_error("this function should never be called for typed RPC function."); + return mixed{}; + } + + virtual class_instance typed_fetch() { + return f$VK$TL$RpcFunctionFetcher$$typedFetch(wrapped_); + } + + virtual void rpc_server_typed_store(const class_instance& result) { + return f$VK$TL$RpcFunctionFetcher$$typedStore(wrapped_, result); + } + +private: + class_instance wrapped_; +}; + +inline std::unique_ptr make_tl_func_base_simple_wrapper(class_instance&& wrapped) { + return std::make_unique(std::move(wrapped)); +} + namespace impl_ { // use template, because t_ReqResult_ is unknown on runtime compilation template class t_ReqResult_> @@ -110,7 +145,15 @@ class KphpRpcRequest final : public RpcRequest { std::unique_ptr store_request() const final { php_assert(CurException.is_null()); CurrentTlQuery::get().set_current_tl_function(tl_function_name()); - std::unique_ptr stored_fetcher = storing_function_.get()->store(); + std::unique_ptr stored_fetcher; + auto custom_fetcher = f$VK$TL$RpcFunction$$typedStore(storing_function_); + if (custom_fetcher.is_null()) { + stored_fetcher = storing_function_.get()->store(); + } else { + stored_fetcher = std::make_unique(std::move(custom_fetcher)); + auto magic = f$VK$TL$RpcFunction$$getTLFunctionMagic(storing_function_); + CurrentTlQuery::get().set_last_stored_tl_function_magic(magic); + } CurrentTlQuery::get().reset(); if (!CurException.is_null()) { CurException = Optional{};