-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
<!-- Thanks for submitting a pull request! We appreciate you spending the time to work on these changes. Please follow the template so that the reviewers can easily understand what the code changes affect. --> ## Summary This PR expands the logic of _log function to print more complex types in JSON-like style. It adds support for the types listed below: - JSI types - JS Objects and Collections ## Test plan <!-- Provide a minimal but complete code snippet that can be used to test out this change along with instructions how to run it and a description of the expected behavior. --> You can use and example app named `Log test` --------- Co-authored-by: Michał Mąka <62388446+michalmaka@users.noreply.github.com> Co-authored-by: Tomek Zawadzki <tomekzawadzki98@gmail.com>
- Loading branch information
1 parent
639f2bc
commit 5dac63a
Showing
14 changed files
with
553 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,340 @@ | ||
#include "JSISerializer.h" | ||
|
||
#include <cxxabi.h> | ||
#include <iostream> | ||
#include <sstream> | ||
|
||
const std::vector<std::string> SUPPORTED_ERROR_TYPES = { | ||
"Error", | ||
"AggregateError", | ||
"EvalError", | ||
"RangeError", | ||
"ReferenceError", | ||
"SyntaxError", | ||
"TypeError", | ||
"URIError", | ||
"InternalError"}; | ||
|
||
const std::vector<std::string> SUPPORTED_INDEXED_COLLECTION_TYPES = { | ||
"Int8Array", | ||
"Uint8Array", | ||
"Uint8ClampedArray", | ||
"Int16Array", | ||
"Uint16Array", | ||
"Int32Array", | ||
"Uint32Array", | ||
"BigInt64Array", | ||
"BigUint64Array", | ||
"Float32Array", | ||
"Float64Array", | ||
}; | ||
|
||
const std::vector<std::string> SUPPORTED_STRUCTURED_DATA_TYPES = { | ||
"ArrayBuffer", | ||
"SharedArrayBuffer", | ||
"DataView", | ||
"Atomics", | ||
"JSON", | ||
}; | ||
|
||
const std::vector<std::string> SUPPORTED_MANAGING_MEMORY_TYPES = { | ||
"WeakRef", | ||
"FinalizationRegistry", | ||
}; | ||
|
||
const std::vector<std::string> SUPPORTED_ABSTRACTION_OBJECT_TYPES = { | ||
"Iterator", | ||
"AsyncIterator", | ||
"Promise", | ||
"GeneratorFunction", | ||
"AsyncGeneratorFunction", | ||
"Generator", | ||
"AsyncGenerator", | ||
"AsyncFunction", | ||
}; | ||
|
||
const std::vector<std::string> 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<std::string> &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(); | ||
auto lastKey = props.back().utf8(rt_); | ||
|
||
if (propsCount > 0) { | ||
ss << '{'; | ||
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 >= 70 | ||
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("unsupported value type"); | ||
} | ||
|
||
std::string stringifyJSIValue(jsi::Runtime &rt, const jsi::Value &value) { | ||
JSISerializer serializer(rt); | ||
|
||
return serializer.stringifyJSIValueRecursively(value, true); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
#pragma once | ||
|
||
#include <jsi/jsi.h> | ||
#include <string> | ||
#include <vector> | ||
|
||
using namespace facebook; | ||
|
||
namespace { | ||
class JSISerializer { | ||
public: | ||
explicit JSISerializer(jsi::Runtime &rt); | ||
std::string stringifyJSIValueRecursively( | ||
const jsi::Value &value, | ||
bool isTopLevel = false); | ||
|
||
private: | ||
std::string stringifyArray(const jsi::Array &arr); | ||
std::string stringifyFunction(const jsi::Function &func); | ||
std::string stringifyHostObject(jsi::HostObject &hostObject); | ||
std::string stringifyObject(const jsi::Object &object); | ||
std::string stringifyError(const jsi::Object &object); | ||
std::string stringifySet(const jsi::Object &object); | ||
std::string stringifyMap(const jsi::Object &object); | ||
std::string stringifyWithName(const jsi::Object &object); | ||
std::string stringifyWithToString(const jsi::Object &object); | ||
std::string stringifyRecursiveType(const jsi::Object &object); | ||
|
||
bool hasBeenVisited(const jsi::Object &object) { | ||
return visitedNodes_.getPropertyAsFunction(rt_, "has") | ||
.callWithThis(rt_, visitedNodes_, object) | ||
.getBool(); | ||
} | ||
|
||
void markAsVisited(const jsi::Object &object) { | ||
visitedNodes_.getPropertyAsFunction(rt_, "add") | ||
.callWithThis(rt_, visitedNodes_, object); | ||
} | ||
|
||
jsi::Runtime &rt_; | ||
jsi::Object visitedNodes_; | ||
}; | ||
} // namespace | ||
|
||
std::string stringifyJSIValue(jsi::Runtime &rt, const jsi::Value &value); |
Oops, something went wrong.