1use std::fmt;
8use std::fmt::{Display as _, Formatter, Write as _};
9
10use colored::{Color, Colorize as _};
11use mozjs::jsapi::{IdentifyStandardPrototype, JS_GetConstructor, JS_GetPrototype, JS_HasInstance, JSProtoKey};
12
13use crate::conversions::ToValue as _;
14use crate::format::Config;
15use crate::symbol::WellKnownSymbolCode;
16use crate::{Context, Function, Object};
17
18fn get_constructor_name(cx: &Context, object: &Object, proto: &mut Object) -> Option<String> {
19 let value = object.as_value(cx);
20 let constructor = unsafe {
21 JS_GetPrototype(cx.as_ptr(), object.handle().into(), proto.handle_mut().into());
22 if proto.handle().get().is_null() {
23 return None;
24 }
25
26 cx.root(JS_GetConstructor(cx.as_ptr(), proto.handle().into()))
27 };
28
29 let constructor_fn = Function::from_object(cx, &constructor)?;
30 let name = constructor_fn.name(cx).ok()?;
31 let mut has_instance = false;
32 (unsafe {
33 JS_HasInstance(
34 cx.as_ptr(),
35 constructor.handle().into(),
36 value.handle().into(),
37 &raw mut has_instance,
38 )
39 } && has_instance)
40 .then_some(name)
41}
42
43fn get_tag(cx: &Context, object: &Object) -> crate::Result<Option<String>> {
44 if object.has_own(cx, WellKnownSymbolCode::ToStringTag)
45 && let Some(tag) = object.get_as::<_, String>(cx, WellKnownSymbolCode::ToStringTag, true, ())?
46 {
47 return Ok((!tag.is_empty()).then_some(tag));
48 }
49 Ok(None)
50}
51
52fn write_tag(f: &mut Formatter, colour: Color, tag: Option<&str>, fallback: &str) -> fmt::Result {
53 if let Some(tag) = tag
54 && tag != fallback
55 {
56 "[".color(colour).fmt(f)?;
57 tag.color(colour).fmt(f)?;
58 "] ".color(colour).fmt(f)?;
59 }
60 Ok(())
61}
62
63pub(crate) fn write_prefix(
64 f: &mut Formatter, cx: &Context, cfg: Config, object: &Object, fallback: &str, standard: JSProtoKey,
65) -> fmt::Result {
66 let mut proto = Object::null(cx);
67 let constructor_name = get_constructor_name(cx, object, &mut proto);
68 let tag = get_tag(cx, object)?;
69
70 let colour = cfg.colours.object;
71 let mut fallback = fallback;
72 if let Some(name) = &constructor_name {
73 let proto = unsafe { IdentifyStandardPrototype(proto.handle().get()) };
74 if proto != standard {
75 name.color(colour).fmt(f)?;
76 f.write_char(' ')?;
77 fallback = name;
78 } else if tag.is_some() {
79 fallback.color(colour).fmt(f)?;
80 f.write_char(' ')?;
81 }
82 } else {
83 "[".color(colour).fmt(f)?;
84 fallback.color(colour).fmt(f)?;
85 ": null prototype] ".color(colour).fmt(f)?;
86 }
87 write_tag(f, colour, tag.as_deref(), fallback)
88}