1 // Copyright © 2021-present 650 Industries, Inc. (aka Expo) 2 3 #pragma once 4 5 #include <fbjni/fbjni.h> 6 #include <jsi/jsi.h> 7 #include <react/jni/ReadableNativeArray.h> 8 #include <jni/JCallback.h> 9 10 #include <map> 11 12 #include "MethodMetadata.h" 13 #include "JNIFunctionBody.h" 14 15 namespace jni = facebook::jni; 16 namespace jsi = facebook::jsi; 17 namespace react = facebook::react; 18 19 namespace expo { 20 class JSIInteropModuleRegistry; 21 22 /** 23 * A CPP part of the module. 24 * 25 * Right now objects of this class are stored by the ModuleHolder to ensure they will live 26 * as long as the RN context. 27 */ 28 class JavaScriptModuleObject : public jni::HybridClass<JavaScriptModuleObject> { 29 public: 30 static auto constexpr 31 kJavaDescriptor = "Lexpo/modules/kotlin/jni/JavaScriptModuleObject;"; 32 static auto constexpr TAG = "JavaScriptModuleObject"; 33 34 static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis); 35 36 static void registerNatives(); 37 38 /** 39 * Pointer to the module registry interop. 40 */ 41 JSIInteropModuleRegistry *jsiInteropModuleRegistry; 42 43 /** 44 * Returns a cached instance of jsi::Object representing this module. 45 * @param runtime 46 * @return Wrapped instance of JavaScriptModuleObject::HostObject 47 */ 48 std::shared_ptr<jsi::Object> getJSIObject(jsi::Runtime &runtime); 49 50 /** 51 * Exports constants that will be assigned to the underlying HostObject. 52 */ 53 void exportConstants(jni::alias_ref<react::NativeMap::javaobject> constants); 54 55 /** 56 * Registers a sync function. 57 * That function can be called via the `JavaScriptModuleObject.callSyncMethod` method. 58 */ 59 void registerSyncFunction( 60 jni::alias_ref<jstring> name, 61 jint args, 62 jni::alias_ref<JNIFunctionBody::javaobject> JSIFunctionBody 63 ); 64 65 /** 66 * Registers a async function. 67 * That function can be called via the `JavaScriptModuleObject.callAsyncMethod` method. 68 */ 69 void registerAsyncFunction( 70 jni::alias_ref<jstring> name, 71 jint args, 72 jni::alias_ref<JNIAsyncFunctionBody::javaobject> JSIAsyncFunctionBody 73 ); 74 75 /** 76 * An inner class of the `JavaScriptModuleObject` that is exported to the JS. 77 * It's an additional communication layer between JS and Kotlin. 78 * So the high-level view on accessing the exported function will look like this: 79 * `JS` --get function--> `JavaScriptModuleObject::HostObject` --access module metadata--> `JavaScriptModuleObject` 80 * --create JSI function--> `MethodMetadata` 81 * 82 * This abstraction wasn't necessary. However, it makes the management of ownership much easier - 83 * `JavaScriptModuleObject` is held by the ModuleHolder and `JavaScriptModuleObject::HostObject` is stored in the JS runtime. 84 * Without this distinction the `JavaScriptModuleObject` would have to turn into `HostObject` and `HybridObject` at the same time. 85 */ 86 class HostObject : public jsi::HostObject { 87 public: 88 HostObject(JavaScriptModuleObject *); 89 90 jsi::Value get(jsi::Runtime &, const jsi::PropNameID &name) override; 91 92 void set(jsi::Runtime &, const jsi::PropNameID &name, const jsi::Value &value) override; 93 94 std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime &rt) override; 95 96 private: 97 JavaScriptModuleObject *jsModule; 98 }; 99 100 private: 101 friend HybridBase; 102 /** 103 * A reference to the `JavaScriptModuleObject::HostObject`. 104 * Simple we cached that value to return the same object each time. 105 */ 106 std::shared_ptr<jsi::Object> jsiObject = nullptr; 107 jni::global_ref<JavaScriptModuleObject::javaobject> javaPart_; 108 109 /** 110 * Metadata map that stores information about all available methods on this module. 111 */ 112 std::map<std::string, MethodMetadata> methodsMetadata; 113 114 /** 115 * A constants map. 116 */ 117 std::map<std::string, folly::dynamic> constants; 118 119 explicit JavaScriptModuleObject(jni::alias_ref<jhybridobject> jThis) 120 : javaPart_(jni::make_global(jThis)) {} 121 }; 122 } // namespace expo 123