1 // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2 
3 #include "JSIInteropModuleRegistry.h"
4 #include "ExpoModulesHostObject.h"
5 #include "JavaReferencesCache.h"
6 #include "JSReferencesCache.h"
7 
8 #include <fbjni/detail/Meta.h>
9 #include <fbjni/fbjni.h>
10 
11 #include <memory>
12 
13 namespace jni = facebook::jni;
14 namespace jsi = facebook::jsi;
15 
16 namespace expo {
17 jni::local_ref<JSIInteropModuleRegistry::jhybriddata>
18 JSIInteropModuleRegistry::initHybrid(jni::alias_ref<jhybridobject> jThis) {
19   return makeCxxInstance(jThis);
20 }
21 
22 void JSIInteropModuleRegistry::registerNatives() {
23   registerHybrid({
24                    makeNativeMethod("initHybrid", JSIInteropModuleRegistry::initHybrid),
25                    makeNativeMethod("installJSI", JSIInteropModuleRegistry::installJSI),
26                    makeNativeMethod("installJSIForTests",
27                                     JSIInteropModuleRegistry::installJSIForTests),
28                    makeNativeMethod("evaluateScript", JSIInteropModuleRegistry::evaluateScript),
29                    makeNativeMethod("global", JSIInteropModuleRegistry::global),
30                    makeNativeMethod("createObject", JSIInteropModuleRegistry::createObject),
31                    makeNativeMethod("drainJSEventLoop", JSIInteropModuleRegistry::drainJSEventLoop),
32                  });
33 }
34 
35 JSIInteropModuleRegistry::JSIInteropModuleRegistry(jni::alias_ref<jhybridobject> jThis)
36   : javaPart_(jni::make_global(jThis)) {}
37 
38 void JSIInteropModuleRegistry::installJSI(
39   jlong jsRuntimePointer,
40   jni::alias_ref<react::CallInvokerHolder::javaobject> jsInvokerHolder,
41   jni::alias_ref<react::CallInvokerHolder::javaobject> nativeInvokerHolder
42 ) {
43   auto runtime = reinterpret_cast<jsi::Runtime *>(jsRuntimePointer);
44 
45   jsRegistry = std::make_unique<JSReferencesCache>(*runtime);
46 
47   runtimeHolder = std::make_shared<JavaScriptRuntime>(
48     this,
49     runtime,
50     jsInvokerHolder->cthis()->getCallInvoker(),
51     nativeInvokerHolder->cthis()->getCallInvoker()
52   );
53 
54   auto expoModules = std::make_shared<ExpoModulesHostObject>(this);
55   auto expoModulesObject = jsi::Object::createFromHostObject(*runtime, expoModules);
56 
57   // Define the `global.expo.modules` object.
58   runtimeHolder
59     ->getMainObject()
60     ->setProperty(
61       *runtime,
62       "modules",
63       expoModulesObject
64     );
65 
66   // Also define `global.ExpoModules` for backwards compatibility (used before SDK47, can be removed in SDK48).
67   runtime
68     ->global()
69     .setProperty(
70       *runtime,
71       "ExpoModules",
72       expoModulesObject
73     );
74 }
75 
76 void JSIInteropModuleRegistry::installJSIForTests() {
77 #if !UNIT_TEST
78   throw std::logic_error("The function is only available when UNIT_TEST is defined.");
79 #else
80   runtimeHolder = std::make_shared<JavaScriptRuntime>(this);
81   jsi::Runtime &jsiRuntime = runtimeHolder->get();
82 
83   jsRegistry = std::make_unique<JSReferencesCache>(jsiRuntime);
84 
85   auto expoModules = std::make_shared<ExpoModulesHostObject>(this);
86   auto expoModulesObject = jsi::Object::createFromHostObject(jsiRuntime, expoModules);
87 
88   runtimeHolder
89     ->getMainObject()
90     ->setProperty(
91       jsiRuntime,
92       "modules",
93       std::move(expoModulesObject)
94     );
95 #endif // !UNIT_TEST
96 }
97 
98 jni::local_ref<JavaScriptModuleObject::javaobject>
99 JSIInteropModuleRegistry::callGetJavaScriptModuleObjectMethod(const std::string &moduleName) const {
100   const static auto method = expo::JSIInteropModuleRegistry::javaClassLocal()
101     ->getMethod<jni::local_ref<JavaScriptModuleObject::javaobject>(
102       std::string)>(
103       "getJavaScriptModuleObject"
104     );
105 
106   return method(javaPart_, moduleName);
107 }
108 
109 jni::local_ref<jni::JArrayClass<jni::JString>>
110 JSIInteropModuleRegistry::callGetJavaScriptModulesNames() const {
111   const static auto method = expo::JSIInteropModuleRegistry::javaClassLocal()
112     ->getMethod<jni::local_ref<jni::JArrayClass<jni::JString>>()>(
113       "getJavaScriptModulesName"
114     );
115   return method(javaPart_);
116 }
117 
118 bool JSIInteropModuleRegistry::callHasModule(const std::string &moduleName) const {
119   const static auto method = expo::JSIInteropModuleRegistry::javaClassLocal()
120     ->getMethod<jboolean(std::string)>(
121       "hasModule"
122     );
123   return (bool) method(javaPart_, moduleName);
124 }
125 
126 jni::local_ref<JavaScriptModuleObject::javaobject>
127 JSIInteropModuleRegistry::getModule(const std::string &moduleName) const {
128   return callGetJavaScriptModuleObjectMethod(moduleName);
129 }
130 
131 bool JSIInteropModuleRegistry::hasModule(const std::string &moduleName) const {
132   return callHasModule(moduleName);
133 }
134 
135 jni::local_ref<jni::JArrayClass<jni::JString>> JSIInteropModuleRegistry::getModulesName() const {
136   return callGetJavaScriptModulesNames();
137 }
138 
139 jni::local_ref<JavaScriptValue::javaobject> JSIInteropModuleRegistry::evaluateScript(
140   jni::JString script
141 ) {
142   return runtimeHolder->evaluateScript(script.toStdString());
143 }
144 
145 jni::local_ref<JavaScriptObject::javaobject> JSIInteropModuleRegistry::global() {
146   return runtimeHolder->global();
147 }
148 
149 jni::local_ref<JavaScriptObject::javaobject> JSIInteropModuleRegistry::createObject() {
150   return runtimeHolder->createObject();
151 }
152 
153 void JSIInteropModuleRegistry::drainJSEventLoop() {
154   runtimeHolder->drainJSEventLoop();
155 }
156 
157 void JSIInteropModuleRegistry::registerSharedObject(
158   jni::local_ref<jobject> native,
159   jni::local_ref<JavaScriptObject::javaobject> js
160 ) {
161   const static auto method = expo::JSIInteropModuleRegistry::javaClassLocal()
162     ->getMethod<void(jni::local_ref<jobject>, jni::local_ref<JavaScriptObject::javaobject>)>(
163       "registerSharedObject"
164     );
165   method(javaPart_, std::move(native), std::move(js));
166 }
167 } // namespace expo
168