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