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