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<JNIDeallocator::javaobject> jniDeallocator,
41   jni::alias_ref<react::CallInvokerHolder::javaobject> jsInvokerHolder,
42   jni::alias_ref<react::CallInvokerHolder::javaobject> nativeInvokerHolder
43 ) {
44   this->jniDeallocator = jni::make_global(jniDeallocator);
45 
46   auto runtime = reinterpret_cast<jsi::Runtime *>(jsRuntimePointer);
47 
48   jsRegistry = std::make_unique<JSReferencesCache>(*runtime);
49 
50   runtimeHolder = std::make_shared<JavaScriptRuntime>(
51     this,
52     runtime,
53     jsInvokerHolder->cthis()->getCallInvoker(),
54     nativeInvokerHolder->cthis()->getCallInvoker()
55   );
56 
57   auto expoModules = std::make_shared<ExpoModulesHostObject>(this);
58   auto expoModulesObject = jsi::Object::createFromHostObject(*runtime, expoModules);
59 
60   // Define the `global.expo.modules` object.
61   runtimeHolder
62     ->getMainObject()
63     ->setProperty(
64       *runtime,
65       "modules",
66       expoModulesObject
67     );
68 
69   // Also define `global.ExpoModules` for backwards compatibility (used before SDK47, can be removed in SDK48).
70   runtime
71     ->global()
72     .setProperty(
73       *runtime,
74       "ExpoModules",
75       expoModulesObject
76     );
77 }
78 
79 void JSIInteropModuleRegistry::installJSIForTests(
80   jni::alias_ref<JNIDeallocator::javaobject> jniDeallocator
81 ) {
82 #if !UNIT_TEST
83   throw std::logic_error("The function is only available when UNIT_TEST is defined.");
84 #else
85   this->jniDeallocator = jni::make_global(jniDeallocator);
86 
87   runtimeHolder = std::make_shared<JavaScriptRuntime>(this);
88   jsi::Runtime &jsiRuntime = runtimeHolder->get();
89 
90   jsRegistry = std::make_unique<JSReferencesCache>(jsiRuntime);
91 
92   auto expoModules = std::make_shared<ExpoModulesHostObject>(this);
93   auto expoModulesObject = jsi::Object::createFromHostObject(jsiRuntime, expoModules);
94 
95   runtimeHolder
96     ->getMainObject()
97     ->setProperty(
98       jsiRuntime,
99       "modules",
100       std::move(expoModulesObject)
101     );
102 #endif // !UNIT_TEST
103 }
104 
105 jni::local_ref<JavaScriptModuleObject::javaobject>
106 JSIInteropModuleRegistry::callGetJavaScriptModuleObjectMethod(const std::string &moduleName) const {
107   const static auto method = expo::JSIInteropModuleRegistry::javaClassLocal()
108     ->getMethod<jni::local_ref<JavaScriptModuleObject::javaobject>(
109       std::string)>(
110       "getJavaScriptModuleObject"
111     );
112 
113   return method(javaPart_, moduleName);
114 }
115 
116 jni::local_ref<JavaScriptModuleObject::javaobject>
117 JSIInteropModuleRegistry::callGetCoreModuleObject() const {
118   const static auto method = expo::JSIInteropModuleRegistry::javaClassLocal()
119     ->getMethod<jni::local_ref<JavaScriptModuleObject::javaobject>()>(
120       "getCoreModuleObject"
121     );
122 
123   return method(javaPart_);
124 }
125 
126 jni::local_ref<jni::JArrayClass<jni::JString>>
127 JSIInteropModuleRegistry::callGetJavaScriptModulesNames() const {
128   const static auto method = expo::JSIInteropModuleRegistry::javaClassLocal()
129     ->getMethod<jni::local_ref<jni::JArrayClass<jni::JString>>()>(
130       "getJavaScriptModulesName"
131     );
132   return method(javaPart_);
133 }
134 
135 bool JSIInteropModuleRegistry::callHasModule(const std::string &moduleName) const {
136   const static auto method = expo::JSIInteropModuleRegistry::javaClassLocal()
137     ->getMethod<jboolean(std::string)>(
138       "hasModule"
139     );
140   return (bool) method(javaPart_, moduleName);
141 }
142 
143 jni::local_ref<JavaScriptModuleObject::javaobject>
144 JSIInteropModuleRegistry::getModule(const std::string &moduleName) const {
145   return callGetJavaScriptModuleObjectMethod(moduleName);
146 }
147 
148 jni::local_ref<JavaScriptModuleObject::javaobject> JSIInteropModuleRegistry::getCoreModule() const {
149   return callGetCoreModuleObject();
150 }
151 
152 bool JSIInteropModuleRegistry::hasModule(const std::string &moduleName) const {
153   return callHasModule(moduleName);
154 }
155 
156 jni::local_ref<jni::JArrayClass<jni::JString>> JSIInteropModuleRegistry::getModulesName() const {
157   return callGetJavaScriptModulesNames();
158 }
159 
160 jni::local_ref<JavaScriptValue::javaobject> JSIInteropModuleRegistry::evaluateScript(
161   jni::JString script
162 ) {
163   return runtimeHolder->evaluateScript(script.toStdString());
164 }
165 
166 jni::local_ref<JavaScriptObject::javaobject> JSIInteropModuleRegistry::global() {
167   return runtimeHolder->global();
168 }
169 
170 jni::local_ref<JavaScriptObject::javaobject> JSIInteropModuleRegistry::createObject() {
171   return runtimeHolder->createObject();
172 }
173 
174 void JSIInteropModuleRegistry::drainJSEventLoop() {
175   runtimeHolder->drainJSEventLoop();
176 }
177 
178 void JSIInteropModuleRegistry::registerSharedObject(
179   jni::local_ref<jobject> native,
180   jni::local_ref<JavaScriptObject::javaobject> js
181 ) {
182   const static auto method = expo::JSIInteropModuleRegistry::javaClassLocal()
183     ->getMethod<void(jni::local_ref<jobject>, jni::local_ref<JavaScriptObject::javaobject>)>(
184       "registerSharedObject"
185     );
186   method(javaPart_, std::move(native), std::move(js));
187 }
188 } // namespace expo
189