1 /*
2  * Copyright (c) Meta Platforms, Inc. and 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 
8 #include "ABI48_0_0Instance.h"
9 
10 #include "ABI48_0_0ErrorUtils.h"
11 #include "ABI48_0_0JSBigString.h"
12 #include "ABI48_0_0JSBundleType.h"
13 #include "ABI48_0_0JSExecutor.h"
14 #include "ABI48_0_0MessageQueueThread.h"
15 #include "ABI48_0_0MethodCall.h"
16 #include "ABI48_0_0NativeToJsBridge.h"
17 #include "ABI48_0_0RAMBundleRegistry.h"
18 #include "ABI48_0_0RecoverableError.h"
19 #include "ABI48_0_0SystraceSection.h"
20 
21 #include <ABI48_0_0cxxreact/ABI48_0_0JSIndexedRAMBundle.h>
22 #include <folly/MoveWrapper.h>
23 #include <folly/json.h>
24 
25 #include <glog/logging.h>
26 
27 #include <condition_variable>
28 #include <exception>
29 #include <memory>
30 #include <mutex>
31 #include <string>
32 
33 namespace ABI48_0_0facebook {
34 namespace ABI48_0_0React {
35 
~Instance()36 Instance::~Instance() {
37   if (nativeToJsBridge_) {
38     nativeToJsBridge_->destroy();
39   }
40 }
41 
initializeBridge(std::unique_ptr<InstanceCallback> callback,std::shared_ptr<JSExecutorFactory> jsef,std::shared_ptr<MessageQueueThread> jsQueue,std::shared_ptr<ModuleRegistry> moduleRegistry)42 void Instance::initializeBridge(
43     std::unique_ptr<InstanceCallback> callback,
44     std::shared_ptr<JSExecutorFactory> jsef,
45     std::shared_ptr<MessageQueueThread> jsQueue,
46     std::shared_ptr<ModuleRegistry> moduleRegistry) {
47   callback_ = std::move(callback);
48   moduleRegistry_ = std::move(moduleRegistry);
49   jsQueue->runOnQueueSync([this, &jsef, jsQueue]() mutable {
50     nativeToJsBridge_ = std::make_shared<NativeToJsBridge>(
51         jsef.get(), moduleRegistry_, jsQueue, callback_);
52 
53     nativeToJsBridge_->initializeRuntime();
54 
55     /**
56      * After NativeToJsBridge is created, the jsi::Runtime should exist.
57      * Also, the JS message queue thread exists. So, it's safe to
58      * schedule all queued up js Calls.
59      */
60     jsCallInvoker_->setNativeToJsBridgeAndFlushCalls(nativeToJsBridge_);
61 
62     std::lock_guard<std::mutex> lock(m_syncMutex);
63     m_syncReady = true;
64     m_syncCV.notify_all();
65   });
66 
67   CHECK(nativeToJsBridge_);
68 }
69 
loadBundle(std::unique_ptr<RAMBundleRegistry> bundleRegistry,std::unique_ptr<const JSBigString> string,std::string sourceURL)70 void Instance::loadBundle(
71     std::unique_ptr<RAMBundleRegistry> bundleRegistry,
72     std::unique_ptr<const JSBigString> string,
73     std::string sourceURL) {
74   callback_->incrementPendingJSCalls();
75   SystraceSection s("Instance::loadBundle", "sourceURL", sourceURL);
76   nativeToJsBridge_->loadBundle(
77       std::move(bundleRegistry), std::move(string), std::move(sourceURL));
78 }
79 
loadBundleSync(std::unique_ptr<RAMBundleRegistry> bundleRegistry,std::unique_ptr<const JSBigString> string,std::string sourceURL)80 void Instance::loadBundleSync(
81     std::unique_ptr<RAMBundleRegistry> bundleRegistry,
82     std::unique_ptr<const JSBigString> string,
83     std::string sourceURL) {
84   std::unique_lock<std::mutex> lock(m_syncMutex);
85   m_syncCV.wait(lock, [this] { return m_syncReady; });
86 
87   SystraceSection s("Instance::loadBundleSync", "sourceURL", sourceURL);
88   nativeToJsBridge_->loadBundleSync(
89       std::move(bundleRegistry), std::move(string), std::move(sourceURL));
90 }
91 
setSourceURL(std::string sourceURL)92 void Instance::setSourceURL(std::string sourceURL) {
93   callback_->incrementPendingJSCalls();
94   SystraceSection s("Instance::setSourceURL", "sourceURL", sourceURL);
95 
96   nativeToJsBridge_->loadBundle(nullptr, nullptr, std::move(sourceURL));
97 }
98 
loadScriptFromString(std::unique_ptr<const JSBigString> string,std::string sourceURL,bool loadSynchronously)99 void Instance::loadScriptFromString(
100     std::unique_ptr<const JSBigString> string,
101     std::string sourceURL,
102     bool loadSynchronously) {
103   SystraceSection s("Instance::loadScriptFromString", "sourceURL", sourceURL);
104   if (loadSynchronously) {
105     loadBundleSync(nullptr, std::move(string), std::move(sourceURL));
106   } else {
107     loadBundle(nullptr, std::move(string), std::move(sourceURL));
108   }
109 }
110 
loadRAMBundleFromString(std::unique_ptr<const JSBigString> script,const std::string & sourceURL)111 void Instance::loadRAMBundleFromString(
112     std::unique_ptr<const JSBigString> script,
113     const std::string &sourceURL) {
114   auto bundle = std::make_unique<JSIndexedRAMBundle>(std::move(script));
115   auto startupScript = bundle->getStartupCode();
116   auto registry = RAMBundleRegistry::singleBundleRegistry(std::move(bundle));
117   loadRAMBundle(std::move(registry), std::move(startupScript), sourceURL, true);
118 }
119 
loadRAMBundleFromFile(const std::string & sourcePath,const std::string & sourceURL,bool loadSynchronously)120 void Instance::loadRAMBundleFromFile(
121     const std::string &sourcePath,
122     const std::string &sourceURL,
123     bool loadSynchronously) {
124   auto bundle = std::make_unique<JSIndexedRAMBundle>(sourcePath.c_str());
125   auto startupScript = bundle->getStartupCode();
126   auto registry = RAMBundleRegistry::multipleBundlesRegistry(
127       std::move(bundle), JSIndexedRAMBundle::buildFactory());
128   loadRAMBundle(
129       std::move(registry),
130       std::move(startupScript),
131       sourceURL,
132       loadSynchronously);
133 }
134 
loadRAMBundle(std::unique_ptr<RAMBundleRegistry> bundleRegistry,std::unique_ptr<const JSBigString> startupScript,std::string startupScriptSourceURL,bool loadSynchronously)135 void Instance::loadRAMBundle(
136     std::unique_ptr<RAMBundleRegistry> bundleRegistry,
137     std::unique_ptr<const JSBigString> startupScript,
138     std::string startupScriptSourceURL,
139     bool loadSynchronously) {
140   if (loadSynchronously) {
141     loadBundleSync(
142         std::move(bundleRegistry),
143         std::move(startupScript),
144         std::move(startupScriptSourceURL));
145   } else {
146     loadBundle(
147         std::move(bundleRegistry),
148         std::move(startupScript),
149         std::move(startupScriptSourceURL));
150   }
151 }
152 
setGlobalVariable(std::string propName,std::unique_ptr<const JSBigString> jsonValue)153 void Instance::setGlobalVariable(
154     std::string propName,
155     std::unique_ptr<const JSBigString> jsonValue) {
156   nativeToJsBridge_->setGlobalVariable(
157       std::move(propName), std::move(jsonValue));
158 }
159 
getJavaScriptContext()160 void *Instance::getJavaScriptContext() {
161   return nativeToJsBridge_ ? nativeToJsBridge_->getJavaScriptContext()
162                            : nullptr;
163 }
164 
isInspectable()165 bool Instance::isInspectable() {
166   return nativeToJsBridge_ ? nativeToJsBridge_->isInspectable() : false;
167 }
168 
isBatchActive()169 bool Instance::isBatchActive() {
170   return nativeToJsBridge_ ? nativeToJsBridge_->isBatchActive() : false;
171 }
172 
callJSFunction(std::string && module,std::string && method,folly::dynamic && params)173 void Instance::callJSFunction(
174     std::string &&module,
175     std::string &&method,
176     folly::dynamic &&params) {
177   callback_->incrementPendingJSCalls();
178   nativeToJsBridge_->callFunction(
179       std::move(module), std::move(method), std::move(params));
180 }
181 
callJSCallback(uint64_t callbackId,folly::dynamic && params)182 void Instance::callJSCallback(uint64_t callbackId, folly::dynamic &&params) {
183   SystraceSection s("Instance::callJSCallback");
184   callback_->incrementPendingJSCalls();
185   nativeToJsBridge_->invokeCallback((double)callbackId, std::move(params));
186 }
187 
registerBundle(uint32_t bundleId,const std::string & bundlePath)188 void Instance::registerBundle(
189     uint32_t bundleId,
190     const std::string &bundlePath) {
191   nativeToJsBridge_->registerBundle(bundleId, bundlePath);
192 }
193 
getModuleRegistry() const194 const ModuleRegistry &Instance::getModuleRegistry() const {
195   return *moduleRegistry_;
196 }
197 
getModuleRegistry()198 ModuleRegistry &Instance::getModuleRegistry() {
199   return *moduleRegistry_;
200 }
201 
handleMemoryPressure(int pressureLevel)202 void Instance::handleMemoryPressure(int pressureLevel) {
203   if (nativeToJsBridge_) {
204     // This class resets `nativeToJsBridge_` only in the destructor,
205     // hence a race is not possible there.
206     nativeToJsBridge_->handleMemoryPressure(pressureLevel);
207   }
208 }
209 
getJSCallInvoker()210 std::shared_ptr<CallInvoker> Instance::getJSCallInvoker() {
211   return std::static_pointer_cast<CallInvoker>(jsCallInvoker_);
212 }
213 
getRuntimeExecutor()214 RuntimeExecutor Instance::getRuntimeExecutor() {
215   std::weak_ptr<NativeToJsBridge> weakNativeToJsBridge = nativeToJsBridge_;
216 
217   auto runtimeExecutor =
218       [weakNativeToJsBridge](
219           std::function<void(jsi::Runtime & runtime)> &&callback) {
220         if (auto strongNativeToJsBridge = weakNativeToJsBridge.lock()) {
221           strongNativeToJsBridge->runOnExecutorQueue(
222               [callback = std::move(callback)](JSExecutor *executor) {
223                 jsi::Runtime *runtime =
224                     (jsi::Runtime *)executor->getJavaScriptContext();
225                 try {
226                   callback(*runtime);
227                   executor->flush();
228                 } catch (jsi::JSError &originalError) {
229                   handleJSError(*runtime, originalError, true);
230                 }
231               });
232         }
233       };
234   return runtimeExecutor;
235 }
236 
getDecoratedNativeCallInvoker(std::shared_ptr<CallInvoker> nativeInvoker)237 std::shared_ptr<CallInvoker> Instance::getDecoratedNativeCallInvoker(
238     std::shared_ptr<CallInvoker> nativeInvoker) {
239   return nativeToJsBridge_->getDecoratedNativeCallInvoker(nativeInvoker);
240 }
241 
setNativeToJsBridgeAndFlushCalls(std::weak_ptr<NativeToJsBridge> nativeToJsBridge)242 void Instance::JSCallInvoker::setNativeToJsBridgeAndFlushCalls(
243     std::weak_ptr<NativeToJsBridge> nativeToJsBridge) {
244   std::lock_guard<std::mutex> guard(m_mutex);
245 
246   m_shouldBuffer = false;
247   m_nativeToJsBridge = nativeToJsBridge;
248   while (m_workBuffer.size() > 0) {
249     scheduleAsync(std::move(m_workBuffer.front()));
250     m_workBuffer.pop_front();
251   }
252 }
253 
invokeSync(std::function<void ()> && work)254 void Instance::JSCallInvoker::invokeSync(std::function<void()> &&work) {
255   // TODO: Replace JS Callinvoker with RuntimeExecutor.
256   throw std::runtime_error(
257       "Synchronous native -> JS calls are currently not supported.");
258 }
259 
invokeAsync(std::function<void ()> && work)260 void Instance::JSCallInvoker::invokeAsync(std::function<void()> &&work) {
261   std::lock_guard<std::mutex> guard(m_mutex);
262 
263   /**
264    * Why is is necessary to queue up async work?
265    *
266    * 1. TurboModuleManager must be created synchronously after the Instance,
267    *    before we load the source code. This is when the NativeModule system
268    *    is initialized. ABI48_0_0RCTDevLoadingView shows bundle download progress.
269    * 2. TurboModuleManager requires a JS CallInvoker.
270    * 3. The JS CallInvoker requires the NativeToJsBridge, which is created on
271    *    the JS thread in Instance::initializeBridge.
272    *
273    * Therefore, although we don't call invokeAsync before the JS bundle is
274    * executed, this buffering is implemented anyways to ensure that work
275    * isn't discarded.
276    */
277   if (m_shouldBuffer) {
278     m_workBuffer.push_back(std::move(work));
279     return;
280   }
281 
282   scheduleAsync(std::move(work));
283 }
284 
scheduleAsync(std::function<void ()> && work)285 void Instance::JSCallInvoker::scheduleAsync(std::function<void()> &&work) {
286   if (auto strongNativeToJsBridge = m_nativeToJsBridge.lock()) {
287     strongNativeToJsBridge->runOnExecutorQueue(
288         [work = std::move(work)](JSExecutor *executor) {
289           work();
290           executor->flush();
291         });
292   }
293 }
294 
295 } // namespace ABI48_0_0React
296 } // namespace ABI48_0_0facebook
297