Skip to main content

boa_engine/builtins/error/
aggregate.rs

1//! This module implements the global `AggregateError` object.
2//!
3//! More information:
4//!  - [MDN documentation][mdn]
5//!  - [ECMAScript reference][spec]
6//!
7//! [spec]: https://tc39.es/ecma262/#sec-aggregate-error
8//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError
9
10use crate::{
11    Context, JsArgs, JsResult, JsString, JsValue,
12    builtins::{
13        Array, BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,
14        iterable::IteratorHint,
15    },
16    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
17    js_string,
18    object::{JsObject, internal_methods::get_prototype_from_constructor},
19    property::{Attribute, PropertyDescriptorBuilder},
20    realm::Realm,
21    string::StaticJsStrings,
22};
23
24use super::{Error, ErrorKind};
25
26#[derive(Debug, Clone, Copy)]
27pub(crate) struct AggregateError;
28
29impl IntrinsicObject for AggregateError {
30    fn init(realm: &Realm) {
31        let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
32        BuiltInBuilder::from_standard_constructor::<Self>(realm)
33            .prototype(realm.intrinsics().constructors().error().constructor())
34            .inherits(Some(realm.intrinsics().constructors().error().prototype()))
35            .property(js_string!("name"), Self::NAME, attribute)
36            .property(js_string!("message"), js_string!(), attribute)
37            .build();
38    }
39
40    fn get(intrinsics: &Intrinsics) -> JsObject {
41        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()
42    }
43}
44
45impl BuiltInObject for AggregateError {
46    const NAME: JsString = StaticJsStrings::AGGREGATE_ERROR;
47}
48
49impl BuiltInConstructor for AggregateError {
50    const CONSTRUCTOR_ARGUMENTS: usize = 2;
51    const PROTOTYPE_STORAGE_SLOTS: usize = 2;
52    const CONSTRUCTOR_STORAGE_SLOTS: usize = 0;
53
54    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =
55        StandardConstructors::aggregate_error;
56
57    /// [`AggregateError ( errors, message [ , options ] )`][spec]
58    ///
59    /// Creates a new aggregate error object.
60    ///
61    /// [spec]: AggregateError ( errors, message [ , options ] )
62    fn constructor(
63        new_target: &JsValue,
64        args: &[JsValue],
65        context: &mut Context,
66    ) -> JsResult<JsValue> {
67        // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget.
68        let new_target = &if new_target.is_undefined() {
69            context
70                .active_function_object()
71                .unwrap_or_else(|| {
72                    context
73                        .intrinsics()
74                        .constructors()
75                        .aggregate_error()
76                        .constructor()
77                })
78                .into()
79        } else {
80            new_target.clone()
81        };
82        // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%AggregateError.prototype%", « [[ErrorData]] »).
83        let prototype = get_prototype_from_constructor(
84            new_target,
85            StandardConstructors::aggregate_error,
86            context,
87        )?;
88        let o = JsObject::from_proto_and_data_with_shared_shape(
89            context.root_shape(),
90            prototype,
91            Error::with_caller_position(ErrorKind::Aggregate, context),
92        );
93
94        // 3. If message is not undefined, then
95        let message = args.get_or_undefined(1);
96        if !message.is_undefined() {
97            // a. Let msg be ? ToString(message).
98            let msg = message.to_string(context)?;
99
100            // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg).
101            o.create_non_enumerable_data_property_or_throw(js_string!("message"), msg, context);
102        }
103
104        // 4. Perform ? InstallErrorCause(O, options).
105        Error::install_error_cause(&o, args.get_or_undefined(2), context)?;
106
107        // 5. Let errorsList be ? IteratorToList(? GetIterator(errors, sync)).
108        let errors = args.get_or_undefined(0);
109        let errors_list = errors
110            .get_iterator(IteratorHint::Sync, context)?
111            .into_list(context)?;
112
113        // 6. Perform ! DefinePropertyOrThrow(O, "errors",
114        //    PropertyDescriptor {
115        //      [[Configurable]]: true,
116        //      [[Enumerable]]: false,
117        //      [[Writable]]: true,
118        //      [[Value]]: CreateArrayFromList(errorsList)
119        //    }).
120        o.define_property_or_throw(
121            js_string!("errors"),
122            PropertyDescriptorBuilder::new()
123                .configurable(true)
124                .enumerable(false)
125                .writable(true)
126                .value(Array::create_array_from_list(errors_list, context))
127                .build(),
128            context,
129        )
130        .expect("should not fail according to spec");
131
132        // 5. Return O.
133        Ok(o.into())
134    }
135}