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