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("registerSyncFunction", 35 JavaScriptModuleObject::registerSyncFunction), 36 makeNativeMethod("registerAsyncFunction", 37 JavaScriptModuleObject::registerAsyncFunction), 38 }); 39 } 40 41 std::shared_ptr<jsi::Object> JavaScriptModuleObject::getJSIObject(jsi::Runtime &runtime) { 42 if (jsiObject == nullptr) { 43 auto hostObject = std::make_shared<JavaScriptModuleObject::HostObject>(this); 44 jsiObject = std::make_shared<jsi::Object>( 45 jsi::Object::createFromHostObject(runtime, hostObject)); 46 } 47 48 return jsiObject; 49 } 50 51 void JavaScriptModuleObject::registerSyncFunction( 52 jni::alias_ref<jstring> name, 53 jint args, 54 jni::alias_ref<JNIFunctionBody::javaobject> body 55 ) { 56 auto cName = name->toStdString(); 57 methodsMetadata.try_emplace(cName, cName, args, false, jni::make_global(body)); 58 } 59 60 void JavaScriptModuleObject::registerAsyncFunction( 61 jni::alias_ref<jstring> name, 62 jint args, 63 jni::alias_ref<JNIAsyncFunctionBody::javaobject> body 64 ) { 65 auto cName = name->toStdString(); 66 methodsMetadata.try_emplace(cName, cName, args, true, jni::make_global(body)); 67 } 68 69 JavaScriptModuleObject::HostObject::HostObject( 70 JavaScriptModuleObject *jsModule) : jsModule(jsModule) {} 71 72 jsi::Value JavaScriptModuleObject::HostObject::get(jsi::Runtime &runtime, 73 const jsi::PropNameID &name) { 74 auto cName = name.utf8(runtime); 75 auto metadataRecord = jsModule->methodsMetadata.find(cName); 76 if (metadataRecord == jsModule->methodsMetadata.end()) { 77 return jsi::Value::undefined(); 78 } 79 auto &metadata = metadataRecord->second; 80 return jsi::Value(runtime, *metadata.toJSFunction(runtime, jsModule->jsiInteropModuleRegistry)); 81 } 82 83 void JavaScriptModuleObject::HostObject::set( 84 jsi::Runtime &runtime, 85 const jsi::PropNameID &name, 86 const jsi::Value &value 87 ) { 88 throw jsi::JSError( 89 runtime, 90 "RuntimeError: Cannot override the host object for expo module '" + name.utf8(runtime) + "'" 91 ); 92 } 93 94 std::vector<jsi::PropNameID> JavaScriptModuleObject::HostObject::getPropertyNames( 95 jsi::Runtime &rt 96 ) { 97 auto &metadata = jsModule->methodsMetadata; 98 std::vector<jsi::PropNameID> result; 99 std::transform( 100 metadata.begin(), 101 metadata.end(), 102 std::back_inserter(result), 103 [&rt](const auto &kv) { 104 return jsi::PropNameID::forUtf8(rt, kv.first); 105 } 106 ); 107 108 return result; 109 } 110 } // namespace expo 111