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 jni::alias_ref<ExpectedType> expectedArgType, 126 jni::alias_ref<JNIFunctionBody::javaobject> getter, 127 jni::alias_ref<JNIFunctionBody::javaobject> setter 128 ); 129 130 private: 131 explicit JavaScriptModuleObject(jni::alias_ref<jhybridobject> jThis); 132 133 private: 134 friend HybridBase; 135 136 friend void decorateObjectWithFunctions( 137 jsi::Runtime &runtime, 138 JSIInteropModuleRegistry *jsiInteropModuleRegistry, 139 jsi::Object *jsObject, 140 JavaScriptModuleObject *objectData 141 ); 142 143 friend void decorateObjectWithProperties( 144 jsi::Runtime &runtime, 145 JSIInteropModuleRegistry *jsiInteropModuleRegistry, 146 jsi::Object *jsObject, 147 JavaScriptModuleObject *objectData 148 ); 149 150 friend void decorateObjectWithConstants( 151 jsi::Runtime &runtime, 152 JSIInteropModuleRegistry *jsiInteropModuleRegistry, 153 jsi::Object *jsObject, 154 JavaScriptModuleObject *objectData 155 ); 156 157 /** 158 * A reference to the `jsi::Object`. 159 * Simple we cached that value to return the same object each time. 160 * It's a weak reference because the JS runtime holds the actual object. 161 * Doing that allows the runtime to deallocate jsi::Object if it's not needed anymore. 162 */ 163 std::weak_ptr<jsi::Object> jsiObject; 164 jni::global_ref<JavaScriptModuleObject::javaobject> javaPart_; 165 166 /** 167 * Metadata map that stores information about all available methods on this module. 168 */ 169 std::unordered_map<std::string, MethodMetadata> methodsMetadata; 170 171 /** 172 * A constants map. 173 */ 174 std::unordered_map<std::string, folly::dynamic> constants; 175 176 /** 177 * A registry of properties 178 * The first MethodMetadata points to the getter and the second one to the setter. 179 */ 180 std::map<std::string, std::pair<MethodMetadata, MethodMetadata>> properties; 181 182 std::map< 183 std::string, 184 std::pair<jni::global_ref<JavaScriptModuleObject::javaobject>, MethodMetadata> 185 > classes; 186 187 jni::global_ref<JavaScriptModuleObject::javaobject> viewPrototype; 188 }; 189 } // namespace expo 190