#include "JSISerializer.h" #include #include #include const std::vector SUPPORTED_ERROR_TYPES = { "Error", "AggregateError", "EvalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError", "URIError", "InternalError"}; const std::vector SUPPORTED_INDEXED_COLLECTION_TYPES = { "Int8Array", "Uint8Array", "Uint8ClampedArray", "Int16Array", "Uint16Array", "Int32Array", "Uint32Array", "BigInt64Array", "BigUint64Array", "Float32Array", "Float64Array", }; const std::vector SUPPORTED_STRUCTURED_DATA_TYPES = { "ArrayBuffer", "SharedArrayBuffer", "DataView", "Atomics", "JSON", }; const std::vector SUPPORTED_MANAGING_MEMORY_TYPES = { "WeakRef", "FinalizationRegistry", }; const std::vector SUPPORTED_ABSTRACTION_OBJECT_TYPES = { "Iterator", "AsyncIterator", "Promise", "GeneratorFunction", "AsyncGeneratorFunction", "Generator", "AsyncGenerator", "AsyncFunction", }; const std::vector SUPPORTED_REFLECTION_TYPES = { "Reflect", "Proxy", }; static inline std::string getObjectTypeName( jsi::Runtime &rt, const jsi::Object &object) { return object.getPropertyAsObject(rt, "constructor") .getProperty(rt, "name") .toString(rt) .utf8(rt); } static inline bool isInstanceOf( jsi::Runtime &rt, const jsi::Object &object, const std::string &type) { return getObjectTypeName(rt, object) == type; } static inline bool isInstanceOfAny( jsi::Runtime &rt, const jsi::Object &object, const std::vector &supportedTypes) { auto instanceType = getObjectTypeName(rt, object); return std::find( supportedTypes.begin(), supportedTypes.end(), instanceType) != supportedTypes.end(); } JSISerializer::JSISerializer(jsi::Runtime &rt) : rt_(rt), visitedNodes_(rt_.global() .getPropertyAsFunction(rt_, "Set") .callAsConstructor(rt_) .asObject(rt_)) {} std::string JSISerializer::stringifyWithName(const jsi::Object &object) { std::stringstream ss; ss << '[' << getObjectTypeName(rt_, object) << ']'; return ss.str(); } std::string JSISerializer::stringifyArray(const jsi::Array &arr) { std::stringstream ss; ss << '['; for (size_t i = 0, length = arr.size(rt_); i < length; i++) { jsi::Value element = arr.getValueAtIndex(rt_, i); ss << stringifyJSIValueRecursively(element); if (i != length - 1) { ss << ", "; } } ss << ']'; return ss.str(); } std::string JSISerializer::stringifyFunction(const jsi::Function &func) { std::stringstream ss; auto kind = (func.isHostFunction(rt_) ? "jsi::HostFunction" : "Function"); auto name = func.getProperty(rt_, "name").toString(rt_).utf8(rt_); name = name.empty() ? "anonymous" : name; ss << '[' << kind << ' ' << name << ']'; return ss.str(); } std::string JSISerializer::stringifyHostObject(jsi::HostObject &hostObject) { int status = -1; char *hostObjClassName = abi::__cxa_demangle(typeid(hostObject).name(), NULL, NULL, &status); if (status != 0) { return "[jsi::HostObject]"; } std::stringstream ss; ss << "[jsi::HostObject(" << hostObjClassName << ")"; std::free(hostObjClassName); auto props = hostObject.getPropertyNames(rt_); auto propsCount = props.size(); if (propsCount > 0) { ss << " {"; auto lastKey = props.back().utf8(rt_); for (const auto &key : props) { auto formattedKey = key.utf8(rt_); auto value = hostObject.get(rt_, key); ss << '"' << formattedKey << '"' << ": " << stringifyJSIValueRecursively(value); if (formattedKey != lastKey) { ss << ", "; } } ss << '}'; } ss << ']'; return ss.str(); } std::string JSISerializer::stringifyObject(const jsi::Object &object) { std::stringstream ss; ss << '{'; auto props = object.getPropertyNames(rt_); for (size_t i = 0, propsCount = props.size(rt_); i < propsCount; i++) { jsi::String propName = props.getValueAtIndex(rt_, i).toString(rt_); ss << '"' << propName.utf8(rt_) << '"' << ": " << stringifyJSIValueRecursively(object.getProperty(rt_, propName)); if (i != propsCount - 1) { ss << ", "; } } ss << '}'; return ss.str(); } std::string JSISerializer::stringifyError(const jsi::Object &object) { std::stringstream ss; ss << '[' << object.getProperty(rt_, "name").toString(rt_).utf8(rt_) << ": " << object.getProperty(rt_, "message").toString(rt_).utf8(rt_) << ']'; return ss.str(); } std::string JSISerializer::stringifySet(const jsi::Object &object) { std::stringstream ss; jsi::Function arrayFrom = rt_.global() .getPropertyAsObject(rt_, "Array") .getPropertyAsFunction(rt_, "from"); jsi::Object result = arrayFrom.call(rt_, object).asObject(rt_); if (!result.isArray(rt_)) { return "[Set]"; } auto arr = result.asArray(rt_); ss << "Set {"; for (size_t i = 0, length = arr.size(rt_); i < length; i++) { ss << stringifyJSIValueRecursively(arr.getValueAtIndex(rt_, i)); if (i != length - 1) { ss << ", "; } } ss << '}'; return ss.str(); } std::string JSISerializer::stringifyMap(const jsi::Object &object) { std::stringstream ss; jsi::Function arrayFrom = rt_.global() .getPropertyAsObject(rt_, "Array") .getPropertyAsFunction(rt_, "from"); jsi::Object result = arrayFrom.call(rt_, object).asObject(rt_); if (!result.isArray(rt_)) { return "[Map]"; } auto arr = result.asArray(rt_); ss << "Map {"; for (size_t i = 0, length = arr.size(rt_); i < length; i++) { auto pair = arr.getValueAtIndex(rt_, i).asObject(rt_).getArray(rt_); auto key = pair.getValueAtIndex(rt_, 0); auto value = pair.getValueAtIndex(rt_, 1); ss << stringifyJSIValueRecursively(key) << ": " << stringifyJSIValueRecursively(value); if (i != length - 1) { ss << ", "; } } ss << '}'; return ss.str(); } std::string JSISerializer::stringifyRecursiveType(const jsi::Object &object) { auto type = getObjectTypeName(rt_, object); if (type == "Array") { return "[...]"; } if (type == "Object") { return "{...}"; } return "..."; } std::string JSISerializer::stringifyWithToString(const jsi::Object &object) { return object.getPropertyAsFunction(rt_, "toString") .callWithThis(rt_, object) .toString(rt_) .utf8(rt_); } std::string JSISerializer::stringifyJSIValueRecursively( const jsi::Value &value, bool isTopLevel) { if (value.isBool() || value.isNumber()) { return value.toString(rt_).utf8(rt_); } if (value.isString()) { return isTopLevel ? value.getString(rt_).utf8(rt_) : '"' + value.getString(rt_).utf8(rt_) + '"'; } if (value.isSymbol()) { return value.getSymbol(rt_).toString(rt_); } #if REACT_NATIVE_MINOR_VERSION >= 71 if (value.isBigInt()) { return value.getBigInt(rt_).toString(rt_).utf8(rt_) + 'n'; } #endif if (value.isUndefined()) { return "undefined"; } if (value.isNull()) { return "null"; } if (value.isObject()) { jsi::Object object = value.asObject(rt_); if (hasBeenVisited(object)) { return stringifyRecursiveType(object); } markAsVisited(object); if (object.isArray(rt_)) { return stringifyArray(object.getArray(rt_)); } if (object.isFunction(rt_)) { return stringifyFunction(object.getFunction(rt_)); } if (object.isHostObject(rt_)) { return stringifyHostObject(*object.getHostObject(rt_)); } if (isInstanceOfAny(rt_, object, SUPPORTED_ERROR_TYPES)) { return stringifyError(object); } if (isInstanceOfAny(rt_, object, SUPPORTED_INDEXED_COLLECTION_TYPES) || isInstanceOfAny(rt_, object, SUPPORTED_STRUCTURED_DATA_TYPES) || isInstanceOfAny(rt_, object, SUPPORTED_MANAGING_MEMORY_TYPES) || isInstanceOfAny(rt_, object, SUPPORTED_ABSTRACTION_OBJECT_TYPES) || isInstanceOfAny(rt_, object, SUPPORTED_REFLECTION_TYPES) || isInstanceOf(rt_, object, "Intl") || isInstanceOf(rt_, object, "WeakMap") || isInstanceOf(rt_, object, "WeakSet")) { // TODO: Consider extending this log info return stringifyWithName(object); } if (isInstanceOf(rt_, object, "Date") || isInstanceOf(rt_, object, "RegExp")) { return stringifyWithToString(object); } if (isInstanceOf(rt_, object, "Map")) { return stringifyMap(object); } if (isInstanceOf(rt_, object, "Set")) { return stringifySet(object); } return stringifyObject(object); } throw std::runtime_error("[Reanimated] Unsupported value type."); } std::string stringifyJSIValue(jsi::Runtime &rt, const jsi::Value &value) { JSISerializer serializer(rt); return serializer.stringifyJSIValueRecursively(value, true); }