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 }); 40 } 41 42 std::shared_ptr<jsi::Object> JavaScriptModuleObject::getJSIObject(jsi::Runtime &runtime) { 43 if (jsiObject == nullptr) { 44 auto hostObject = std::make_shared<JavaScriptModuleObject::HostObject>(this); 45 jsiObject = std::make_shared<jsi::Object>( 46 jsi::Object::createFromHostObject(runtime, hostObject)); 47 } 48 49 return jsiObject; 50 } 51 52 void JavaScriptModuleObject::exportConstants( 53 jni::alias_ref<react::NativeMap::javaobject> constants 54 ) { 55 auto dynamic = constants->cthis()->consume(); 56 assert(dynamic.isObject()); 57 58 for (const auto &[key, value]: dynamic.items()) { 59 this->constants[key.asString()] = value; 60 } 61 } 62 63 void JavaScriptModuleObject::registerSyncFunction( 64 jni::alias_ref<jstring> name, 65 jint args, 66 jni::alias_ref<jni::JArrayInt> desiredTypes, 67 jni::alias_ref<JNIFunctionBody::javaobject> body 68 ) { 69 std::string cName = name->toStdString(); 70 std::unique_ptr<int[]> types = desiredTypes->getRegion(0, args); 71 72 methodsMetadata.try_emplace( 73 cName, 74 cName, 75 args, 76 false, 77 std::move(types), 78 jni::make_global(body) 79 ); 80 } 81 82 void JavaScriptModuleObject::registerAsyncFunction( 83 jni::alias_ref<jstring> name, 84 jint args, 85 jni::alias_ref<jni::JArrayInt> desiredTypes, 86 jni::alias_ref<JNIAsyncFunctionBody::javaobject> body 87 ) { 88 auto cName = name->toStdString(); 89 std::unique_ptr<int[]> types = desiredTypes->getRegion(0, args); 90 91 methodsMetadata.try_emplace( 92 cName, 93 cName, 94 args, 95 true, 96 std::move(types), 97 jni::make_global(body) 98 ); 99 } 100 101 JavaScriptModuleObject::HostObject::HostObject( 102 JavaScriptModuleObject *jsModule) : jsModule(jsModule) {} 103 104 jsi::Value JavaScriptModuleObject::HostObject::get(jsi::Runtime &runtime, 105 const jsi::PropNameID &name) { 106 auto cName = name.utf8(runtime); 107 108 auto constantsRecord = jsModule->constants.find(cName); 109 if (constantsRecord != jsModule->constants.end()) { 110 auto dynamic = constantsRecord->second; 111 return jsi::valueFromDynamic(runtime, dynamic); 112 } 113 114 auto metadataRecord = jsModule->methodsMetadata.find(cName); 115 if (metadataRecord == jsModule->methodsMetadata.end()) { 116 return jsi::Value::undefined(); 117 } 118 auto &metadata = metadataRecord->second; 119 return jsi::Value(runtime, *metadata.toJSFunction(runtime, jsModule->jsiInteropModuleRegistry)); 120 } 121 122 void JavaScriptModuleObject::HostObject::set( 123 jsi::Runtime &runtime, 124 const jsi::PropNameID &name, 125 const jsi::Value &value 126 ) { 127 throw jsi::JSError( 128 runtime, 129 "RuntimeError: Cannot override the host object for expo module '" + name.utf8(runtime) + "'" 130 ); 131 } 132 133 std::vector<jsi::PropNameID> JavaScriptModuleObject::HostObject::getPropertyNames( 134 jsi::Runtime &rt 135 ) { 136 auto &metadata = jsModule->methodsMetadata; 137 std::vector<jsi::PropNameID> result; 138 std::transform( 139 metadata.begin(), 140 metadata.end(), 141 std::back_inserter(result), 142 [&rt](const auto &kv) { 143 return jsi::PropNameID::forUtf8(rt, kv.first); 144 } 145 ); 146 147 auto &constants = jsModule->constants; 148 std::transform( 149 constants.begin(), 150 constants.end(), 151 std::back_inserter(result), 152 [&rt](const auto &kv) { 153 return jsi::PropNameID::forUtf8(rt, kv.first); 154 } 155 ); 156 157 return result; 158 } 159 } // namespace expo 160