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 }; 33 34 /** 35 * A class that holds information about the exported function. 36 */ 37 class MethodMetadata { 38 public: 39 /** 40 * Function name 41 */ 42 std::string name; 43 /** 44 * Number of arguments 45 */ 46 int args; 47 /* 48 * Whether this function is async 49 */ 50 bool isAsync; 51 52 std::unique_ptr<int[]> desiredTypes; 53 54 MethodMetadata( 55 std::string name, 56 int args, 57 bool isAsync, 58 std::unique_ptr<int[]> desiredTypes, 59 jni::global_ref<jobject> &&jBodyReference 60 ); 61 62 // We deleted the copy contractor to not deal with transforming the ownership of the `jBodyReference`. 63 MethodMetadata(const MethodMetadata &) = delete; 64 65 MethodMetadata(MethodMetadata &&other) = default; 66 67 /** 68 * MethodMetadata owns the only reference to the Kotlin function. 69 * We have to clean that, cause it's a `global_ref`. 70 */ 71 ~MethodMetadata() { 72 if (jBodyReference != nullptr) { 73 jBodyReference.release(); 74 } 75 } 76 77 /** 78 * Transforms metadata to a jsi::Function. 79 * 80 * @param runtime 81 * @param moduleRegistry 82 * @return shared ptr to the jsi::Function that wrapped the underlying Kotlin's function. 83 */ 84 std::shared_ptr<jsi::Function> toJSFunction( 85 jsi::Runtime &runtime, 86 JSIInteropModuleRegistry *moduleRegistry 87 ); 88 89 /** 90 * Calls the underlying Kotlin function. 91 */ 92 jsi::Value callSync( 93 jsi::Runtime &rt, 94 JSIInteropModuleRegistry *moduleRegistry, 95 const jsi::Value *args, 96 size_t count 97 ); 98 99 private: 100 /** 101 * Reference to one of two java objects - `JNIFunctionBody` or `JNIAsyncFunctionBody`. 102 * 103 * In case when `isAsync` is `true`, this variable will point to `JNIAsyncFunctionBody`. 104 * Otherwise to `JNIFunctionBody` 105 */ 106 jni::global_ref<jobject> jBodyReference; 107 108 /** 109 * To not create a jsi::Function always when we need it, we cached that value. 110 */ 111 std::shared_ptr<jsi::Function> body = nullptr; 112 113 jsi::Function toSyncFunction(jsi::Runtime &runtime, JSIInteropModuleRegistry *moduleRegistry); 114 115 jsi::Function toAsyncFunction(jsi::Runtime &runtime, JSIInteropModuleRegistry *moduleRegistry); 116 117 jsi::Function createPromiseBody( 118 jsi::Runtime &runtime, 119 JSIInteropModuleRegistry *moduleRegistry, 120 std::vector<jvalue> &&args 121 ); 122 123 std::vector<jvalue> convertJSIArgsToJNI( 124 JSIInteropModuleRegistry *moduleRegistry, 125 JNIEnv *env, 126 jsi::Runtime &rt, 127 const jsi::Value *args, 128 size_t count, 129 bool returnGlobalReferences 130 ); 131 }; 132 } // namespace expo 133