add support for registering and calling sync methods in C++ modules

Differential Revision: D3574800

fbshipit-source-id: 4c238fd96c8f83e9424c8e2bf2c8ebb1d39d397a
This commit is contained in:
Marc Horowitz 2016-08-03 18:03:42 -07:00 committed by Facebook Github Bot 8
parent a7ca90e7c7
commit e5ccdc4c2d
3 changed files with 78 additions and 5 deletions

View File

@ -42,14 +42,23 @@ namespace facebook { namespace xplat { namespace module {
*/ */
class CxxModule { class CxxModule {
class AsyncTagType {};
class SyncTagType {};
public: public:
typedef std::function<void(std::vector<folly::dynamic>)> Callback; typedef std::function<void(std::vector<folly::dynamic>)> Callback;
constexpr static AsyncTagType AsyncTag = AsyncTagType();
constexpr static SyncTagType SyncTag = SyncTagType();
struct Method { struct Method {
std::string name; std::string name;
size_t callbacks; size_t callbacks;
std::function<void(folly::dynamic, Callback, Callback)> func; std::function<void(folly::dynamic, Callback, Callback)> func;
std::function<folly::dynamic(folly::dynamic)> syncFunc;
// std::function/lambda ctors // std::function/lambda ctors
Method(std::string aname, Method(std::string aname,
@ -101,6 +110,29 @@ public:
: name(std::move(aname)) : name(std::move(aname))
, callbacks(2) , callbacks(2)
, func(std::bind(method, t, _1, _2, _3)) {} , 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<folly::dynamic()>&& afunc,
SyncTagType)
: name(std::move(aname))
, callbacks(0)
, syncFunc([afunc=std::move(afunc)] (const folly::dynamic&)
{ return afunc(); })
{}
Method(std::string aname,
std::function<folly::dynamic(folly::dynamic)>&& afunc,
SyncTagType)
: name(std::move(aname))
, callbacks(0)
, syncFunc(std::move(afunc))
{}
}; };
/** /**

View File

@ -3,6 +3,8 @@
#include "CxxNativeModule.h" #include "CxxNativeModule.h"
#include "Instance.h" #include "Instance.h"
#include <folly/json.h>
#include <cxxreact/JsArgumentHelpers.h> #include <cxxreact/JsArgumentHelpers.h>
using facebook::xplat::module::CxxModule; using facebook::xplat::module::CxxModule;
@ -37,10 +39,12 @@ std::string CxxNativeModule::getName() {
std::vector<MethodDescriptor> CxxNativeModule::getMethods() { std::vector<MethodDescriptor> CxxNativeModule::getMethods() {
// Same as MessageQueue.MethodTypes.remote // Same as MessageQueue.MethodTypes.remote
static const auto kMethodTypeRemote = "remote"; static const auto kMethodTypeRemote = "remote";
static const auto kMethodTypeSyncHook = "syncHook";
std::vector<MethodDescriptor> descs; std::vector<MethodDescriptor> descs;
for (auto& method : methods_) { 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; return descs;
} }
@ -73,6 +77,12 @@ void CxxNativeModule::invoke(ExecutorToken token, unsigned int reactMethodId, fo
const auto& method = methods_[reactMethodId]; const auto& method = methods_[reactMethodId];
if (!method.func) {
throw std::runtime_error(
folly::to<std::string>("Method ", method.name,
" is synchronous but invoked asynchronously"));
}
if (params.size() < method.callbacks) { if (params.size() < method.callbacks) {
throw std::invalid_argument( throw std::invalid_argument(
folly::to<std::string>("Expected ", method.callbacks, " callbacks, but only ", folly::to<std::string>("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) { MethodCallResult CxxNativeModule::callSerializableNativeHook(
throw std::runtime_error("Not supported"); ExecutorToken token, unsigned int hookId, folly::dynamic&& args) {
if (hookId >= methods_.size()) {
throw std::invalid_argument(
folly::to<std::string>("methodId ", hookId, " out of range [0..", methods_.size(), "]"));
}
const auto& method = methods_[hookId];
if (!method.syncFunc) {
throw std::runtime_error(
folly::to<std::string>("Method ", method.name,
" is asynchronous but invoked synchronously"));
}
if (!args.isString()) {
throw std::invalid_argument(
folly::to<std::string>("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<std::string>("parsed method parameters should be array, but are ",
args.typeName()));
}
return { method.syncFunc(std::move(params)), false };
} }
} }

View File

@ -65,18 +65,22 @@ folly::dynamic ModuleRegistry::getConfig(const std::string& name) {
folly::dynamic methodNames = folly::dynamic::array; folly::dynamic methodNames = folly::dynamic::array;
folly::dynamic asyncMethodIds = folly::dynamic::array; folly::dynamic asyncMethodIds = folly::dynamic::array;
folly::dynamic syncHookIds = folly::dynamic::array;
for (auto& descriptor : methods) { for (auto& descriptor : methods) {
methodNames.push_back(std::move(descriptor.name)); methodNames.push_back(std::move(descriptor.name));
if (descriptor.type == "remoteAsync") { if (descriptor.type == "remoteAsync") {
asyncMethodIds.push_back(methodNames.size() - 1); asyncMethodIds.push_back(methodNames.size() - 1);
} else if (descriptor.type == "syncHook") {
syncHookIds.push_back(methodNames.size() - 1);
} }
} }
if (!methodNames.empty()) { if (!methodNames.empty()) {
config.push_back(std::move(methodNames)); 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));
} }
} }
} }