1 // Copyright © 2021-present 650 Industries, Inc. (aka Expo) 2 3 #pragma once 4 5 #include "JSIObjectWrapper.h" 6 #include "JSITypeConverter.h" 7 #include "JavaScriptRuntime.h" 8 #include "WeakRuntimeHolder.h" 9 #include "JNIFunctionBody.h" 10 11 #include <fbjni/fbjni.h> 12 #include <jsi/jsi.h> 13 14 #include <memory> 15 16 namespace jni = facebook::jni; 17 namespace jsi = facebook::jsi; 18 19 namespace expo { 20 class JavaScriptValue; 21 22 /** 23 * Represents any JavaScript object. Its purpose is to exposes `jsi::Object` API back to Kotlin. 24 */ 25 class JavaScriptObject : public jni::HybridClass<JavaScriptObject>, JSIObjectWrapper { 26 public: 27 static auto constexpr 28 kJavaDescriptor = "Lexpo/modules/kotlin/jni/JavaScriptObject;"; 29 static auto constexpr TAG = "JavaScriptObject"; 30 31 static void registerNatives(); 32 33 JavaScriptObject( 34 std::weak_ptr<JavaScriptRuntime> runtime, 35 std::shared_ptr<jsi::Object> jsObject 36 ); 37 38 JavaScriptObject( 39 WeakRuntimeHolder runtime, 40 std::shared_ptr<jsi::Object> jsObject 41 ); 42 43 std::shared_ptr<jsi::Object> get() override; 44 45 /** 46 * @return a bool whether the object has a property with the given name 47 */ 48 bool hasProperty(const std::string &name); 49 50 /** 51 * @return the property of the object with the given name. 52 * If the name isn't a property on the object, returns the `jsi::Value::undefined` value. 53 */ 54 jsi::Value getProperty(const std::string &name); 55 56 /** 57 * @return a vector consisting of all enumerable property names in the object and its prototype chain. 58 */ 59 std::vector<std::string> getPropertyNames(); 60 61 void setProperty(const std::string &name, jsi::Value value); 62 63 static jsi::Object preparePropertyDescriptor(jsi::Runtime &jsRuntime, int options); 64 65 static void defineProperty( 66 jsi::Runtime &runtime, 67 jsi::Object *jsthis, 68 const std::string &name, 69 jsi::Object descriptor 70 ); 71 72 void defineNativeDeallocator( 73 jni::alias_ref<JNIFunctionBody::javaobject> deallocator 74 ); 75 76 protected: 77 WeakRuntimeHolder runtimeHolder; 78 std::shared_ptr<jsi::Object> jsObject; 79 80 private: 81 friend HybridBase; 82 83 bool jniHasProperty(jni::alias_ref<jstring> name); 84 85 jni::local_ref<jni::HybridClass<JavaScriptValue>::javaobject> jniGetProperty( 86 jni::alias_ref<jstring> name 87 ); 88 89 jni::local_ref<jni::JArrayClass<jstring>> jniGetPropertyNames(); 90 91 /** 92 * Unsets property with the given name. 93 */ 94 void unsetProperty(jni::alias_ref<jstring> name); 95 96 /** 97 * A template to generate different versions of the `setProperty` method based on the `jsi_type_converter` trait. 98 * Those generated methods will be exported and visible in the Kotlin codebase. 99 * On the other hand, we could just make one function that would take a generic Java Object, 100 * but then we would have to decide what to do with it and how to convert it to jsi::Value 101 * in cpp. That would be expensive. So it's easier to ensure that 102 * we call the correct version of `setProperty` in the Kotlin code. 103 * 104 * This template will work only if the jsi_type_converter exists for a given type. 105 */ 106 template< 107 class T, 108 typename = std::enable_if_t<is_jsi_type_converter_defined<T>> 109 > 110 void setProperty(jni::alias_ref<jstring> name, T value) { 111 jsi::Runtime &jsRuntime = runtimeHolder.getJSRuntime(); 112 113 auto cName = name->toStdString(); 114 jsObject->setProperty( 115 jsRuntime, 116 cName.c_str(), 117 jsi_type_converter<T>::convert(jsRuntime, value) 118 ); 119 } 120 121 template< 122 class T, 123 typename = std::enable_if_t<is_jsi_type_converter_defined<T>> 124 > 125 void defineProperty(jni::alias_ref<jstring> name, T value, int options) { 126 jsi::Runtime &jsRuntime = runtimeHolder.getJSRuntime(); 127 128 auto cName = name->toStdString(); 129 jsi::Object descriptor = preparePropertyDescriptor(jsRuntime, options); 130 descriptor.setProperty(jsRuntime, "value", jsi_type_converter<T>::convert(jsRuntime, value)); 131 JavaScriptObject::defineProperty(jsRuntime, jsObject.get(), cName, std::move(descriptor)); 132 } 133 }; 134 } // namespace expo 135