1 // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2 
3 #include "JavaScriptValue.h"
4 
5 #include "JavaScriptRuntime.h"
6 #include "JavaScriptObject.h"
7 #include "JavaScriptTypedArray.h"
8 #include "JavaScriptFunction.h"
9 #include "TypedArray.h"
10 #include "Exceptions.h"
11 #include "JSIInteropModuleRegistry.h"
12 
13 namespace expo {
registerNatives()14 void JavaScriptValue::registerNatives() {
15   registerHybrid({
16                    makeNativeMethod("kind", JavaScriptValue::jniKind),
17                    makeNativeMethod("isNull", JavaScriptValue::isNull),
18                    makeNativeMethod("isUndefined", JavaScriptValue::isUndefined),
19                    makeNativeMethod("isBool", JavaScriptValue::isBool),
20                    makeNativeMethod("isNumber", JavaScriptValue::isNumber),
21                    makeNativeMethod("isString", JavaScriptValue::isString),
22                    makeNativeMethod("isSymbol", JavaScriptValue::isSymbol),
23                    makeNativeMethod("isFunction", JavaScriptValue::isFunction),
24                    makeNativeMethod("isArray", JavaScriptValue::isArray),
25                    makeNativeMethod("isTypedArray", JavaScriptValue::isTypedArray),
26                    makeNativeMethod("isObject", JavaScriptValue::isObject),
27                    makeNativeMethod("getBool", JavaScriptValue::getBool),
28                    makeNativeMethod("getDouble", JavaScriptValue::getDouble),
29                    makeNativeMethod("getString", JavaScriptValue::jniGetString),
30                    makeNativeMethod("getObject", JavaScriptValue::getObject),
31                    makeNativeMethod("getArray", JavaScriptValue::getArray),
32                    makeNativeMethod("getTypedArray", JavaScriptValue::getTypedArray),
33                    makeNativeMethod("jniGetFunction", JavaScriptValue::jniGetFunction),
34                  });
35 }
36 
JavaScriptValue(std::weak_ptr<JavaScriptRuntime> runtime,std::shared_ptr<jsi::Value> jsValue)37 JavaScriptValue::JavaScriptValue(
38   std::weak_ptr<JavaScriptRuntime> runtime,
39   std::shared_ptr<jsi::Value> jsValue
40 ) : runtimeHolder(std::move(runtime)), jsValue(std::move(jsValue)) {
41   runtimeHolder.ensureRuntimeIsValid();
42 }
43 
JavaScriptValue(WeakRuntimeHolder runtime,std::shared_ptr<jsi::Value> jsValue)44 JavaScriptValue::JavaScriptValue(
45   WeakRuntimeHolder runtime,
46   std::shared_ptr<jsi::Value> jsValue
47 ) : runtimeHolder(std::move(runtime)), jsValue(std::move(jsValue)) {
48   runtimeHolder.ensureRuntimeIsValid();
49 }
50 
get()51 std::shared_ptr<jsi::Value> JavaScriptValue::get() {
52   return jsValue;
53 }
54 
kind()55 std::string JavaScriptValue::kind() {
56   if (isNull()) {
57     return "null";
58   }
59   if (isUndefined()) {
60     return "undefined";
61   }
62   if (isBool()) {
63     return "boolean";
64   }
65   if (isNumber()) {
66     return "number";
67   }
68   if (isString()) {
69     return "string";
70   }
71   if (isSymbol()) {
72     return "symbol";
73   }
74   if (isFunction()) {
75     return "function";
76   }
77   if (isArray()) {
78     return "array";
79   }
80   if (isObject()) {
81     return "object";
82   }
83 
84   throwNewJavaException(
85     UnexpectedException::create("Unknown type").get()
86   );
87 }
88 
isNull()89 bool JavaScriptValue::isNull() {
90   return jsValue->isNull();
91 }
92 
isUndefined()93 bool JavaScriptValue::isUndefined() {
94   return jsValue->isUndefined();
95 }
96 
isBool()97 bool JavaScriptValue::isBool() {
98   return jsValue->isBool();
99 }
100 
isNumber()101 bool JavaScriptValue::isNumber() {
102   return jsValue->isNumber();
103 }
104 
isString()105 bool JavaScriptValue::isString() {
106   return jsValue->isString();
107 }
108 
isSymbol()109 bool JavaScriptValue::isSymbol() {
110   return jsValue->isSymbol();
111 }
112 
isFunction()113 bool JavaScriptValue::isFunction() {
114   if (jsValue->isObject()) {
115     auto &jsRuntime = runtimeHolder.getJSRuntime();
116     return jsValue->asObject(jsRuntime).isFunction(jsRuntime);
117   }
118 
119   return false;
120 }
121 
isArray()122 bool JavaScriptValue::isArray() {
123   if (jsValue->isObject()) {
124     auto &jsRuntime = runtimeHolder.getJSRuntime();
125     return jsValue->asObject(jsRuntime).isArray(jsRuntime);
126   }
127 
128   return false;
129 }
130 
isObject()131 bool JavaScriptValue::isObject() {
132   return jsValue->isObject();
133 }
134 
isTypedArray()135 bool JavaScriptValue::isTypedArray() {
136   if (jsValue->isObject()) {
137     jsi::Runtime &jsRuntime = runtimeHolder.getJSRuntime();
138     return expo::isTypedArray(jsRuntime, jsValue->getObject(jsRuntime));
139   }
140   return false;
141 }
142 
getBool()143 bool JavaScriptValue::getBool() {
144   return jsValue->getBool();
145 }
146 
getDouble()147 double JavaScriptValue::getDouble() {
148   return jsValue->getNumber();
149 }
150 
getString()151 std::string JavaScriptValue::getString() {
152   auto &jsRuntime = runtimeHolder.getJSRuntime();
153   return jsValue->getString(jsRuntime).utf8(jsRuntime);
154 }
155 
getObject()156 jni::local_ref<JavaScriptObject::javaobject> JavaScriptValue::getObject() {
157   auto &jsRuntime = runtimeHolder.getJSRuntime();
158   auto jsObject = std::make_shared<jsi::Object>(jsValue->getObject(jsRuntime));
159   return JavaScriptObject::newInstance(
160     runtimeHolder.getModuleRegistry(),
161     runtimeHolder,
162     jsObject
163   );
164 }
165 
jniGetFunction()166 jni::local_ref<JavaScriptFunction::javaobject> JavaScriptValue::jniGetFunction() {
167   auto &jsRuntime = runtimeHolder.getJSRuntime();
168   auto jsFunction = std::make_shared<jsi::Function>(
169     jsValue->getObject(jsRuntime).asFunction(jsRuntime));
170   return JavaScriptFunction::newInstance(
171     runtimeHolder.getModuleRegistry(),
172     runtimeHolder,
173     jsFunction
174   );
175 }
176 
getArray()177 jni::local_ref<jni::JArrayClass<JavaScriptValue::javaobject>> JavaScriptValue::getArray() {
178   auto &jsRuntime = runtimeHolder.getJSRuntime();
179   auto moduleRegistry = runtimeHolder.getModuleRegistry();
180 
181   auto jsArray = jsValue
182     ->getObject(jsRuntime)
183     .asArray(jsRuntime);
184   size_t size = jsArray.size(jsRuntime);
185 
186   auto result = jni::JArrayClass<JavaScriptValue::javaobject>::newArray(size);
187   for (size_t i = 0; i < size; i++) {
188     auto element = JavaScriptValue::newInstance(
189       moduleRegistry,
190       runtimeHolder,
191       std::make_shared<jsi::Value>(jsArray.getValueAtIndex(jsRuntime, i))
192     );
193 
194     result->setElement(i, element.release());
195   }
196   return result;
197 }
198 
jniKind()199 jni::local_ref<jstring> JavaScriptValue::jniKind() {
200   auto result = kind();
201   return jni::make_jstring(result);
202 }
203 
jniGetString()204 jni::local_ref<jstring> JavaScriptValue::jniGetString() {
205   auto result = getString();
206   return jni::make_jstring(result);
207 }
208 
getTypedArray()209 jni::local_ref<JavaScriptTypedArray::javaobject> JavaScriptValue::getTypedArray() {
210   auto &jsRuntime = runtimeHolder.getJSRuntime();
211   auto jsObject = std::make_shared<jsi::Object>(jsValue->getObject(jsRuntime));
212   return JavaScriptTypedArray::newInstance(
213     runtimeHolder.getModuleRegistry(),
214     runtimeHolder,
215     jsObject
216   );
217 }
218 
newInstance(JSIInteropModuleRegistry * jsiInteropModuleRegistry,std::weak_ptr<JavaScriptRuntime> runtime,std::shared_ptr<jsi::Value> jsValue)219 jni::local_ref<JavaScriptValue::javaobject> JavaScriptValue::newInstance(
220   JSIInteropModuleRegistry *jsiInteropModuleRegistry,
221   std::weak_ptr<JavaScriptRuntime> runtime,
222   std::shared_ptr<jsi::Value> jsValue
223 ) {
224   auto value = JavaScriptValue::newObjectCxxArgs(
225     std::move(runtime),
226     std::move(jsValue)
227   );
228   jsiInteropModuleRegistry->jniDeallocator->addReference(value);
229   return value;
230 }
231 } // namespace expo
232