Implement incremental module requires
Differential Revision: D3234844 fbshipit-source-id: 24615528ad6a049aad7c2dbb7ce55e8b034c79e7
This commit is contained in:
parent
7f1346bfdd
commit
779314a413
|
@ -1,277 +0,0 @@
|
||||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
|
||||||
|
|
||||||
#include "Bridge.h"
|
|
||||||
|
|
||||||
#ifdef WITH_FBSYSTRACE
|
|
||||||
#include <fbsystrace.h>
|
|
||||||
using fbsystrace::FbSystraceAsyncFlow;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <folly/json.h>
|
|
||||||
#include <folly/Memory.h>
|
|
||||||
#include <folly/MoveWrapper.h>
|
|
||||||
|
|
||||||
#include "Platform.h"
|
|
||||||
#include "SystraceSection.h"
|
|
||||||
|
|
||||||
namespace facebook {
|
|
||||||
namespace react {
|
|
||||||
|
|
||||||
Bridge::Bridge(
|
|
||||||
JSExecutorFactory* jsExecutorFactory,
|
|
||||||
std::shared_ptr<MessageQueueThread> jsQueue,
|
|
||||||
std::unique_ptr<ExecutorTokenFactory> executorTokenFactory,
|
|
||||||
std::unique_ptr<BridgeCallback> callback) :
|
|
||||||
m_callback(std::move(callback)),
|
|
||||||
m_destroyed(std::make_shared<bool>(false)),
|
|
||||||
m_executorTokenFactory(std::move(executorTokenFactory)) {
|
|
||||||
std::unique_ptr<JSExecutor> mainExecutor = jsExecutorFactory->createJSExecutor(this, jsQueue);
|
|
||||||
// cached to avoid locked map lookup in the common case
|
|
||||||
m_mainExecutor = mainExecutor.get();
|
|
||||||
m_mainExecutorToken = folly::make_unique<ExecutorToken>(registerExecutor(
|
|
||||||
std::move(mainExecutor), jsQueue));
|
|
||||||
}
|
|
||||||
|
|
||||||
// This must be called on the same thread on which the constructor was called.
|
|
||||||
Bridge::~Bridge() {
|
|
||||||
CHECK(*m_destroyed) << "Bridge::destroy() must be called before deallocating the Bridge!";
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bridge::loadApplicationScript(std::unique_ptr<const JSBigString> script,
|
|
||||||
std::string sourceURL) {
|
|
||||||
// TODO(t11144533): Add assert that we are on the correct thread
|
|
||||||
m_mainExecutor->loadApplicationScript(std::move(script), std::move(sourceURL));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bridge::loadApplicationUnbundle(
|
|
||||||
std::unique_ptr<JSModulesUnbundle> unbundle,
|
|
||||||
std::unique_ptr<const JSBigString> startupScript,
|
|
||||||
std::string startupScriptSourceURL) {
|
|
||||||
runOnExecutorQueue(
|
|
||||||
*m_mainExecutorToken,
|
|
||||||
[unbundle=folly::makeMoveWrapper(std::move(unbundle)),
|
|
||||||
startupScript=folly::makeMoveWrapper(std::move(startupScript)),
|
|
||||||
startupScriptSourceURL=std::move(startupScriptSourceURL)]
|
|
||||||
(JSExecutor* executor) mutable {
|
|
||||||
|
|
||||||
executor->setJSModulesUnbundle(unbundle.move());
|
|
||||||
executor->loadApplicationScript(std::move(*startupScript),
|
|
||||||
std::move(startupScriptSourceURL));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bridge::callFunction(
|
|
||||||
ExecutorToken executorToken,
|
|
||||||
const std::string& moduleId,
|
|
||||||
const std::string& methodId,
|
|
||||||
const folly::dynamic& arguments,
|
|
||||||
const std::string& tracingName) {
|
|
||||||
#ifdef WITH_FBSYSTRACE
|
|
||||||
int systraceCookie = m_systraceCookie++;
|
|
||||||
FbSystraceAsyncFlow::begin(
|
|
||||||
TRACE_TAG_REACT_CXX_BRIDGE,
|
|
||||||
tracingName.c_str(),
|
|
||||||
systraceCookie);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
runOnExecutorQueue(executorToken, [moduleId, methodId, arguments, tracingName, systraceCookie] (JSExecutor* executor) {
|
|
||||||
#ifdef WITH_FBSYSTRACE
|
|
||||||
FbSystraceAsyncFlow::end(
|
|
||||||
TRACE_TAG_REACT_CXX_BRIDGE,
|
|
||||||
tracingName.c_str(),
|
|
||||||
systraceCookie);
|
|
||||||
SystraceSection s(tracingName.c_str());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// This is safe because we are running on the executor's thread: it won't
|
|
||||||
// destruct until after it's been unregistered (which we check above) and
|
|
||||||
// that will happen on this thread
|
|
||||||
executor->callFunction(moduleId, methodId, arguments);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bridge::invokeCallback(ExecutorToken executorToken, const double callbackId, const folly::dynamic& arguments) {
|
|
||||||
#ifdef WITH_FBSYSTRACE
|
|
||||||
int systraceCookie = m_systraceCookie++;
|
|
||||||
FbSystraceAsyncFlow::begin(
|
|
||||||
TRACE_TAG_REACT_CXX_BRIDGE,
|
|
||||||
"<callback>",
|
|
||||||
systraceCookie);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
runOnExecutorQueue(executorToken, [callbackId, arguments, systraceCookie] (JSExecutor* executor) {
|
|
||||||
#ifdef WITH_FBSYSTRACE
|
|
||||||
FbSystraceAsyncFlow::end(
|
|
||||||
TRACE_TAG_REACT_CXX_BRIDGE,
|
|
||||||
"<callback>",
|
|
||||||
systraceCookie);
|
|
||||||
SystraceSection s("Bridge.invokeCallback");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
executor->invokeCallback(callbackId, arguments);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bridge::setGlobalVariable(std::string propName,
|
|
||||||
std::unique_ptr<const JSBigString> jsonValue) {
|
|
||||||
runOnExecutorQueue(
|
|
||||||
*m_mainExecutorToken,
|
|
||||||
[propName=std::move(propName), jsonValue=folly::makeMoveWrapper(std::move(jsonValue))]
|
|
||||||
(JSExecutor* executor) mutable {
|
|
||||||
executor->setGlobalVariable(propName, jsonValue.move());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void* Bridge::getJavaScriptContext() {
|
|
||||||
// TODO(cjhopman): this seems unsafe unless we require that it is only called on the main js queue.
|
|
||||||
return m_mainExecutor->getJavaScriptContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Bridge::supportsProfiling() {
|
|
||||||
// Intentionally doesn't post to jsqueue. supportsProfiling() can be called from any thread.
|
|
||||||
return m_mainExecutor->supportsProfiling();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bridge::startProfiler(const std::string& title) {
|
|
||||||
runOnExecutorQueue(*m_mainExecutorToken, [=] (JSExecutor* executor) {
|
|
||||||
executor->startProfiler(title);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bridge::stopProfiler(const std::string& title, const std::string& filename) {
|
|
||||||
runOnExecutorQueue(*m_mainExecutorToken, [=] (JSExecutor* executor) {
|
|
||||||
executor->stopProfiler(title, filename);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bridge::handleMemoryPressureModerate() {
|
|
||||||
runOnExecutorQueue(*m_mainExecutorToken, [=] (JSExecutor* executor) {
|
|
||||||
executor->handleMemoryPressureModerate();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bridge::handleMemoryPressureCritical() {
|
|
||||||
runOnExecutorQueue(*m_mainExecutorToken, [=] (JSExecutor* executor) {
|
|
||||||
executor->handleMemoryPressureCritical();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bridge::callNativeModules(JSExecutor& executor, const std::string& callJSON, bool isEndOfBatch) {
|
|
||||||
// This is called by the executor and thus runs on the executor's own queue.
|
|
||||||
// This means that the executor has not yet been unregistered (and we are
|
|
||||||
// guaranteed to be able to get the token).
|
|
||||||
m_callback->onCallNativeModules(getTokenForExecutor(executor), callJSON, isEndOfBatch);
|
|
||||||
}
|
|
||||||
|
|
||||||
MethodCallResult Bridge::callSerializableNativeHook(unsigned int moduleId, unsigned int methodId, const std::string& argsJSON) {
|
|
||||||
return m_callback->callSerializableNativeHook(*m_mainExecutorToken, moduleId, methodId, folly::parseJson(argsJSON));
|
|
||||||
}
|
|
||||||
|
|
||||||
ExecutorToken Bridge::getMainExecutorToken() const {
|
|
||||||
return *m_mainExecutorToken.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
ExecutorToken Bridge::registerExecutor(
|
|
||||||
std::unique_ptr<JSExecutor> executor,
|
|
||||||
std::shared_ptr<MessageQueueThread> messageQueueThread) {
|
|
||||||
auto token = m_executorTokenFactory->createExecutorToken();
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> registrationGuard(m_registrationMutex);
|
|
||||||
|
|
||||||
CHECK(m_executorTokenMap.find(executor.get()) == m_executorTokenMap.end())
|
|
||||||
<< "Trying to register an already registered executor!";
|
|
||||||
|
|
||||||
m_executorTokenMap.emplace(executor.get(), token);
|
|
||||||
m_executorMap.emplace(
|
|
||||||
token,
|
|
||||||
folly::make_unique<ExecutorRegistration>(std::move(executor), std::move(messageQueueThread)));
|
|
||||||
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<JSExecutor> Bridge::unregisterExecutor(ExecutorToken executorToken) {
|
|
||||||
std::unique_ptr<JSExecutor> executor;
|
|
||||||
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> registrationGuard(m_registrationMutex);
|
|
||||||
|
|
||||||
auto it = m_executorMap.find(executorToken);
|
|
||||||
CHECK(it != m_executorMap.end())
|
|
||||||
<< "Trying to unregister an executor that was never registered!";
|
|
||||||
|
|
||||||
executor = std::move(it->second->executor_);
|
|
||||||
m_executorMap.erase(it);
|
|
||||||
m_executorTokenMap.erase(executor.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
m_callback->onExecutorUnregistered(executorToken);
|
|
||||||
|
|
||||||
return executor;
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageQueueThread* Bridge::getMessageQueueThread(const ExecutorToken& executorToken) {
|
|
||||||
std::lock_guard<std::mutex> registrationGuard(m_registrationMutex);
|
|
||||||
auto it = m_executorMap.find(executorToken);
|
|
||||||
if (it == m_executorMap.end()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return it->second->messageQueueThread_.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
JSExecutor* Bridge::getExecutor(const ExecutorToken& executorToken) {
|
|
||||||
std::lock_guard<std::mutex> registrationGuard(m_registrationMutex);
|
|
||||||
auto it = m_executorMap.find(executorToken);
|
|
||||||
if (it == m_executorMap.end()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return it->second->executor_.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
ExecutorToken Bridge::getTokenForExecutor(JSExecutor& executor) {
|
|
||||||
std::lock_guard<std::mutex> registrationGuard(m_registrationMutex);
|
|
||||||
return m_executorTokenMap.at(&executor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bridge::destroy() {
|
|
||||||
auto executorMessageQueueThread = getMessageQueueThread(*m_mainExecutorToken);
|
|
||||||
executorMessageQueueThread->runOnQueueSync([this, &executorMessageQueueThread] {
|
|
||||||
executorMessageQueueThread->quitSynchronous();
|
|
||||||
*m_destroyed = true;
|
|
||||||
m_mainExecutor = nullptr;
|
|
||||||
std::unique_ptr<JSExecutor> mainExecutor = unregisterExecutor(*m_mainExecutorToken);
|
|
||||||
mainExecutor->destroy();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bridge::runOnExecutorQueue(ExecutorToken executorToken, std::function<void(JSExecutor*)> task) {
|
|
||||||
if (*m_destroyed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto executorMessageQueueThread = getMessageQueueThread(executorToken);
|
|
||||||
if (executorMessageQueueThread == nullptr) {
|
|
||||||
LOG(WARNING) << "Dropping JS action for executor that has been unregistered...";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<bool> isDestroyed = m_destroyed;
|
|
||||||
executorMessageQueueThread->runOnQueue([this, isDestroyed, executorToken, task=std::move(task)] {
|
|
||||||
if (*isDestroyed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSExecutor *executor = getExecutor(executorToken);
|
|
||||||
if (executor == nullptr) {
|
|
||||||
LOG(WARNING) << "Dropping JS call for executor that has been unregistered...";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The executor is guaranteed to be valid for the duration of the task because:
|
|
||||||
// 1. the executor is only destroyed after it is unregistered
|
|
||||||
// 2. the executor is unregistered on this queue
|
|
||||||
// 3. we just confirmed that the executor hasn't been unregistered above
|
|
||||||
task(executor);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} }
|
|
|
@ -7,26 +7,45 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <folly/dynamic.h>
|
||||||
|
|
||||||
#include "JSModulesUnbundle.h"
|
#include "JSModulesUnbundle.h"
|
||||||
|
|
||||||
namespace folly {
|
|
||||||
|
|
||||||
struct dynamic;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace react {
|
namespace react {
|
||||||
|
|
||||||
class Bridge;
|
|
||||||
class JSExecutor;
|
class JSExecutor;
|
||||||
class MessageQueueThread;
|
class MessageQueueThread;
|
||||||
|
|
||||||
|
struct MethodCallResult {
|
||||||
|
folly::dynamic result;
|
||||||
|
bool isUndefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This interface describes the delegate interface required by
|
||||||
|
// Executor implementations to call from JS into native code.
|
||||||
|
class ExecutorDelegate {
|
||||||
|
public:
|
||||||
|
virtual ~ExecutorDelegate() {}
|
||||||
|
|
||||||
|
virtual void registerExecutor(std::unique_ptr<JSExecutor> executor,
|
||||||
|
std::shared_ptr<MessageQueueThread> queue) = 0;
|
||||||
|
virtual std::unique_ptr<JSExecutor> unregisterExecutor(JSExecutor& executor) = 0;
|
||||||
|
|
||||||
|
virtual std::vector<std::string> moduleNames() = 0;
|
||||||
|
virtual folly::dynamic getModuleConfig(const std::string& name) = 0;
|
||||||
|
virtual void callNativeModules(
|
||||||
|
JSExecutor& executor, std::string callJSON, bool isEndOfBatch) = 0;
|
||||||
|
virtual MethodCallResult callSerializableNativeHook(
|
||||||
|
JSExecutor& executor, unsigned int moduleId, unsigned int methodId, folly::dynamic&& args) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class JSExecutorFactory {
|
class JSExecutorFactory {
|
||||||
public:
|
public:
|
||||||
virtual std::unique_ptr<JSExecutor> createJSExecutor(
|
virtual std::unique_ptr<JSExecutor> createJSExecutor(
|
||||||
Bridge *bridge, std::shared_ptr<MessageQueueThread> jsQueue) = 0;
|
std::shared_ptr<ExecutorDelegate> delegate,
|
||||||
virtual ~JSExecutorFactory() {};
|
std::shared_ptr<MessageQueueThread> jsQueue) = 0;
|
||||||
|
virtual ~JSExecutorFactory() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// JSExecutor functions sometimes take large strings, on the order of
|
// JSExecutor functions sometimes take large strings, on the order of
|
||||||
|
@ -143,18 +162,18 @@ public:
|
||||||
std::unique_ptr<const JSBigString> jsonValue) = 0;
|
std::unique_ptr<const JSBigString> jsonValue) = 0;
|
||||||
virtual void* getJavaScriptContext() {
|
virtual void* getJavaScriptContext() {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
};
|
}
|
||||||
virtual bool supportsProfiling() {
|
virtual bool supportsProfiling() {
|
||||||
return false;
|
return false;
|
||||||
};
|
}
|
||||||
virtual void startProfiler(const std::string &titleString) {};
|
virtual void startProfiler(const std::string &titleString) {}
|
||||||
virtual void stopProfiler(const std::string &titleString, const std::string &filename) {};
|
virtual void stopProfiler(const std::string &titleString, const std::string &filename) {}
|
||||||
virtual void handleMemoryPressureModerate() {};
|
virtual void handleMemoryPressureModerate() {}
|
||||||
virtual void handleMemoryPressureCritical() {
|
virtual void handleMemoryPressureCritical() {
|
||||||
handleMemoryPressureModerate();
|
handleMemoryPressureModerate();
|
||||||
};
|
}
|
||||||
virtual void destroy() {};
|
virtual void destroy() {}
|
||||||
virtual ~JSExecutor() {};
|
virtual ~JSExecutor() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
} }
|
} }
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include <folly/json.h>
|
#include <folly/json.h>
|
||||||
#include <folly/Memory.h>
|
#include <folly/Memory.h>
|
||||||
|
#include <folly/MoveWrapper.h>
|
||||||
|
|
||||||
#include <glog/logging.h>
|
#include <glog/logging.h>
|
||||||
|
|
||||||
|
@ -19,42 +20,9 @@
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace react {
|
namespace react {
|
||||||
|
|
||||||
namespace {
|
|
||||||
struct ExecutorTokenFactoryImpl : ExecutorTokenFactory {
|
|
||||||
ExecutorTokenFactoryImpl(InstanceCallback* callback): callback_(callback) {}
|
|
||||||
virtual ExecutorToken createExecutorToken() const {
|
|
||||||
return callback_->createExecutorToken();
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
InstanceCallback* callback_;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
class Instance::BridgeCallbackImpl : public BridgeCallback {
|
|
||||||
public:
|
|
||||||
explicit BridgeCallbackImpl(Instance* instance) : instance_(instance) {}
|
|
||||||
virtual void onCallNativeModules(
|
|
||||||
ExecutorToken executorToken,
|
|
||||||
const std::string& calls,
|
|
||||||
bool isEndOfBatch) override {
|
|
||||||
instance_->callNativeModules(executorToken, calls, isEndOfBatch);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void onExecutorUnregistered(ExecutorToken executorToken) override {
|
|
||||||
// TODO(cjhopman): implement this.
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual MethodCallResult callSerializableNativeHook(ExecutorToken token, unsigned int moduleId, unsigned int hookId, folly::dynamic&& params) override {
|
|
||||||
return instance_->callSerializableNativeHook(token, moduleId, hookId, std::move(params));
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
Instance* instance_;
|
|
||||||
};
|
|
||||||
|
|
||||||
Instance::~Instance() {
|
Instance::~Instance() {
|
||||||
if (nativeQueue_) {
|
if (nativeToJsBridge_) {
|
||||||
nativeQueue_->quitSynchronous();
|
nativeToJsBridge_->destroy();
|
||||||
bridge_->destroy();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,32 +33,15 @@ void Instance::initializeBridge(
|
||||||
std::unique_ptr<MessageQueueThread> nativeQueue,
|
std::unique_ptr<MessageQueueThread> nativeQueue,
|
||||||
std::shared_ptr<ModuleRegistry> moduleRegistry) {
|
std::shared_ptr<ModuleRegistry> moduleRegistry) {
|
||||||
callback_ = std::move(callback);
|
callback_ = std::move(callback);
|
||||||
nativeQueue_ = std::move(nativeQueue);
|
|
||||||
jsQueue_ = jsQueue;
|
|
||||||
moduleRegistry_ = moduleRegistry;
|
|
||||||
|
|
||||||
jsQueue_->runOnQueueSync([this, &jsef] {
|
jsQueue->runOnQueueSync(
|
||||||
bridge_ = folly::make_unique<Bridge>(
|
[this, &jsef, moduleRegistry, jsQueue,
|
||||||
jsef.get(), jsQueue_, folly::make_unique<ExecutorTokenFactoryImpl>(callback_.get()), folly::make_unique<BridgeCallbackImpl>(this));
|
nativeQueue=folly::makeMoveWrapper(std::move(nativeQueue))] () mutable {
|
||||||
});
|
nativeToJsBridge_ = folly::make_unique<NativeToJsBridge>(
|
||||||
SystraceSection s("setBatchedBridgeConfig");
|
jsef.get(), moduleRegistry, jsQueue, nativeQueue.move(), callback_);
|
||||||
|
});
|
||||||
|
|
||||||
CHECK(bridge_);
|
CHECK(nativeToJsBridge_);
|
||||||
|
|
||||||
folly::dynamic nativeModuleDescriptions = folly::dynamic::array();
|
|
||||||
{
|
|
||||||
SystraceSection s("collectNativeModuleDescriptions");
|
|
||||||
nativeModuleDescriptions = moduleRegistry_->moduleDescriptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
folly::dynamic config =
|
|
||||||
folly::dynamic::object
|
|
||||||
("remoteModuleConfig", std::move(nativeModuleDescriptions));
|
|
||||||
|
|
||||||
SystraceSection t("setGlobalVariable");
|
|
||||||
setGlobalVariable(
|
|
||||||
"__fbBatchedBridgeConfig",
|
|
||||||
folly::make_unique<JSBigStdString>(folly::toJson(config)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Instance::loadScriptFromString(std::unique_ptr<const JSBigString> string,
|
void Instance::loadScriptFromString(std::unique_ptr<const JSBigString> string,
|
||||||
|
@ -99,7 +50,7 @@ void Instance::loadScriptFromString(std::unique_ptr<const JSBigString> string,
|
||||||
SystraceSection s("reactbridge_xplat_loadScriptFromString",
|
SystraceSection s("reactbridge_xplat_loadScriptFromString",
|
||||||
"sourceURL", sourceURL);
|
"sourceURL", sourceURL);
|
||||||
// TODO mhorowitz: ReactMarker around loadApplicationScript
|
// TODO mhorowitz: ReactMarker around loadApplicationScript
|
||||||
bridge_->loadApplicationScript(std::move(string), std::move(sourceURL));
|
nativeToJsBridge_->loadApplicationScript(std::move(string), std::move(sourceURL));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Instance::loadScriptFromFile(const std::string& filename,
|
void Instance::loadScriptFromFile(const std::string& filename,
|
||||||
|
@ -129,71 +80,42 @@ void Instance::loadUnbundle(std::unique_ptr<JSModulesUnbundle> unbundle,
|
||||||
std::string startupScriptSourceURL) {
|
std::string startupScriptSourceURL) {
|
||||||
callback_->incrementPendingJSCalls();
|
callback_->incrementPendingJSCalls();
|
||||||
SystraceSection s("reactbridge_xplat_setJSModulesUnbundle");
|
SystraceSection s("reactbridge_xplat_setJSModulesUnbundle");
|
||||||
bridge_->loadApplicationUnbundle(std::move(unbundle), std::move(startupScript),
|
nativeToJsBridge_->loadApplicationUnbundle(std::move(unbundle), std::move(startupScript),
|
||||||
std::move(startupScriptSourceURL));
|
std::move(startupScriptSourceURL));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Instance::supportsProfiling() {
|
bool Instance::supportsProfiling() {
|
||||||
return bridge_->supportsProfiling();
|
return nativeToJsBridge_->supportsProfiling();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Instance::startProfiler(const std::string& title) {
|
void Instance::startProfiler(const std::string& title) {
|
||||||
return bridge_->startProfiler(title);
|
return nativeToJsBridge_->startProfiler(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Instance::stopProfiler(const std::string& title, const std::string& filename) {
|
void Instance::stopProfiler(const std::string& title, const std::string& filename) {
|
||||||
return bridge_->stopProfiler(title, filename);
|
return nativeToJsBridge_->stopProfiler(title, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Instance::setGlobalVariable(std::string propName,
|
void Instance::setGlobalVariable(std::string propName,
|
||||||
std::unique_ptr<const JSBigString> jsonValue) {
|
std::unique_ptr<const JSBigString> jsonValue) {
|
||||||
bridge_->setGlobalVariable(std::move(propName), std::move(jsonValue));
|
nativeToJsBridge_->setGlobalVariable(std::move(propName), std::move(jsonValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Instance::callJSFunction(ExecutorToken token, const std::string& module, const std::string& method,
|
void Instance::callJSFunction(ExecutorToken token, const std::string& module, const std::string& method,
|
||||||
folly::dynamic&& params, const std::string& tracingName) {
|
folly::dynamic&& params, const std::string& tracingName) {
|
||||||
SystraceSection s(tracingName.c_str());
|
SystraceSection s(tracingName.c_str());
|
||||||
callback_->incrementPendingJSCalls();
|
callback_->incrementPendingJSCalls();
|
||||||
bridge_->callFunction(token, module, method, std::move(params), tracingName);
|
nativeToJsBridge_->callFunction(token, module, method, std::move(params), tracingName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Instance::callJSCallback(ExecutorToken token, uint64_t callbackId, folly::dynamic&& params) {
|
void Instance::callJSCallback(ExecutorToken token, uint64_t callbackId, folly::dynamic&& params) {
|
||||||
SystraceSection s("<callback>");
|
SystraceSection s("<callback>");
|
||||||
callback_->incrementPendingJSCalls();
|
callback_->incrementPendingJSCalls();
|
||||||
bridge_->invokeCallback(token, (double) callbackId, std::move(params));
|
nativeToJsBridge_->invokeCallback(token, (double) callbackId, std::move(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
ExecutorToken Instance::getMainExecutorToken() {
|
ExecutorToken Instance::getMainExecutorToken() {
|
||||||
return bridge_->getMainExecutorToken();
|
return nativeToJsBridge_->getMainExecutorToken();
|
||||||
}
|
|
||||||
|
|
||||||
void Instance::callNativeModules(ExecutorToken token, const std::string& calls, bool isEndOfBatch) {
|
|
||||||
// TODO mhorowitz: avoid copying calls here.
|
|
||||||
nativeQueue_->runOnQueue([this, token, calls, isEndOfBatch] {
|
|
||||||
try {
|
|
||||||
// An exception anywhere in here stops processing of the batch. This
|
|
||||||
// was the behavior of the Android bridge, and since exception handling
|
|
||||||
// terminates the whole bridge, there's not much point in continuing.
|
|
||||||
for (auto& call : react::parseMethodCalls(calls)) {
|
|
||||||
moduleRegistry_->callNativeMethod(
|
|
||||||
token, call.moduleId, call.methodId, std::move(call.arguments), call.callId);
|
|
||||||
}
|
|
||||||
if (isEndOfBatch) {
|
|
||||||
callback_->onBatchComplete();
|
|
||||||
callback_->decrementPendingJSCalls();
|
|
||||||
}
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
LOG(ERROR) << folly::exceptionStr(e).toStdString();
|
|
||||||
callback_->onNativeException(folly::exceptionStr(e).toStdString());
|
|
||||||
} catch (...) {
|
|
||||||
LOG(ERROR) << "Unknown exception";
|
|
||||||
callback_->onNativeException("Unknown exception");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
MethodCallResult Instance::callSerializableNativeHook(ExecutorToken token, unsigned int moduleId, unsigned int methodId, folly::dynamic&& params) {
|
|
||||||
return moduleRegistry_->callSerializableNativeHook(token, moduleId, methodId, std::move(params));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace react
|
} // namespace react
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
#include <folly/dynamic.h>
|
#include <folly/dynamic.h>
|
||||||
|
|
||||||
#include "Bridge.h"
|
#include "NativeToJsBridge.h"
|
||||||
#include "ModuleRegistry.h"
|
#include "ModuleRegistry.h"
|
||||||
#include "NativeModule.h"
|
#include "NativeModule.h"
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ struct InstanceCallback {
|
||||||
virtual void decrementPendingJSCalls() = 0;
|
virtual void decrementPendingJSCalls() = 0;
|
||||||
virtual void onNativeException(const std::string& what) = 0;
|
virtual void onNativeException(const std::string& what) = 0;
|
||||||
virtual ExecutorToken createExecutorToken() = 0;
|
virtual ExecutorToken createExecutorToken() = 0;
|
||||||
|
virtual void onExecutorStopped(ExecutorToken) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Instance {
|
class Instance {
|
||||||
|
@ -46,21 +47,15 @@ class Instance {
|
||||||
void callJSFunction(ExecutorToken token, const std::string& module, const std::string& method,
|
void callJSFunction(ExecutorToken token, const std::string& module, const std::string& method,
|
||||||
folly::dynamic&& params, const std::string& tracingName);
|
folly::dynamic&& params, const std::string& tracingName);
|
||||||
void callJSCallback(ExecutorToken token, uint64_t callbackId, folly::dynamic&& params);
|
void callJSCallback(ExecutorToken token, uint64_t callbackId, folly::dynamic&& params);
|
||||||
MethodCallResult callSerializableNativeHook(ExecutorToken token, unsigned int moduleId, unsigned int methodId, folly::dynamic&& args);
|
MethodCallResult callSerializableNativeHook(ExecutorToken token, unsigned int moduleId,
|
||||||
|
unsigned int methodId, folly::dynamic&& args);
|
||||||
ExecutorToken getMainExecutorToken();
|
ExecutorToken getMainExecutorToken();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class BridgeCallbackImpl;
|
|
||||||
|
|
||||||
void callNativeModules(ExecutorToken token, const std::string& calls, bool isEndOfBatch);
|
void callNativeModules(ExecutorToken token, const std::string& calls, bool isEndOfBatch);
|
||||||
|
|
||||||
std::unique_ptr<InstanceCallback> callback_;
|
std::shared_ptr<InstanceCallback> callback_;
|
||||||
std::shared_ptr<ModuleRegistry> moduleRegistry_;
|
std::unique_ptr<NativeToJsBridge> nativeToJsBridge_;
|
||||||
// TODO #10487027: clean up the ownership of this.
|
|
||||||
std::shared_ptr<MessageQueueThread> jsQueue_;
|
|
||||||
std::unique_ptr<MessageQueueThread> nativeQueue_;
|
|
||||||
|
|
||||||
std::unique_ptr<Bridge> bridge_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
#include <folly/Conv.h>
|
#include <folly/Conv.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
|
||||||
#include "Bridge.h"
|
|
||||||
#include "JSCHelpers.h"
|
#include "JSCHelpers.h"
|
||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
#include "SystraceSection.h"
|
#include "SystraceSection.h"
|
||||||
|
@ -107,29 +106,51 @@ static std::string executeJSCallWithJSC(
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<JSExecutor> JSCExecutorFactory::createJSExecutor(
|
std::unique_ptr<JSExecutor> JSCExecutorFactory::createJSExecutor(
|
||||||
Bridge *bridge, std::shared_ptr<MessageQueueThread> jsQueue) {
|
std::shared_ptr<ExecutorDelegate> delegate, std::shared_ptr<MessageQueueThread> jsQueue) {
|
||||||
return std::unique_ptr<JSExecutor>(
|
return std::unique_ptr<JSExecutor>(
|
||||||
new JSCExecutor(bridge, jsQueue, cacheDir_, m_jscConfig));
|
new JSCExecutor(delegate, jsQueue, m_cacheDir, m_jscConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
JSCExecutor::JSCExecutor(Bridge *bridge, std::shared_ptr<MessageQueueThread> messageQueueThread,
|
JSCExecutor::JSCExecutor(std::shared_ptr<ExecutorDelegate> delegate,
|
||||||
const std::string& cacheDir, const folly::dynamic& jscConfig) :
|
std::shared_ptr<MessageQueueThread> messageQueueThread,
|
||||||
m_bridge(bridge),
|
const std::string& cacheDir,
|
||||||
|
const folly::dynamic& jscConfig) :
|
||||||
|
m_delegate(delegate),
|
||||||
m_deviceCacheDir(cacheDir),
|
m_deviceCacheDir(cacheDir),
|
||||||
m_messageQueueThread(messageQueueThread),
|
m_messageQueueThread(messageQueueThread),
|
||||||
m_jscConfig(jscConfig) {
|
m_jscConfig(jscConfig) {
|
||||||
initOnJSVMThread();
|
initOnJSVMThread();
|
||||||
|
|
||||||
|
SystraceSection s("setBatchedBridgeConfig");
|
||||||
|
|
||||||
|
folly::dynamic nativeModuleConfig = folly::dynamic::array();
|
||||||
|
|
||||||
|
{
|
||||||
|
SystraceSection s("collectNativeModuleNames");
|
||||||
|
std::vector<std::string> names = delegate->moduleNames();
|
||||||
|
for (auto& name : delegate->moduleNames()) {
|
||||||
|
nativeModuleConfig.push_back(folly::dynamic::array(std::move(name)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
folly::dynamic config =
|
||||||
|
folly::dynamic::object("remoteModuleConfig", std::move(nativeModuleConfig));
|
||||||
|
|
||||||
|
SystraceSection t("setGlobalVariable");
|
||||||
|
setGlobalVariable(
|
||||||
|
"__fbBatchedBridgeConfig",
|
||||||
|
folly::make_unique<JSBigStdString>(folly::toJson(config)));
|
||||||
}
|
}
|
||||||
|
|
||||||
JSCExecutor::JSCExecutor(
|
JSCExecutor::JSCExecutor(
|
||||||
Bridge *bridge,
|
std::shared_ptr<ExecutorDelegate> delegate,
|
||||||
std::shared_ptr<MessageQueueThread> messageQueueThread,
|
std::shared_ptr<MessageQueueThread> messageQueueThread,
|
||||||
int workerId,
|
int workerId,
|
||||||
JSCExecutor *owner,
|
JSCExecutor *owner,
|
||||||
std::string scriptURL,
|
std::string scriptURL,
|
||||||
std::unordered_map<std::string, std::string> globalObjAsJSON,
|
std::unordered_map<std::string, std::string> globalObjAsJSON,
|
||||||
const folly::dynamic& jscConfig) :
|
const folly::dynamic& jscConfig) :
|
||||||
m_bridge(bridge),
|
m_delegate(delegate),
|
||||||
m_workerId(workerId),
|
m_workerId(workerId),
|
||||||
m_owner(owner),
|
m_owner(owner),
|
||||||
m_deviceCacheDir(owner->m_deviceCacheDir),
|
m_deviceCacheDir(owner->m_deviceCacheDir),
|
||||||
|
@ -198,6 +219,7 @@ void JSCExecutor::initOnJSVMThread() {
|
||||||
// Add a pointer to ourselves so we can retrieve it later in our hooks
|
// Add a pointer to ourselves so we can retrieve it later in our hooks
|
||||||
JSObjectSetPrivate(JSContextGetGlobalObject(m_context), this);
|
JSObjectSetPrivate(JSContextGetGlobalObject(m_context), this);
|
||||||
|
|
||||||
|
installNativeHook<&JSCExecutor::nativeRequireModuleConfig>("nativeRequireModuleConfig");
|
||||||
installNativeHook<&JSCExecutor::nativeFlushQueueImmediate>("nativeFlushQueueImmediate");
|
installNativeHook<&JSCExecutor::nativeFlushQueueImmediate>("nativeFlushQueueImmediate");
|
||||||
installNativeHook<&JSCExecutor::nativeStartWorker>("nativeStartWorker");
|
installNativeHook<&JSCExecutor::nativeStartWorker>("nativeStartWorker");
|
||||||
installNativeHook<&JSCExecutor::nativePostMessageToWorker>("nativePostMessageToWorker");
|
installNativeHook<&JSCExecutor::nativePostMessageToWorker>("nativePostMessageToWorker");
|
||||||
|
@ -280,7 +302,7 @@ void JSCExecutor::setJSModulesUnbundle(std::unique_ptr<JSModulesUnbundle> unbund
|
||||||
void JSCExecutor::flush() {
|
void JSCExecutor::flush() {
|
||||||
// TODO: Make this a first class function instead of evaling. #9317773
|
// TODO: Make this a first class function instead of evaling. #9317773
|
||||||
std::string calls = executeJSCallWithJSC(m_context, "flushedQueue", std::vector<folly::dynamic>());
|
std::string calls = executeJSCallWithJSC(m_context, "flushedQueue", std::vector<folly::dynamic>());
|
||||||
m_bridge->callNativeModules(*this, calls, true);
|
m_delegate->callNativeModules(*this, std::move(calls), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JSCExecutor::callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) {
|
void JSCExecutor::callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) {
|
||||||
|
@ -291,7 +313,7 @@ void JSCExecutor::callFunction(const std::string& moduleId, const std::string& m
|
||||||
std::move(arguments),
|
std::move(arguments),
|
||||||
};
|
};
|
||||||
std::string calls = executeJSCallWithJSC(m_context, "callFunctionReturnFlushedQueue", std::move(call));
|
std::string calls = executeJSCallWithJSC(m_context, "callFunctionReturnFlushedQueue", std::move(call));
|
||||||
m_bridge->callNativeModules(*this, calls, true);
|
m_delegate->callNativeModules(*this, std::move(calls), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JSCExecutor::invokeCallback(const double callbackId, const folly::dynamic& arguments) {
|
void JSCExecutor::invokeCallback(const double callbackId, const folly::dynamic& arguments) {
|
||||||
|
@ -301,7 +323,7 @@ void JSCExecutor::invokeCallback(const double callbackId, const folly::dynamic&
|
||||||
std::move(arguments)
|
std::move(arguments)
|
||||||
};
|
};
|
||||||
std::string calls = executeJSCallWithJSC(m_context, "invokeCallbackAndReturnFlushedQueue", std::move(call));
|
std::string calls = executeJSCallWithJSC(m_context, "invokeCallbackAndReturnFlushedQueue", std::move(call));
|
||||||
m_bridge->callNativeModules(*this, calls, true);
|
m_delegate->callNativeModules(*this, std::move(calls), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JSCExecutor::setGlobalVariable(std::string propName, std::unique_ptr<const JSBigString> jsonValue) {
|
void JSCExecutor::setGlobalVariable(std::string propName, std::unique_ptr<const JSBigString> jsonValue) {
|
||||||
|
@ -362,7 +384,7 @@ void JSCExecutor::handleMemoryPressureCritical() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void JSCExecutor::flushQueueImmediate(std::string queueJSON) {
|
void JSCExecutor::flushQueueImmediate(std::string queueJSON) {
|
||||||
m_bridge->callNativeModules(*this, queueJSON, false);
|
m_delegate->callNativeModules(*this, std::move(queueJSON), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JSCExecutor::loadModule(uint32_t moduleId) {
|
void JSCExecutor::loadModule(uint32_t moduleId) {
|
||||||
|
@ -388,7 +410,7 @@ int JSCExecutor::addWebWorker(
|
||||||
WebWorkerUtil::createWebWorkerThread(workerId, m_messageQueueThread.get());
|
WebWorkerUtil::createWebWorkerThread(workerId, m_messageQueueThread.get());
|
||||||
std::unique_ptr<JSCExecutor> worker;
|
std::unique_ptr<JSCExecutor> worker;
|
||||||
workerMQT->runOnQueueSync([this, &worker, &workerMQT, &scriptURL, &globalObj, workerId, &workerJscConfig] () {
|
workerMQT->runOnQueueSync([this, &worker, &workerMQT, &scriptURL, &globalObj, workerId, &workerJscConfig] () {
|
||||||
worker.reset(new JSCExecutor(m_bridge, workerMQT, workerId, this, scriptURL,
|
worker.reset(new JSCExecutor(m_delegate, workerMQT, workerId, this, std::move(scriptURL),
|
||||||
globalObj.toJSONMap(), workerJscConfig));
|
globalObj.toJSONMap(), workerJscConfig));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -397,14 +419,14 @@ int JSCExecutor::addWebWorker(
|
||||||
|
|
||||||
JSCExecutor *workerPtr = worker.get();
|
JSCExecutor *workerPtr = worker.get();
|
||||||
std::shared_ptr<MessageQueueThread> sharedMessageQueueThread = worker->m_messageQueueThread;
|
std::shared_ptr<MessageQueueThread> sharedMessageQueueThread = worker->m_messageQueueThread;
|
||||||
ExecutorToken token = m_bridge->registerExecutor(
|
m_delegate->registerExecutor(
|
||||||
std::move(worker),
|
std::move(worker),
|
||||||
std::move(sharedMessageQueueThread));
|
std::move(sharedMessageQueueThread));
|
||||||
|
|
||||||
m_ownedWorkers.emplace(
|
m_ownedWorkers.emplace(
|
||||||
std::piecewise_construct,
|
std::piecewise_construct,
|
||||||
std::forward_as_tuple(workerId),
|
std::forward_as_tuple(workerId),
|
||||||
std::forward_as_tuple(workerPtr, token, std::move(workerObj)));
|
std::forward_as_tuple(workerPtr, std::move(workerObj)));
|
||||||
|
|
||||||
return workerId;
|
return workerId;
|
||||||
}
|
}
|
||||||
|
@ -464,12 +486,11 @@ void JSCExecutor::receiveMessageFromOwner(const std::string& msgString) {
|
||||||
void JSCExecutor::terminateOwnedWebWorker(int workerId) {
|
void JSCExecutor::terminateOwnedWebWorker(int workerId) {
|
||||||
auto& workerRegistration = m_ownedWorkers.at(workerId);
|
auto& workerRegistration = m_ownedWorkers.at(workerId);
|
||||||
std::shared_ptr<MessageQueueThread> workerMQT = workerRegistration.executor->m_messageQueueThread;
|
std::shared_ptr<MessageQueueThread> workerMQT = workerRegistration.executor->m_messageQueueThread;
|
||||||
ExecutorToken workerExecutorToken = workerRegistration.executorToken;
|
|
||||||
m_ownedWorkers.erase(workerId);
|
m_ownedWorkers.erase(workerId);
|
||||||
|
|
||||||
workerMQT->runOnQueueSync([this, workerExecutorToken, &workerMQT] {
|
workerMQT->runOnQueueSync([this, &workerMQT] {
|
||||||
workerMQT->quitSynchronous();
|
workerMQT->quitSynchronous();
|
||||||
std::unique_ptr<JSExecutor> worker = m_bridge->unregisterExecutor(workerExecutorToken);
|
std::unique_ptr<JSExecutor> worker = m_delegate->unregisterExecutor(*this);
|
||||||
worker->destroy();
|
worker->destroy();
|
||||||
worker.reset();
|
worker.reset();
|
||||||
});
|
});
|
||||||
|
@ -521,6 +542,18 @@ JSValueRef JSCExecutor::nativeRequire(
|
||||||
return JSValueMakeUndefined(m_context);
|
return JSValueMakeUndefined(m_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JSValueRef JSCExecutor::nativeRequireModuleConfig(
|
||||||
|
size_t argumentCount,
|
||||||
|
const JSValueRef arguments[]) {
|
||||||
|
if (argumentCount != 1) {
|
||||||
|
throw std::invalid_argument("Got wrong number of args");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string moduleName = Value(m_context, arguments[0]).toString().str();
|
||||||
|
folly::dynamic config = m_delegate->getModuleConfig(moduleName);
|
||||||
|
return JSValueMakeString(m_context, String(folly::toJson(config).c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
JSValueRef JSCExecutor::nativeFlushQueueImmediate(
|
JSValueRef JSCExecutor::nativeFlushQueueImmediate(
|
||||||
size_t argumentCount,
|
size_t argumentCount,
|
||||||
const JSValueRef arguments[]) {
|
const JSValueRef arguments[]) {
|
||||||
|
@ -529,7 +562,7 @@ JSValueRef JSCExecutor::nativeFlushQueueImmediate(
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string resStr = Value(m_context, arguments[0]).toJSONString();
|
std::string resStr = Value(m_context, arguments[0]).toJSONString();
|
||||||
flushQueueImmediate(resStr);
|
flushQueueImmediate(std::move(resStr));
|
||||||
return JSValueMakeUndefined(m_context);
|
return JSValueMakeUndefined(m_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -595,7 +628,8 @@ JSValueRef JSCExecutor::nativeCallSyncHook(
|
||||||
unsigned int methodId = Value(m_context, arguments[1]).asUnsignedInteger();
|
unsigned int methodId = Value(m_context, arguments[1]).asUnsignedInteger();
|
||||||
std::string argsJson = Value(m_context, arguments[2]).toJSONString();
|
std::string argsJson = Value(m_context, arguments[2]).toJSONString();
|
||||||
|
|
||||||
MethodCallResult result = m_bridge->callSerializableNativeHook(
|
MethodCallResult result = m_delegate->callSerializableNativeHook(
|
||||||
|
*this,
|
||||||
moduleId,
|
moduleId,
|
||||||
methodId,
|
methodId,
|
||||||
argsJson);
|
argsJson);
|
||||||
|
|
|
@ -23,25 +23,24 @@ class MessageQueueThread;
|
||||||
class JSCExecutorFactory : public JSExecutorFactory {
|
class JSCExecutorFactory : public JSExecutorFactory {
|
||||||
public:
|
public:
|
||||||
JSCExecutorFactory(const std::string& cacheDir, const folly::dynamic& jscConfig) :
|
JSCExecutorFactory(const std::string& cacheDir, const folly::dynamic& jscConfig) :
|
||||||
cacheDir_(cacheDir),
|
m_cacheDir(cacheDir),
|
||||||
m_jscConfig(jscConfig) {}
|
m_jscConfig(jscConfig) {}
|
||||||
virtual std::unique_ptr<JSExecutor> createJSExecutor(
|
virtual std::unique_ptr<JSExecutor> createJSExecutor(
|
||||||
Bridge *bridge, std::shared_ptr<MessageQueueThread> jsQueue) override;
|
std::shared_ptr<ExecutorDelegate> delegate,
|
||||||
|
std::shared_ptr<MessageQueueThread> jsQueue) override;
|
||||||
private:
|
private:
|
||||||
std::string cacheDir_;
|
std::string m_cacheDir;
|
||||||
folly::dynamic m_jscConfig;
|
folly::dynamic m_jscConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
class JSCExecutor;
|
class JSCExecutor;
|
||||||
class WorkerRegistration : public noncopyable {
|
class WorkerRegistration : public noncopyable {
|
||||||
public:
|
public:
|
||||||
explicit WorkerRegistration(JSCExecutor* executor_, ExecutorToken executorToken_, Object jsObj_) :
|
explicit WorkerRegistration(JSCExecutor* executor_, Object jsObj_) :
|
||||||
executor(executor_),
|
executor(executor_),
|
||||||
executorToken(executorToken_),
|
|
||||||
jsObj(std::move(jsObj_)) {}
|
jsObj(std::move(jsObj_)) {}
|
||||||
|
|
||||||
JSCExecutor *executor;
|
JSCExecutor *executor;
|
||||||
ExecutorToken executorToken;
|
|
||||||
Object jsObj;
|
Object jsObj;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -50,8 +49,10 @@ public:
|
||||||
/**
|
/**
|
||||||
* Must be invoked from thread this Executor will run on.
|
* Must be invoked from thread this Executor will run on.
|
||||||
*/
|
*/
|
||||||
explicit JSCExecutor(Bridge *bridge, std::shared_ptr<MessageQueueThread> messageQueueThread,
|
explicit JSCExecutor(std::shared_ptr<ExecutorDelegate> delegate,
|
||||||
const std::string& cacheDir, const folly::dynamic& jscConfig);
|
std::shared_ptr<MessageQueueThread> messageQueueThread,
|
||||||
|
const std::string& cacheDir,
|
||||||
|
const folly::dynamic& jscConfig);
|
||||||
~JSCExecutor() override;
|
~JSCExecutor() override;
|
||||||
|
|
||||||
virtual void loadApplicationScript(
|
virtual void loadApplicationScript(
|
||||||
|
@ -79,7 +80,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
JSGlobalContextRef m_context;
|
JSGlobalContextRef m_context;
|
||||||
Bridge *m_bridge;
|
std::shared_ptr<ExecutorDelegate> m_delegate;
|
||||||
int m_workerId = 0; // if this is a worker executor, this is non-zero
|
int m_workerId = 0; // if this is a worker executor, this is non-zero
|
||||||
JSCExecutor *m_owner = nullptr; // if this is a worker executor, this is non-null
|
JSCExecutor *m_owner = nullptr; // if this is a worker executor, this is non-null
|
||||||
std::shared_ptr<bool> m_isDestroyed = std::shared_ptr<bool>(new bool(false));
|
std::shared_ptr<bool> m_isDestroyed = std::shared_ptr<bool>(new bool(false));
|
||||||
|
@ -93,7 +94,7 @@ private:
|
||||||
* WebWorker constructor. Must be invoked from thread this Executor will run on.
|
* WebWorker constructor. Must be invoked from thread this Executor will run on.
|
||||||
*/
|
*/
|
||||||
JSCExecutor(
|
JSCExecutor(
|
||||||
Bridge *bridge,
|
std::shared_ptr<ExecutorDelegate> delegate,
|
||||||
std::shared_ptr<MessageQueueThread> messageQueueThread,
|
std::shared_ptr<MessageQueueThread> messageQueueThread,
|
||||||
int workerId,
|
int workerId,
|
||||||
JSCExecutor *owner,
|
JSCExecutor *owner,
|
||||||
|
@ -118,6 +119,9 @@ private:
|
||||||
template< JSValueRef (JSCExecutor::*method)(size_t, const JSValueRef[])>
|
template< JSValueRef (JSCExecutor::*method)(size_t, const JSValueRef[])>
|
||||||
void installNativeHook(const char* name);
|
void installNativeHook(const char* name);
|
||||||
|
|
||||||
|
JSValueRef nativeRequireModuleConfig(
|
||||||
|
size_t argumentCount,
|
||||||
|
const JSValueRef arguments[]);
|
||||||
JSValueRef nativeStartWorker(
|
JSValueRef nativeStartWorker(
|
||||||
size_t argumentCount,
|
size_t argumentCount,
|
||||||
const JSValueRef arguments[]);
|
const JSValueRef arguments[]);
|
||||||
|
|
|
@ -8,45 +8,86 @@
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace react {
|
namespace react {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::string normalizeName(std::string name) {
|
||||||
|
// TODO mhorowitz #10487027: This is super ugly. We should just
|
||||||
|
// change iOS to emit normalized names, drop the "RK..." from
|
||||||
|
// names hardcoded in Android, and then delete this and the
|
||||||
|
// similar hacks in js.
|
||||||
|
if (name.compare(0, 3, "RCT") == 0) {
|
||||||
|
return name.substr(3);
|
||||||
|
} else if (name.compare(0, 2, "RK") == 0) {
|
||||||
|
return name.substr(2);
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
ModuleRegistry::ModuleRegistry(std::vector<std::unique_ptr<NativeModule>> modules)
|
ModuleRegistry::ModuleRegistry(std::vector<std::unique_ptr<NativeModule>> modules)
|
||||||
: modules_(std::move(modules)) {}
|
: modules_(std::move(modules)) {}
|
||||||
|
|
||||||
folly::dynamic ModuleRegistry::moduleDescriptions() {
|
std::vector<std::string> ModuleRegistry::moduleNames() {
|
||||||
folly::dynamic modDescs = folly::dynamic::object;
|
std::vector<std::string> names;
|
||||||
|
for (size_t i = 0; i < modules_.size(); i++) {
|
||||||
for (size_t moduleId = 0; moduleId < modules_.size(); ++moduleId) {
|
std::string name = normalizeName(modules_[i]->getName());
|
||||||
const auto& module = modules_[moduleId];
|
modulesByName_[name] = i;
|
||||||
|
names.push_back(std::move(name));
|
||||||
folly::dynamic methodDescs = folly::dynamic::object;
|
}
|
||||||
std::vector<MethodDescriptor> methods;
|
return names;
|
||||||
{
|
}
|
||||||
SystraceSection s("getMethods",
|
|
||||||
"module", module->getName());
|
folly::dynamic ModuleRegistry::getConfig(const std::string& name) {
|
||||||
methods = module->getMethods();
|
auto it = modulesByName_.find(name);
|
||||||
}
|
if (it == modulesByName_.end()) {
|
||||||
for (size_t methodId = 0; methodId < methods.size(); ++methodId) {
|
return nullptr;
|
||||||
methodDescs.insert(std::move(methods[methodId].name),
|
}
|
||||||
folly::dynamic::object
|
CHECK(it->second < modules_.size());
|
||||||
("methodID", methodId)
|
|
||||||
("type", std::move(methods[methodId].type)));
|
NativeModule* module = modules_[it->second].get();
|
||||||
}
|
|
||||||
|
// string name, [object constants,] array methodNames (methodId is index), [array asyncMethodIds]
|
||||||
folly::dynamic constants = folly::dynamic::array();
|
folly::dynamic config = folly::dynamic::array(name);
|
||||||
{
|
|
||||||
SystraceSection s("getConstants",
|
{
|
||||||
"module", module->getName());
|
SystraceSection s("getConfig constants",
|
||||||
constants = module->getConstants();
|
"module", name);
|
||||||
}
|
folly::dynamic constants = module->getConstants();
|
||||||
|
if (constants.isObject() && constants.size() > 0) {
|
||||||
modDescs.insert(module->getName(),
|
config.push_back(std::move(constants));
|
||||||
folly::dynamic::object
|
}
|
||||||
("supportsWebWorkers", module->supportsWebWorkers())
|
}
|
||||||
("moduleID", moduleId)
|
|
||||||
("methods", std::move(methodDescs))
|
{
|
||||||
("constants", std::move(constants)));
|
SystraceSection s("getConfig methods",
|
||||||
|
"module", name);
|
||||||
|
std::vector<MethodDescriptor> methods = module->getMethods();
|
||||||
|
|
||||||
|
folly::dynamic methodNames = folly::dynamic::array;
|
||||||
|
folly::dynamic asyncMethodIds = folly::dynamic::array;
|
||||||
|
|
||||||
|
for (auto& descriptor : methods) {
|
||||||
|
methodNames.push_back(std::move(descriptor.name));
|
||||||
|
if (descriptor.type == "remoteAsync") {
|
||||||
|
asyncMethodIds.push_back(methodNames.size() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!methodNames.empty()) {
|
||||||
|
config.push_back(std::move(methodNames));
|
||||||
|
if (!asyncMethodIds.empty()) {
|
||||||
|
config.push_back(std::move(asyncMethodIds));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.size() == 1) {
|
||||||
|
// no constants or methods
|
||||||
|
return nullptr;
|
||||||
|
} else {
|
||||||
|
return config;
|
||||||
}
|
}
|
||||||
return modDescs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModuleRegistry::callNativeMethod(ExecutorToken token, unsigned int moduleId, unsigned int methodId,
|
void ModuleRegistry::callNativeMethod(ExecutorToken token, unsigned int moduleId, unsigned int methodId,
|
||||||
|
@ -75,7 +116,7 @@ void ModuleRegistry::callNativeMethod(ExecutorToken token, unsigned int moduleId
|
||||||
// fall through;
|
// fall through;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string moduleName = modules_[moduleId]->getName();
|
std::string moduleName = normalizeName(modules_[moduleId]->getName());
|
||||||
auto descs = modules_[moduleId]->getMethods();
|
auto descs = modules_[moduleId]->getMethods();
|
||||||
std::string methodName;
|
std::string methodName;
|
||||||
if (methodId < descs.size()) {
|
if (methodId < descs.size()) {
|
||||||
|
|
|
@ -25,13 +25,18 @@ class ModuleRegistry {
|
||||||
// notifyCatalystInstanceDestroy: use RAII instead
|
// notifyCatalystInstanceDestroy: use RAII instead
|
||||||
|
|
||||||
ModuleRegistry(std::vector<std::unique_ptr<NativeModule>> modules);
|
ModuleRegistry(std::vector<std::unique_ptr<NativeModule>> modules);
|
||||||
folly::dynamic moduleDescriptions();
|
std::vector<std::string> moduleNames();
|
||||||
|
folly::dynamic getConfig(const std::string& name);
|
||||||
void callNativeMethod(ExecutorToken token, unsigned int moduleId, unsigned int methodId,
|
void callNativeMethod(ExecutorToken token, unsigned int moduleId, unsigned int methodId,
|
||||||
folly::dynamic&& params, int callId);
|
folly::dynamic&& params, int callId);
|
||||||
MethodCallResult callSerializableNativeHook(ExecutorToken token, unsigned int moduleId, unsigned int methodId, folly::dynamic&& args);
|
MethodCallResult callSerializableNativeHook(ExecutorToken token, unsigned int moduleId, unsigned int methodId, folly::dynamic&& args);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// This is always populated
|
||||||
std::vector<std::unique_ptr<NativeModule>> modules_;
|
std::vector<std::unique_ptr<NativeModule>> modules_;
|
||||||
|
|
||||||
|
// This is only populated if moduleNames() is called. Values are indices into modules_.
|
||||||
|
std::unordered_map<std::string, size_t> modulesByName_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,6 @@
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace react {
|
namespace react {
|
||||||
|
|
||||||
struct MethodCallResult {
|
|
||||||
folly::dynamic result;
|
|
||||||
bool isUndefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MethodDescriptor {
|
struct MethodDescriptor {
|
||||||
std::string name;
|
std::string name;
|
||||||
// type is one of js MessageQueue.MethodTypes
|
// type is one of js MessageQueue.MethodTypes
|
||||||
|
|
|
@ -0,0 +1,347 @@
|
||||||
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "NativeToJsBridge.h"
|
||||||
|
|
||||||
|
#ifdef WITH_FBSYSTRACE
|
||||||
|
#include <fbsystrace.h>
|
||||||
|
using fbsystrace::FbSystraceAsyncFlow;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <folly/json.h>
|
||||||
|
#include <folly/Memory.h>
|
||||||
|
#include <folly/MoveWrapper.h>
|
||||||
|
|
||||||
|
#include "Instance.h"
|
||||||
|
#include "ModuleRegistry.h"
|
||||||
|
#include "Platform.h"
|
||||||
|
#include "SystraceSection.h"
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace react {
|
||||||
|
|
||||||
|
// This class manages calls from JS to native code.
|
||||||
|
class JsToNativeBridge : public react::ExecutorDelegate {
|
||||||
|
public:
|
||||||
|
JsToNativeBridge(NativeToJsBridge* nativeToJs,
|
||||||
|
std::shared_ptr<ModuleRegistry> registry,
|
||||||
|
std::unique_ptr<MessageQueueThread> nativeQueue,
|
||||||
|
std::shared_ptr<InstanceCallback> callback)
|
||||||
|
: m_nativeToJs(nativeToJs)
|
||||||
|
, m_registry(registry)
|
||||||
|
, m_nativeQueue(std::move(nativeQueue))
|
||||||
|
, m_callback(callback) {}
|
||||||
|
|
||||||
|
void registerExecutor(std::unique_ptr<JSExecutor> executor,
|
||||||
|
std::shared_ptr<MessageQueueThread> queue) override {
|
||||||
|
m_nativeToJs->registerExecutor(m_callback->createExecutorToken(), std::move(executor), queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<JSExecutor> unregisterExecutor(JSExecutor& executor) override {
|
||||||
|
m_callback->onExecutorStopped(m_nativeToJs->getTokenForExecutor(executor));
|
||||||
|
return m_nativeToJs->unregisterExecutor(executor);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> moduleNames() override {
|
||||||
|
// If this turns out to be too expensive to run on the js thread,
|
||||||
|
// we can compute it in the ctor, and just return std::move() it
|
||||||
|
// here.
|
||||||
|
return m_registry->moduleNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
folly::dynamic getModuleConfig(const std::string& name) override {
|
||||||
|
return m_registry->getConfig(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void callNativeModules(
|
||||||
|
JSExecutor& executor, std::string callJSON, bool isEndOfBatch) override {
|
||||||
|
ExecutorToken token = m_nativeToJs->getTokenForExecutor(executor);
|
||||||
|
m_nativeQueue->runOnQueue([this, token, callJSON=std::move(callJSON), isEndOfBatch] {
|
||||||
|
// An exception anywhere in here stops processing of the batch. This
|
||||||
|
// was the behavior of the Android bridge, and since exception handling
|
||||||
|
// terminates the whole bridge, there's not much point in continuing.
|
||||||
|
for (auto& call : react::parseMethodCalls(callJSON)) {
|
||||||
|
m_registry->callNativeMethod(
|
||||||
|
token, call.moduleId, call.methodId, std::move(call.arguments), call.callId);
|
||||||
|
}
|
||||||
|
if (isEndOfBatch) {
|
||||||
|
m_callback->onBatchComplete();
|
||||||
|
m_callback->decrementPendingJSCalls();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodCallResult callSerializableNativeHook(
|
||||||
|
JSExecutor& executor, unsigned int moduleId, unsigned int methodId,
|
||||||
|
folly::dynamic&& args) override {
|
||||||
|
ExecutorToken token = m_nativeToJs->getTokenForExecutor(executor);
|
||||||
|
return m_registry->callSerializableNativeHook(token, moduleId, methodId, std::move(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
void quitQueueSynchronous() {
|
||||||
|
m_nativeQueue->quitSynchronous();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// These methods are always invoked from an Executor. The NativeToJsBridge
|
||||||
|
// keeps a reference to the root executor, and when destroy() is
|
||||||
|
// called, the Executors are all destroyed synchronously on their
|
||||||
|
// bridges. So, the bridge pointer will will always point to a
|
||||||
|
// valid object during a call to a delegate method from an exectuto.
|
||||||
|
NativeToJsBridge* m_nativeToJs;
|
||||||
|
std::shared_ptr<ModuleRegistry> m_registry;
|
||||||
|
std::unique_ptr<MessageQueueThread> m_nativeQueue;
|
||||||
|
std::shared_ptr<InstanceCallback> m_callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
NativeToJsBridge::NativeToJsBridge(
|
||||||
|
JSExecutorFactory* jsExecutorFactory,
|
||||||
|
std::shared_ptr<ModuleRegistry> registry,
|
||||||
|
std::shared_ptr<MessageQueueThread> jsQueue,
|
||||||
|
std::unique_ptr<MessageQueueThread> nativeQueue,
|
||||||
|
std::shared_ptr<InstanceCallback> callback)
|
||||||
|
: m_destroyed(std::make_shared<bool>(false))
|
||||||
|
, m_mainExecutorToken(callback->createExecutorToken())
|
||||||
|
, m_delegate(
|
||||||
|
std::make_shared<JsToNativeBridge>(
|
||||||
|
this, registry, std::move(nativeQueue), callback)) {
|
||||||
|
std::unique_ptr<JSExecutor> mainExecutor =
|
||||||
|
jsExecutorFactory->createJSExecutor(m_delegate, jsQueue);
|
||||||
|
// cached to avoid locked map lookup in the common case
|
||||||
|
m_mainExecutor = mainExecutor.get();
|
||||||
|
registerExecutor(m_mainExecutorToken, std::move(mainExecutor), jsQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This must be called on the same thread on which the constructor was called.
|
||||||
|
NativeToJsBridge::~NativeToJsBridge() {
|
||||||
|
CHECK(*m_destroyed) <<
|
||||||
|
"NativeToJsBridge::destroy() must be called before deallocating the NativeToJsBridge!";
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeToJsBridge::loadApplicationScript(std::unique_ptr<const JSBigString> script,
|
||||||
|
std::string sourceURL) {
|
||||||
|
// TODO(t11144533): Add assert that we are on the correct thread
|
||||||
|
m_mainExecutor->loadApplicationScript(std::move(script), std::move(sourceURL));
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeToJsBridge::loadApplicationUnbundle(
|
||||||
|
std::unique_ptr<JSModulesUnbundle> unbundle,
|
||||||
|
std::unique_ptr<const JSBigString> startupScript,
|
||||||
|
std::string startupScriptSourceURL) {
|
||||||
|
runOnExecutorQueue(
|
||||||
|
m_mainExecutorToken,
|
||||||
|
[unbundle=folly::makeMoveWrapper(std::move(unbundle)),
|
||||||
|
startupScript=folly::makeMoveWrapper(std::move(startupScript)),
|
||||||
|
startupScriptSourceURL=std::move(startupScriptSourceURL)]
|
||||||
|
(JSExecutor* executor) mutable {
|
||||||
|
|
||||||
|
executor->setJSModulesUnbundle(unbundle.move());
|
||||||
|
executor->loadApplicationScript(std::move(*startupScript),
|
||||||
|
std::move(startupScriptSourceURL));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeToJsBridge::callFunction(
|
||||||
|
ExecutorToken executorToken,
|
||||||
|
const std::string& moduleId,
|
||||||
|
const std::string& methodId,
|
||||||
|
const folly::dynamic& arguments,
|
||||||
|
const std::string& tracingName) {
|
||||||
|
#ifdef WITH_FBSYSTRACE
|
||||||
|
int systraceCookie = m_systraceCookie++;
|
||||||
|
FbSystraceAsyncFlow::begin(
|
||||||
|
TRACE_TAG_REACT_CXX_BRIDGE,
|
||||||
|
tracingName.c_str(),
|
||||||
|
systraceCookie);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
runOnExecutorQueue(executorToken, [moduleId, methodId, arguments, tracingName, systraceCookie] (JSExecutor* executor) {
|
||||||
|
#ifdef WITH_FBSYSTRACE
|
||||||
|
FbSystraceAsyncFlow::end(
|
||||||
|
TRACE_TAG_REACT_CXX_BRIDGE,
|
||||||
|
tracingName.c_str(),
|
||||||
|
systraceCookie);
|
||||||
|
SystraceSection s(tracingName.c_str());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// This is safe because we are running on the executor's thread: it won't
|
||||||
|
// destruct until after it's been unregistered (which we check above) and
|
||||||
|
// that will happen on this thread
|
||||||
|
executor->callFunction(moduleId, methodId, arguments);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeToJsBridge::invokeCallback(ExecutorToken executorToken, const double callbackId,
|
||||||
|
const folly::dynamic& arguments) {
|
||||||
|
#ifdef WITH_FBSYSTRACE
|
||||||
|
int systraceCookie = m_systraceCookie++;
|
||||||
|
FbSystraceAsyncFlow::begin(
|
||||||
|
TRACE_TAG_REACT_CXX_BRIDGE,
|
||||||
|
"<callback>",
|
||||||
|
systraceCookie);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
runOnExecutorQueue(executorToken, [callbackId, arguments, systraceCookie] (JSExecutor* executor) {
|
||||||
|
#ifdef WITH_FBSYSTRACE
|
||||||
|
FbSystraceAsyncFlow::end(
|
||||||
|
TRACE_TAG_REACT_CXX_BRIDGE,
|
||||||
|
"<callback>",
|
||||||
|
systraceCookie);
|
||||||
|
SystraceSection s("NativeToJsBridge.invokeCallback");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
executor->invokeCallback(callbackId, arguments);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeToJsBridge::setGlobalVariable(std::string propName,
|
||||||
|
std::unique_ptr<const JSBigString> jsonValue) {
|
||||||
|
runOnExecutorQueue(
|
||||||
|
m_mainExecutorToken,
|
||||||
|
[propName=std::move(propName), jsonValue=folly::makeMoveWrapper(std::move(jsonValue))]
|
||||||
|
(JSExecutor* executor) mutable {
|
||||||
|
executor->setGlobalVariable(propName, jsonValue.move());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void* NativeToJsBridge::getJavaScriptContext() {
|
||||||
|
// TODO(cjhopman): this seems unsafe unless we require that it is only called on the main js queue.
|
||||||
|
return m_mainExecutor->getJavaScriptContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NativeToJsBridge::supportsProfiling() {
|
||||||
|
// Intentionally doesn't post to jsqueue. supportsProfiling() can be called from any thread.
|
||||||
|
return m_mainExecutor->supportsProfiling();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeToJsBridge::startProfiler(const std::string& title) {
|
||||||
|
runOnExecutorQueue(m_mainExecutorToken, [=] (JSExecutor* executor) {
|
||||||
|
executor->startProfiler(title);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeToJsBridge::stopProfiler(const std::string& title, const std::string& filename) {
|
||||||
|
runOnExecutorQueue(m_mainExecutorToken, [=] (JSExecutor* executor) {
|
||||||
|
executor->stopProfiler(title, filename);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeToJsBridge::handleMemoryPressureModerate() {
|
||||||
|
runOnExecutorQueue(m_mainExecutorToken, [=] (JSExecutor* executor) {
|
||||||
|
executor->handleMemoryPressureModerate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeToJsBridge::handleMemoryPressureCritical() {
|
||||||
|
runOnExecutorQueue(m_mainExecutorToken, [=] (JSExecutor* executor) {
|
||||||
|
executor->handleMemoryPressureCritical();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecutorToken NativeToJsBridge::getMainExecutorToken() const {
|
||||||
|
return m_mainExecutorToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecutorToken NativeToJsBridge::registerExecutor(
|
||||||
|
ExecutorToken token,
|
||||||
|
std::unique_ptr<JSExecutor> executor,
|
||||||
|
std::shared_ptr<MessageQueueThread> messageQueueThread) {
|
||||||
|
std::lock_guard<std::mutex> registrationGuard(m_registrationMutex);
|
||||||
|
|
||||||
|
CHECK(m_executorTokenMap.find(executor.get()) == m_executorTokenMap.end())
|
||||||
|
<< "Trying to register an already registered executor!";
|
||||||
|
|
||||||
|
m_executorTokenMap.emplace(executor.get(), token);
|
||||||
|
m_executorMap.emplace(
|
||||||
|
token,
|
||||||
|
ExecutorRegistration(std::move(executor), messageQueueThread));
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<JSExecutor> NativeToJsBridge::unregisterExecutor(JSExecutor& executor) {
|
||||||
|
std::unique_ptr<JSExecutor> ret;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> registrationGuard(m_registrationMutex);
|
||||||
|
|
||||||
|
auto it = m_executorTokenMap.find(&executor);
|
||||||
|
CHECK(it != m_executorTokenMap.end())
|
||||||
|
<< "Trying to unregister an executor that was never registered!";
|
||||||
|
auto it2 = m_executorMap.find(it->second);
|
||||||
|
ret = std::move(it2->second.executor_);
|
||||||
|
|
||||||
|
m_executorTokenMap.erase(it);
|
||||||
|
m_executorMap.erase(it2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageQueueThread* NativeToJsBridge::getMessageQueueThread(const ExecutorToken& executorToken) {
|
||||||
|
std::lock_guard<std::mutex> registrationGuard(m_registrationMutex);
|
||||||
|
auto it = m_executorMap.find(executorToken);
|
||||||
|
if (it == m_executorMap.end()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return it->second.messageQueueThread_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
JSExecutor* NativeToJsBridge::getExecutor(const ExecutorToken& executorToken) {
|
||||||
|
std::lock_guard<std::mutex> registrationGuard(m_registrationMutex);
|
||||||
|
auto it = m_executorMap.find(executorToken);
|
||||||
|
if (it == m_executorMap.end()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return it->second.executor_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecutorToken NativeToJsBridge::getTokenForExecutor(JSExecutor& executor) {
|
||||||
|
std::lock_guard<std::mutex> registrationGuard(m_registrationMutex);
|
||||||
|
return m_executorTokenMap.at(&executor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeToJsBridge::destroy() {
|
||||||
|
m_delegate->quitQueueSynchronous();
|
||||||
|
auto executorMessageQueueThread = getMessageQueueThread(m_mainExecutorToken);
|
||||||
|
executorMessageQueueThread->runOnQueueSync([this, &executorMessageQueueThread] {
|
||||||
|
executorMessageQueueThread->quitSynchronous();
|
||||||
|
*m_destroyed = true;
|
||||||
|
std::unique_ptr<JSExecutor> mainExecutor = unregisterExecutor(*m_mainExecutor);
|
||||||
|
m_mainExecutor = nullptr;
|
||||||
|
mainExecutor->destroy();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeToJsBridge::runOnExecutorQueue(ExecutorToken executorToken, std::function<void(JSExecutor*)> task) {
|
||||||
|
if (*m_destroyed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto executorMessageQueueThread = getMessageQueueThread(executorToken);
|
||||||
|
if (executorMessageQueueThread == nullptr) {
|
||||||
|
LOG(WARNING) << "Dropping JS action for executor that has been unregistered...";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<bool> isDestroyed = m_destroyed;
|
||||||
|
executorMessageQueueThread->runOnQueue([this, isDestroyed, executorToken, task=std::move(task)] {
|
||||||
|
if (*isDestroyed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSExecutor *executor = getExecutor(executorToken);
|
||||||
|
if (executor == nullptr) {
|
||||||
|
LOG(WARNING) << "Dropping JS call for executor that has been unregistered...";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The executor is guaranteed to be valid for the duration of the task because:
|
||||||
|
// 1. the executor is only destroyed after it is unregistered
|
||||||
|
// 2. the executor is unregistered on this queue
|
||||||
|
// 3. we just confirmed that the executor hasn't been unregistered above
|
||||||
|
task(executor);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} }
|
|
@ -11,7 +11,6 @@
|
||||||
|
|
||||||
#include "Executor.h"
|
#include "Executor.h"
|
||||||
#include "ExecutorToken.h"
|
#include "ExecutorToken.h"
|
||||||
#include "ExecutorTokenFactory.h"
|
|
||||||
#include "JSModulesUnbundle.h"
|
#include "JSModulesUnbundle.h"
|
||||||
#include "MessageQueueThread.h"
|
#include "MessageQueueThread.h"
|
||||||
#include "MethodCall.h"
|
#include "MethodCall.h"
|
||||||
|
@ -27,21 +26,9 @@ struct dynamic;
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace react {
|
namespace react {
|
||||||
|
|
||||||
class BridgeCallback {
|
struct InstanceCallback;
|
||||||
public:
|
class ModuleRegistry;
|
||||||
virtual ~BridgeCallback() {};
|
|
||||||
|
|
||||||
virtual void onCallNativeModules(
|
|
||||||
ExecutorToken executorToken,
|
|
||||||
const std::string& callJSON,
|
|
||||||
bool isEndOfBatch) = 0;
|
|
||||||
|
|
||||||
virtual void onExecutorUnregistered(ExecutorToken executorToken) = 0;
|
|
||||||
|
|
||||||
virtual MethodCallResult callSerializableNativeHook(ExecutorToken token, unsigned int moduleId, unsigned int methodId, folly::dynamic&& args) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Bridge;
|
|
||||||
class ExecutorRegistration {
|
class ExecutorRegistration {
|
||||||
public:
|
public:
|
||||||
ExecutorRegistration(
|
ExecutorRegistration(
|
||||||
|
@ -54,17 +41,26 @@ public:
|
||||||
std::shared_ptr<MessageQueueThread> messageQueueThread_;
|
std::shared_ptr<MessageQueueThread> messageQueueThread_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Bridge {
|
class JsToNativeBridge;
|
||||||
|
|
||||||
|
// This class manages calls from native code to JS. It also manages
|
||||||
|
// executors and their threads. This part is used by both bridges for
|
||||||
|
// now, but further refactorings should separate the bridges more
|
||||||
|
// fully #11247981.
|
||||||
|
class NativeToJsBridge {
|
||||||
public:
|
public:
|
||||||
|
friend class JsToNativeBridge;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This must be called on the main JS thread.
|
* This must be called on the main JS thread.
|
||||||
*/
|
*/
|
||||||
Bridge(
|
NativeToJsBridge(
|
||||||
JSExecutorFactory* jsExecutorFactory,
|
JSExecutorFactory* jsExecutorFactory,
|
||||||
|
std::shared_ptr<ModuleRegistry> registry,
|
||||||
std::shared_ptr<MessageQueueThread> jsQueue,
|
std::shared_ptr<MessageQueueThread> jsQueue,
|
||||||
std::unique_ptr<ExecutorTokenFactory> executorTokenFactory,
|
std::unique_ptr<MessageQueueThread> nativeQueue,
|
||||||
std::unique_ptr<BridgeCallback> callback);
|
std::shared_ptr<InstanceCallback> callback);
|
||||||
virtual ~Bridge();
|
virtual ~NativeToJsBridge();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes a function with the module ID and method ID and any additional
|
* Executes a function with the module ID and method ID and any additional
|
||||||
|
@ -108,58 +104,48 @@ public:
|
||||||
void handleMemoryPressureModerate();
|
void handleMemoryPressureModerate();
|
||||||
void handleMemoryPressureCritical();
|
void handleMemoryPressureCritical();
|
||||||
|
|
||||||
/**
|
|
||||||
* Invokes a set of native module calls on behalf of the given executor.
|
|
||||||
*
|
|
||||||
* TODO: get rid of isEndOfBatch
|
|
||||||
*/
|
|
||||||
void callNativeModules(JSExecutor& executor, const std::string& callJSON, bool isEndOfBatch);
|
|
||||||
|
|
||||||
MethodCallResult callSerializableNativeHook(unsigned int moduleId, unsigned int methodId, const std::string& argsJSON);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the ExecutorToken corresponding to the main JSExecutor.
|
* Returns the ExecutorToken corresponding to the main JSExecutor.
|
||||||
*/
|
*/
|
||||||
ExecutorToken getMainExecutorToken() const;
|
ExecutorToken getMainExecutorToken() const;
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers the given JSExecutor which runs on the given MessageQueueThread
|
|
||||||
* with the Bridge. Part of this registration is transfering ownership of this
|
|
||||||
* JSExecutor to the Bridge for the duration of the registration.
|
|
||||||
*
|
|
||||||
* Returns a ExecutorToken which can be used to refer to this JSExecutor
|
|
||||||
* in the Bridge.
|
|
||||||
*/
|
|
||||||
ExecutorToken registerExecutor(
|
|
||||||
std::unique_ptr<JSExecutor> executor,
|
|
||||||
std::shared_ptr<MessageQueueThread> executorMessageQueueThread);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unregisters a JSExecutor that was previously registered with this Bridge
|
|
||||||
* using registerExecutor. Use the ExecutorToken returned from this
|
|
||||||
* registerExecutor call. This method will return ownership of the unregistered
|
|
||||||
* executor to the caller for it to retain or tear down.
|
|
||||||
*
|
|
||||||
* Returns ownership of the unregistered executor.
|
|
||||||
*/
|
|
||||||
std::unique_ptr<JSExecutor> unregisterExecutor(ExecutorToken executorToken);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Synchronously tears down the bridge and the main executor.
|
* Synchronously tears down the bridge and the main executor.
|
||||||
*/
|
*/
|
||||||
void destroy();
|
void destroy();
|
||||||
private:
|
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<JSExecutor> executor,
|
||||||
|
std::shared_ptr<MessageQueueThread> executorMessageQueueThread);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters a JSExecutor that was previously registered with this NativeToJsBridge
|
||||||
|
* using registerExecutor.
|
||||||
|
*/
|
||||||
|
std::unique_ptr<JSExecutor> unregisterExecutor(JSExecutor& executorToken);
|
||||||
|
|
||||||
void runOnExecutorQueue(ExecutorToken token, std::function<void(JSExecutor*)> task);
|
void runOnExecutorQueue(ExecutorToken token, std::function<void(JSExecutor*)> task);
|
||||||
std::unique_ptr<BridgeCallback> m_callback;
|
|
||||||
// This is used to avoid a race condition where a proxyCallback gets queued after ~Bridge(),
|
// This is used to avoid a race condition where a proxyCallback gets queued
|
||||||
// on the same thread. In that case, the callback will try to run the task on m_callback which
|
// after ~NativeToJsBridge(), on the same thread. In that case, the callback
|
||||||
// will have been destroyed within ~Bridge(), thus causing a SIGSEGV.
|
// will try to run the task on m_callback which will have been destroyed
|
||||||
|
// within ~NativeToJsBridge(), thus causing a SIGSEGV.
|
||||||
std::shared_ptr<bool> m_destroyed;
|
std::shared_ptr<bool> m_destroyed;
|
||||||
JSExecutor* m_mainExecutor;
|
JSExecutor* m_mainExecutor;
|
||||||
std::unique_ptr<ExecutorToken> m_mainExecutorToken;
|
ExecutorToken m_mainExecutorToken;
|
||||||
std::unique_ptr<ExecutorTokenFactory> m_executorTokenFactory;
|
std::shared_ptr<JsToNativeBridge> m_delegate;
|
||||||
std::unordered_map<JSExecutor*, ExecutorToken> m_executorTokenMap;
|
std::unordered_map<JSExecutor*, ExecutorToken> m_executorTokenMap;
|
||||||
std::unordered_map<ExecutorToken, std::unique_ptr<ExecutorRegistration>> m_executorMap;
|
std::unordered_map<ExecutorToken, ExecutorRegistration> m_executorMap;
|
||||||
std::mutex m_registrationMutex;
|
std::mutex m_registrationMutex;
|
||||||
#ifdef WITH_FBSYSTRACE
|
#ifdef WITH_FBSYSTRACE
|
||||||
std::atomic_uint_least32_t m_systraceCookie = ATOMIC_VAR_INIT();
|
std::atomic_uint_least32_t m_systraceCookie = ATOMIC_VAR_INIT();
|
||||||
|
@ -167,7 +153,7 @@ private:
|
||||||
|
|
||||||
MessageQueueThread* getMessageQueueThread(const ExecutorToken& executorToken);
|
MessageQueueThread* getMessageQueueThread(const ExecutorToken& executorToken);
|
||||||
JSExecutor* getExecutor(const ExecutorToken& executorToken);
|
JSExecutor* getExecutor(const ExecutorToken& executorToken);
|
||||||
inline ExecutorToken getTokenForExecutor(JSExecutor& executor);
|
ExecutorToken getTokenForExecutor(JSExecutor& executor);
|
||||||
};
|
};
|
||||||
|
|
||||||
} }
|
} }
|
Loading…
Reference in New Issue