1 // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2 
3 #include "JavaScriptObject.h"
4 #include "JavaScriptValue.h"
5 #include "JavaScriptFunction.h"
6 #include "JavaScriptRuntime.h"
7 #include "JSITypeConverter.h"
8 #include "ObjectDeallocator.h"
9 #include "JavaReferencesCache.h"
10 #include "JSIInteropModuleRegistry.h"
11 
12 namespace expo {
registerNatives()13 void JavaScriptObject::registerNatives() {
14   registerHybrid({
15                    makeNativeMethod("hasProperty", JavaScriptObject::jniHasProperty),
16                    makeNativeMethod("getProperty", JavaScriptObject::jniGetProperty),
17                    makeNativeMethod("getPropertyNames", JavaScriptObject::jniGetPropertyNames),
18                    makeNativeMethod("setBoolProperty", JavaScriptObject::setProperty<bool>),
19                    makeNativeMethod("setDoubleProperty", JavaScriptObject::setProperty<double>),
20                    makeNativeMethod("setStringProperty",
21                                     JavaScriptObject::setProperty<jni::alias_ref<jstring>>),
22                    makeNativeMethod("setJSValueProperty",
23                                     JavaScriptObject::setProperty<jni::alias_ref<JavaScriptValue::javaobject>>),
24                    makeNativeMethod("setJSObjectProperty",
25                                     JavaScriptObject::setProperty<jni::alias_ref<JavaScriptObject::javaobject>>),
26                    makeNativeMethod("unsetProperty", JavaScriptObject::unsetProperty),
27                    makeNativeMethod("defineBoolProperty", JavaScriptObject::defineProperty<bool>),
28                    makeNativeMethod("defineDoubleProperty",
29                                     JavaScriptObject::defineProperty<double>),
30                    makeNativeMethod("defineStringProperty",
31                                     JavaScriptObject::defineProperty<jni::alias_ref<jstring>>),
32                    makeNativeMethod("defineJSValueProperty",
33                                     JavaScriptObject::defineProperty<jni::alias_ref<JavaScriptValue::javaobject>>),
34                    makeNativeMethod("defineJSObjectProperty",
35                                     JavaScriptObject::defineProperty<jni::alias_ref<JavaScriptObject::javaobject>>),
36                    makeNativeMethod("defineNativeDeallocator",
37                                     JavaScriptObject::defineNativeDeallocator),
38                  });
39 }
40 
JavaScriptObject(std::weak_ptr<JavaScriptRuntime> runtime,std::shared_ptr<jsi::Object> jsObject)41 JavaScriptObject::JavaScriptObject(
42   std::weak_ptr<JavaScriptRuntime> runtime,
43   std::shared_ptr<jsi::Object> jsObject
44 ) : runtimeHolder(std::move(runtime)), jsObject(std::move(jsObject)) {
45   runtimeHolder.ensureRuntimeIsValid();
46 }
47 
JavaScriptObject(WeakRuntimeHolder runtime,std::shared_ptr<jsi::Object> jsObject)48 JavaScriptObject::JavaScriptObject(
49   WeakRuntimeHolder runtime,
50   std::shared_ptr<jsi::Object> jsObject
51 ) : runtimeHolder(std::move(runtime)), jsObject(std::move(jsObject)) {
52   runtimeHolder.ensureRuntimeIsValid();
53 }
54 
get()55 std::shared_ptr<jsi::Object> JavaScriptObject::get() {
56   return jsObject;
57 }
58 
hasProperty(const std::string & name)59 bool JavaScriptObject::hasProperty(const std::string &name) {
60   auto &jsRuntime = runtimeHolder.getJSRuntime();
61   return jsObject->hasProperty(jsRuntime, name.c_str());
62 }
63 
getProperty(const std::string & name)64 jsi::Value JavaScriptObject::getProperty(const std::string &name) {
65   auto &jsRuntime = runtimeHolder.getJSRuntime();
66   return jsObject->getProperty(jsRuntime, name.c_str());
67 }
68 
jniHasProperty(jni::alias_ref<jstring> name)69 bool JavaScriptObject::jniHasProperty(jni::alias_ref<jstring> name) {
70   return hasProperty(name->toStdString());
71 }
72 
jniGetProperty(jni::alias_ref<jstring> name)73 jni::local_ref<JavaScriptValue::javaobject> JavaScriptObject::jniGetProperty(
74   jni::alias_ref<jstring> name
75 ) {
76   auto result = std::make_shared<jsi::Value>(getProperty(name->toStdString()));
77   return JavaScriptValue::newInstance(
78     runtimeHolder.getModuleRegistry(),
79     runtimeHolder,
80     result
81   );
82 }
83 
getPropertyNames()84 std::vector<std::string> JavaScriptObject::getPropertyNames() {
85   auto &jsRuntime = runtimeHolder.getJSRuntime();
86 
87   jsi::Array properties = jsObject->getPropertyNames(jsRuntime);
88   auto size = properties.size(jsRuntime);
89 
90   std::vector<std::string> names(size);
91   for (size_t i = 0; i < size; i++) {
92     auto propertyName = properties
93       .getValueAtIndex(jsRuntime, i)
94       .asString(jsRuntime)
95       .utf8(jsRuntime);
96     names[i] = propertyName;
97   }
98 
99   return names;
100 }
101 
jniGetPropertyNames()102 jni::local_ref<jni::JArrayClass<jstring>> JavaScriptObject::jniGetPropertyNames() {
103   std::vector<std::string> cResult = getPropertyNames();
104   auto paredResult = jni::JArrayClass<jstring>::newArray(cResult.size());
105   for (size_t i = 0; i < cResult.size(); i++) {
106     paredResult->setElement(i, jni::make_jstring(cResult[i]).get());
107   }
108 
109   return paredResult;
110 }
111 
jniAsFunction()112 jni::local_ref<JavaScriptFunction::javaobject> JavaScriptObject::jniAsFunction() {
113   auto &jsRuntime = runtimeHolder.getJSRuntime();
114   auto jsFuncion = std::make_shared<jsi::Function>(jsObject->asFunction(jsRuntime));
115   return JavaScriptFunction::newInstance(
116     runtimeHolder.getModuleRegistry(),
117     runtimeHolder,
118     jsFuncion
119   );
120 }
121 
setProperty(const std::string & name,jsi::Value value)122 void JavaScriptObject::setProperty(const std::string &name, jsi::Value value) {
123   auto &jsRuntime = runtimeHolder.getJSRuntime();
124   jsObject->setProperty(jsRuntime, name.c_str(), value);
125 }
126 
unsetProperty(jni::alias_ref<jstring> name)127 void JavaScriptObject::unsetProperty(jni::alias_ref<jstring> name) {
128   auto &jsRuntime = runtimeHolder.getJSRuntime();
129   auto cName = name->toStdString();
130   jsObject->setProperty(
131     jsRuntime,
132     cName.c_str(),
133     jsi::Value::undefined()
134   );
135 }
136 
preparePropertyDescriptor(jsi::Runtime & jsRuntime,int options)137 jsi::Object JavaScriptObject::preparePropertyDescriptor(
138   jsi::Runtime &jsRuntime,
139   int options
140 ) {
141   jsi::Object descriptor(jsRuntime);
142   descriptor.setProperty(jsRuntime, "configurable", (bool) ((1 << 0) & options));
143   descriptor.setProperty(jsRuntime, "enumerable", (bool) ((1 << 1) & options));
144   if ((bool) (1 << 2 & options)) {
145     descriptor.setProperty(jsRuntime, "writable", true);
146   }
147   return descriptor;
148 }
149 
newInstance(JSIInteropModuleRegistry * jsiInteropModuleRegistry,std::weak_ptr<JavaScriptRuntime> runtime,std::shared_ptr<jsi::Object> jsObject)150 jni::local_ref<JavaScriptObject::javaobject> JavaScriptObject::newInstance(
151   JSIInteropModuleRegistry *jsiInteropModuleRegistry,
152   std::weak_ptr<JavaScriptRuntime> runtime,
153   std::shared_ptr<jsi::Object> jsObject
154 ) {
155   auto object = JavaScriptObject::newObjectCxxArgs(std::move(runtime), std::move(jsObject));
156   jsiInteropModuleRegistry->jniDeallocator->addReference(object);
157   return object;
158 }
159 
defineNativeDeallocator(jni::alias_ref<JNIFunctionBody::javaobject> deallocator)160 void JavaScriptObject::defineNativeDeallocator(
161   jni::alias_ref<JNIFunctionBody::javaobject> deallocator
162 ) {
163   auto &rt = runtimeHolder.getJSRuntime();
164   jni::global_ref<JNIFunctionBody::javaobject> globalRef = jni::make_global(deallocator);
165 
166   common::setDeallocator(
167     rt,
168     jsObject,
169     [globalRef = std::move(globalRef)]() mutable {
170       auto args = jni::Environment::current()->NewObjectArray(
171         0,
172         JavaReferencesCache::instance()->getJClass("java/lang/Object").clazz,
173         nullptr
174       );
175       globalRef->invoke(args);
176       globalRef.reset();
177     },
178     "__expo_shared_object_deallocator__"
179   );
180 }
181 } // namespace expo
182