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