1 // Copyright © 2021-present 650 Industries, Inc. (aka Expo) 2 3 #pragma once 4 5 #include <jsi/jsi.h> 6 #include <fbjni/fbjni.h> 7 #include <ReactCommon/TurboModuleUtils.h> 8 #include <react/jni/ReadableNativeArray.h> 9 #include <memory> 10 #include <folly/dynamic.h> 11 #include <jsi/JSIDynamic.h> 12 13 namespace jni = facebook::jni; 14 namespace jsi = facebook::jsi; 15 namespace react = facebook::react; 16 17 namespace expo { 18 class JSIInteropModuleRegistry; 19 20 /** 21 * A cpp version of the `expo.modules.kotlin.jni.CppType` enum. 22 * Used to determine which representation of the js value should be sent to the Kotlin. 23 */ 24 enum CppType { 25 DOUBLE = 1 << 0, 26 BOOLEAN = 1 << 1, 27 STRING = 1 << 2, 28 JS_OBJECT = 1 << 3, 29 JS_VALUE = 1 << 4, 30 READABLE_ARRAY = 1 << 5, 31 READABLE_MAP = 1 << 6, 32 TYPED_ARRAY = 1 << 7 33 }; 34 35 /** 36 * A class that holds information about the exported function. 37 */ 38 class MethodMetadata { 39 public: 40 /** 41 * Function name 42 */ 43 std::string name; 44 /** 45 * Number of arguments 46 */ 47 int args; 48 /* 49 * Whether this function is async 50 */ 51 bool isAsync; 52 53 std::unique_ptr<int[]> desiredTypes; 54 55 MethodMetadata( 56 std::string name, 57 int args, 58 bool isAsync, 59 std::unique_ptr<int[]> desiredTypes, 60 jni::global_ref<jobject> &&jBodyReference 61 ); 62 63 // We deleted the copy contractor to not deal with transforming the ownership of the `jBodyReference`. 64 MethodMetadata(const MethodMetadata &) = delete; 65 66 MethodMetadata(MethodMetadata &&other) = default; 67 68 /** 69 * MethodMetadata owns the only reference to the Kotlin function. 70 * We have to clean that, cause it's a `global_ref`. 71 */ 72 ~MethodMetadata() { 73 if (jBodyReference != nullptr) { 74 jBodyReference.release(); 75 } 76 } 77 78 /** 79 * Transforms metadata to a jsi::Function. 80 * 81 * @param runtime 82 * @param moduleRegistry 83 * @return shared ptr to the jsi::Function that wrapped the underlying Kotlin's function. 84 */ 85 std::shared_ptr<jsi::Function> toJSFunction( 86 jsi::Runtime &runtime, 87 JSIInteropModuleRegistry *moduleRegistry 88 ); 89 90 /** 91 * Calls the underlying Kotlin function. 92 */ 93 jsi::Value callSync( 94 jsi::Runtime &rt, 95 JSIInteropModuleRegistry *moduleRegistry, 96 const jsi::Value *args, 97 size_t count 98 ); 99 100 private: 101 /** 102 * Reference to one of two java objects - `JNIFunctionBody` or `JNIAsyncFunctionBody`. 103 * 104 * In case when `isAsync` is `true`, this variable will point to `JNIAsyncFunctionBody`. 105 * Otherwise to `JNIFunctionBody` 106 */ 107 jni::global_ref<jobject> jBodyReference; 108 109 /** 110 * To not create a jsi::Function always when we need it, we cached that value. 111 */ 112 std::shared_ptr<jsi::Function> body = nullptr; 113 114 jsi::Function toSyncFunction(jsi::Runtime &runtime, JSIInteropModuleRegistry *moduleRegistry); 115 116 jsi::Function toAsyncFunction(jsi::Runtime &runtime, JSIInteropModuleRegistry *moduleRegistry); 117 118 jsi::Function createPromiseBody( 119 jsi::Runtime &runtime, 120 JSIInteropModuleRegistry *moduleRegistry, 121 std::vector<jvalue> &&args 122 ); 123 124 std::vector<jvalue> convertJSIArgsToJNI( 125 JSIInteropModuleRegistry *moduleRegistry, 126 JNIEnv *env, 127 jsi::Runtime &rt, 128 const jsi::Value *args, 129 size_t count, 130 bool returnGlobalReferences 131 ); 132 }; 133 } // namespace expo 134