1 // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2 
3 #include "ExpoModulesHostObject.h"
4 #include "LazyObject.h"
5 
6 #include <folly/dynamic.h>
7 #include <jsi/JSIDynamic.h>
8 #include <react/bridging/LongLivedObject.h>
9 
10 namespace jsi = facebook::jsi;
11 
12 namespace expo {
13 
14 ExpoModulesHostObject::ExpoModulesHostObject(JSIInteropModuleRegistry *installer)
15   : installer(installer) {}
16 
17 /**
18  * Clears jsi references held by JSRegistry and JavaScriptRuntime.
19  */
20 ExpoModulesHostObject::~ExpoModulesHostObject() {
21   facebook::react::LongLivedObjectCollection::get().clear();
22   installer->jsRegistry.reset();
23   installer->runtimeHolder.reset();
24   installer->jsInvoker.reset();
25   installer->nativeInvoker.reset();
26   installer->jniDeallocator.reset();
27 }
28 
29 jsi::Value ExpoModulesHostObject::get(jsi::Runtime &runtime, const jsi::PropNameID &name) {
30   auto cName = name.utf8(runtime);
31 
32   if (!installer->hasModule(cName)) {
33     modulesCache.erase(cName);
34     return jsi::Value::undefined();
35   }
36   if (UniqueJSIObject &cachedObject = modulesCache[cName]) {
37     return jsi::Value(runtime, *cachedObject);
38   }
39 
40   // Create a lazy object for the specific module. It defers initialization of the final module object.
41   LazyObject::Shared moduleLazyObject = std::make_shared<LazyObject>(
42     [this, cName](jsi::Runtime &rt) {
43       auto module = installer->getModule(cName);
44       module->cthis()->jsiInteropModuleRegistry = installer;
45       return module->cthis()->getJSIObject(rt);
46     });
47 
48   // Save the module's lazy host object for later use.
49   modulesCache[cName] = std::make_unique<jsi::Object>(
50     jsi::Object::createFromHostObject(runtime, moduleLazyObject));
51 
52   return jsi::Value(runtime, *modulesCache[cName]);
53 }
54 
55 void ExpoModulesHostObject::set(jsi::Runtime &runtime, const jsi::PropNameID &name,
56                                 const jsi::Value &value) {
57   throw jsi::JSError(
58     runtime,
59     "RuntimeError: Cannot override the host object for expo module '" + name.utf8(runtime) + "'"
60   );
61 }
62 
63 std::vector<jsi::PropNameID> ExpoModulesHostObject::getPropertyNames(jsi::Runtime &rt) {
64   auto names = installer->getModulesName();
65   size_t size = names->size();
66   std::vector<jsi::PropNameID> result;
67   result.reserve(size);
68   for (int i = 0; i < size; i++) {
69     result.push_back(
70       jsi::PropNameID::forUtf8(rt, names->getElement(i)->toStdString())
71     );
72   }
73   return result;
74 }
75 } // namespace expo
76