NativeToJsBridge.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #include "NativeToJsBridge.h"
  8. #include <ReactCommon/CallInvoker.h>
  9. #include <folly/MoveWrapper.h>
  10. #include <folly/json.h>
  11. #include <glog/logging.h>
  12. #include "Instance.h"
  13. #include "JSBigString.h"
  14. #include "MessageQueueThread.h"
  15. #include "MethodCall.h"
  16. #include "ModuleRegistry.h"
  17. #include "RAMBundleRegistry.h"
  18. #include "SystraceSection.h"
  19. #include <memory>
  20. #ifdef WITH_FBSYSTRACE
  21. #include <fbsystrace.h>
  22. using fbsystrace::FbSystraceAsyncFlow;
  23. #endif
  24. namespace facebook {
  25. namespace react {
  26. // This class manages calls from JS to native code.
  27. class JsToNativeBridge : public react::ExecutorDelegate {
  28. public:
  29. JsToNativeBridge(
  30. std::shared_ptr<ModuleRegistry> registry,
  31. std::shared_ptr<InstanceCallback> callback)
  32. : m_registry(registry), m_callback(callback) {}
  33. std::shared_ptr<ModuleRegistry> getModuleRegistry() override {
  34. return m_registry;
  35. }
  36. bool isBatchActive() {
  37. return m_batchHadNativeModuleOrTurboModuleCalls;
  38. }
  39. void callNativeModules(
  40. __unused JSExecutor &executor,
  41. folly::dynamic &&calls,
  42. bool isEndOfBatch) override {
  43. CHECK(m_registry || calls.empty())
  44. << "native module calls cannot be completed with no native modules";
  45. m_batchHadNativeModuleOrTurboModuleCalls =
  46. m_batchHadNativeModuleOrTurboModuleCalls || !calls.empty();
  47. // An exception anywhere in here stops processing of the batch. This
  48. // was the behavior of the Android bridge, and since exception handling
  49. // terminates the whole bridge, there's not much point in continuing.
  50. for (auto &call : parseMethodCalls(std::move(calls))) {
  51. m_registry->callNativeMethod(
  52. call.moduleId, call.methodId, std::move(call.arguments), call.callId);
  53. }
  54. if (isEndOfBatch) {
  55. // onBatchComplete will be called on the native (module) queue, but
  56. // decrementPendingJSCalls will be called sync. Be aware that the bridge
  57. // may still be processing native calls when the bridge idle signaler
  58. // fires.
  59. if (m_batchHadNativeModuleOrTurboModuleCalls) {
  60. m_callback->onBatchComplete();
  61. m_batchHadNativeModuleOrTurboModuleCalls = false;
  62. }
  63. m_callback->decrementPendingJSCalls();
  64. }
  65. }
  66. MethodCallResult callSerializableNativeHook(
  67. __unused JSExecutor &executor,
  68. unsigned int moduleId,
  69. unsigned int methodId,
  70. folly::dynamic &&args) override {
  71. return m_registry->callSerializableNativeHook(
  72. moduleId, methodId, std::move(args));
  73. }
  74. void recordTurboModuleAsyncMethodCall() {
  75. m_batchHadNativeModuleOrTurboModuleCalls = true;
  76. }
  77. private:
  78. // These methods are always invoked from an Executor. The NativeToJsBridge
  79. // keeps a reference to the executor, and when destroy() is called, the
  80. // executor is destroyed synchronously on its queue.
  81. std::shared_ptr<ModuleRegistry> m_registry;
  82. std::shared_ptr<InstanceCallback> m_callback;
  83. bool m_batchHadNativeModuleOrTurboModuleCalls = false;
  84. };
  85. NativeToJsBridge::NativeToJsBridge(
  86. JSExecutorFactory *jsExecutorFactory,
  87. std::shared_ptr<ModuleRegistry> registry,
  88. std::shared_ptr<MessageQueueThread> jsQueue,
  89. std::shared_ptr<InstanceCallback> callback)
  90. : m_destroyed(std::make_shared<bool>(false)),
  91. m_delegate(std::make_shared<JsToNativeBridge>(registry, callback)),
  92. m_executor(jsExecutorFactory->createJSExecutor(m_delegate, jsQueue)),
  93. m_executorMessageQueueThread(std::move(jsQueue)),
  94. m_inspectable(m_executor->isInspectable()) {}
  95. // This must be called on the same thread on which the constructor was called.
  96. NativeToJsBridge::~NativeToJsBridge() {
  97. CHECK(*m_destroyed)
  98. << "NativeToJsBridge::destroy() must be called before deallocating the NativeToJsBridge!";
  99. }
  100. void NativeToJsBridge::initializeRuntime() {
  101. runOnExecutorQueue(
  102. [](JSExecutor *executor) mutable { executor->initializeRuntime(); });
  103. }
  104. void NativeToJsBridge::loadBundle(
  105. std::unique_ptr<RAMBundleRegistry> bundleRegistry,
  106. std::unique_ptr<const JSBigString> startupScript,
  107. std::string startupScriptSourceURL) {
  108. runOnExecutorQueue(
  109. [this,
  110. bundleRegistryWrap = folly::makeMoveWrapper(std::move(bundleRegistry)),
  111. startupScript = folly::makeMoveWrapper(std::move(startupScript)),
  112. startupScriptSourceURL =
  113. std::move(startupScriptSourceURL)](JSExecutor *executor) mutable {
  114. auto bundleRegistry = bundleRegistryWrap.move();
  115. if (bundleRegistry) {
  116. executor->setBundleRegistry(std::move(bundleRegistry));
  117. }
  118. try {
  119. executor->loadBundle(
  120. std::move(*startupScript), std::move(startupScriptSourceURL));
  121. } catch (...) {
  122. m_applicationScriptHasFailure = true;
  123. throw;
  124. }
  125. });
  126. }
  127. void NativeToJsBridge::loadBundleSync(
  128. std::unique_ptr<RAMBundleRegistry> bundleRegistry,
  129. std::unique_ptr<const JSBigString> startupScript,
  130. std::string startupScriptSourceURL) {
  131. if (bundleRegistry) {
  132. m_executor->setBundleRegistry(std::move(bundleRegistry));
  133. }
  134. try {
  135. m_executor->loadBundle(
  136. std::move(startupScript), std::move(startupScriptSourceURL));
  137. } catch (...) {
  138. m_applicationScriptHasFailure = true;
  139. throw;
  140. }
  141. }
  142. void NativeToJsBridge::callFunction(
  143. std::string &&module,
  144. std::string &&method,
  145. folly::dynamic &&arguments) {
  146. int systraceCookie = -1;
  147. #ifdef WITH_FBSYSTRACE
  148. systraceCookie = m_systraceCookie++;
  149. FbSystraceAsyncFlow::begin(
  150. TRACE_TAG_REACT_CXX_BRIDGE, "JSCall", systraceCookie);
  151. #endif
  152. runOnExecutorQueue([this,
  153. module = std::move(module),
  154. method = std::move(method),
  155. arguments = std::move(arguments),
  156. systraceCookie](JSExecutor *executor) {
  157. if (m_applicationScriptHasFailure) {
  158. LOG(ERROR)
  159. << "Attempting to call JS function on a bad application bundle: "
  160. << module.c_str() << "." << method.c_str() << "()";
  161. throw std::runtime_error(
  162. "Attempting to call JS function on a bad application bundle: " +
  163. module + "." + method + "()");
  164. }
  165. #ifdef WITH_FBSYSTRACE
  166. FbSystraceAsyncFlow::end(
  167. TRACE_TAG_REACT_CXX_BRIDGE, "JSCall", systraceCookie);
  168. SystraceSection s(
  169. "NativeToJsBridge::callFunction", "module", module, "method", method);
  170. #else
  171. (void)(systraceCookie);
  172. #endif
  173. // This is safe because we are running on the executor's thread: it won't
  174. // destruct until after it's been unregistered (which we check above) and
  175. // that will happen on this thread
  176. executor->callFunction(module, method, arguments);
  177. });
  178. }
  179. void NativeToJsBridge::invokeCallback(
  180. double callbackId,
  181. folly::dynamic &&arguments) {
  182. int systraceCookie = -1;
  183. #ifdef WITH_FBSYSTRACE
  184. systraceCookie = m_systraceCookie++;
  185. FbSystraceAsyncFlow::begin(
  186. TRACE_TAG_REACT_CXX_BRIDGE, "<callback>", systraceCookie);
  187. #endif
  188. runOnExecutorQueue(
  189. [this, callbackId, arguments = std::move(arguments), systraceCookie](
  190. JSExecutor *executor) {
  191. if (m_applicationScriptHasFailure) {
  192. LOG(ERROR)
  193. << "Attempting to call JS callback on a bad application bundle: "
  194. << callbackId;
  195. throw std::runtime_error(
  196. "Attempting to invoke JS callback on a bad application bundle.");
  197. }
  198. #ifdef WITH_FBSYSTRACE
  199. FbSystraceAsyncFlow::end(
  200. TRACE_TAG_REACT_CXX_BRIDGE, "<callback>", systraceCookie);
  201. SystraceSection s("NativeToJsBridge::invokeCallback");
  202. #else
  203. (void)(systraceCookie);
  204. #endif
  205. executor->invokeCallback(callbackId, arguments);
  206. });
  207. }
  208. void NativeToJsBridge::registerBundle(
  209. uint32_t bundleId,
  210. const std::string &bundlePath) {
  211. runOnExecutorQueue([bundleId, bundlePath](JSExecutor *executor) {
  212. executor->registerBundle(bundleId, bundlePath);
  213. });
  214. }
  215. void NativeToJsBridge::setGlobalVariable(
  216. std::string propName,
  217. std::unique_ptr<const JSBigString> jsonValue) {
  218. runOnExecutorQueue([propName = std::move(propName),
  219. jsonValue = folly::makeMoveWrapper(std::move(jsonValue))](
  220. JSExecutor *executor) mutable {
  221. executor->setGlobalVariable(propName, jsonValue.move());
  222. });
  223. }
  224. void *NativeToJsBridge::getJavaScriptContext() {
  225. // TODO(cjhopman): this seems unsafe unless we require that it is only called
  226. // on the main js queue.
  227. return m_executor->getJavaScriptContext();
  228. }
  229. bool NativeToJsBridge::isInspectable() {
  230. return m_inspectable;
  231. }
  232. bool NativeToJsBridge::isBatchActive() {
  233. return m_delegate->isBatchActive();
  234. }
  235. void NativeToJsBridge::handleMemoryPressure(int pressureLevel) {
  236. runOnExecutorQueue([=](JSExecutor *executor) {
  237. executor->handleMemoryPressure(pressureLevel);
  238. });
  239. }
  240. void NativeToJsBridge::destroy() {
  241. // All calls made through runOnExecutorQueue have an early exit if
  242. // m_destroyed is true. Setting this before the runOnQueueSync will cause
  243. // pending work to be cancelled and we won't have to wait for it.
  244. *m_destroyed = true;
  245. m_executorMessageQueueThread->runOnQueueSync([this] {
  246. m_executor->destroy();
  247. m_executorMessageQueueThread->quitSynchronous();
  248. m_executor = nullptr;
  249. });
  250. }
  251. void NativeToJsBridge::runOnExecutorQueue(
  252. std::function<void(JSExecutor *)> task) {
  253. if (*m_destroyed) {
  254. return;
  255. }
  256. std::shared_ptr<bool> isDestroyed = m_destroyed;
  257. m_executorMessageQueueThread->runOnQueue(
  258. [this, isDestroyed, task = std::move(task)] {
  259. if (*isDestroyed) {
  260. return;
  261. }
  262. // The executor is guaranteed to be valid for the duration of the task
  263. // because:
  264. // 1. the executor is only destroyed after it is unregistered
  265. // 2. the executor is unregistered on this queue
  266. // 3. we just confirmed that the executor hasn't been unregistered above
  267. task(m_executor.get());
  268. });
  269. }
  270. std::shared_ptr<CallInvoker> NativeToJsBridge::getDecoratedNativeCallInvoker(
  271. std::shared_ptr<CallInvoker> nativeInvoker) {
  272. class NativeCallInvoker : public CallInvoker {
  273. private:
  274. std::weak_ptr<JsToNativeBridge> m_jsToNativeBridge;
  275. std::shared_ptr<CallInvoker> m_nativeInvoker;
  276. public:
  277. NativeCallInvoker(
  278. std::weak_ptr<JsToNativeBridge> jsToNativeBridge,
  279. std::shared_ptr<CallInvoker> nativeInvoker)
  280. : m_jsToNativeBridge(jsToNativeBridge),
  281. m_nativeInvoker(nativeInvoker) {}
  282. void invokeAsync(std::function<void()> &&func) override {
  283. if (auto strongJsToNativeBridge = m_jsToNativeBridge.lock()) {
  284. strongJsToNativeBridge->recordTurboModuleAsyncMethodCall();
  285. }
  286. m_nativeInvoker->invokeAsync(std::move(func));
  287. }
  288. void invokeSync(std::function<void()> &&func) override {
  289. m_nativeInvoker->invokeSync(std::move(func));
  290. }
  291. };
  292. return std::make_shared<NativeCallInvoker>(m_delegate, nativeInvoker);
  293. }
  294. } // namespace react
  295. } // namespace facebook