From e5ccdc4c2d354727482941867cbeadff28beb2d9 Mon Sep 17 00:00:00 2001 From: Marc Horowitz Date: Wed, 3 Aug 2016 18:03:42 -0700 Subject: [PATCH] add support for registering and calling sync methods in C++ modules Differential Revision: D3574800 fbshipit-source-id: 4c238fd96c8f83e9424c8e2bf2c8ebb1d39d397a --- ReactCommon/cxxreact/CxxModule.h | 32 ++++++++++++++++++ ReactCommon/cxxreact/CxxNativeModule.cpp | 43 ++++++++++++++++++++++-- ReactCommon/cxxreact/ModuleRegistry.cpp | 8 +++-- 3 files changed, 78 insertions(+), 5 deletions(-) diff --git a/ReactCommon/cxxreact/CxxModule.h b/ReactCommon/cxxreact/CxxModule.h index 7c4b72ec1..55b79f10c 100644 --- a/ReactCommon/cxxreact/CxxModule.h +++ b/ReactCommon/cxxreact/CxxModule.h @@ -42,14 +42,23 @@ namespace facebook { namespace xplat { namespace module { */ class CxxModule { + class AsyncTagType {}; + class SyncTagType {}; + public: typedef std::function)> Callback; + constexpr static AsyncTagType AsyncTag = AsyncTagType(); + constexpr static SyncTagType SyncTag = SyncTagType(); + struct Method { std::string name; + size_t callbacks; std::function func; + std::function syncFunc; + // std::function/lambda ctors Method(std::string aname, @@ -101,6 +110,29 @@ public: : name(std::move(aname)) , callbacks(2) , func(std::bind(method, t, _1, _2, _3)) {} + + // sync std::function/lambda ctors + + // Overloads for functions returning void give ambiguity errors. + // I am not sure if this is a runtime/compiler bug, or a + // limitation I do not understand. + + Method(std::string aname, + std::function&& afunc, + SyncTagType) + : name(std::move(aname)) + , callbacks(0) + , syncFunc([afunc=std::move(afunc)] (const folly::dynamic&) + { return afunc(); }) + {} + + Method(std::string aname, + std::function&& afunc, + SyncTagType) + : name(std::move(aname)) + , callbacks(0) + , syncFunc(std::move(afunc)) + {} }; /** diff --git a/ReactCommon/cxxreact/CxxNativeModule.cpp b/ReactCommon/cxxreact/CxxNativeModule.cpp index b18721394..8e3bb9b17 100644 --- a/ReactCommon/cxxreact/CxxNativeModule.cpp +++ b/ReactCommon/cxxreact/CxxNativeModule.cpp @@ -3,6 +3,8 @@ #include "CxxNativeModule.h" #include "Instance.h" +#include + #include using facebook::xplat::module::CxxModule; @@ -37,10 +39,12 @@ std::string CxxNativeModule::getName() { std::vector CxxNativeModule::getMethods() { // Same as MessageQueue.MethodTypes.remote static const auto kMethodTypeRemote = "remote"; + static const auto kMethodTypeSyncHook = "syncHook"; std::vector descs; for (auto& method : methods_) { - descs.emplace_back(method.name, kMethodTypeRemote); + assert(method.func || method.syncFunc); + descs.emplace_back(method.name, method.func ? kMethodTypeRemote : kMethodTypeSyncHook); } return descs; } @@ -73,6 +77,12 @@ void CxxNativeModule::invoke(ExecutorToken token, unsigned int reactMethodId, fo const auto& method = methods_[reactMethodId]; + if (!method.func) { + throw std::runtime_error( + folly::to("Method ", method.name, + " is synchronous but invoked asynchronously")); + } + if (params.size() < method.callbacks) { throw std::invalid_argument( folly::to("Expected ", method.callbacks, " callbacks, but only ", @@ -119,8 +129,35 @@ void CxxNativeModule::invoke(ExecutorToken token, unsigned int reactMethodId, fo } } -MethodCallResult CxxNativeModule::callSerializableNativeHook(ExecutorToken token, unsigned int hookId, folly::dynamic&& args) { - throw std::runtime_error("Not supported"); +MethodCallResult CxxNativeModule::callSerializableNativeHook( + ExecutorToken token, unsigned int hookId, folly::dynamic&& args) { + if (hookId >= methods_.size()) { + throw std::invalid_argument( + folly::to("methodId ", hookId, " out of range [0..", methods_.size(), "]")); + } + + const auto& method = methods_[hookId]; + + if (!method.syncFunc) { + throw std::runtime_error( + folly::to("Method ", method.name, + " is asynchronous but invoked synchronously")); + } + + if (!args.isString()) { + throw std::invalid_argument( + folly::to("method parameters should be string, but are ", args.typeName())); + } + + folly::dynamic params = folly::parseJson(args.stringPiece()); + + if (!params.isArray()) { + throw std::invalid_argument( + folly::to("parsed method parameters should be array, but are ", + args.typeName())); + } + + return { method.syncFunc(std::move(params)), false }; } } diff --git a/ReactCommon/cxxreact/ModuleRegistry.cpp b/ReactCommon/cxxreact/ModuleRegistry.cpp index a7c09bca3..9f0058b60 100644 --- a/ReactCommon/cxxreact/ModuleRegistry.cpp +++ b/ReactCommon/cxxreact/ModuleRegistry.cpp @@ -65,18 +65,22 @@ folly::dynamic ModuleRegistry::getConfig(const std::string& name) { folly::dynamic methodNames = folly::dynamic::array; folly::dynamic asyncMethodIds = folly::dynamic::array; + folly::dynamic syncHookIds = folly::dynamic::array; for (auto& descriptor : methods) { methodNames.push_back(std::move(descriptor.name)); if (descriptor.type == "remoteAsync") { asyncMethodIds.push_back(methodNames.size() - 1); + } else if (descriptor.type == "syncHook") { + syncHookIds.push_back(methodNames.size() - 1); } } if (!methodNames.empty()) { config.push_back(std::move(methodNames)); - if (!asyncMethodIds.empty()) { - config.push_back(std::move(asyncMethodIds)); + config.push_back(std::move(asyncMethodIds)); + if (!syncHookIds.empty()) { + config.push_back(std::move(syncHookIds)); } } }