1 // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2 
3 #include "JavaScriptObject.h"
4 #include "JavaScriptValue.h"
5 #include "JavaScriptRuntime.h"
6 #include "JSITypeConverter.h"
7 
8 namespace expo {
9 void JavaScriptObject::registerNatives() {
10   registerHybrid({
11                    makeNativeMethod("hasProperty", JavaScriptObject::jniHasProperty),
12                    makeNativeMethod("getProperty", JavaScriptObject::jniGetProperty),
13                    makeNativeMethod("getPropertyNames", JavaScriptObject::jniGetPropertyNames),
14                    makeNativeMethod("setBoolProperty", JavaScriptObject::setProperty<bool>),
15                    makeNativeMethod("setDoubleProperty", JavaScriptObject::setProperty<double>),
16                    makeNativeMethod("setStringProperty",
17                                     JavaScriptObject::setProperty<jni::alias_ref<jstring>>),
18                    makeNativeMethod("setJSValueProperty",
19                                     JavaScriptObject::setProperty<jni::alias_ref<JavaScriptValue::javaobject>>),
20                    makeNativeMethod("setJSObjectProperty",
21                                     JavaScriptObject::setProperty<jni::alias_ref<JavaScriptObject::javaobject>>),
22                    makeNativeMethod("unsetProperty", JavaScriptObject::unsetProperty),
23                    makeNativeMethod("defineBoolProperty", JavaScriptObject::defineProperty<bool>),
24                    makeNativeMethod("defineDoubleProperty",
25                                     JavaScriptObject::defineProperty<double>),
26                    makeNativeMethod("defineStringProperty",
27                                     JavaScriptObject::defineProperty<jni::alias_ref<jstring>>),
28                    makeNativeMethod("defineJSValueProperty",
29                                     JavaScriptObject::defineProperty<jni::alias_ref<JavaScriptValue::javaobject>>),
30                    makeNativeMethod("defineJSObjectProperty",
31                                     JavaScriptObject::defineProperty<jni::alias_ref<JavaScriptObject::javaobject>>),
32                  });
33 }
34 
35 JavaScriptObject::JavaScriptObject(
36   std::weak_ptr<JavaScriptRuntime> runtime,
37   std::shared_ptr<jsi::Object> jsObject
38 ) : runtimeHolder(std::move(runtime)), jsObject(std::move(jsObject)) {
39   runtimeHolder.ensureRuntimeIsValid();
40 }
41 
42 JavaScriptObject::JavaScriptObject(
43   WeakRuntimeHolder runtime,
44   std::shared_ptr<jsi::Object> jsObject
45 ) : runtimeHolder(std::move(runtime)), jsObject(std::move(jsObject)) {
46   runtimeHolder.ensureRuntimeIsValid();
47 }
48 
49 std::shared_ptr<jsi::Object> JavaScriptObject::get() {
50   return jsObject;
51 }
52 
53 bool JavaScriptObject::hasProperty(const std::string &name) {
54   auto &jsRuntime = runtimeHolder.getJSRuntime();
55   return jsObject->hasProperty(jsRuntime, name.c_str());
56 }
57 
58 jsi::Value JavaScriptObject::getProperty(const std::string &name) {
59   auto &jsRuntime = runtimeHolder.getJSRuntime();
60   return jsObject->getProperty(jsRuntime, name.c_str());
61 }
62 
63 bool JavaScriptObject::jniHasProperty(jni::alias_ref<jstring> name) {
64   return hasProperty(name->toStdString());
65 }
66 
67 jni::local_ref<JavaScriptValue::javaobject> JavaScriptObject::jniGetProperty(
68   jni::alias_ref<jstring> name
69 ) {
70   auto result = std::make_shared<jsi::Value>(getProperty(name->toStdString()));
71   return JavaScriptValue::newObjectCxxArgs(runtimeHolder, result);
72 }
73 
74 std::vector<std::string> JavaScriptObject::getPropertyNames() {
75   auto &jsRuntime = runtimeHolder.getJSRuntime();
76 
77   jsi::Array properties = jsObject->getPropertyNames(jsRuntime);
78   auto size = properties.size(jsRuntime);
79 
80   std::vector<std::string> names(size);
81   for (size_t i = 0; i < size; i++) {
82     auto propertyName = properties
83       .getValueAtIndex(jsRuntime, i)
84       .asString(jsRuntime)
85       .utf8(jsRuntime);
86     names[i] = propertyName;
87   }
88 
89   return names;
90 }
91 
92 jni::local_ref<jni::JArrayClass<jstring>> JavaScriptObject::jniGetPropertyNames() {
93   std::vector<std::string> cResult = getPropertyNames();
94   auto paredResult = jni::JArrayClass<jstring>::newArray(cResult.size());
95   for (size_t i = 0; i < cResult.size(); i++) {
96     paredResult->setElement(i, jni::make_jstring(cResult[i]).get());
97   }
98 
99   return paredResult;
100 }
101 
102 void JavaScriptObject::setProperty(const std::string &name, jsi::Value value) {
103   auto &jsRuntime = runtimeHolder.getJSRuntime();
104   jsObject->setProperty(jsRuntime, name.c_str(), value);
105 }
106 
107 void JavaScriptObject::unsetProperty(jni::alias_ref<jstring> name) {
108   auto &jsRuntime = runtimeHolder.getJSRuntime();
109   auto cName = name->toStdString();
110   jsObject->setProperty(
111     jsRuntime,
112     cName.c_str(),
113     jsi::Value::undefined()
114   );
115 }
116 
117 jsi::Object JavaScriptObject::preparePropertyDescriptor(
118   jsi::Runtime &jsRuntime,
119   int options
120 ) {
121   jsi::Object descriptor(jsRuntime);
122   descriptor.setProperty(jsRuntime, "configurable", (bool) ((1 << 0) & options));
123   descriptor.setProperty(jsRuntime, "enumerable", (bool) ((1 << 1) & options));
124   if ((bool) (1 << 2 & options)) {
125     descriptor.setProperty(jsRuntime, "writable", true);
126   }
127   return descriptor;
128 }
129 
130 void JavaScriptObject::defineProperty(
131   jsi::Runtime &runtime,
132   jsi::Object *jsthis,
133   const std::string &name,
134   jsi::Object descriptor
135 ) {
136   jsi::Object global = runtime.global();
137   jsi::Object objectClass = global.getPropertyAsObject(runtime, "Object");
138   jsi::Function definePropertyFunction = objectClass.getPropertyAsFunction(
139     runtime,
140     "defineProperty"
141   );
142 
143   // This call is basically the same as `Object.defineProperty(object, name, descriptor)` in JS
144   definePropertyFunction.callWithThis(runtime, objectClass, {
145     jsi::Value(runtime, *jsthis),
146     jsi::String::createFromUtf8(runtime, name),
147     std::move(descriptor),
148   });
149 }
150 } // namespace expo
151