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 class JavaScriptModuleObject; 25 26 void decorateObjectWithFunctions( 27 jsi::Runtime &runtime, 28 JSIInteropModuleRegistry *jsiInteropModuleRegistry, 29 jsi::Object *jsObject, 30 JavaScriptModuleObject *objectData 31 ); 32 33 void decorateObjectWithProperties( 34 jsi::Runtime &runtime, 35 JSIInteropModuleRegistry *jsiInteropModuleRegistry, 36 jsi::Object *jsObject, 37 JavaScriptModuleObject *objectData 38 ); 39 40 void decorateObjectWithConstants( 41 jsi::Runtime &runtime, 42 JSIInteropModuleRegistry *jsiInteropModuleRegistry, 43 jsi::Object *jsObject, 44 JavaScriptModuleObject *objectData 45 ); 46 47 /** 48 * A CPP part of the module. 49 * 50 * Right now objects of this class are stored by the ModuleHolder to ensure they will live 51 * as long as the RN context. 52 */ 53 class JavaScriptModuleObject : public jni::HybridClass<JavaScriptModuleObject> { 54 public: 55 static auto constexpr 56 kJavaDescriptor = "Lexpo/modules/kotlin/jni/JavaScriptModuleObject;"; 57 static auto constexpr TAG = "JavaScriptModuleObject"; 58 59 static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis); 60 61 static void registerNatives(); 62 63 /** 64 * Pointer to the module registry interop. 65 */ 66 JSIInteropModuleRegistry *jsiInteropModuleRegistry; 67 68 /** 69 * Returns a cached instance of jsi::Object representing this module. 70 * @param runtime 71 * @return Wrapped instance of JavaScriptModuleObject::HostObject 72 */ 73 std::shared_ptr<jsi::Object> getJSIObject(jsi::Runtime &runtime); 74 75 /** 76 * Exports constants that will be assigned to the underlying HostObject. 77 */ 78 void exportConstants(jni::alias_ref<react::NativeMap::javaobject> constants); 79 80 /** 81 * Registers a sync function. 82 * That function can be called via the `JavaScriptModuleObject.callSyncMethod` method. 83 */ 84 void registerSyncFunction( 85 jni::alias_ref<jstring> name, 86 jboolean takesOwner, 87 jint args, 88 jni::alias_ref<jni::JArrayClass<ExpectedType>> expectedArgTypes, 89 jni::alias_ref<JNIFunctionBody::javaobject> body 90 ); 91 92 /** 93 * Registers a async function. 94 * That function can be called via the `JavaScriptModuleObject.callAsyncMethod` method. 95 */ 96 void registerAsyncFunction( 97 jni::alias_ref<jstring> name, 98 jboolean takesOwner, 99 jint args, 100 jni::alias_ref<jni::JArrayClass<ExpectedType>> expectedArgTypes, 101 jni::alias_ref<JNIAsyncFunctionBody::javaobject> body 102 ); 103 104 void registerClass( 105 jni::alias_ref<jstring> name, 106 jni::alias_ref<JavaScriptModuleObject::javaobject> classObject 107 ); 108 109 void registerViewPrototype( 110 jni::alias_ref<JavaScriptModuleObject::javaobject> viewPrototype 111 ); 112 113 /** 114 * Registers a property 115 * @param name of the property 116 * @param desiredType of the setter argument 117 * @param getter body for the get method - can be nullptr 118 * @param setter body for the set method - can be nullptr 119 */ 120 void registerProperty( 121 jni::alias_ref<jstring> name, 122 jni::alias_ref<ExpectedType> expectedArgType, 123 jni::alias_ref<JNIFunctionBody::javaobject> getter, 124 jni::alias_ref<JNIFunctionBody::javaobject> setter 125 ); 126 127 private: 128 explicit JavaScriptModuleObject(jni::alias_ref<jhybridobject> jThis); 129 130 private: 131 friend HybridBase; 132 133 friend void decorateObjectWithFunctions( 134 jsi::Runtime &runtime, 135 JSIInteropModuleRegistry *jsiInteropModuleRegistry, 136 jsi::Object *jsObject, 137 JavaScriptModuleObject *objectData 138 ); 139 140 friend void decorateObjectWithProperties( 141 jsi::Runtime &runtime, 142 JSIInteropModuleRegistry *jsiInteropModuleRegistry, 143 jsi::Object *jsObject, 144 JavaScriptModuleObject *objectData 145 ); 146 147 friend void decorateObjectWithConstants( 148 jsi::Runtime &runtime, 149 JSIInteropModuleRegistry *jsiInteropModuleRegistry, 150 jsi::Object *jsObject, 151 JavaScriptModuleObject *objectData 152 ); 153 154 /** 155 * A reference to the `jsi::Object`. 156 * Simple we cached that value to return the same object each time. 157 * It's a weak reference because the JS runtime holds the actual object. 158 * Doing that allows the runtime to deallocate jsi::Object if it's not needed anymore. 159 */ 160 std::weak_ptr<jsi::Object> jsiObject; 161 jni::global_ref<JavaScriptModuleObject::javaobject> javaPart_; 162 163 /** 164 * Metadata map that stores information about all available methods on this module. 165 */ 166 std::unordered_map<std::string, MethodMetadata> methodsMetadata; 167 168 /** 169 * A constants map. 170 */ 171 std::unordered_map<std::string, folly::dynamic> constants; 172 173 /** 174 * A registry of properties 175 * The first MethodMetadata points to the getter and the second one to the setter. 176 */ 177 std::map<std::string, std::pair<MethodMetadata, MethodMetadata>> properties; 178 179 /** 180 * The `LongLivedObjectCollection` to hold `LongLivedObject` (callbacks or promises) for this module. 181 */ 182 std::shared_ptr<react::LongLivedObjectCollection> longLivedObjectCollection_; 183 184 std::map<std::string, jni::global_ref<JavaScriptModuleObject::javaobject>> classes; 185 186 jni::global_ref<JavaScriptModuleObject::javaobject> viewPrototype; 187 }; 188 } // namespace expo 189