#include "JsiValue.h" namespace RNJsi { JsiValue::JsiValue() : _type(PropType::Undefined) {} JsiValue::JsiValue(jsi::Runtime &runtime, const jsi::Value &value) : JsiValue() { setCurrent(runtime, value); } void JsiValue::setCurrent(jsi::Runtime &runtime, const jsi::Value &value) { _stringValue = ""; _hostObject = nullptr; _hostFunction = nullptr; _props.clear(); _array.clear(); _keysCache.clear(); if (value.isNumber()) { _type = PropType::Number; _numberValue = value.asNumber(); } else if (value.isBool()) { _type = PropType::Bool; _boolValue = value.getBool(); } else if (value.isString()) { _type = PropType::String; _stringValue = value.asString(runtime).utf8(runtime); } else if (value.isUndefined()) { _type = PropType::Undefined; } else if (value.isNull()) { _type = PropType::Null; } else if (value.isObject()) { setObject(runtime, value); } else { throw std::runtime_error("Could not store jsi::Value of provided type"); } } bool JsiValue::getAsBool() const { if (_type != PropType::Bool) { throw std::runtime_error("Expected type bool, got " + getTypeAsString(_type)); } return _boolValue; } double JsiValue::getAsNumber() const { if (_type != PropType::Number) { throw std::runtime_error("Expected type number, got " + getTypeAsString(_type)); } return _numberValue; } const std::string &JsiValue::getAsString() const { if (_type == PropType::Number) { return std::move(std::to_string(_numberValue)); } if (_type != PropType::String) { throw std::runtime_error("Expected type string, got " + getTypeAsString(_type)); } return _stringValue; } const std::vector &JsiValue::getAsArray() const { if (_type != PropType::Array) { throw std::runtime_error("Expected type array, got " + getTypeAsString(_type)); } return _array; } const JsiValue &JsiValue::getValue(PropId name) const { if (_type != PropType::Object) { throw std::runtime_error("Expected type object, got " + getTypeAsString(_type)); } return _props.at(name); } bool JsiValue::hasValue(PropId name) const { if (_type != PropType::Object) { throw std::runtime_error("Expected type object, got " + getTypeAsString(_type)); } return _props.count(name) > 0; } std::vector JsiValue::getKeys() const { if (_type != PropType::Object) { throw std::runtime_error("Expected type object, got " + getTypeAsString(_type)); } return _keysCache; } std::shared_ptr JsiValue::getAsHostObject() const { if (_type != PropType::HostObject) { throw std::runtime_error("Expected type host object, got " + getTypeAsString(_type)); } return _hostObject; } jsi::HostFunctionType JsiValue::getAsHostFunction() const { if (_type != PropType::HostFunction) { throw std::runtime_error("Expected type host function, got " + getTypeAsString(_type)); } return _hostFunction; } const jsi::HostFunctionType JsiValue::getAsFunction() const { return getAsHostFunction(); } std::string JsiValue::asString() const { switch (_type) { case PropType::Null: return "(null)"; case PropType::Undefined: return "(undefined)"; case PropType::Number: return std::to_string(_numberValue); case PropType::Bool: return std::to_string(_boolValue); case PropType::String: return _stringValue; case PropType::Object: return "[Object]"; case PropType::Array: return "[Array]"; case PropType::HostObject: return "[HostObject]"; case PropType::HostFunction: return "[HostFunction]"; } } jsi::Value JsiValue::getAsJsiValue(jsi::Runtime &runtime) const { switch (_type) { case PropType::Undefined: return jsi::Value::undefined(); case PropType::Null: return jsi::Value::null(); case PropType::Number: return _numberValue; case PropType::Bool: return _boolValue; case PropType::String: return jsi::String::createFromUtf8(runtime, _stringValue); case PropType::Object: return getObject(runtime); case PropType::Array: return getArray(runtime); case PropType::HostObject: return getHostObject(runtime); case PropType::HostFunction: return getHostFunction(runtime); } } std::string JsiValue::getTypeAsString(PropType type) { switch (type) { case PropType::Undefined: return "undefined"; case PropType::Null: return "null"; case PropType::Number: return "number"; case PropType::Bool: return "boolean"; case PropType::String: return "string"; case PropType::Object: return "object"; case PropType::Array: return "array"; case PropType::HostObject: return "hostobject"; case PropType::HostFunction: return "hostfunction"; } } void JsiValue::setObject(jsi::Runtime &runtime, const jsi::Value &value) { auto obj = value.asObject(runtime); if (obj.isFunction(runtime)) { setFunction(runtime, value); } else if (obj.isArray(runtime)) { setArray(runtime, obj); } else if (obj.isHostObject(runtime)) { setHostObject(runtime, obj); } else { _type = PropType::Object; // Read object keys auto keys = obj.getPropertyNames(runtime); size_t size = keys.size(runtime); _keysCache.clear(); _keysCache.reserve(size); _props.clear(); _props.reserve(size); for (size_t i = 0; i < size; ++i) { auto key = JsiPropId::get( keys.getValueAtIndex(runtime, i).asString(runtime).utf8(runtime)); try { _props.try_emplace(key, runtime, obj.getProperty(runtime, key)); _keysCache.push_back(key); } catch (jsi::JSError e) { throw jsi::JSError(runtime, "Could not set property for key " + std::string(key) + ":\n" + e.getMessage(), e.getStack()); } } } } jsi::Object JsiValue::getObject(jsi::Runtime &runtime) const { assert(_type == PropType::Object); auto obj = jsi::Object(runtime); for (auto &p : _props) { obj.setProperty(runtime, p.first, p.second.getAsJsiValue(runtime)); } return obj; } bool JsiValue::operator!=(const JsiValue &other) const { return !(this->operator==(other)); } bool JsiValue::operator==(const JsiValue &other) const { if (other.getType() != getType()) { return false; } switch (_type) { case PropType::Null: case PropType::Undefined: return true; case PropType::Number: return _numberValue == other.getAsNumber(); case PropType::Bool: return _boolValue == other.getAsBool(); case PropType::String: return _stringValue == other.getAsString(); case PropType::Object: { if (_props.size() != other.getProps().size()) { return false; } for (auto &p : _props) { auto t = p.second.operator==(other.getValue(p.first)); if (!t) { return false; } } return true; } case PropType::Array: { auto otherArr = other.getAsArray(); if (_array.size() != otherArr.size()) { return false; } for (size_t i = 0; i < _array.size(); ++i) { if (!_array[i].operator==(otherArr[i])) { return false; } } return true; } case PropType::HostObject: return getAsHostObject() == other.getAsHostObject(); case PropType::HostFunction: // Unable to compare host functions return false; } throw std::runtime_error( "Wrong type in equals call. Should not happen. File a bug."); return false; } void JsiValue::setFunction(jsi::Runtime &runtime, const jsi::Value &value) { auto func = value.asObject(runtime).asFunction(runtime); if (func.isHostFunction(runtime)) { _type = PropType::HostFunction; _hostFunction = func.getHostFunction(runtime); } else { _type = PropType::HostFunction; auto obj = std::make_shared(value.asObject(runtime)); _hostFunction = [obj](jsi::Runtime &runtime, const jsi::Value &thisValue, const jsi::Value *arguments, size_t count) -> jsi::Value { auto func = obj->asFunction(runtime); if (thisValue.isNull() || thisValue.isUndefined()) { return func.call(runtime, arguments, count); } else { return func.callWithThis(runtime, thisValue.asObject(runtime), arguments, count); } }; } } jsi::Object JsiValue::getHostFunction(jsi::Runtime &runtime) const { assert(_type == PropType::HostFunction); return jsi::Function::createFromHostFunction( runtime, jsi::PropNameID::forUtf8(runtime, "fn"), 0, _hostFunction); } void JsiValue::setArray(jsi::Runtime &runtime, const jsi::Object &obj) { _type = PropType::Array; auto arr = obj.asArray(runtime); size_t size = arr.size(runtime); _array.reserve(size); for (size_t i = 0; i < size; ++i) { _array.emplace_back(runtime, arr.getValueAtIndex(runtime, i)); } } jsi::Array JsiValue::getArray(jsi::Runtime &runtime) const { assert(_type == PropType::Array); jsi::Array arr = jsi::Array(runtime, _array.size()); for (size_t i = 0; i < _array.size(); ++i) { arr.setValueAtIndex(runtime, i, _array[i].getAsJsiValue(runtime)); } return arr; } void JsiValue::setHostObject(jsi::Runtime &runtime, const jsi::Object &obj) { _type = PropType::HostObject; _hostObject = obj.asHostObject(runtime); } jsi::Object JsiValue::getHostObject(jsi::Runtime &runtime) const { assert(_type == PropType::HostObject); return jsi::Object::createFromHostObject(runtime, _hostObject); } } // namespace RNJsi