1a416e6dbSŁukasz Kosmaty // Copyright © 2021-present 650 Industries, Inc. (aka Expo) 2a416e6dbSŁukasz Kosmaty 3a416e6dbSŁukasz Kosmaty #pragma once 4a416e6dbSŁukasz Kosmaty 514c0f05dSŁukasz Kosmaty #include "JSIObjectWrapper.h" 614c0f05dSŁukasz Kosmaty #include "JSITypeConverter.h" 714c0f05dSŁukasz Kosmaty #include "JavaScriptRuntime.h" 8256b5942SŁukasz Kosmaty #include "WeakRuntimeHolder.h" 97cfebc52SŁukasz Kosmaty #include "JNIFunctionBody.h" 1029e8b6f8SŁukasz Kosmaty #include "JNIDeallocator.h" 11*e1f25825SŁukasz Kosmaty #include "JSIUtils.h" 1214c0f05dSŁukasz Kosmaty 13a416e6dbSŁukasz Kosmaty #include <fbjni/fbjni.h> 14a416e6dbSŁukasz Kosmaty #include <jsi/jsi.h> 15a416e6dbSŁukasz Kosmaty 16a416e6dbSŁukasz Kosmaty #include <memory> 17a416e6dbSŁukasz Kosmaty 18a416e6dbSŁukasz Kosmaty namespace jni = facebook::jni; 19a416e6dbSŁukasz Kosmaty namespace jsi = facebook::jsi; 20a416e6dbSŁukasz Kosmaty 21a416e6dbSŁukasz Kosmaty namespace expo { 22a416e6dbSŁukasz Kosmaty class JavaScriptValue; 23a416e6dbSŁukasz Kosmaty 24879827bbSŁukasz Kosmaty class JavaScriptFunction; 25879827bbSŁukasz Kosmaty 26a416e6dbSŁukasz Kosmaty /** 27a416e6dbSŁukasz Kosmaty * Represents any JavaScript object. Its purpose is to exposes `jsi::Object` API back to Kotlin. 28a416e6dbSŁukasz Kosmaty */ 2929e8b6f8SŁukasz Kosmaty class JavaScriptObject : public jni::HybridClass<JavaScriptObject, Destructible>, JSIObjectWrapper { 30a416e6dbSŁukasz Kosmaty public: 31a416e6dbSŁukasz Kosmaty static auto constexpr 32a416e6dbSŁukasz Kosmaty kJavaDescriptor = "Lexpo/modules/kotlin/jni/JavaScriptObject;"; 33a416e6dbSŁukasz Kosmaty static auto constexpr TAG = "JavaScriptObject"; 34a416e6dbSŁukasz Kosmaty 35a416e6dbSŁukasz Kosmaty static void registerNatives(); 36a416e6dbSŁukasz Kosmaty 3729e8b6f8SŁukasz Kosmaty static jni::local_ref<JavaScriptObject::javaobject> newInstance( 3829e8b6f8SŁukasz Kosmaty JSIInteropModuleRegistry *jsiInteropModuleRegistry, 3929e8b6f8SŁukasz Kosmaty std::weak_ptr<JavaScriptRuntime> runtime, 4029e8b6f8SŁukasz Kosmaty std::shared_ptr<jsi::Object> jsObject 4129e8b6f8SŁukasz Kosmaty ); 4229e8b6f8SŁukasz Kosmaty 43a416e6dbSŁukasz Kosmaty JavaScriptObject( 44a416e6dbSŁukasz Kosmaty std::weak_ptr<JavaScriptRuntime> runtime, 45a416e6dbSŁukasz Kosmaty std::shared_ptr<jsi::Object> jsObject 46a416e6dbSŁukasz Kosmaty ); 47a416e6dbSŁukasz Kosmaty 48256b5942SŁukasz Kosmaty JavaScriptObject( 49256b5942SŁukasz Kosmaty WeakRuntimeHolder runtime, 50256b5942SŁukasz Kosmaty std::shared_ptr<jsi::Object> jsObject 51256b5942SŁukasz Kosmaty ); 52256b5942SŁukasz Kosmaty 5314c0f05dSŁukasz Kosmaty std::shared_ptr<jsi::Object> get() override; 5414c0f05dSŁukasz Kosmaty 55a416e6dbSŁukasz Kosmaty /** 56a416e6dbSŁukasz Kosmaty * @return a bool whether the object has a property with the given name 57a416e6dbSŁukasz Kosmaty */ 58a416e6dbSŁukasz Kosmaty bool hasProperty(const std::string &name); 59a416e6dbSŁukasz Kosmaty 60a416e6dbSŁukasz Kosmaty /** 61a416e6dbSŁukasz Kosmaty * @return the property of the object with the given name. 62a416e6dbSŁukasz Kosmaty * If the name isn't a property on the object, returns the `jsi::Value::undefined` value. 63a416e6dbSŁukasz Kosmaty */ 64a416e6dbSŁukasz Kosmaty jsi::Value getProperty(const std::string &name); 65a416e6dbSŁukasz Kosmaty 66a416e6dbSŁukasz Kosmaty /** 67a416e6dbSŁukasz Kosmaty * @return a vector consisting of all enumerable property names in the object and its prototype chain. 68a416e6dbSŁukasz Kosmaty */ 69a416e6dbSŁukasz Kosmaty std::vector<std::string> getPropertyNames(); 70a416e6dbSŁukasz Kosmaty 7114c0f05dSŁukasz Kosmaty void setProperty(const std::string &name, jsi::Value value); 7214c0f05dSŁukasz Kosmaty 73dedc0ffdSŁukasz Kosmaty static jsi::Object preparePropertyDescriptor(jsi::Runtime &jsRuntime, int options); 74dedc0ffdSŁukasz Kosmaty 757cfebc52SŁukasz Kosmaty void defineNativeDeallocator( 767cfebc52SŁukasz Kosmaty jni::alias_ref<JNIFunctionBody::javaobject> deallocator 777cfebc52SŁukasz Kosmaty ); 787cfebc52SŁukasz Kosmaty 7905c5e37dSŁukasz Kosmaty protected: 80256b5942SŁukasz Kosmaty WeakRuntimeHolder runtimeHolder; 81a416e6dbSŁukasz Kosmaty std::shared_ptr<jsi::Object> jsObject; 82a416e6dbSŁukasz Kosmaty 8305c5e37dSŁukasz Kosmaty private: 8405c5e37dSŁukasz Kosmaty friend HybridBase; 8505c5e37dSŁukasz Kosmaty 86a416e6dbSŁukasz Kosmaty bool jniHasProperty(jni::alias_ref<jstring> name); 87a416e6dbSŁukasz Kosmaty 8829e8b6f8SŁukasz Kosmaty jni::local_ref<jni::HybridClass<JavaScriptValue, Destructible>::javaobject> jniGetProperty( 89a416e6dbSŁukasz Kosmaty jni::alias_ref<jstring> name 90a416e6dbSŁukasz Kosmaty ); 91a416e6dbSŁukasz Kosmaty 92a416e6dbSŁukasz Kosmaty jni::local_ref<jni::JArrayClass<jstring>> jniGetPropertyNames(); 9314c0f05dSŁukasz Kosmaty 9429e8b6f8SŁukasz Kosmaty jni::local_ref<jni::HybridClass<JavaScriptFunction, Destructible>::javaobject> jniAsFunction(); 95879827bbSŁukasz Kosmaty 9614c0f05dSŁukasz Kosmaty /** 9714c0f05dSŁukasz Kosmaty * Unsets property with the given name. 9814c0f05dSŁukasz Kosmaty */ 9914c0f05dSŁukasz Kosmaty void unsetProperty(jni::alias_ref<jstring> name); 10014c0f05dSŁukasz Kosmaty 10114c0f05dSŁukasz Kosmaty /** 10214c0f05dSŁukasz Kosmaty * A template to generate different versions of the `setProperty` method based on the `jsi_type_converter` trait. 10314c0f05dSŁukasz Kosmaty * Those generated methods will be exported and visible in the Kotlin codebase. 10414c0f05dSŁukasz Kosmaty * On the other hand, we could just make one function that would take a generic Java Object, 10514c0f05dSŁukasz Kosmaty * but then we would have to decide what to do with it and how to convert it to jsi::Value 10614c0f05dSŁukasz Kosmaty * in cpp. That would be expensive. So it's easier to ensure that 10714c0f05dSŁukasz Kosmaty * we call the correct version of `setProperty` in the Kotlin code. 10814c0f05dSŁukasz Kosmaty * 10914c0f05dSŁukasz Kosmaty * This template will work only if the jsi_type_converter exists for a given type. 11014c0f05dSŁukasz Kosmaty */ 11114c0f05dSŁukasz Kosmaty template< 11214c0f05dSŁukasz Kosmaty class T, 11314c0f05dSŁukasz Kosmaty typename = std::enable_if_t<is_jsi_type_converter_defined<T>> 11414c0f05dSŁukasz Kosmaty > setProperty(jni::alias_ref<jstring> name,T value)11514c0f05dSŁukasz Kosmaty void setProperty(jni::alias_ref<jstring> name, T value) { 116256b5942SŁukasz Kosmaty jsi::Runtime &jsRuntime = runtimeHolder.getJSRuntime(); 11714c0f05dSŁukasz Kosmaty 118256b5942SŁukasz Kosmaty auto cName = name->toStdString(); 11914c0f05dSŁukasz Kosmaty jsObject->setProperty( 120256b5942SŁukasz Kosmaty jsRuntime, 12114c0f05dSŁukasz Kosmaty cName.c_str(), 122256b5942SŁukasz Kosmaty jsi_type_converter<T>::convert(jsRuntime, value) 12314c0f05dSŁukasz Kosmaty ); 12414c0f05dSŁukasz Kosmaty } 12564f4fd05SŁukasz Kosmaty 12664f4fd05SŁukasz Kosmaty template< 12764f4fd05SŁukasz Kosmaty class T, 12864f4fd05SŁukasz Kosmaty typename = std::enable_if_t<is_jsi_type_converter_defined<T>> 12964f4fd05SŁukasz Kosmaty > defineProperty(jni::alias_ref<jstring> name,T value,int options)13064f4fd05SŁukasz Kosmaty void defineProperty(jni::alias_ref<jstring> name, T value, int options) { 131256b5942SŁukasz Kosmaty jsi::Runtime &jsRuntime = runtimeHolder.getJSRuntime(); 13264f4fd05SŁukasz Kosmaty 13364f4fd05SŁukasz Kosmaty auto cName = name->toStdString(); 13464f4fd05SŁukasz Kosmaty jsi::Object descriptor = preparePropertyDescriptor(jsRuntime, options); 13564f4fd05SŁukasz Kosmaty descriptor.setProperty(jsRuntime, "value", jsi_type_converter<T>::convert(jsRuntime, value)); 136*e1f25825SŁukasz Kosmaty common::definePropertyOnJSIObject(jsRuntime, jsObject.get(), cName.c_str(), std::move(descriptor)); 13764f4fd05SŁukasz Kosmaty } 138a416e6dbSŁukasz Kosmaty }; 139a416e6dbSŁukasz Kosmaty } // namespace expo 140