diff --git a/builtin-functions/kphp-full/_functions.txt b/builtin-functions/kphp-full/_functions.txt index 31325ede8b..4637f42107 100644 --- a/builtin-functions/kphp-full/_functions.txt +++ b/builtin-functions/kphp-full/_functions.txt @@ -1010,6 +1010,13 @@ interface RpcFunction { public function typedFetch() : @tl\RpcFunctionFetcher; } +/** + * @kphp-required + */ +interface VK\TL\RpcFunctionFactory { + public function createFunction(int $magic) : @tl\RpcFunction; +} + /** @kphp-tl-class */ interface RpcFunctionReturnResult {} @@ -1036,6 +1043,8 @@ function typed_rpc_tl_query_result_synchronously (int[] $query_ids) ::: @tl\RpcR /** @kphp-extern-func-info can_throw */ function rpc_server_fetch_request() ::: @tl\RpcFunction; /** @kphp-extern-func-info can_throw */ +function rpc_server_fetch_request2(VK\TL\RpcFunctionFactory $factory) ::: @tl\RpcFunction; +/** @kphp-extern-func-info can_throw */ function rpc_server_store_response(@tl\RpcFunctionReturnResult $response) ::: void; /** @kphp-extern-func-info can_throw resumable cpp_template_call */ diff --git a/builtin-functions/kphp-light/stdlib/rpc.txt b/builtin-functions/kphp-light/stdlib/rpc.txt index c88b464163..c5e16080a6 100644 --- a/builtin-functions/kphp-light/stdlib/rpc.txt +++ b/builtin-functions/kphp-light/stdlib/rpc.txt @@ -15,6 +15,13 @@ interface RpcFunction { public function typedFetch() : @tl\RpcFunctionFetcher; } +/** + * @kphp-required +*/ +interface VK\TL\RpcFunctionFactory { + public function createFunction(int $magic) : @tl\RpcFunction; +} + /** @kphp-tl-class */ interface RpcFunctionReturnResult {} @@ -105,6 +112,9 @@ function store_error ($error_code ::: int, $error_text ::: string) ::: bool; /** @kphp-extern-func-info can_throw */ function rpc_server_fetch_request() ::: @tl\RpcFunction; +/** @kphp-extern-func-info can_throw */ +function rpc_server_fetch_request2(VK\TL\RpcFunctionFactory $factory) ::: @tl\RpcFunction; + /** @kphp-extern-func-info can_throw interruptible */ function rpc_server_store_response(@tl\RpcFunctionReturnResult $response) ::: void; diff --git a/compiler/code-gen/declarations.cpp b/compiler/code-gen/declarations.cpp index 0aa48c32da..4e242db4d5 100644 --- a/compiler/code-gen/declarations.cpp +++ b/compiler/code-gen/declarations.cpp @@ -525,6 +525,13 @@ void ClassDeclaration::compile(CodeGenerator &W) const { W << "return " << f_tl_cpp_struct_name << "::typed_store(this);" << NL; W << END << NL; } + if (tl2cpp::is_php_class_a_tl_function_not_in_tlo(klass)) { + // typedStore() should never return null for such a function, so this code is never executed + W << NL; + FunctionSignatureGenerator(W).set_final().set_const_this() << "std::unique_ptr store() " << BEGIN; + W << "return std::unique_ptr{};" << NL; + W << END << NL; + } compile_job_worker_shared_memory_piece_methods(W, true); diff --git a/compiler/code-gen/files/tl2cpp/tl2cpp-utils.cpp b/compiler/code-gen/files/tl2cpp/tl2cpp-utils.cpp index 580b487bab..fe2a7ca383 100644 --- a/compiler/code-gen/files/tl2cpp/tl2cpp-utils.cpp +++ b/compiler/code-gen/files/tl2cpp/tl2cpp-utils.cpp @@ -219,6 +219,10 @@ bool is_php_class_a_tl_function(ClassPtr klass) { return klass->is_tl_class && klass->implements.size() == 1 && vk::string_view{klass->implements.front()->name}.ends_with("RpcFunction"); } +bool is_php_class_a_tl_function_not_in_tlo(ClassPtr klass) { + return !klass->is_tl_class && klass->implements.size() == 1 && vk::string_view{klass->implements.front()->name}.ends_with("RpcFunction"); +} + // classes VK\TL\*\Types\* are a non-interface types that correspond to the TL-constructors // (or non-polymorphic types with a single constructor that was inlined) bool is_php_class_a_tl_constructor(ClassPtr klass) { diff --git a/compiler/code-gen/files/tl2cpp/tl2cpp-utils.h b/compiler/code-gen/files/tl2cpp/tl2cpp-utils.h index dff616979d..0dcf2d45c0 100644 --- a/compiler/code-gen/files/tl2cpp/tl2cpp-utils.h +++ b/compiler/code-gen/files/tl2cpp/tl2cpp-utils.h @@ -63,6 +63,7 @@ std::string get_php_runtime_type(const vk::tlo_parsing::combinator *c, bool wrap std::string get_php_runtime_type(const vk::tlo_parsing::type *t); bool is_php_class_a_tl_function(ClassPtr klass); +bool is_php_class_a_tl_function_not_in_tlo(ClassPtr klass); bool is_php_class_a_tl_constructor(ClassPtr klass); bool is_php_class_a_tl_polymorphic_type(ClassPtr klass); bool is_php_class_a_tl_array_item(ClassPtr klass); diff --git a/compiler/code-gen/files/tl2cpp/tl2cpp.cpp b/compiler/code-gen/files/tl2cpp/tl2cpp.cpp index 34af97fcb3..0b37209cc8 100644 --- a/compiler/code-gen/files/tl2cpp/tl2cpp.cpp +++ b/compiler/code-gen/files/tl2cpp/tl2cpp.cpp @@ -89,7 +89,33 @@ void write_rpc_server_functions(CodeGenerator &W) { } W << deps << NL; W << ExternInclude{G->settings().runtime_headers.get()} << NL; - FunctionSignatureGenerator(W) << "class_instance f$rpc_server_fetch_request() " << BEGIN; + FunctionSignatureGenerator(W) << "class_instance f$rpc_server_fetch_request2(const class_instance & factory)" << BEGIN; + W << "auto start = tl_parse_save_pos();" << NL; + W << "auto function_magic = static_cast(f$fetch_int());" << NL; + W << "int tl_version = function_magic == 0x30324c54 ? 2 : 1; // TL2 marker" << NL; + W << "if (tl_version == 2) " << BEGIN; + W << "function_magic = static_cast(f$fetch_int());" << NL; + W << END << NL; + W << "(void)tl_parse_restore_pos(start);" << NL; + W << "auto php_fun = f$VK$TL$RpcFunctionFactory$$createFunction(factory, function_magic);" << NL; + W << "if (php_fun.is_null())" << BEGIN + << "return f$rpc_server_fetch_request();" << NL + << END << NL + << "CurrentTlQuery::get().set_current_tl_function(f$VK$TL$RpcFunction$$getTLFunctionName(php_fun));" << NL + << "auto custom_fetcher = f$VK$TL$RpcFunction$$typedFetch(php_fun);" << NL + << "CHECK_EXCEPTION(" << NL + << " php_warning(\"Exception when calling typedFetch for function magic: 0x%08x tl_version: %d\", function_magic, tl_version);" << NL + << " return {};" << NL + << ")" << NL + << "if (custom_fetcher.is_null())" << BEGIN + << "php_warning(\"function returned by factory must not return null from typedFetch(): 0x%08x tl_version: %d\", function_magic, tl_version);" << NL + << "return {};" << NL + << END << NL + << "CurrentRpcServerQuery::get().save(make_tl_func_base_simple_wrapper(std::move(custom_fetcher)));" << NL + << "return php_fun;" << NL; + W << END << NL; + + FunctionSignatureGenerator(W) << "class_instance f$rpc_server_fetch_request()" << BEGIN; // PHP typedFetch expects full body with optional TL2 marker and function magic, // otherwise it cannot determine TL version. // C++ rpc_server_typed_fetch expects body after function magic. diff --git a/compiler/pipes/check-tl-classes.cpp b/compiler/pipes/check-tl-classes.cpp index 482ed41ade..9bd9022927 100644 --- a/compiler/pipes/check-tl-classes.cpp +++ b/compiler/pipes/check-tl-classes.cpp @@ -17,12 +17,6 @@ void verify_class_against_repr(ClassPtr class_id, const vk::tl::PhpClassRepresen kphp_error_return(class_id->is_interface() == repr.is_interface, fmt_format("Tl-class '{}' is{} expected to be an interface", class_id->name, repr.is_interface ? "" : " not")); - for (const auto &child : class_id->derived_classes) { - kphp_error_return(child->phpdoc->has_tag(PhpDocType::kphp_tl_class), - fmt_format("Class '{}' is expected to be tl-class, because it inherits from tl-class '{}'", - child->name, class_id->name)); - } - if (repr.parent) { const std::string expected = repr.parent->php_class_namespace + "\\" + repr.parent->php_class_name; kphp_error_return(class_id->implements.size() == 1, diff --git a/runtime-light/stdlib/rpc/rpc-api.h b/runtime-light/stdlib/rpc/rpc-api.h index 5a9a43872b..1d5acceda0 100644 --- a/runtime-light/stdlib/rpc/rpc-api.h +++ b/runtime-light/stdlib/rpc/rpc-api.h @@ -241,6 +241,9 @@ inline bool f$rpc_parse(const Optional& new_rpc_data) noexcept { // 2. switch over all @kphp functions // 3. tl_func_state storing inside the CurrentRpcServerQuery class_instance f$rpc_server_fetch_request() noexcept; +// calls user-provided factory, if it returns !null, sets up returned function for processing, +// otherwise, calls f$rpc_server_fetch_request() +class_instance f$rpc_server_fetch_request2(const class_instance& factory) noexcept; inline kphp::coro::task f$store_error(int64_t error_code, string error_msg) noexcept { if (tl::is_int32_overflow(error_code)) [[unlikely]] { diff --git a/runtime-light/stdlib/rpc/rpc-tl-function.h b/runtime-light/stdlib/rpc/rpc-tl-function.h index b4a77f14d1..48455663ce 100644 --- a/runtime-light/stdlib/rpc/rpc-tl-function.h +++ b/runtime-light/stdlib/rpc/rpc-tl-function.h @@ -105,6 +105,31 @@ struct C$VK$TL$RpcFunctionFetcher : abstract_refcountable_php_interface { ~C$VK$TL$RpcFunctionFetcher() override = default; }; +struct C$VK$TL$RpcFunctionFactory : abstract_refcountable_php_interface { + virtual const char* get_class() const { + return "VK\\TL\\RpcFunctionFactory"; + } + virtual int32_t get_hash() const { + std::string_view name_view{C$VK$TL$RpcFunctionFactory::get_class()}; + return static_cast(vk::murmur_hash(name_view.data(), name_view.size())); + } + + virtual void accept(ToArrayVisitor&) noexcept {} + virtual void accept(CommonMemoryEstimateVisitor&) noexcept {} + virtual void accept(InstanceReferencesCountingVisitor&) noexcept {} + virtual void accept(InstanceDeepCopyVisitor&) noexcept {} + virtual void accept(InstanceDeepDestroyVisitor&) noexcept {} + + virtual size_t virtual_builtin_sizeof() const noexcept { + return 0; + } + virtual C$VK$TL$RpcFunctionFactory* virtual_builtin_clone() const noexcept { + return nullptr; + } + + virtual ~C$VK$TL$RpcFunctionFactory() = default; +}; + // function call response — ReqResult from the TL scheme — is a rpcResponseOk|rpcResponseHeader|rpcResponseError; // if it's rpcResponseOk or rpcResponseHeader, then their bodies can be retrieved by a fetcher that was returned by a store struct C$VK$TL$RpcResponse : abstract_refcountable_php_interface { diff --git a/runtime-light/stdlib/rpc/rpc-tl-kphp-request.h b/runtime-light/stdlib/rpc/rpc-tl-kphp-request.h index 993b16f2c6..41fe72b56b 100644 --- a/runtime-light/stdlib/rpc/rpc-tl-kphp-request.h +++ b/runtime-light/stdlib/rpc/rpc-tl-kphp-request.h @@ -15,6 +15,7 @@ #include "runtime-light/stdlib/rpc/rpc-tl-query.h" #include "runtime-light/stdlib/rpc/rpc-tl-request.h" +class_instance f$VK$TL$RpcFunctionFactory$$createFunction(class_instance const& arg, int64_t magic) noexcept; int64_t f$VK$TL$RpcFunction$$getTLFunctionMagic(class_instance const& arg) noexcept; string f$VK$TL$RpcFunction$$getTLFunctionName(class_instance const& arg) noexcept; class_instance f$VK$TL$RpcFunction$$typedStore(class_instance const& arg) noexcept; diff --git a/runtime/tl/rpc_function.h b/runtime/tl/rpc_function.h index 4c6b09e789..ad516ae4d4 100644 --- a/runtime/tl/rpc_function.h +++ b/runtime/tl/rpc_function.h @@ -105,6 +105,31 @@ struct C$VK$TL$RpcFunctionFetcher : abstract_refcountable_php_interface { virtual ~C$VK$TL$RpcFunctionFetcher() = default; }; +struct C$VK$TL$RpcFunctionFactory : abstract_refcountable_php_interface { + virtual const char* get_class() const { + return "VK\\TL\\RpcFunctionFactory"; + } + virtual int32_t get_hash() const { + std::string_view name_view{C$VK$TL$RpcFunctionFactory::get_class()}; + return static_cast(vk::murmur_hash(name_view.data(), name_view.size())); + } + + virtual void accept(ToArrayVisitor&) noexcept {} + virtual void accept(CommonMemoryEstimateVisitor&) noexcept {} + virtual void accept(InstanceReferencesCountingVisitor&) noexcept {} + virtual void accept(InstanceDeepCopyVisitor&) noexcept {} + virtual void accept(InstanceDeepDestroyVisitor&) noexcept {} + + virtual size_t virtual_builtin_sizeof() const noexcept { + return 0; + } + virtual C$VK$TL$RpcFunctionFactory* virtual_builtin_clone() const noexcept { + return nullptr; + } + + virtual ~C$VK$TL$RpcFunctionFactory() = default; +}; + // function call response — ReqResult from the TL scheme — is a rpcResponseOk|rpcResponseHeader|rpcResponseError; // if it's rpcResponseOk or rpcResponseHeader, then their bodies can be retrieved by a fetcher that was returned by a store struct C$VK$TL$RpcResponse : abstract_refcountable_php_interface { diff --git a/runtime/tl/rpc_request.h b/runtime/tl/rpc_request.h index d3956ac651..b431f0b658 100644 --- a/runtime/tl/rpc_request.h +++ b/runtime/tl/rpc_request.h @@ -11,6 +11,7 @@ #include "runtime/tl/tl_builtins.h" #include "runtime/tl/tl_func_base.h" +class_instance f$VK$TL$RpcFunctionFactory$$createFunction(class_instance const& arg, int64_t magic) noexcept; int64_t f$VK$TL$RpcFunction$$getTLFunctionMagic(class_instance const& arg) noexcept; string f$VK$TL$RpcFunction$$getTLFunctionName(class_instance const& arg) noexcept; class_instance f$VK$TL$RpcFunction$$typedStore(class_instance const& arg) noexcept; diff --git a/runtime/tl/rpc_server.h b/runtime/tl/rpc_server.h index 0e849ce55e..250c0afd39 100644 --- a/runtime/tl/rpc_server.h +++ b/runtime/tl/rpc_server.h @@ -26,4 +26,5 @@ struct C$VK$TL$RpcFunction; struct C$VK$TL$RpcFunctionReturnResult; class_instance f$rpc_server_fetch_request() noexcept; +class_instance f$rpc_server_fetch_request2(const class_instance& factory) noexcept; void f$rpc_server_store_response(const class_instance& response) noexcept;