// Copyright 2004-present Facebook. All Rights Reserved. #pragma once #include #include #include #include #include #include #include "Executor.h" #include "ExecutorToken.h" #include "JSCExecutor.h" #include "JSModulesUnbundle.h" #include "MessageQueueThread.h" #include "MethodCall.h" #include "NativeModule.h" namespace folly { struct dynamic; } namespace facebook { namespace react { struct InstanceCallback; class ModuleRegistry; class ExecutorRegistration { public: ExecutorRegistration( std::unique_ptr executor, std::shared_ptr executorMessageQueueThread) : executor_(std::move(executor)), messageQueueThread_(executorMessageQueueThread) {} std::unique_ptr executor_; std::shared_ptr messageQueueThread_; }; class JsToNativeBridge; // This class manages calls from native code to JS. It also manages // executors and their threads. All functions here can be called from // any thread. // // Except for loadApplicationScriptSync(), all void methods will queue // work to run on the jsQueue passed to the ctor, and return // immediately. class NativeToJsBridge { public: friend class JsToNativeBridge; /** * This must be called on the main JS thread. */ NativeToJsBridge( JSExecutorFactory* jsExecutorFactory, std::shared_ptr registry, std::shared_ptr jsQueue, std::unique_ptr nativeQueue, std::shared_ptr callback); virtual ~NativeToJsBridge(); /** * Executes a function with the module ID and method ID and any additional * arguments in JS. */ void callFunction( ExecutorToken executorToken, std::string&& module, std::string&& method, folly::dynamic&& args); /** * Invokes a callback with the cbID, and optional additional arguments in JS. */ void invokeCallback(ExecutorToken executorToken, double callbackId, folly::dynamic&& args); /** * Executes a JS method on the given executor synchronously, returning its * return value. JSException will be thrown if JS throws an exception; * another standard exception may be thrown for C++ bridge failures, or if * the executor is not capable of synchronous calls. * * This method is experimental, and may be modified or removed. * * loadApplicationScriptSync() must be called and finished executing * before callFunctionSync(). */ template Value callFunctionSync(const std::string& module, const std::string& method, T&& args) { if (*m_destroyed) { throw std::logic_error( folly::to("Synchronous call to ", module, ".", method, " after bridge is destroyed")); } JSCExecutor *jscExecutor = dynamic_cast(m_mainExecutor); if (!jscExecutor) { throw std::invalid_argument( folly::to("Executor type ", typeid(*m_mainExecutor).name(), " does not support synchronous calls")); } return jscExecutor->callFunctionSync(module, method, std::forward(args)); } /** * Starts the JS application. If unbundle is non-null, then it is * used to fetch JavaScript modules as individual scripts. * Otherwise, the script is assumed to include all the modules. */ void loadApplication( std::unique_ptr unbundle, std::unique_ptr startupCode, std::string sourceURL); void loadApplicationSync( std::unique_ptr unbundle, std::unique_ptr startupCode, std::string sourceURL); /** * Similar to loading a "bundle", but instead of passing js source this method accepts * path to a directory containing files prepared for particular JSExecutor. */ void loadOptimizedApplicationScript(std::string bundlePath, std::string sourceURL, int flags); void setGlobalVariable(std::string propName, std::unique_ptr jsonValue); void* getJavaScriptContext(); bool supportsProfiling(); void startProfiler(const std::string& title); void stopProfiler(const std::string& title, const std::string& filename); void handleMemoryPressureUiHidden(); void handleMemoryPressureModerate(); void handleMemoryPressureCritical(); /** * Returns the ExecutorToken corresponding to the main JSExecutor. */ ExecutorToken getMainExecutorToken() const; /** * Synchronously tears down the bridge and the main executor. */ void destroy(); private: /** * Registers the given JSExecutor which runs on the given MessageQueueThread * with the NativeToJsBridge. Part of this registration is transfering * ownership of this JSExecutor to the NativeToJsBridge for the duration of * the registration. * * Returns a ExecutorToken which can be used to refer to this JSExecutor * in the NativeToJsBridge. */ ExecutorToken registerExecutor( ExecutorToken token, std::unique_ptr executor, std::shared_ptr executorMessageQueueThread); /** * Unregisters a JSExecutor that was previously registered with this NativeToJsBridge * using registerExecutor. */ std::unique_ptr unregisterExecutor(JSExecutor& executorToken); void runOnExecutorQueue(ExecutorToken token, std::function task); // This is used to avoid a race condition where a proxyCallback gets queued // after ~NativeToJsBridge(), on the same thread. In that case, the callback // will try to run the task on m_callback which will have been destroyed // within ~NativeToJsBridge(), thus causing a SIGSEGV. std::shared_ptr m_destroyed; JSExecutor* m_mainExecutor; ExecutorToken m_mainExecutorToken; std::shared_ptr m_delegate; std::unordered_map m_executorTokenMap; std::unordered_map m_executorMap; std::mutex m_registrationMutex; #ifdef WITH_FBSYSTRACE std::atomic_uint_least32_t m_systraceCookie = ATOMIC_VAR_INIT(); #endif MessageQueueThread* getMessageQueueThread(const ExecutorToken& executorToken); JSExecutor* getExecutor(const ExecutorToken& executorToken); ExecutorToken getTokenForExecutor(JSExecutor& executor); }; } }