1 #include "JsiValue.h"
2
3 namespace RNJsi {
4
JsiValue()5 JsiValue::JsiValue() : _type(PropType::Undefined) {}
6
JsiValue(jsi::Runtime & runtime,const jsi::Value & value)7 JsiValue::JsiValue(jsi::Runtime &runtime, const jsi::Value &value)
8 : JsiValue() {
9 setCurrent(runtime, value);
10 }
11
setCurrent(jsi::Runtime & runtime,const jsi::Value & value)12 void JsiValue::setCurrent(jsi::Runtime &runtime, const jsi::Value &value) {
13 _stringValue = "";
14 _hostObject = nullptr;
15 _hostFunction = nullptr;
16 _props.clear();
17 _array.clear();
18 _keysCache.clear();
19
20 if (value.isNumber()) {
21 _type = PropType::Number;
22 _numberValue = value.asNumber();
23 } else if (value.isBool()) {
24 _type = PropType::Bool;
25 _boolValue = value.getBool();
26 } else if (value.isString()) {
27 _type = PropType::String;
28 _stringValue = value.asString(runtime).utf8(runtime);
29 } else if (value.isUndefined()) {
30 _type = PropType::Undefined;
31 } else if (value.isNull()) {
32 _type = PropType::Null;
33 } else if (value.isObject()) {
34 setObject(runtime, value);
35 } else {
36 throw std::runtime_error("Could not store jsi::Value of provided type");
37 }
38 }
39
getAsBool() const40 bool JsiValue::getAsBool() const {
41 if (_type != PropType::Bool) {
42 throw std::runtime_error("Expected type bool, got " +
43 getTypeAsString(_type));
44 }
45 return _boolValue;
46 }
47
getAsNumber() const48 double JsiValue::getAsNumber() const {
49 if (_type != PropType::Number) {
50 throw std::runtime_error("Expected type number, got " +
51 getTypeAsString(_type));
52 }
53 return _numberValue;
54 }
55
getAsString() const56 const std::string &JsiValue::getAsString() const {
57 if (_type == PropType::Number) {
58 return std::move(std::to_string(_numberValue));
59 }
60
61 if (_type != PropType::String) {
62 throw std::runtime_error("Expected type string, got " +
63 getTypeAsString(_type));
64 }
65 return _stringValue;
66 }
67
getAsArray() const68 const std::vector<JsiValue> &JsiValue::getAsArray() const {
69 if (_type != PropType::Array) {
70 throw std::runtime_error("Expected type array, got " +
71 getTypeAsString(_type));
72 }
73 return _array;
74 }
75
getValue(PropId name) const76 const JsiValue &JsiValue::getValue(PropId name) const {
77 if (_type != PropType::Object) {
78 throw std::runtime_error("Expected type object, got " +
79 getTypeAsString(_type));
80 }
81 return _props.at(name);
82 }
83
hasValue(PropId name) const84 bool JsiValue::hasValue(PropId name) const {
85 if (_type != PropType::Object) {
86 throw std::runtime_error("Expected type object, got " +
87 getTypeAsString(_type));
88 }
89 return _props.count(name) > 0;
90 }
91
getKeys() const92 std::vector<PropId> JsiValue::getKeys() const {
93 if (_type != PropType::Object) {
94 throw std::runtime_error("Expected type object, got " +
95 getTypeAsString(_type));
96 }
97 return _keysCache;
98 }
99
getAsHostObject() const100 std::shared_ptr<jsi::HostObject> JsiValue::getAsHostObject() const {
101 if (_type != PropType::HostObject) {
102 throw std::runtime_error("Expected type host object, got " +
103 getTypeAsString(_type));
104 }
105 return _hostObject;
106 }
107
getAsHostFunction() const108 jsi::HostFunctionType JsiValue::getAsHostFunction() const {
109 if (_type != PropType::HostFunction) {
110 throw std::runtime_error("Expected type host function, got " +
111 getTypeAsString(_type));
112 }
113 return _hostFunction;
114 }
115
getAsFunction() const116 const jsi::HostFunctionType JsiValue::getAsFunction() const {
117 return getAsHostFunction();
118 }
119
asString() const120 std::string JsiValue::asString() const {
121 switch (_type) {
122 case PropType::Null:
123 return "(null)";
124 case PropType::Undefined:
125 return "(undefined)";
126 case PropType::Number:
127 return std::to_string(_numberValue);
128 case PropType::Bool:
129 return std::to_string(_boolValue);
130 case PropType::String:
131 return _stringValue;
132 case PropType::Object:
133 return "[Object]";
134 case PropType::Array:
135 return "[Array]";
136 case PropType::HostObject:
137 return "[HostObject]";
138 case PropType::HostFunction:
139 return "[HostFunction]";
140 }
141 }
142
getAsJsiValue(jsi::Runtime & runtime) const143 jsi::Value JsiValue::getAsJsiValue(jsi::Runtime &runtime) const {
144 switch (_type) {
145 case PropType::Undefined:
146 return jsi::Value::undefined();
147 case PropType::Null:
148 return jsi::Value::null();
149 case PropType::Number:
150 return _numberValue;
151 case PropType::Bool:
152 return _boolValue;
153 case PropType::String:
154 return jsi::String::createFromUtf8(runtime, _stringValue);
155 case PropType::Object:
156 return getObject(runtime);
157 case PropType::Array:
158 return getArray(runtime);
159 case PropType::HostObject:
160 return getHostObject(runtime);
161 case PropType::HostFunction:
162 return getHostFunction(runtime);
163 }
164 }
165
getTypeAsString(PropType type)166 std::string JsiValue::getTypeAsString(PropType type) {
167 switch (type) {
168 case PropType::Undefined:
169 return "undefined";
170 case PropType::Null:
171 return "null";
172 case PropType::Number:
173 return "number";
174 case PropType::Bool:
175 return "boolean";
176 case PropType::String:
177 return "string";
178 case PropType::Object:
179 return "object";
180 case PropType::Array:
181 return "array";
182 case PropType::HostObject:
183 return "hostobject";
184 case PropType::HostFunction:
185 return "hostfunction";
186 }
187 }
188
setObject(jsi::Runtime & runtime,const jsi::Value & value)189 void JsiValue::setObject(jsi::Runtime &runtime, const jsi::Value &value) {
190 auto obj = value.asObject(runtime);
191 if (obj.isFunction(runtime)) {
192 setFunction(runtime, value);
193 } else if (obj.isArray(runtime)) {
194 setArray(runtime, obj);
195 } else if (obj.isHostObject(runtime)) {
196 setHostObject(runtime, obj);
197 } else {
198 _type = PropType::Object;
199 // Read object keys
200 auto keys = obj.getPropertyNames(runtime);
201 size_t size = keys.size(runtime);
202 _keysCache.clear();
203 _keysCache.reserve(size);
204 _props.clear();
205 _props.reserve(size);
206
207 for (size_t i = 0; i < size; ++i) {
208 auto key = JsiPropId::get(
209 keys.getValueAtIndex(runtime, i).asString(runtime).utf8(runtime));
210 try {
211 _props.try_emplace(key, runtime, obj.getProperty(runtime, key));
212 _keysCache.push_back(key);
213 } catch (jsi::JSError e) {
214 throw jsi::JSError(runtime,
215 "Could not set property for key " +
216 std::string(key) + ":\n" + e.getMessage(),
217 e.getStack());
218 }
219 }
220 }
221 }
222
getObject(jsi::Runtime & runtime) const223 jsi::Object JsiValue::getObject(jsi::Runtime &runtime) const {
224 assert(_type == PropType::Object);
225 auto obj = jsi::Object(runtime);
226 for (auto &p : _props) {
227 obj.setProperty(runtime, p.first, p.second.getAsJsiValue(runtime));
228 }
229 return obj;
230 }
231
operator !=(const JsiValue & other) const232 bool JsiValue::operator!=(const JsiValue &other) const {
233 return !(this->operator==(other));
234 }
235
operator ==(const JsiValue & other) const236 bool JsiValue::operator==(const JsiValue &other) const {
237
238 if (other.getType() != getType()) {
239 return false;
240 }
241
242 switch (_type) {
243 case PropType::Null:
244 case PropType::Undefined:
245 return true;
246 case PropType::Number:
247 return _numberValue == other.getAsNumber();
248 case PropType::Bool:
249 return _boolValue == other.getAsBool();
250 case PropType::String:
251 return _stringValue == other.getAsString();
252 case PropType::Object: {
253 if (_props.size() != other.getProps().size()) {
254 return false;
255 }
256 for (auto &p : _props) {
257 auto t = p.second.operator==(other.getValue(p.first));
258 if (!t) {
259 return false;
260 }
261 }
262 return true;
263 }
264 case PropType::Array: {
265 auto otherArr = other.getAsArray();
266 if (_array.size() != otherArr.size()) {
267 return false;
268 }
269 for (size_t i = 0; i < _array.size(); ++i) {
270 if (!_array[i].operator==(otherArr[i])) {
271 return false;
272 }
273 }
274 return true;
275 }
276 case PropType::HostObject:
277 return getAsHostObject() == other.getAsHostObject();
278 case PropType::HostFunction:
279 // Unable to compare host functions
280 return false;
281 }
282
283 throw std::runtime_error(
284 "Wrong type in equals call. Should not happen. File a bug.");
285
286 return false;
287 }
288
setFunction(jsi::Runtime & runtime,const jsi::Value & value)289 void JsiValue::setFunction(jsi::Runtime &runtime, const jsi::Value &value) {
290 auto func = value.asObject(runtime).asFunction(runtime);
291 if (func.isHostFunction(runtime)) {
292 _type = PropType::HostFunction;
293 _hostFunction = func.getHostFunction(runtime);
294 } else {
295 _type = PropType::HostFunction;
296 auto obj = std::make_shared<jsi::Object>(value.asObject(runtime));
297 _hostFunction = [obj](jsi::Runtime &runtime, const jsi::Value &thisValue,
298 const jsi::Value *arguments,
299 size_t count) -> jsi::Value {
300 auto func = obj->asFunction(runtime);
301 if (thisValue.isNull() || thisValue.isUndefined()) {
302 return func.call(runtime, arguments, count);
303 } else {
304 return func.callWithThis(runtime, thisValue.asObject(runtime),
305 arguments, count);
306 }
307 };
308 }
309 }
310
getHostFunction(jsi::Runtime & runtime) const311 jsi::Object JsiValue::getHostFunction(jsi::Runtime &runtime) const {
312 assert(_type == PropType::HostFunction);
313 return jsi::Function::createFromHostFunction(
314 runtime, jsi::PropNameID::forUtf8(runtime, "fn"), 0, _hostFunction);
315 }
316
setArray(jsi::Runtime & runtime,const jsi::Object & obj)317 void JsiValue::setArray(jsi::Runtime &runtime, const jsi::Object &obj) {
318 _type = PropType::Array;
319 auto arr = obj.asArray(runtime);
320 size_t size = arr.size(runtime);
321 _array.reserve(size);
322 for (size_t i = 0; i < size; ++i) {
323 _array.emplace_back(runtime, arr.getValueAtIndex(runtime, i));
324 }
325 }
326
getArray(jsi::Runtime & runtime) const327 jsi::Array JsiValue::getArray(jsi::Runtime &runtime) const {
328 assert(_type == PropType::Array);
329 jsi::Array arr = jsi::Array(runtime, _array.size());
330 for (size_t i = 0; i < _array.size(); ++i) {
331 arr.setValueAtIndex(runtime, i, _array[i].getAsJsiValue(runtime));
332 }
333 return arr;
334 }
335
setHostObject(jsi::Runtime & runtime,const jsi::Object & obj)336 void JsiValue::setHostObject(jsi::Runtime &runtime, const jsi::Object &obj) {
337 _type = PropType::HostObject;
338 _hostObject = obj.asHostObject(runtime);
339 }
340
getHostObject(jsi::Runtime & runtime) const341 jsi::Object JsiValue::getHostObject(jsi::Runtime &runtime) const {
342 assert(_type == PropType::HostObject);
343 return jsi::Object::createFromHostObject(runtime, _hostObject);
344 }
345
346 } // namespace RNJsi
347