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 jboolean takesOwner, 108 jint args, 109 jni::alias_ref<jni::JArrayClass<ExpectedType>> expectedArgTypes, 110 jni::alias_ref<JNIFunctionBody::javaobject> body 111 ); 112 113 void registerViewPrototype( 114 jni::alias_ref<JavaScriptModuleObject::javaobject> viewPrototype 115 ); 116 117 /** 118 * Registers a property 119 * @param name of the property 120 * @param desiredType of the setter argument 121 * @param getter body for the get method - can be nullptr 122 * @param setter body for the set method - can be nullptr 123 */ 124 void registerProperty( 125 jni::alias_ref<jstring> name, 126 jni::alias_ref<ExpectedType> expectedArgType, 127 jni::alias_ref<JNIFunctionBody::javaobject> getter, 128 jni::alias_ref<JNIFunctionBody::javaobject> setter 129 ); 130 131 private: 132 explicit JavaScriptModuleObject(jni::alias_ref<jhybridobject> jThis); 133 134 private: 135 friend HybridBase; 136 137 friend void decorateObjectWithFunctions( 138 jsi::Runtime &runtime, 139 JSIInteropModuleRegistry *jsiInteropModuleRegistry, 140 jsi::Object *jsObject, 141 JavaScriptModuleObject *objectData 142 ); 143 144 friend void decorateObjectWithProperties( 145 jsi::Runtime &runtime, 146 JSIInteropModuleRegistry *jsiInteropModuleRegistry, 147 jsi::Object *jsObject, 148 JavaScriptModuleObject *objectData 149 ); 150 151 friend void decorateObjectWithConstants( 152 jsi::Runtime &runtime, 153 JSIInteropModuleRegistry *jsiInteropModuleRegistry, 154 jsi::Object *jsObject, 155 JavaScriptModuleObject *objectData 156 ); 157 158 /** 159 * A reference to the `jsi::Object`. 160 * Simple we cached that value to return the same object each time. 161 * It's a weak reference because the JS runtime holds the actual object. 162 * Doing that allows the runtime to deallocate jsi::Object if it's not needed anymore. 163 */ 164 std::weak_ptr<jsi::Object> jsiObject; 165 jni::global_ref<JavaScriptModuleObject::javaobject> javaPart_; 166 167 /** 168 * Metadata map that stores information about all available methods on this module. 169 */ 170 std::unordered_map<std::string, MethodMetadata> methodsMetadata; 171 172 /** 173 * A constants map. 174 */ 175 std::unordered_map<std::string, folly::dynamic> constants; 176 177 /** 178 * A registry of properties 179 * The first MethodMetadata points to the getter and the second one to the setter. 180 */ 181 std::map<std::string, std::pair<MethodMetadata, MethodMetadata>> properties; 182 183 /** 184 * The `LongLivedObjectCollection` to hold `LongLivedObject` (callbacks or promises) for this module. 185 */ 186 std::shared_ptr<react::LongLivedObjectCollection> longLivedObjectCollection_; 187 188 std::map< 189 std::string, 190 std::pair<jni::global_ref<JavaScriptModuleObject::javaobject>, MethodMetadata> 191 > classes; 192 193 jni::global_ref<JavaScriptModuleObject::javaobject> viewPrototype; 194 }; 195 } // namespace expo 196