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(jni::alias_ref<react::NativeMap::javaobject> 53 constants) { 54 auto dynamic = constants->cthis()->consume(); 55 assert(dynamic.isObject()); 56 57 for (const auto &[key, value]: dynamic.items()) { 58 this->constants[key.asString()] = value; 59 } 60 } 61 62 void JavaScriptModuleObject::registerSyncFunction( 63 jni::alias_ref<jstring> name, 64 jint args, 65 jni::alias_ref<JNIFunctionBody::javaobject> body 66 ) { 67 auto cName = name->toStdString(); 68 methodsMetadata.try_emplace(cName, cName, args, false, jni::make_global(body)); 69 } 70 71 void JavaScriptModuleObject::registerAsyncFunction( 72 jni::alias_ref<jstring> name, 73 jint args, 74 jni::alias_ref<JNIAsyncFunctionBody::javaobject> body 75 ) { 76 auto cName = name->toStdString(); 77 methodsMetadata.try_emplace(cName, cName, args, true, jni::make_global(body)); 78 } 79 80 JavaScriptModuleObject::HostObject::HostObject( 81 JavaScriptModuleObject *jsModule) : jsModule(jsModule) {} 82 83 jsi::Value JavaScriptModuleObject::HostObject::get(jsi::Runtime &runtime, 84 const jsi::PropNameID &name) { 85 auto cName = name.utf8(runtime); 86 87 auto constantsRecord = jsModule->constants.find(cName); 88 if (constantsRecord != jsModule->constants.end()) { 89 auto dynamic = constantsRecord->second; 90 return jsi::valueFromDynamic(runtime, dynamic); 91 } 92 93 auto metadataRecord = jsModule->methodsMetadata.find(cName); 94 if (metadataRecord == jsModule->methodsMetadata.end()) { 95 return jsi::Value::undefined(); 96 } 97 auto &metadata = metadataRecord->second; 98 return jsi::Value(runtime, *metadata.toJSFunction(runtime, jsModule->jsiInteropModuleRegistry)); 99 } 100 101 void JavaScriptModuleObject::HostObject::set( 102 jsi::Runtime &runtime, 103 const jsi::PropNameID &name, 104 const jsi::Value &value 105 ) { 106 throw jsi::JSError( 107 runtime, 108 "RuntimeError: Cannot override the host object for expo module '" + name.utf8(runtime) + "'" 109 ); 110 } 111 112 std::vector<jsi::PropNameID> JavaScriptModuleObject::HostObject::getPropertyNames( 113 jsi::Runtime &rt 114 ) { 115 auto &metadata = jsModule->methodsMetadata; 116 std::vector<jsi::PropNameID> result; 117 std::transform( 118 metadata.begin(), 119 metadata.end(), 120 std::back_inserter(result), 121 [&rt](const auto &kv) { 122 return jsi::PropNameID::forUtf8(rt, kv.first); 123 } 124 ); 125 126 auto &constants = jsModule->constants; 127 std::transform( 128 constants.begin(), 129 constants.end(), 130 std::back_inserter(result), 131 [&rt](const auto &kv) { 132 return jsi::PropNameID::forUtf8(rt, kv.first); 133 } 134 ); 135 136 return result; 137 } 138 } // namespace expo 139