boa/builtins/error/
mod.rs

1//! This module implements the global `Error` object.
2//!
3//! Error objects are thrown when runtime errors occur.
4//! The Error object can also be used as a base object for user-defined exceptions.
5//!
6//! More information:
7//!  - [MDN documentation][mdn]
8//!  - [ECMAScript reference][spec]
9//!
10//! [spec]: https://tc39.es/ecma262/#sec-error-objects
11//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
12
13use crate::{
14    builtins::BuiltIn,
15    context::StandardObjects,
16    object::{internal_methods::get_prototype_from_constructor, ConstructorBuilder, ObjectData},
17    profiler::BoaProfiler,
18    property::Attribute,
19    Context, JsResult, JsValue,
20};
21
22pub(crate) mod eval;
23pub(crate) mod range;
24pub(crate) mod reference;
25pub(crate) mod syntax;
26pub(crate) mod r#type;
27pub(crate) mod uri;
28
29#[cfg(test)]
30mod tests;
31
32pub(crate) use self::eval::EvalError;
33pub(crate) use self::r#type::TypeError;
34pub(crate) use self::range::RangeError;
35pub(crate) use self::reference::ReferenceError;
36pub(crate) use self::syntax::SyntaxError;
37pub(crate) use self::uri::UriError;
38
39/// Built-in `Error` object.
40#[derive(Debug, Clone, Copy)]
41pub(crate) struct Error;
42
43impl BuiltIn for Error {
44    const NAME: &'static str = "Error";
45
46    fn attribute() -> Attribute {
47        Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
48    }
49
50    fn init(context: &mut Context) -> (&'static str, JsValue, Attribute) {
51        let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
52
53        let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
54        let error_object = ConstructorBuilder::with_standard_object(
55            context,
56            Self::constructor,
57            context.standard_objects().error_object().clone(),
58        )
59        .name(Self::NAME)
60        .length(Self::LENGTH)
61        .property("name", Self::NAME, attribute)
62        .property("message", "", attribute)
63        .method(Self::to_string, "toString", 0)
64        .build();
65
66        (Self::NAME, error_object.into(), Self::attribute())
67    }
68}
69
70impl Error {
71    /// The amount of arguments this function object takes.
72    pub(crate) const LENGTH: usize = 1;
73
74    /// `Error( message )`
75    ///
76    /// Create a new error object.
77    pub(crate) fn constructor(
78        new_target: &JsValue,
79        args: &[JsValue],
80        context: &mut Context,
81    ) -> JsResult<JsValue> {
82        let prototype =
83            get_prototype_from_constructor(new_target, StandardObjects::error_object, context)?;
84        let obj = context.construct_object();
85        obj.set_prototype_instance(prototype.into());
86        let this = JsValue::new(obj);
87        if let Some(message) = args.get(0) {
88            if !message.is_undefined() {
89                this.set_field("message", message.to_string(context)?, false, context)?;
90            }
91        }
92
93        // This value is used by console.log and other routines to match Object type
94        // to its Javascript Identifier (global constructor method name)
95        this.set_data(ObjectData::error());
96        Ok(this)
97    }
98
99    /// `Error.prototype.toString()`
100    ///
101    /// The toString() method returns a string representing the specified Error object.
102    ///
103    /// More information:
104    ///  - [MDN documentation][mdn]
105    ///  - [ECMAScript reference][spec]
106    ///
107    /// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring
108    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
109    #[allow(clippy::wrong_self_convention)]
110    pub(crate) fn to_string(
111        this: &JsValue,
112        _: &[JsValue],
113        context: &mut Context,
114    ) -> JsResult<JsValue> {
115        if !this.is_object() {
116            return context.throw_type_error("'this' is not an Object");
117        }
118        let name = this.get_field("name", context)?;
119        let name_to_string;
120        let name = if name.is_undefined() {
121            "Error"
122        } else {
123            name_to_string = name.to_string(context)?;
124            name_to_string.as_str()
125        };
126
127        let message = this.get_field("message", context)?;
128        let message_to_string;
129        let message = if message.is_undefined() {
130            ""
131        } else {
132            message_to_string = message.to_string(context)?;
133            message_to_string.as_str()
134        };
135
136        if name.is_empty() {
137            Ok(message.into())
138        } else if message.is_empty() {
139            Ok(name.into())
140        } else {
141            Ok(format!("{}: {}", name, message).into())
142        }
143    }
144}