1 // Copyright © 2021-present 650 Industries, Inc. (aka Expo) 2 3 #include "JavaScriptModuleObject.h" 4 #include "JSIInteropModuleRegistry.h" 5 6 #include <folly/dynamic.h> 7 #include <jsi/JSIDynamic.h> 8 #include <react/jni/ReadableNativeArray.h> 9 #include <fbjni/detail/Hybrid.h> 10 #include <ReactCommon/TurboModuleUtils.h> 11 #include <jni/JCallback.h> 12 #include <jsi/JSIDynamic.h> 13 #include <fbjni/fbjni.h> 14 #include <jsi/jsi.h> 15 16 #include <utility> 17 #include <tuple> 18 #include <algorithm> 19 20 namespace jni = facebook::jni; 21 namespace jsi = facebook::jsi; 22 namespace react = facebook::react; 23 24 namespace expo { 25 26 jni::local_ref<jni::HybridClass<JavaScriptModuleObject>::jhybriddata> 27 JavaScriptModuleObject::initHybrid(jni::alias_ref<jhybridobject> jThis) { 28 return makeCxxInstance(jThis); 29 } 30 31 void JavaScriptModuleObject::registerNatives() { 32 registerHybrid({ 33 makeNativeMethod("initHybrid", JavaScriptModuleObject::initHybrid), 34 makeNativeMethod("exportConstants", JavaScriptModuleObject::exportConstants), 35 makeNativeMethod("registerSyncFunction", 36 JavaScriptModuleObject::registerSyncFunction), 37 makeNativeMethod("registerAsyncFunction", 38 JavaScriptModuleObject::registerAsyncFunction), 39 makeNativeMethod("registerProperty", 40 JavaScriptModuleObject::registerProperty), 41 }); 42 } 43 44 std::shared_ptr<jsi::Object> JavaScriptModuleObject::getJSIObject(jsi::Runtime &runtime) { 45 if (auto object = jsiObject.lock()) { 46 return object; 47 } 48 49 auto moduleObject = std::make_shared<jsi::Object>(runtime); 50 51 for (const auto &[name, value]: constants) { 52 moduleObject->setProperty( 53 runtime, 54 jsi::String::createFromUtf8(runtime, name), 55 jsi::valueFromDynamic(runtime, value) 56 ); 57 } 58 59 for (auto &[name, property]: properties) { 60 auto &[getter, setter] = property; 61 62 auto descriptor = JavaScriptObject::preparePropertyDescriptor(runtime, 1 << 1 /* enumerable */); 63 descriptor.setProperty(runtime, "get", jsi::Value(runtime, *getter.toJSFunction(runtime, 64 jsiInteropModuleRegistry))); 65 descriptor.setProperty(runtime, "set", jsi::Value(runtime, *setter.toJSFunction(runtime, 66 jsiInteropModuleRegistry))); 67 JavaScriptObject::defineProperty(runtime, moduleObject, name, std::move(descriptor)); 68 } 69 70 for (auto &[name, method]: methodsMetadata) { 71 moduleObject->setProperty( 72 runtime, 73 jsi::String::createFromUtf8(runtime, name), 74 jsi::Value(runtime, *method.toJSFunction(runtime, jsiInteropModuleRegistry)) 75 ); 76 } 77 78 jsiObject = moduleObject; 79 return moduleObject; 80 } 81 82 void JavaScriptModuleObject::exportConstants( 83 jni::alias_ref<react::NativeMap::javaobject> constants 84 ) { 85 auto dynamic = constants->cthis()->consume(); 86 assert(dynamic.isObject()); 87 88 for (const auto &[key, value]: dynamic.items()) { 89 this->constants[key.asString()] = value; 90 } 91 } 92 93 void JavaScriptModuleObject::registerSyncFunction( 94 jni::alias_ref<jstring> name, 95 jint args, 96 jni::alias_ref<jni::JArrayClass<ExpectedType>> expectedArgTypes, 97 jni::alias_ref<JNIFunctionBody::javaobject> body 98 ) { 99 std::string cName = name->toStdString(); 100 101 methodsMetadata.try_emplace( 102 cName, 103 longLivedObjectCollection_, 104 cName, 105 args, 106 false, 107 jni::make_local(expectedArgTypes), 108 jni::make_global(body) 109 ); 110 } 111 112 void JavaScriptModuleObject::registerAsyncFunction( 113 jni::alias_ref<jstring> name, 114 jint args, 115 jni::alias_ref<jni::JArrayClass<ExpectedType>> expectedArgTypes, 116 jni::alias_ref<JNIAsyncFunctionBody::javaobject> body 117 ) { 118 std::string cName = name->toStdString(); 119 120 methodsMetadata.try_emplace( 121 cName, 122 longLivedObjectCollection_, 123 cName, 124 args, 125 true, 126 jni::make_local(expectedArgTypes), 127 jni::make_global(body) 128 ); 129 } 130 131 void JavaScriptModuleObject::registerProperty( 132 jni::alias_ref<jstring> name, 133 jni::alias_ref<ExpectedType> expectedArgType, 134 jni::alias_ref<JNIFunctionBody::javaobject> getter, 135 jni::alias_ref<JNIFunctionBody::javaobject> setter 136 ) { 137 auto cName = name->toStdString(); 138 139 auto getterMetadata = MethodMetadata( 140 longLivedObjectCollection_, 141 cName, 142 0, 143 false, 144 std::vector<std::unique_ptr<AnyType>>(), 145 jni::make_global(getter) 146 ); 147 148 auto types = std::vector<std::unique_ptr<AnyType>>(); 149 types.push_back(std::make_unique<AnyType>(jni::make_local(expectedArgType))); 150 auto setterMetadata = MethodMetadata( 151 longLivedObjectCollection_, 152 cName, 153 1, 154 false, 155 std::move(types), 156 jni::make_global(setter) 157 ); 158 159 auto functions = std::make_pair( 160 std::move(getterMetadata), 161 std::move(setterMetadata) 162 ); 163 164 properties.insert({cName, std::move(functions)}); 165 } 166 167 JavaScriptModuleObject::JavaScriptModuleObject(jni::alias_ref<jhybridobject> jThis) 168 : javaPart_(jni::make_global(jThis)) { 169 longLivedObjectCollection_ = std::make_shared<react::LongLivedObjectCollection>(); 170 } 171 } // namespace expo 172