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/bridging/LongLivedObject.h> 8 #include <react/jni/ReadableNativeArray.h> 9 #include <jni/JCallback.h> 10 11 #include <unordered_map> 12 13 #include "MethodMetadata.h" 14 #include "JNIFunctionBody.h" 15 #include "types/ExpectedType.h" 16 17 namespace jni = facebook::jni; 18 namespace jsi = facebook::jsi; 19 namespace react = facebook::react; 20 21 namespace expo { 22 class JSIInteropModuleRegistry; 23 24 /** 25 * A CPP part of the module. 26 * 27 * Right now objects of this class are stored by the ModuleHolder to ensure they will live 28 * as long as the RN context. 29 */ 30 class JavaScriptModuleObject : public jni::HybridClass<JavaScriptModuleObject> { 31 public: 32 static auto constexpr 33 kJavaDescriptor = "Lexpo/modules/kotlin/jni/JavaScriptModuleObject;"; 34 static auto constexpr TAG = "JavaScriptModuleObject"; 35 36 static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis); 37 38 static void registerNatives(); 39 40 /** 41 * Pointer to the module registry interop. 42 */ 43 JSIInteropModuleRegistry *jsiInteropModuleRegistry; 44 45 /** 46 * Returns a cached instance of jsi::Object representing this module. 47 * @param runtime 48 * @return Wrapped instance of JavaScriptModuleObject::HostObject 49 */ 50 std::shared_ptr<jsi::Object> getJSIObject(jsi::Runtime &runtime); 51 52 /** 53 * Exports constants that will be assigned to the underlying HostObject. 54 */ 55 void exportConstants(jni::alias_ref<react::NativeMap::javaobject> constants); 56 57 /** 58 * Registers a sync function. 59 * That function can be called via the `JavaScriptModuleObject.callSyncMethod` method. 60 */ 61 void registerSyncFunction( 62 jni::alias_ref<jstring> name, 63 jint args, 64 jni::alias_ref<jni::JArrayClass<ExpectedType>> expectedArgTypes, 65 jni::alias_ref<JNIFunctionBody::javaobject> body 66 ); 67 68 /** 69 * Registers a async function. 70 * That function can be called via the `JavaScriptModuleObject.callAsyncMethod` method. 71 */ 72 void registerAsyncFunction( 73 jni::alias_ref<jstring> name, 74 jint args, 75 jni::alias_ref<jni::JArrayClass<ExpectedType>> expectedArgTypes, 76 jni::alias_ref<JNIAsyncFunctionBody::javaobject> body 77 ); 78 79 /** 80 * Registers a property 81 * @param name of the property 82 * @param desiredType of the setter argument 83 * @param getter body for the get method - can be nullptr 84 * @param setter body for the set method - can be nullptr 85 */ 86 void registerProperty( 87 jni::alias_ref<jstring> name, 88 jni::alias_ref<ExpectedType> expectedArgType, 89 jni::alias_ref<JNIFunctionBody::javaobject> getter, 90 jni::alias_ref<JNIFunctionBody::javaobject> setter 91 ); 92 93 /** 94 * An inner class of the `JavaScriptModuleObject` that is exported to the JS. 95 * It's an additional communication layer between JS and Kotlin. 96 * So the high-level view on accessing the exported function will look like this: 97 * `JS` --get function--> `JavaScriptModuleObject::HostObject` --access module metadata--> `JavaScriptModuleObject` 98 * --create JSI function--> `MethodMetadata` 99 * 100 * This abstraction wasn't necessary. However, it makes the management of ownership much easier - 101 * `JavaScriptModuleObject` is held by the ModuleHolder and `JavaScriptModuleObject::HostObject` is stored in the JS runtime. 102 * Without this distinction the `JavaScriptModuleObject` would have to turn into `HostObject` and `HybridObject` at the same time. 103 */ 104 class HostObject : public jsi::HostObject { 105 public: 106 HostObject(JavaScriptModuleObject *); 107 108 ~HostObject() override; 109 110 jsi::Value get(jsi::Runtime &, const jsi::PropNameID &name) override; 111 112 void set(jsi::Runtime &, const jsi::PropNameID &name, const jsi::Value &value) override; 113 114 std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime &rt) override; 115 116 private: 117 JavaScriptModuleObject *jsModule; 118 }; 119 120 private: 121 explicit JavaScriptModuleObject(jni::alias_ref<jhybridobject> jThis); 122 123 private: 124 friend HybridBase; 125 /** 126 * A reference to the `JavaScriptModuleObject::HostObject`. 127 * Simple we cached that value to return the same object each time. 128 */ 129 std::shared_ptr<jsi::Object> jsiObject = nullptr; 130 jni::global_ref<JavaScriptModuleObject::javaobject> javaPart_; 131 132 /** 133 * Metadata map that stores information about all available methods on this module. 134 */ 135 std::unordered_map<std::string, MethodMetadata> methodsMetadata; 136 137 /** 138 * A constants map. 139 */ 140 std::unordered_map<std::string, folly::dynamic> constants; 141 142 /** 143 * A registry of properties 144 * The first MethodMetadata points to the getter and the second one to the setter. 145 */ 146 std::map<std::string, std::pair<MethodMetadata, MethodMetadata>> properties; 147 148 /** 149 * The `LongLivedObjectCollection` to hold `LongLivedObject` (callbacks or promises) for this module. 150 */ 151 std::shared_ptr<react::LongLivedObjectCollection> longLivedObjectCollection_; 152 153 }; 154 } // namespace expo 155