// Copyright (c) 2004-present, Facebook, Inc. // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree. #include "ModuleRegistry.h" #include #include "NativeModule.h" #include "SystraceSection.h" namespace facebook { namespace react { namespace { std::string normalizeName(std::string name) { // TODO mhorowitz #10487027: This is super ugly. We should just // change iOS to emit normalized names, drop the "RK..." from // names hardcoded in Android, and then delete this and the // similar hacks in js. if (name.compare(0, 3, "RCT") == 0) { return name.substr(3); } else if (name.compare(0, 2, "RK") == 0) { return name.substr(2); } return name; } } ModuleRegistry::ModuleRegistry(std::vector> modules, ModuleNotFoundCallback callback) : modules_{std::move(modules)}, moduleNotFoundCallback_{callback} {} void ModuleRegistry::updateModuleNamesFromIndex(size_t index) { for (; index < modules_.size(); index++ ) { std::string name = normalizeName(modules_[index]->getName()); modulesByName_[name] = index; } } void ModuleRegistry::registerModules(std::vector> modules) { SystraceSection s_("ModuleRegistry::registerModules"); if (modules_.empty() && unknownModules_.empty()) { modules_ = std::move(modules); } else { size_t modulesSize = modules_.size(); size_t addModulesSize = modules.size(); bool addToNames = !modulesByName_.empty(); modules_.reserve(modulesSize + addModulesSize); std::move(modules.begin(), modules.end(), std::back_inserter(modules_)); if (!unknownModules_.empty()) { for (size_t index = modulesSize; index < modulesSize + addModulesSize; index++) { std::string name = normalizeName(modules_[index]->getName()); auto it = unknownModules_.find(name); if (it != unknownModules_.end()) { throw std::runtime_error( folly::to("module ", name, " was required without being registered and is now being registered.")); } else if (addToNames) { modulesByName_[name] = index; } } } else if (addToNames) { updateModuleNamesFromIndex(modulesSize); } } } std::vector ModuleRegistry::moduleNames() { SystraceSection s_("ModuleRegistry::moduleNames"); std::vector names; for (size_t i = 0; i < modules_.size(); i++) { std::string name = normalizeName(modules_[i]->getName()); modulesByName_[name] = i; names.push_back(std::move(name)); } return names; } folly::Optional ModuleRegistry::getConfig(const std::string& name) { SystraceSection s("ModuleRegistry::getConfig", "module", name); // Initialize modulesByName_ if (modulesByName_.empty() && !modules_.empty()) { moduleNames(); } auto it = modulesByName_.find(name); if (it == modulesByName_.end()) { if (unknownModules_.find(name) != unknownModules_.end()) { return nullptr; } if (!moduleNotFoundCallback_ || !moduleNotFoundCallback_(name) || (it = modulesByName_.find(name)) == modulesByName_.end()) { unknownModules_.insert(name); return nullptr; } } size_t index = it->second; CHECK(index < modules_.size()); NativeModule *module = modules_[index].get(); // string name, object constants, array methodNames (methodId is index), [array promiseMethodIds], [array syncMethodIds] folly::dynamic config = folly::dynamic::array(name); { SystraceSection s_("ModuleRegistry::getConstants", "module", name); config.push_back(module->getConstants()); } { SystraceSection s_("ModuleRegistry::getMethods", "module", name); std::vector methods = module->getMethods(); folly::dynamic methodNames = folly::dynamic::array; folly::dynamic promiseMethodIds = folly::dynamic::array; folly::dynamic syncMethodIds = folly::dynamic::array; for (auto& descriptor : methods) { // TODO: #10487027 compare tags instead of doing string comparison? methodNames.push_back(std::move(descriptor.name)); if (descriptor.type == "promise") { promiseMethodIds.push_back(methodNames.size() - 1); } else if (descriptor.type == "sync") { syncMethodIds.push_back(methodNames.size() - 1); } } if (!methodNames.empty()) { config.push_back(std::move(methodNames)); if (!promiseMethodIds.empty() || !syncMethodIds.empty()) { config.push_back(std::move(promiseMethodIds)); if (!syncMethodIds.empty()) { config.push_back(std::move(syncMethodIds)); } } } } if (config.size() == 2 && config[1].empty()) { // no constants or methods return nullptr; } else { return ModuleConfig{index, config}; } } void ModuleRegistry::callNativeMethod(unsigned int moduleId, unsigned int methodId, folly::dynamic&& params, int callId) { if (moduleId >= modules_.size()) { throw std::runtime_error( folly::to("moduleId ", moduleId, " out of range [0..", modules_.size(), ")")); } modules_[moduleId]->invoke(methodId, std::move(params), callId); } MethodCallResult ModuleRegistry::callSerializableNativeHook(unsigned int moduleId, unsigned int methodId, folly::dynamic&& params) { if (moduleId >= modules_.size()) { throw std::runtime_error( folly::to("moduleId ", moduleId, "out of range [0..", modules_.size(), ")")); } return modules_[moduleId]->callSerializableNativeHook(methodId, std::move(params)); } }}