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