ion/format/
prefix.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 */
6
7use 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}