/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #pragma once #include <functional> #include <map> #include <tuple> #include <vector> #include <folly/dynamic.h> using namespace std::placeholders; namespace facebook { namespace react { class Instance; } } // namespace facebook namespace facebook { namespace xplat { namespace module { /** * Base class for Catalyst native modules whose implementations are * written in C++. Native methods are represented by instances of the * Method struct. Generally, a derived class will manage an instance * which represents the data for the module, and non-Catalyst-specific * methods can be wrapped in lambdas which convert between * folly::dynamic and native C++ objects. The Callback arguments will * pass through to js functions passed to the analogous javascript * methods. At most two callbacks will be converted. Results should * be passed to the first callback, and errors to the second callback. * Exceptions thrown by a method will be converted to platform * exceptions, and handled however they are handled on that platform. * (TODO mhorowitz #7128529: this exception behavior is not yet * implemented.) * * There are two sets of constructors here. The first set initializes * a Method using a name and anything convertible to a std::function. * This is most useful for registering a lambda as a RN method. There * are overloads to support functions which take no arguments, * arguments only, and zero, one, or two callbacks. * * The second set of methods is similar, but instead of taking a * function, takes the method name, an object, and a pointer to a * method on that object. */ class CxxModule { class AsyncTagType {}; class SyncTagType {}; public: typedef std::function<std::unique_ptr<CxxModule>()> Provider; typedef std::function<void(std::vector<folly::dynamic>)> Callback; constexpr static AsyncTagType AsyncTag = AsyncTagType(); constexpr static SyncTagType SyncTag = SyncTagType(); struct Method { std::string name; size_t callbacks; bool isPromise; std::function<void(folly::dynamic, Callback, Callback)> func; std::function<folly::dynamic(folly::dynamic)> syncFunc; const char *getType() { assert(func || syncFunc); return func ? (isPromise ? "promise" : "async") : "sync"; } // std::function/lambda ctors Method(std::string aname, std::function<void()> &&afunc) : name(std::move(aname)), callbacks(0), isPromise(false), func(std::bind(std::move(afunc))) {} Method(std::string aname, std::function<void(folly::dynamic)> &&afunc) : name(std::move(aname)), callbacks(0), isPromise(false), func(std::bind(std::move(afunc), std::placeholders::_1)) {} Method( std::string aname, std::function<void(folly::dynamic, Callback)> &&afunc) : name(std::move(aname)), callbacks(1), isPromise(false), func(std::bind( std::move(afunc), std::placeholders::_1, std::placeholders::_2)) {} Method( std::string aname, std::function<void(folly::dynamic, Callback, Callback)> &&afunc) : name(std::move(aname)), callbacks(2), isPromise(true), func(std::move(afunc)) {} Method( std::string aname, std::function<void(folly::dynamic, Callback, Callback)> &&afunc, AsyncTagType) : name(std::move(aname)), callbacks(2), isPromise(false), func(std::move(afunc)) {} // method pointer ctors template <typename T> Method(std::string aname, T *t, void (T::*method)()) : name(std::move(aname)), callbacks(0), isPromise(false), func(std::bind(method, t)) {} template <typename T> Method(std::string aname, T *t, void (T::*method)(folly::dynamic)) : name(std::move(aname)), callbacks(0), isPromise(false), func(std::bind(method, t, std::placeholders::_1)) {} template <typename T> Method(std::string aname, T *t, void (T::*method)(folly::dynamic, Callback)) : name(std::move(aname)), callbacks(1), isPromise(false), func(std::bind( method, t, std::placeholders::_1, std::placeholders::_2)) {} template <typename T> Method( std::string aname, T *t, void (T::*method)(folly::dynamic, Callback, Callback)) : name(std::move(aname)), callbacks(2), isPromise(true), func(std::bind( method, t, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)) {} template <typename T> Method( std::string aname, T *t, void (T::*method)(folly::dynamic, Callback, Callback), AsyncTagType) : name(std::move(aname)), callbacks(2), isPromise(false), func(std::bind( method, t, std::placeholders::_1, std::placeholders::_2, std::placeholders::_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), isPromise(false), 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), isPromise(false), syncFunc(std::move(afunc)) {} }; /** * This may block, if necessary to complete cleanup before the * object is destroyed. */ virtual ~CxxModule() {} /** * @return the name of this module. This will be the name used to {@code * require()} this module from javascript. */ virtual std::string getName() = 0; /** * Each entry in the map will be exported as a property to JS. The * key is the property name, and the value can be anything. */ virtual auto getConstants() -> std::map<std::string, folly::dynamic> { return {}; }; /** * @return a list of methods this module exports to JS. */ virtual auto getMethods() -> std::vector<Method> = 0; /** * Called during the construction of CxxNativeModule. */ void setInstance(std::weak_ptr<react::Instance> instance) { instance_ = instance; } /** * @return a weak_ptr to the current instance of the bridge. * When used with CxxNativeModule, this gives Cxx modules access to functions * such as `callJSFunction`, allowing them to communicate back to JS outside * of the regular callbacks. */ std::weak_ptr<react::Instance> getInstance() { return instance_; } private: std::weak_ptr<react::Instance> instance_; }; } // namespace module } // namespace xplat } // namespace facebook