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