add support for registering and calling sync methods in C++ modules
Differential Revision: D3574800 fbshipit-source-id: 4c238fd96c8f83e9424c8e2bf2c8ebb1d39d397a
This commit is contained in:
parent
a7ca90e7c7
commit
e5ccdc4c2d
|
@ -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))
|
||||||
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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 };
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue