|
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