1use crate::object::ObjectKind;
2
3use super::*;
4
5#[derive(Debug, Clone, Copy)]
7pub struct ValueDisplay<'value> {
8 pub(super) value: &'value JsValue,
9}
10
11macro_rules! print_obj_value {
20 (all of $obj:expr, $display_fn:ident, $indent:expr, $encounters:expr) => {
21 {
22 let mut internals = print_obj_value!(internals of $obj, $display_fn, $indent, $encounters);
23 let mut props = print_obj_value!(props of $obj, $display_fn, $indent, $encounters, true);
24
25 props.reserve(internals.len());
26 props.append(&mut internals);
27
28 props
29 }
30 };
31 (internals of $obj:expr, $display_fn:ident, $indent:expr, $encounters:expr) => {
32 {
33 let object = $obj.borrow();
34 if object.prototype_instance().is_object() {
35 vec![format!(
36 "{:>width$}: {}",
37 "__proto__",
38 $display_fn(object.prototype_instance(), $encounters, $indent.wrapping_add(4), true),
39 width = $indent,
40 )]
41 } else {
42 vec![format!(
43 "{:>width$}: {}",
44 "__proto__",
45 object.prototype_instance().display(),
46 width = $indent,
47 )]
48 }
49 }
50 };
51 (props of $obj:expr, $display_fn:ident, $indent:expr, $encounters:expr, $print_internals:expr) => {
52 print_obj_value!(impl $obj, |(key, val)| {
53 if val.is_data_descriptor() {
54 let v = &val.expect_value();
55 format!(
56 "{:>width$}: {}",
57 key,
58 $display_fn(v, $encounters, $indent.wrapping_add(4), $print_internals),
59 width = $indent,
60 )
61 } else {
62 let display = match (val.set().is_some(), val.get().is_some()) {
63 (true, true) => "Getter & Setter",
64 (true, false) => "Setter",
65 (false, true) => "Getter",
66 _ => "No Getter/Setter"
67 };
68 format!("{:>width$}: {}", key, display, width = $indent)
69 }
70 })
71 };
72
73 (impl $v:expr, $f:expr) => {
76 $v
77 .borrow()
78 .properties()
79 .iter()
80 .map($f)
81 .collect::<Vec<String>>()
82 };
83}
84
85pub(crate) fn log_string_from(x: &JsValue, print_internals: bool, print_children: bool) -> String {
86 match x {
87 JsValue::Object(ref v) => {
89 match v.borrow().kind() {
92 ObjectKind::String(ref string) => format!("String {{ \"{}\" }}", string),
93 ObjectKind::Boolean(boolean) => format!("Boolean {{ {} }}", boolean),
94 ObjectKind::Number(rational) => {
95 if rational.is_sign_negative() && *rational == 0.0 {
96 "Number { -0 }".to_string()
97 } else {
98 let mut buffer = ryu_js::Buffer::new();
99 format!("Number {{ {} }}", buffer.format(*rational))
100 }
101 }
102 ObjectKind::Array => {
103 let len = v
104 .borrow()
105 .properties()
106 .get(&PropertyKey::from("length"))
107 .unwrap()
109 .expect_value()
111 .as_number()
112 .map(|n| n as i32)
113 .unwrap_or_default();
114
115 if print_children {
116 if len == 0 {
117 return String::from("[]");
118 }
119
120 let arr = (0..len)
121 .map(|i| {
122 log_string_from(
125 v.borrow()
126 .properties()
127 .get(&i.into())
128 .and_then(|p| p.value())
130 .unwrap_or(&JsValue::Undefined),
131 print_internals,
132 false,
133 )
134 })
135 .collect::<Vec<String>>()
136 .join(", ");
137
138 format!("[ {} ]", arr)
139 } else {
140 format!("Array({})", len)
141 }
142 }
143 ObjectKind::Map(ref map) => {
144 let size = map.len();
145 if size == 0 {
146 return String::from("Map(0)");
147 }
148
149 if print_children {
150 let mappings = map
151 .iter()
152 .map(|(key, value)| {
153 let key = log_string_from(key, print_internals, false);
154 let value = log_string_from(value, print_internals, false);
155 format!("{} → {}", key, value)
156 })
157 .collect::<Vec<String>>()
158 .join(", ");
159 format!("Map {{ {} }}", mappings)
160 } else {
161 format!("Map({})", size)
162 }
163 }
164 ObjectKind::Set(ref set) => {
165 let size = set.size();
166
167 if size == 0 {
168 return String::from("Set(0)");
169 }
170
171 if print_children {
172 let entries = set
173 .iter()
174 .map(|value| log_string_from(value, print_internals, false))
175 .collect::<Vec<String>>()
176 .join(", ");
177 format!("Set {{ {} }}", entries)
178 } else {
179 format!("Set({})", size)
180 }
181 }
182 _ => display_obj(x, print_internals),
183 }
184 }
185 JsValue::Symbol(ref symbol) => symbol.to_string(),
186 _ => format!("{}", x.display()),
187 }
188}
189
190pub(crate) fn display_obj(v: &JsValue, print_internals: bool) -> String {
192 fn address_of<T>(t: &T) -> usize {
195 let my_ptr: *const T = t;
196 my_ptr as usize
197 }
198
199 let mut encounters = HashSet::new();
202
203 if let JsValue::Object(object) = v {
204 if object.borrow().is_error() {
205 let name = v
206 .get_property("name")
207 .as_ref()
208 .and_then(|d| d.value())
209 .unwrap_or(&JsValue::Undefined)
210 .display()
211 .to_string();
212 let message = v
213 .get_property("message")
214 .as_ref()
215 .and_then(|d| d.value())
216 .unwrap_or(&JsValue::Undefined)
217 .display()
218 .to_string();
219 return format!("{}: {}", name, message);
220 }
221 }
222
223 fn display_obj_internal(
224 data: &JsValue,
225 encounters: &mut HashSet<usize>,
226 indent: usize,
227 print_internals: bool,
228 ) -> String {
229 if let JsValue::Object(ref v) = *data {
230 let addr = address_of(v.as_ref());
232
233 if encounters.contains(&addr) {
236 return String::from("[Cycle]");
237 }
238
239 encounters.insert(addr);
241
242 let result = if print_internals {
243 print_obj_value!(all of v, display_obj_internal, indent, encounters).join(",\n")
244 } else {
245 print_obj_value!(props of v, display_obj_internal, indent, encounters, print_internals)
246 .join(",\n")
247 };
248
249 encounters.remove(&addr);
252
253 let closing_indent = String::from_utf8(vec![b' '; indent.wrapping_sub(4)])
254 .expect("Could not create the closing brace's indentation string");
255
256 format!("{{\n{}\n{}}}", result, closing_indent)
257 } else {
258 format!("{}", data.display())
260 }
261 }
262
263 display_obj_internal(v, &mut encounters, 4, print_internals)
264}
265
266impl Display for ValueDisplay<'_> {
267 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
268 match self.value {
269 JsValue::Null => write!(f, "null"),
270 JsValue::Undefined => write!(f, "undefined"),
271 JsValue::Boolean(v) => write!(f, "{}", v),
272 JsValue::Symbol(ref symbol) => match symbol.description() {
273 Some(description) => write!(f, "Symbol({})", description),
274 None => write!(f, "Symbol()"),
275 },
276 JsValue::String(ref v) => write!(f, "\"{}\"", v),
277 JsValue::Rational(v) => format_rational(*v, f),
278 JsValue::Object(_) => write!(f, "{}", log_string_from(self.value, true, true)),
279 JsValue::Integer(v) => write!(f, "{}", v),
280 JsValue::BigInt(ref num) => write!(f, "{}n", num),
281 }
282 }
283}
284
285fn format_rational(v: f64, f: &mut fmt::Formatter<'_>) -> fmt::Result {
291 if v.is_sign_negative() && v == 0.0 {
292 f.write_str("-0")
293 } else {
294 let mut buffer = ryu_js::Buffer::new();
295 write!(f, "{}", buffer.format(v))
296 }
297}