|
4 | 4 | #include <algorithm> // std::equal, std::all_of |
5 | 5 | #include <cctype> // std::isalnum |
6 | 6 | #include "CxxNodeApiHostModule.hpp" |
| 7 | +#include "AddonRegistry.hpp" |
7 | 8 | #include "Logger.hpp" |
8 | 9 |
|
9 | 10 | using namespace facebook; |
@@ -124,6 +125,8 @@ rpartition(const std::string_view &input, char delimiter) { |
124 | 125 |
|
125 | 126 | namespace callstack::nodeapihost { |
126 | 127 |
|
| 128 | +AddonRegistry g_platformAddonRegistry; |
| 129 | + |
127 | 130 | CxxNodeApiHostModule::CxxNodeApiHostModule( |
128 | 131 | std::shared_ptr<react::CallInvoker> jsInvoker) |
129 | 132 | : TurboModule(CxxNodeApiHostModule::kModuleName, jsInvoker) { |
@@ -216,109 +219,20 @@ CxxNodeApiHostModule::resolveRelativePath(facebook::jsi::Runtime &rt, |
216 | 219 | mergedSubpath); |
217 | 220 |
|
218 | 221 | if (!isCached) { |
219 | | - const std::string libraryNameStr(requiredPath); |
220 | | - auto [it, inserted] = nodeAddons_.emplace(libraryNameStr, NodeAddon()); |
221 | | - NodeAddon &addon = it->second; |
222 | | - |
223 | | - // Check if this module has been loaded already, if not then load it... |
224 | | - if (inserted) { |
225 | | - if (!loadNodeAddon(addon, libraryNameStr)) { |
226 | | - return jsi::Value::undefined(); |
227 | | - } |
228 | | - } |
229 | | - |
230 | | - // Initialize the addon if it has not already been initialized |
231 | | - if (!rt.global().hasProperty(rt, addon.generatedName.data())) { |
232 | | - initializeNodeModule(rt, addon); |
233 | | - } |
234 | | - |
235 | | - // Look up the exports (using JSI), add to cache and return |
236 | | - exports = rt.global().getProperty(rt, addon.generatedName.data()); |
| 222 | + // Ask the global addon registry to load given Node-API addon. |
| 223 | + // If other runtime loaded it already, the OS will return the same pointer. |
| 224 | + // NOTE: This method might try multiple platform-specific paths. |
| 225 | + const std::string packageNameCopy(requiredPackageName); |
| 226 | + auto &addon = g_platformAddonRegistry.loadAddon(packageNameCopy, mergedSubpath); |
| 227 | + |
| 228 | + // Create a `napi_env` and initialize the addon |
| 229 | + exports = g_platformAddonRegistry.instantiateAddonInRuntime(rt, addon); |
237 | 230 | updateRequireCache(rt, requiredPackageName, mergedSubpath, exports); |
238 | 231 | } |
239 | 232 |
|
240 | 233 | return std::move(exports); |
241 | 234 | } |
242 | 235 |
|
243 | | -bool CxxNodeApiHostModule::loadNodeAddon(NodeAddon &addon, |
244 | | - const std::string &libraryName) const { |
245 | | -#if defined(__APPLE__) |
246 | | - std::string libraryPath = |
247 | | - "@rpath/" + libraryName + ".framework/" + libraryName; |
248 | | -#elif defined(__ANDROID__) |
249 | | - std::string libraryPath = "lib" + libraryName + ".so"; |
250 | | -#else |
251 | | - abort() |
252 | | -#endif |
253 | | - |
254 | | - log_debug("[%s] Loading addon by '%s'", libraryName.c_str(), |
255 | | - libraryPath.c_str()); |
256 | | - |
257 | | - typename LoaderPolicy::Symbol initFn = NULL; |
258 | | - typename LoaderPolicy::Module library = |
259 | | - LoaderPolicy::loadLibrary(libraryPath.c_str()); |
260 | | - if (NULL != library) { |
261 | | - log_debug("[%s] Loaded addon", libraryName.c_str()); |
262 | | - addon.moduleHandle = library; |
263 | | - |
264 | | - // Generate a name allowing us to reference the exports object from JSI |
265 | | - // later Instead of using random numbers to avoid name clashes, we just use |
266 | | - // the pointer address of the loaded module |
267 | | - addon.generatedName.resize(32, '\0'); |
268 | | - snprintf(addon.generatedName.data(), addon.generatedName.size(), |
269 | | - "RN$NodeAddon_%p", addon.moduleHandle); |
270 | | - |
271 | | - initFn = LoaderPolicy::getSymbol(library, "napi_register_module_v1"); |
272 | | - if (NULL != initFn) { |
273 | | - log_debug("[%s] Found napi_register_module_v1 (%p)", libraryName.c_str(), |
274 | | - initFn); |
275 | | - addon.init = (napi_addon_register_func)initFn; |
276 | | - } else { |
277 | | - log_debug("[%s] Failed to find napi_register_module_v1. Expecting the " |
278 | | - "addon to call napi_module_register to register itself.", |
279 | | - libraryName.c_str()); |
280 | | - } |
281 | | - // TODO: Read "node_api_module_get_api_version_v1" to support the addon |
282 | | - // declaring its Node-API version |
283 | | - // @see |
284 | | - // https://github.com/callstackincubator/react-native-node-api-modules/issues/4 |
285 | | - } else { |
286 | | - log_debug("[%s] Failed to load library", libraryName.c_str()); |
287 | | - } |
288 | | - return NULL != initFn; |
289 | | -} |
290 | | - |
291 | | -bool CxxNodeApiHostModule::initializeNodeModule(jsi::Runtime &rt, |
292 | | - NodeAddon &addon) { |
293 | | - // We should check if the module has already been initialized |
294 | | - assert(NULL != addon.moduleHandle); |
295 | | - assert(NULL != addon.init); |
296 | | - napi_status status = napi_ok; |
297 | | - // TODO: Read the version from the addon |
298 | | - // @see |
299 | | - // https://github.com/callstackincubator/react-native-node-api-modules/issues/4 |
300 | | - napi_env env = reinterpret_cast<napi_env>(rt.createNodeApiEnv(8)); |
301 | | - |
302 | | - // Create the "exports" object |
303 | | - napi_value exports; |
304 | | - status = napi_create_object(env, &exports); |
305 | | - assert(status == napi_ok); |
306 | | - |
307 | | - // Call the addon init function to populate the "exports" object |
308 | | - // Allowing it to replace the value entirely by its return value |
309 | | - exports = addon.init(env, exports); |
310 | | - |
311 | | - napi_value global; |
312 | | - napi_get_global(env, &global); |
313 | | - assert(status == napi_ok); |
314 | | - |
315 | | - status = |
316 | | - napi_set_named_property(env, global, addon.generatedName.data(), exports); |
317 | | - assert(status == napi_ok); |
318 | | - |
319 | | - return true; |
320 | | -} |
321 | | - |
322 | 236 | std::pair<jsi::Value, bool> |
323 | 237 | CxxNodeApiHostModule::lookupRequireCache(::jsi::Runtime &rt, |
324 | 238 | const std::string_view &packageName, |
|
0 commit comments