/* * 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 #include #include #include #include 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()> Provider; typedef std::function)> Callback; constexpr static AsyncTagType AsyncTag = AsyncTagType(); constexpr static SyncTagType SyncTag = SyncTagType(); struct Method { std::string name; size_t callbacks; bool isPromise; std::function func; std::function syncFunc; const char *getType() { assert(func || syncFunc); return func ? (isPromise ? "promise" : "async") : "sync"; } // std::function/lambda ctors Method(std::string aname, std::function &&afunc) : name(std::move(aname)), callbacks(0), isPromise(false), func(std::bind(std::move(afunc))) {} Method(std::string aname, std::function &&afunc) : name(std::move(aname)), callbacks(0), isPromise(false), func(std::bind(std::move(afunc), std::placeholders::_1)) {} Method( std::string aname, std::function &&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 &&afunc) : name(std::move(aname)), callbacks(2), isPromise(true), func(std::move(afunc)) {} Method( std::string aname, std::function &&afunc, AsyncTagType) : name(std::move(aname)), callbacks(2), isPromise(false), func(std::move(afunc)) {} // method pointer ctors template Method(std::string aname, T *t, void (T::*method)()) : name(std::move(aname)), callbacks(0), isPromise(false), func(std::bind(method, t)) {} template 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 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 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 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 &&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 &&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 { return {}; }; /** * @return a list of methods this module exports to JS. */ virtual auto getMethods() -> std::vector = 0; /** * Called during the construction of CxxNativeModule. */ void setInstance(std::weak_ptr 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 getInstance() { return instance_; } private: std::weak_ptr instance_; }; } // namespace module } // namespace xplat } // namespace facebook