use super::*;
#[derive(Debug, Clone, Copy)]
pub struct ValueDisplay<'value> {
pub(super) value: &'value Value,
}
macro_rules! print_obj_value {
(all of $obj:expr, $display_fn:ident, $indent:expr, $encounters:expr) => {
{
let mut internals = print_obj_value!(internals of $obj, $display_fn, $indent, $encounters);
let mut props = print_obj_value!(props of $obj, $display_fn, $indent, $encounters, true);
props.reserve(internals.len());
props.append(&mut internals);
props
}
};
(internals of $obj:expr, $display_fn:ident, $indent:expr, $encounters:expr) => {
{
let object = $obj.borrow();
if object.prototype_instance().is_object() {
vec![format!(
"{:>width$}: {}",
"__proto__",
$display_fn(object.prototype_instance(), $encounters, $indent.wrapping_add(4), true),
width = $indent,
)]
} else {
vec![format!(
"{:>width$}: {}",
"__proto__",
object.prototype_instance().display(),
width = $indent,
)]
}
}
};
(props of $obj:expr, $display_fn:ident, $indent:expr, $encounters:expr, $print_internals:expr) => {
print_obj_value!(impl $obj, |(key, val)| {
let v = &val
.value
.as_ref()
.expect("Could not get the property's value");
format!(
"{:>width$}: {}",
key,
$display_fn(v, $encounters, $indent.wrapping_add(4), $print_internals),
width = $indent,
)
})
};
(impl $v:expr, $f:expr) => {
$v
.borrow()
.iter()
.map($f)
.collect::<Vec<String>>()
};
}
pub(crate) fn log_string_from(x: &Value, print_internals: bool, print_children: bool) -> String {
match x {
Value::Object(ref v) => {
match v.borrow().data {
ObjectData::String(ref string) => format!("String {{ \"{}\" }}", string),
ObjectData::Boolean(boolean) => format!("Boolean {{ {} }}", boolean),
ObjectData::Number(rational) => {
if rational.is_sign_negative() && rational == 0.0 {
"Number { -0 }".to_string()
} else {
let mut buffer = ryu_js::Buffer::new();
format!("Number {{ {} }}", buffer.format(rational))
}
}
ObjectData::Array => {
let len = v
.borrow()
.get_own_property(&PropertyKey::from("length"))
.value
.clone()
.expect("Could not borrow value")
.as_number()
.unwrap() as i32;
if print_children {
if len == 0 {
return String::from("[]");
}
let arr = (0..len)
.map(|i| {
log_string_from(
&v.borrow()
.get_own_property(&i.into())
.value
.clone()
.expect("Could not borrow value"),
print_internals,
false,
)
})
.collect::<Vec<String>>()
.join(", ");
format!("[ {} ]", arr)
} else {
format!("Array({})", len)
}
}
ObjectData::Map(ref map) => {
let size = v
.borrow()
.get_own_property(&PropertyKey::from("size"))
.value
.clone()
.expect("Could not borrow value")
.as_number()
.unwrap() as i32;
if size == 0 {
return String::from("Map(0)");
}
if print_children {
let mappings = map
.iter()
.map(|(key, value)| {
let key = log_string_from(key, print_internals, false);
let value = log_string_from(value, print_internals, false);
format!("{} → {}", key, value)
})
.collect::<Vec<String>>()
.join(", ");
format!("Map {{ {} }}", mappings)
} else {
format!("Map({})", size)
}
}
_ => display_obj(&x, print_internals),
}
}
Value::Symbol(ref symbol) => symbol.to_string(),
_ => format!("{}", x.display()),
}
}
pub(crate) fn display_obj(v: &Value, print_internals: bool) -> String {
fn address_of<T>(t: &T) -> usize {
let my_ptr: *const T = t;
my_ptr as usize
}
let mut encounters = HashSet::new();
if let Value::Object(object) = v {
if object.borrow().is_error() {
let name = v.get_field("name");
let message = v.get_field("message");
return format!("{}: {}", name.display(), message.display());
}
}
fn display_obj_internal(
data: &Value,
encounters: &mut HashSet<usize>,
indent: usize,
print_internals: bool,
) -> String {
if let Value::Object(ref v) = *data {
let addr = address_of(v.as_ref());
if encounters.contains(&addr) {
return String::from("[Cycle]");
}
encounters.insert(addr);
let result = if print_internals {
print_obj_value!(all of v, display_obj_internal, indent, encounters).join(",\n")
} else {
print_obj_value!(props of v, display_obj_internal, indent, encounters, print_internals)
.join(",\n")
};
encounters.remove(&addr);
let closing_indent = String::from_utf8(vec![b' '; indent.wrapping_sub(4)])
.expect("Could not create the closing brace's indentation string");
format!("{{\n{}\n{}}}", result, closing_indent)
} else {
format!("{}", data.display())
}
}
display_obj_internal(v, &mut encounters, 4, print_internals)
}
impl Display for ValueDisplay<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.value {
Value::Null => write!(f, "null"),
Value::Undefined => write!(f, "undefined"),
Value::Boolean(v) => write!(f, "{}", v),
Value::Symbol(ref symbol) => match symbol.description() {
Some(description) => write!(f, "Symbol({})", description),
None => write!(f, "Symbol()"),
},
Value::String(ref v) => write!(f, "\"{}\"", v),
Value::Rational(v) => format_rational(*v, f),
Value::Object(_) => write!(f, "{}", log_string_from(self.value, true, true)),
Value::Integer(v) => write!(f, "{}", v),
Value::BigInt(ref num) => write!(f, "{}n", num),
}
}
}
fn format_rational(v: f64, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if v.is_sign_negative() && v == 0.0 {
f.write_str("-0")
} else {
let mut buffer = ryu_js::Buffer::new();
write!(f, "{}", buffer.format(v))
}
}