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 &¶ms) {
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 &¶ms) {
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