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 * Registers a sync function. 52 * That function can be called via the `JavaScriptModuleObject.callSyncMethod` method. 53 */ 54 void registerSyncFunction( 55 jni::alias_ref<jstring> name, 56 jint args, 57 jni::alias_ref<JNIFunctionBody::javaobject> JSIFunctionBody 58 ); 59 60 /** 61 * Registers a async function. 62 * That function can be called via the `JavaScriptModuleObject.callAsyncMethod` method. 63 */ 64 void registerAsyncFunction( 65 jni::alias_ref<jstring> name, 66 jint args, 67 jni::alias_ref<JNIAsyncFunctionBody::javaobject> JSIAsyncFunctionBody 68 ); 69 70 /** 71 * An inner class of the `JavaScriptModuleObject` that is exported to the JS. 72 * It's an additional communication layer between JS and Kotlin. 73 * So the high-level view on accessing the exported function will look like this: 74 * `JS` --get function--> `JavaScriptModuleObject::HostObject` --access module metadata--> `JavaScriptModuleObject` 75 * --create JSI function--> `MethodMetadata` 76 * 77 * This abstraction wasn't necessary. However, it makes the management of ownership much easier - 78 * `JavaScriptModuleObject` is held by the ModuleHolder and `JavaScriptModuleObject::HostObject` is stored in the JS runtime. 79 * Without this distinction the `JavaScriptModuleObject` would have to turn into `HostObject` and `HybridObject` at the same time. 80 */ 81 class HostObject : public jsi::HostObject { 82 public: 83 HostObject(JavaScriptModuleObject *); 84 85 jsi::Value get(jsi::Runtime &, const jsi::PropNameID &name) override; 86 87 void set(jsi::Runtime &, const jsi::PropNameID &name, const jsi::Value &value) override; 88 89 std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime &rt) override; 90 91 private: 92 JavaScriptModuleObject *jsModule; 93 }; 94 95 private: 96 friend HybridBase; 97 /** 98 * A reference to the `JavaScriptModuleObject::HostObject`. 99 * Simple we cached that value to return the same object each time. 100 */ 101 std::shared_ptr<jsi::Object> jsiObject = nullptr; 102 jni::global_ref<JavaScriptModuleObject::javaobject> javaPart_; 103 104 /** 105 * Metadata map that stores information about all available methods on this module. 106 */ 107 std::map<std::string, MethodMetadata> methodsMetadata; 108 109 explicit JavaScriptModuleObject(jni::alias_ref<jhybridobject> jThis) 110 : javaPart_(jni::make_global(jThis)) {} 111 }; 112 } // namespace expo 113