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