1a416e6dbSŁukasz Kosmaty // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2a416e6dbSŁukasz Kosmaty 
3a416e6dbSŁukasz Kosmaty #include "JavaScriptObject.h"
4a416e6dbSŁukasz Kosmaty #include "JavaScriptValue.h"
5879827bbSŁukasz Kosmaty #include "JavaScriptFunction.h"
614c0f05dSŁukasz Kosmaty #include "JavaScriptRuntime.h"
714c0f05dSŁukasz Kosmaty #include "JSITypeConverter.h"
87cfebc52SŁukasz Kosmaty #include "ObjectDeallocator.h"
97cfebc52SŁukasz Kosmaty #include "JavaReferencesCache.h"
1029e8b6f8SŁukasz Kosmaty #include "JSIInteropModuleRegistry.h"
11a416e6dbSŁukasz Kosmaty 
12a416e6dbSŁukasz Kosmaty namespace expo {
registerNatives()13a416e6dbSŁukasz Kosmaty void JavaScriptObject::registerNatives() {
14a416e6dbSŁukasz Kosmaty   registerHybrid({
15a416e6dbSŁukasz Kosmaty                    makeNativeMethod("hasProperty", JavaScriptObject::jniHasProperty),
16a416e6dbSŁukasz Kosmaty                    makeNativeMethod("getProperty", JavaScriptObject::jniGetProperty),
1714c0f05dSŁukasz Kosmaty                    makeNativeMethod("getPropertyNames", JavaScriptObject::jniGetPropertyNames),
1814c0f05dSŁukasz Kosmaty                    makeNativeMethod("setBoolProperty", JavaScriptObject::setProperty<bool>),
1914c0f05dSŁukasz Kosmaty                    makeNativeMethod("setDoubleProperty", JavaScriptObject::setProperty<double>),
2014c0f05dSŁukasz Kosmaty                    makeNativeMethod("setStringProperty",
2114c0f05dSŁukasz Kosmaty                                     JavaScriptObject::setProperty<jni::alias_ref<jstring>>),
2214c0f05dSŁukasz Kosmaty                    makeNativeMethod("setJSValueProperty",
2314c0f05dSŁukasz Kosmaty                                     JavaScriptObject::setProperty<jni::alias_ref<JavaScriptValue::javaobject>>),
2414c0f05dSŁukasz Kosmaty                    makeNativeMethod("setJSObjectProperty",
2514c0f05dSŁukasz Kosmaty                                     JavaScriptObject::setProperty<jni::alias_ref<JavaScriptObject::javaobject>>),
2614c0f05dSŁukasz Kosmaty                    makeNativeMethod("unsetProperty", JavaScriptObject::unsetProperty),
2764f4fd05SŁukasz Kosmaty                    makeNativeMethod("defineBoolProperty", JavaScriptObject::defineProperty<bool>),
2864f4fd05SŁukasz Kosmaty                    makeNativeMethod("defineDoubleProperty",
2964f4fd05SŁukasz Kosmaty                                     JavaScriptObject::defineProperty<double>),
3064f4fd05SŁukasz Kosmaty                    makeNativeMethod("defineStringProperty",
3164f4fd05SŁukasz Kosmaty                                     JavaScriptObject::defineProperty<jni::alias_ref<jstring>>),
3264f4fd05SŁukasz Kosmaty                    makeNativeMethod("defineJSValueProperty",
3364f4fd05SŁukasz Kosmaty                                     JavaScriptObject::defineProperty<jni::alias_ref<JavaScriptValue::javaobject>>),
3464f4fd05SŁukasz Kosmaty                    makeNativeMethod("defineJSObjectProperty",
3564f4fd05SŁukasz Kosmaty                                     JavaScriptObject::defineProperty<jni::alias_ref<JavaScriptObject::javaobject>>),
367cfebc52SŁukasz Kosmaty                    makeNativeMethod("defineNativeDeallocator",
377cfebc52SŁukasz Kosmaty                                     JavaScriptObject::defineNativeDeallocator),
38a416e6dbSŁukasz Kosmaty                  });
39a416e6dbSŁukasz Kosmaty }
40a416e6dbSŁukasz Kosmaty 
JavaScriptObject(std::weak_ptr<JavaScriptRuntime> runtime,std::shared_ptr<jsi::Object> jsObject)41a416e6dbSŁukasz Kosmaty JavaScriptObject::JavaScriptObject(
42a416e6dbSŁukasz Kosmaty   std::weak_ptr<JavaScriptRuntime> runtime,
43a416e6dbSŁukasz Kosmaty   std::shared_ptr<jsi::Object> jsObject
44a416e6dbSŁukasz Kosmaty ) : runtimeHolder(std::move(runtime)), jsObject(std::move(jsObject)) {
45256b5942SŁukasz Kosmaty   runtimeHolder.ensureRuntimeIsValid();
46256b5942SŁukasz Kosmaty }
47256b5942SŁukasz Kosmaty 
JavaScriptObject(WeakRuntimeHolder runtime,std::shared_ptr<jsi::Object> jsObject)48256b5942SŁukasz Kosmaty JavaScriptObject::JavaScriptObject(
49256b5942SŁukasz Kosmaty   WeakRuntimeHolder runtime,
50256b5942SŁukasz Kosmaty   std::shared_ptr<jsi::Object> jsObject
51256b5942SŁukasz Kosmaty ) : runtimeHolder(std::move(runtime)), jsObject(std::move(jsObject)) {
52256b5942SŁukasz Kosmaty   runtimeHolder.ensureRuntimeIsValid();
53a416e6dbSŁukasz Kosmaty }
54a416e6dbSŁukasz Kosmaty 
get()5514c0f05dSŁukasz Kosmaty std::shared_ptr<jsi::Object> JavaScriptObject::get() {
5614c0f05dSŁukasz Kosmaty   return jsObject;
5714c0f05dSŁukasz Kosmaty }
5814c0f05dSŁukasz Kosmaty 
hasProperty(const std::string & name)59a416e6dbSŁukasz Kosmaty bool JavaScriptObject::hasProperty(const std::string &name) {
60256b5942SŁukasz Kosmaty   auto &jsRuntime = runtimeHolder.getJSRuntime();
61256b5942SŁukasz Kosmaty   return jsObject->hasProperty(jsRuntime, name.c_str());
62a416e6dbSŁukasz Kosmaty }
63a416e6dbSŁukasz Kosmaty 
getProperty(const std::string & name)64a416e6dbSŁukasz Kosmaty jsi::Value JavaScriptObject::getProperty(const std::string &name) {
65256b5942SŁukasz Kosmaty   auto &jsRuntime = runtimeHolder.getJSRuntime();
66256b5942SŁukasz Kosmaty   return jsObject->getProperty(jsRuntime, name.c_str());
67a416e6dbSŁukasz Kosmaty }
68a416e6dbSŁukasz Kosmaty 
jniHasProperty(jni::alias_ref<jstring> name)69a416e6dbSŁukasz Kosmaty bool JavaScriptObject::jniHasProperty(jni::alias_ref<jstring> name) {
70a416e6dbSŁukasz Kosmaty   return hasProperty(name->toStdString());
71a416e6dbSŁukasz Kosmaty }
72a416e6dbSŁukasz Kosmaty 
jniGetProperty(jni::alias_ref<jstring> name)73a416e6dbSŁukasz Kosmaty jni::local_ref<JavaScriptValue::javaobject> JavaScriptObject::jniGetProperty(
74a416e6dbSŁukasz Kosmaty   jni::alias_ref<jstring> name
75a416e6dbSŁukasz Kosmaty ) {
76a416e6dbSŁukasz Kosmaty   auto result = std::make_shared<jsi::Value>(getProperty(name->toStdString()));
7729e8b6f8SŁukasz Kosmaty   return JavaScriptValue::newInstance(
7829e8b6f8SŁukasz Kosmaty     runtimeHolder.getModuleRegistry(),
7929e8b6f8SŁukasz Kosmaty     runtimeHolder,
8029e8b6f8SŁukasz Kosmaty     result
8129e8b6f8SŁukasz Kosmaty   );
82a416e6dbSŁukasz Kosmaty }
83a416e6dbSŁukasz Kosmaty 
getPropertyNames()84a416e6dbSŁukasz Kosmaty std::vector<std::string> JavaScriptObject::getPropertyNames() {
85256b5942SŁukasz Kosmaty   auto &jsRuntime = runtimeHolder.getJSRuntime();
86a416e6dbSŁukasz Kosmaty 
87256b5942SŁukasz Kosmaty   jsi::Array properties = jsObject->getPropertyNames(jsRuntime);
88256b5942SŁukasz Kosmaty   auto size = properties.size(jsRuntime);
89a416e6dbSŁukasz Kosmaty 
90a416e6dbSŁukasz Kosmaty   std::vector<std::string> names(size);
91a416e6dbSŁukasz Kosmaty   for (size_t i = 0; i < size; i++) {
92a416e6dbSŁukasz Kosmaty     auto propertyName = properties
93256b5942SŁukasz Kosmaty       .getValueAtIndex(jsRuntime, i)
94256b5942SŁukasz Kosmaty       .asString(jsRuntime)
95256b5942SŁukasz Kosmaty       .utf8(jsRuntime);
96a416e6dbSŁukasz Kosmaty     names[i] = propertyName;
97a416e6dbSŁukasz Kosmaty   }
98a416e6dbSŁukasz Kosmaty 
99a416e6dbSŁukasz Kosmaty   return names;
100a416e6dbSŁukasz Kosmaty }
101a416e6dbSŁukasz Kosmaty 
jniGetPropertyNames()102a416e6dbSŁukasz Kosmaty jni::local_ref<jni::JArrayClass<jstring>> JavaScriptObject::jniGetPropertyNames() {
103a416e6dbSŁukasz Kosmaty   std::vector<std::string> cResult = getPropertyNames();
104a416e6dbSŁukasz Kosmaty   auto paredResult = jni::JArrayClass<jstring>::newArray(cResult.size());
105a416e6dbSŁukasz Kosmaty   for (size_t i = 0; i < cResult.size(); i++) {
106a416e6dbSŁukasz Kosmaty     paredResult->setElement(i, jni::make_jstring(cResult[i]).get());
107a416e6dbSŁukasz Kosmaty   }
108a416e6dbSŁukasz Kosmaty 
109a416e6dbSŁukasz Kosmaty   return paredResult;
110a416e6dbSŁukasz Kosmaty }
11114c0f05dSŁukasz Kosmaty 
jniAsFunction()112879827bbSŁukasz Kosmaty jni::local_ref<JavaScriptFunction::javaobject> JavaScriptObject::jniAsFunction() {
113879827bbSŁukasz Kosmaty   auto &jsRuntime = runtimeHolder.getJSRuntime();
114879827bbSŁukasz Kosmaty   auto jsFuncion = std::make_shared<jsi::Function>(jsObject->asFunction(jsRuntime));
115*e1f25825SŁukasz Kosmaty   return JavaScriptFunction::newInstance(
116*e1f25825SŁukasz Kosmaty     runtimeHolder.getModuleRegistry(),
117*e1f25825SŁukasz Kosmaty     runtimeHolder,
118*e1f25825SŁukasz Kosmaty     jsFuncion
119*e1f25825SŁukasz Kosmaty   );
120879827bbSŁukasz Kosmaty }
121879827bbSŁukasz Kosmaty 
setProperty(const std::string & name,jsi::Value value)12214c0f05dSŁukasz Kosmaty void JavaScriptObject::setProperty(const std::string &name, jsi::Value value) {
123256b5942SŁukasz Kosmaty   auto &jsRuntime = runtimeHolder.getJSRuntime();
124256b5942SŁukasz Kosmaty   jsObject->setProperty(jsRuntime, name.c_str(), value);
12514c0f05dSŁukasz Kosmaty }
12614c0f05dSŁukasz Kosmaty 
unsetProperty(jni::alias_ref<jstring> name)12714c0f05dSŁukasz Kosmaty void JavaScriptObject::unsetProperty(jni::alias_ref<jstring> name) {
128256b5942SŁukasz Kosmaty   auto &jsRuntime = runtimeHolder.getJSRuntime();
12914c0f05dSŁukasz Kosmaty   auto cName = name->toStdString();
13014c0f05dSŁukasz Kosmaty   jsObject->setProperty(
131256b5942SŁukasz Kosmaty     jsRuntime,
13214c0f05dSŁukasz Kosmaty     cName.c_str(),
13314c0f05dSŁukasz Kosmaty     jsi::Value::undefined()
13414c0f05dSŁukasz Kosmaty   );
13514c0f05dSŁukasz Kosmaty }
13664f4fd05SŁukasz Kosmaty 
preparePropertyDescriptor(jsi::Runtime & jsRuntime,int options)13764f4fd05SŁukasz Kosmaty jsi::Object JavaScriptObject::preparePropertyDescriptor(
13864f4fd05SŁukasz Kosmaty   jsi::Runtime &jsRuntime,
13964f4fd05SŁukasz Kosmaty   int options
14064f4fd05SŁukasz Kosmaty ) {
14164f4fd05SŁukasz Kosmaty   jsi::Object descriptor(jsRuntime);
14264f4fd05SŁukasz Kosmaty   descriptor.setProperty(jsRuntime, "configurable", (bool) ((1 << 0) & options));
14364f4fd05SŁukasz Kosmaty   descriptor.setProperty(jsRuntime, "enumerable", (bool) ((1 << 1) & options));
144cf4fff55SŁukasz Kosmaty   if ((bool) (1 << 2 & options)) {
145cf4fff55SŁukasz Kosmaty     descriptor.setProperty(jsRuntime, "writable", true);
146cf4fff55SŁukasz Kosmaty   }
14764f4fd05SŁukasz Kosmaty   return descriptor;
14864f4fd05SŁukasz Kosmaty }
149b627df43SŁukasz Kosmaty 
newInstance(JSIInteropModuleRegistry * jsiInteropModuleRegistry,std::weak_ptr<JavaScriptRuntime> runtime,std::shared_ptr<jsi::Object> jsObject)15029e8b6f8SŁukasz Kosmaty jni::local_ref<JavaScriptObject::javaobject> JavaScriptObject::newInstance(
15129e8b6f8SŁukasz Kosmaty   JSIInteropModuleRegistry *jsiInteropModuleRegistry,
15229e8b6f8SŁukasz Kosmaty   std::weak_ptr<JavaScriptRuntime> runtime,
15329e8b6f8SŁukasz Kosmaty   std::shared_ptr<jsi::Object> jsObject
15429e8b6f8SŁukasz Kosmaty ) {
15529e8b6f8SŁukasz Kosmaty   auto object = JavaScriptObject::newObjectCxxArgs(std::move(runtime), std::move(jsObject));
15629e8b6f8SŁukasz Kosmaty   jsiInteropModuleRegistry->jniDeallocator->addReference(object);
15729e8b6f8SŁukasz Kosmaty   return object;
15829e8b6f8SŁukasz Kosmaty }
15929e8b6f8SŁukasz Kosmaty 
defineNativeDeallocator(jni::alias_ref<JNIFunctionBody::javaobject> deallocator)1607cfebc52SŁukasz Kosmaty void JavaScriptObject::defineNativeDeallocator(
1617cfebc52SŁukasz Kosmaty   jni::alias_ref<JNIFunctionBody::javaobject> deallocator
1627cfebc52SŁukasz Kosmaty ) {
1637cfebc52SŁukasz Kosmaty   auto &rt = runtimeHolder.getJSRuntime();
1647cfebc52SŁukasz Kosmaty   jni::global_ref<JNIFunctionBody::javaobject> globalRef = jni::make_global(deallocator);
165*e1f25825SŁukasz Kosmaty 
166*e1f25825SŁukasz Kosmaty   common::setDeallocator(
167*e1f25825SŁukasz Kosmaty     rt,
168*e1f25825SŁukasz Kosmaty     jsObject,
1697cfebc52SŁukasz Kosmaty     [globalRef = std::move(globalRef)]() mutable {
1707cfebc52SŁukasz Kosmaty       auto args = jni::Environment::current()->NewObjectArray(
1717cfebc52SŁukasz Kosmaty         0,
1727cfebc52SŁukasz Kosmaty         JavaReferencesCache::instance()->getJClass("java/lang/Object").clazz,
1737cfebc52SŁukasz Kosmaty         nullptr
1747cfebc52SŁukasz Kosmaty       );
1757cfebc52SŁukasz Kosmaty       globalRef->invoke(args);
1767cfebc52SŁukasz Kosmaty       globalRef.reset();
177*e1f25825SŁukasz Kosmaty     },
178*e1f25825SŁukasz Kosmaty     "__expo_shared_object_deallocator__"
179*e1f25825SŁukasz Kosmaty   );
1807cfebc52SŁukasz Kosmaty }
181a416e6dbSŁukasz Kosmaty } // namespace expo
182