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