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 "ABI47_0_0ModuleRegistry.h"
9
10 #include <glog/logging.h>
11 #include <ABI47_0_0Reactperflogger/ABI47_0_0BridgeNativeModulePerfLogger.h>
12
13 #include "ABI47_0_0NativeModule.h"
14 #include "ABI47_0_0SystraceSection.h"
15
16 namespace ABI47_0_0facebook {
17 namespace ABI47_0_0React {
18
19 namespace {
20
normalizeName(std::string name)21 std::string normalizeName(std::string name) {
22 if (name.compare(0, 9, "ABI47_0_0") == 0) {
23 name = name.substr(9);
24 }
25
26 // TODO mhorowitz #10487027: This is super ugly. We should just
27 // change iOS to emit normalized names, drop the "RK..." from
28 // names hardcoded in Android, and then delete this and the
29 // similar hacks in js.
30 if (name.compare(0, 3, "RCT") == 0) {
31 return name.substr(3);
32 } else if (name.compare(0, 2, "RK") == 0) {
33 return name.substr(2);
34 }
35 return name;
36 }
37
38 } // namespace
39
ModuleRegistry(std::vector<std::unique_ptr<NativeModule>> modules,ModuleNotFoundCallback callback)40 ModuleRegistry::ModuleRegistry(
41 std::vector<std::unique_ptr<NativeModule>> modules,
42 ModuleNotFoundCallback callback)
43 : modules_{std::move(modules)}, moduleNotFoundCallback_{callback} {}
44
updateModuleNamesFromIndex(size_t index)45 void ModuleRegistry::updateModuleNamesFromIndex(size_t index) {
46 for (; index < modules_.size(); index++) {
47 std::string name = normalizeName(modules_[index]->getName());
48 modulesByName_[name] = index;
49 }
50 }
51
registerModules(std::vector<std::unique_ptr<NativeModule>> modules)52 void ModuleRegistry::registerModules(
53 std::vector<std::unique_ptr<NativeModule>> modules) {
54 SystraceSection s_("ModuleRegistry::registerModules");
55 // Noop if there are no NativeModules to add
56 if (modules.empty()) {
57 return;
58 }
59
60 if (modules_.empty() && unknownModules_.empty()) {
61 modules_ = std::move(modules);
62 } else {
63 size_t modulesSize = modules_.size();
64 size_t addModulesSize = modules.size();
65 bool addToNames = !modulesByName_.empty();
66 modules_.reserve(modulesSize + addModulesSize);
67 std::move(modules.begin(), modules.end(), std::back_inserter(modules_));
68 if (!unknownModules_.empty()) {
69 for (size_t index = modulesSize; index < modulesSize + addModulesSize;
70 index++) {
71 std::string name = normalizeName(modules_[index]->getName());
72 auto it = unknownModules_.find(name);
73 if (it != unknownModules_.end()) {
74 throw std::runtime_error(folly::to<std::string>(
75 "module ",
76 name,
77 " was required without being registered and is now being registered."));
78 } else if (addToNames) {
79 modulesByName_[name] = index;
80 }
81 }
82 } else if (addToNames) {
83 updateModuleNamesFromIndex(modulesSize);
84 }
85 }
86 }
87
moduleNames()88 std::vector<std::string> ModuleRegistry::moduleNames() {
89 SystraceSection s_("ModuleRegistry::moduleNames");
90 std::vector<std::string> names;
91 for (size_t i = 0; i < modules_.size(); i++) {
92 std::string name = normalizeName(modules_[i]->getName());
93 modulesByName_[name] = i;
94 names.push_back(std::move(name));
95 }
96 return names;
97 }
98
getConfig(const std::string & name)99 folly::Optional<ModuleConfig> ModuleRegistry::getConfig(
100 const std::string &name) {
101 SystraceSection s("ModuleRegistry::getConfig", "module", name);
102
103 // Initialize modulesByName_
104 if (modulesByName_.empty() && !modules_.empty()) {
105 moduleNames();
106 }
107
108 auto it = modulesByName_.find(name);
109
110 if (it == modulesByName_.end()) {
111 if (unknownModules_.find(name) != unknownModules_.end()) {
112 BridgeNativeModulePerfLogger::moduleJSRequireBeginningFail(name.c_str());
113 BridgeNativeModulePerfLogger::moduleJSRequireEndingStart(name.c_str());
114 return folly::none;
115 }
116
117 if (!moduleNotFoundCallback_) {
118 unknownModules_.insert(name);
119 BridgeNativeModulePerfLogger::moduleJSRequireBeginningFail(name.c_str());
120 BridgeNativeModulePerfLogger::moduleJSRequireEndingStart(name.c_str());
121 return folly::none;
122 }
123
124 BridgeNativeModulePerfLogger::moduleJSRequireBeginningEnd(name.c_str());
125
126 bool wasModuleLazilyLoaded = moduleNotFoundCallback_(name);
127 it = modulesByName_.find(name);
128
129 bool wasModuleRegisteredWithRegistry =
130 wasModuleLazilyLoaded && it != modulesByName_.end();
131
132 if (!wasModuleRegisteredWithRegistry) {
133 BridgeNativeModulePerfLogger::moduleJSRequireEndingStart(name.c_str());
134 unknownModules_.insert(name);
135 return folly::none;
136 }
137 } else {
138 BridgeNativeModulePerfLogger::moduleJSRequireBeginningEnd(name.c_str());
139 }
140
141 // If we've gotten this far, then we've signaled moduleJSRequireBeginningEnd
142
143 size_t index = it->second;
144
145 CHECK(index < modules_.size());
146 NativeModule *module = modules_[index].get();
147
148 // string name, object constants, array methodNames (methodId is index),
149 // [array promiseMethodIds], [array syncMethodIds]
150 folly::dynamic config = folly::dynamic::array(name);
151
152 {
153 SystraceSection s_("ModuleRegistry::getConstants", "module", name);
154 /**
155 * In the case that there are constants, we'll initialize the NativeModule,
156 * and signal moduleJSRequireEndingStart. Otherwise, we'll simply signal the
157 * event. The Module will be initialized when we invoke one of its
158 * NativeModule methods.
159 */
160 config.push_back(module->getConstants());
161 }
162
163 {
164 SystraceSection s_("ModuleRegistry::getMethods", "module", name);
165 std::vector<MethodDescriptor> methods = module->getMethods();
166
167 folly::dynamic methodNames = folly::dynamic::array;
168 folly::dynamic promiseMethodIds = folly::dynamic::array;
169 folly::dynamic syncMethodIds = folly::dynamic::array;
170
171 for (auto &descriptor : methods) {
172 // TODO: #10487027 compare tags instead of doing string comparison?
173 methodNames.push_back(std::move(descriptor.name));
174 if (descriptor.type == "promise") {
175 promiseMethodIds.push_back(methodNames.size() - 1);
176 } else if (descriptor.type == "sync") {
177 syncMethodIds.push_back(methodNames.size() - 1);
178 }
179 }
180
181 if (!methodNames.empty()) {
182 config.push_back(std::move(methodNames));
183 if (!promiseMethodIds.empty() || !syncMethodIds.empty()) {
184 config.push_back(std::move(promiseMethodIds));
185 if (!syncMethodIds.empty()) {
186 config.push_back(std::move(syncMethodIds));
187 }
188 }
189 }
190 }
191
192 if (config.size() == 2 && config[1].empty()) {
193 // no constants or methods
194 return folly::none;
195 } else {
196 return ModuleConfig{index, std::move(config)};
197 }
198 }
199
getModuleName(unsigned int moduleId)200 std::string ModuleRegistry::getModuleName(unsigned int moduleId) {
201 if (moduleId >= modules_.size()) {
202 throw std::runtime_error(folly::to<std::string>(
203 "moduleId ", moduleId, " out of range [0..", modules_.size(), ")"));
204 }
205
206 return modules_[moduleId]->getName();
207 }
208
getModuleSyncMethodName(unsigned int moduleId,unsigned int methodId)209 std::string ModuleRegistry::getModuleSyncMethodName(
210 unsigned int moduleId,
211 unsigned int methodId) {
212 if (moduleId >= modules_.size()) {
213 throw std::runtime_error(folly::to<std::string>(
214 "moduleId ", moduleId, " out of range [0..", modules_.size(), ")"));
215 }
216
217 return modules_[moduleId]->getSyncMethodName(methodId);
218 }
219
callNativeMethod(unsigned int moduleId,unsigned int methodId,folly::dynamic && params,int callId)220 void ModuleRegistry::callNativeMethod(
221 unsigned int moduleId,
222 unsigned int methodId,
223 folly::dynamic &¶ms,
224 int callId) {
225 if (moduleId >= modules_.size()) {
226 throw std::runtime_error(folly::to<std::string>(
227 "moduleId ", moduleId, " out of range [0..", modules_.size(), ")"));
228 }
229 modules_[moduleId]->invoke(methodId, std::move(params), callId);
230 }
231
callSerializableNativeHook(unsigned int moduleId,unsigned int methodId,folly::dynamic && params)232 MethodCallResult ModuleRegistry::callSerializableNativeHook(
233 unsigned int moduleId,
234 unsigned int methodId,
235 folly::dynamic &¶ms) {
236 if (moduleId >= modules_.size()) {
237 throw std::runtime_error(folly::to<std::string>(
238 "moduleId ", moduleId, "out of range [0..", modules_.size(), ")"));
239 }
240 return modules_[moduleId]->callSerializableNativeHook(
241 methodId, std::move(params));
242 }
243
244 } // namespace ABI47_0_0React
245 } // namespace ABI47_0_0facebook
246