Skip to main content

boa_engine/builtins/
mod.rs

1//! Boa's ECMAScript built-in object implementations, e.g. Object, String, Math, Array, etc.
2
3pub mod array;
4pub mod array_buffer;
5pub mod async_function;
6pub mod async_generator;
7pub mod async_generator_function;
8pub mod atomics;
9pub mod bigint;
10pub mod boolean;
11pub mod dataview;
12pub mod date;
13pub mod error;
14pub mod eval;
15pub mod finalization_registry;
16pub mod function;
17pub mod generator;
18pub mod generator_function;
19#[cfg(feature = "annex-b")]
20pub mod is_html_dda;
21pub mod iterable;
22pub mod json;
23pub mod map;
24pub mod math;
25pub mod number;
26pub mod object;
27pub mod promise;
28pub mod proxy;
29pub mod reflect;
30pub mod regexp;
31pub mod set;
32pub mod string;
33pub mod symbol;
34pub mod typed_array;
35pub mod uri;
36pub mod weak;
37pub mod weak_map;
38pub mod weak_set;
39
40mod builder;
41
42use builder::BuiltInBuilder;
43use error::Error;
44use num_traits::Zero;
45
46#[cfg(feature = "annex-b")]
47pub mod escape;
48
49#[cfg(feature = "intl")]
50pub mod intl;
51
52// TODO: remove `cfg` when `Temporal` gets to stage 4.
53#[cfg(any(feature = "intl", feature = "temporal"))]
54pub(crate) mod options;
55
56#[cfg(feature = "temporal")]
57pub mod temporal;
58
59pub(crate) use self::{
60    array::Array,
61    async_function::AsyncFunction,
62    bigint::BigInt,
63    boolean::Boolean,
64    dataview::DataView,
65    date::Date,
66    error::{
67        AggregateError, EvalError, RangeError, ReferenceError, SyntaxError, TypeError, UriError,
68    },
69    eval::Eval,
70    finalization_registry::FinalizationRegistry,
71    function::BuiltInFunctionObject,
72    json::Json,
73    map::Map,
74    math::Math,
75    number::{IsFinite, IsNaN, Number, ParseFloat, ParseInt},
76    object::OrdinaryObject,
77    promise::Promise,
78    proxy::Proxy,
79    reflect::Reflect,
80    regexp::RegExp,
81    set::Set,
82    string::String,
83    symbol::Symbol,
84    typed_array::{
85        BigInt64Array, BigUint64Array, Float32Array, Float64Array, Int8Array, Int16Array,
86        Int32Array, Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array,
87    },
88};
89
90use crate::{
91    Context, JsResult, JsString, JsValue,
92    builtins::{
93        array::ArrayIterator,
94        array_buffer::{ArrayBuffer, SharedArrayBuffer},
95        async_generator::AsyncGenerator,
96        async_generator_function::AsyncGeneratorFunction,
97        atomics::Atomics,
98        error::r#type::ThrowTypeError,
99        generator::Generator,
100        generator_function::GeneratorFunction,
101        iterable::iterator_constructor::IteratorConstructor,
102        iterable::iterator_helper::IteratorHelper,
103        iterable::wrap_for_valid_iterator::WrapForValidIterator,
104        iterable::{AsyncFromSyncIterator, AsyncIterator, Iterator},
105        map::MapIterator,
106        regexp::RegExpStringIterator,
107        set::SetIterator,
108        string::StringIterator,
109        typed_array::BuiltinTypedArray,
110        uri::{DecodeUri, DecodeUriComponent, EncodeUri, EncodeUriComponent},
111        weak::WeakRef,
112        weak_map::WeakMap,
113        weak_set::WeakSet,
114    },
115    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
116    js_string,
117    object::JsObject,
118    property::{Attribute, PropertyDescriptor},
119    realm::Realm,
120};
121
122/// A [Well-Known Intrinsic Object].
123///
124/// Well-known intrinsics are built-in objects that are explicitly referenced by the algorithms of
125/// the specification and which usually have realm-specific identities.
126///
127/// [Well-Known Intrinsic Object]: https://tc39.es/ecma262/#sec-well-known-intrinsic-objects
128pub(crate) trait IntrinsicObject {
129    /// Initializes the intrinsic object.
130    ///
131    /// This is where the methods, properties, static methods and the constructor of a built-in must
132    /// be initialized to be accessible from ECMAScript.
133    fn init(realm: &Realm);
134
135    /// Gets the intrinsic object.
136    fn get(intrinsics: &Intrinsics) -> JsObject;
137}
138
139/// A [built-in object].
140///
141/// This trait must be implemented for any global built-in that lives in the global context of a script.
142///
143/// [built-in object]: https://tc39.es/ecma262/#sec-built-in-object
144pub(crate) trait BuiltInObject: IntrinsicObject {
145    /// Binding name of the builtin inside the global object.
146    ///
147    /// E.g. If you want access the properties of a `Complex` built-in with the name `Cplx` you must
148    /// assign `"Cplx"` to this constant, making any property inside it accessible from ECMAScript
149    /// as `Cplx.prop`
150    // `JsString` can only be const-constructed for static strings.
151    #[allow(clippy::declare_interior_mutable_const)]
152    const NAME: JsString;
153
154    /// Property attribute flags of the built-in. Check [`Attribute`] for more information.
155    const ATTRIBUTE: Attribute = Attribute::WRITABLE
156        .union(Attribute::NON_ENUMERABLE)
157        .union(Attribute::CONFIGURABLE);
158}
159
160/// A [built-in object] that is also a constructor.
161///
162/// This trait must be implemented for any global built-in that can also be called with `new` to
163/// construct an object instance e.g. `Array`, `Map` or `Object`.
164///
165/// [built-in object]: https://tc39.es/ecma262/#sec-built-in-object
166pub(crate) trait BuiltInConstructor: BuiltInObject {
167    /// The minimum storage slots that need to be allocated for the constructor's
168    /// prototype object.
169    ///
170    /// This is always equivalent to the number of plain properties + 2 times the
171    /// number of properties that require accessor functions.
172    ///
173    /// Note that a "storage slot" is any `JsValue` that needs to be stored
174    /// in the prototype object; for accessors the storage count would need
175    /// to be increased by two, since accessors can have a getter and a setter
176    /// value.
177    const PROTOTYPE_STORAGE_SLOTS: usize;
178
179    /// The minimum storage slots that need to be allocated for the constructor
180    /// object.
181    ///
182    /// This is always equivalent to the number of plain static properties + 2
183    /// times the number of static properties that require accessor functions.
184    ///
185    /// Note that a "storage slot" is any `JsValue` that needs to be stored
186    /// in the constructor object; for accessors the storage count would need
187    /// to be increased by two, since accessors can have a getter and a setter
188    /// value.
189    const CONSTRUCTOR_STORAGE_SLOTS: usize;
190
191    /// The amount of arguments the constructor function takes.
192    const CONSTRUCTOR_ARGUMENTS: usize;
193
194    /// The corresponding standard constructor of this constructor.
195    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor;
196
197    /// The native constructor function.
198    fn constructor(
199        new_target: &JsValue,
200        args: &[JsValue],
201        context: &mut Context,
202    ) -> JsResult<JsValue>;
203}
204
205fn global_binding<B: BuiltInObject>(context: &mut Context) -> JsResult<()> {
206    let name = B::NAME;
207    let attr = B::ATTRIBUTE;
208    let intrinsic = B::get(context.intrinsics());
209    let global_object = context.global_object();
210
211    global_object.define_property_or_throw(
212        name,
213        PropertyDescriptor::builder()
214            .value(intrinsic)
215            .writable(attr.writable())
216            .enumerable(attr.enumerable())
217            .configurable(attr.configurable())
218            .build(),
219        context,
220    )?;
221    Ok(())
222}
223
224/// [`CanonicalizeKeyedCollectionKey ( key )`][spec]
225///
226/// The abstract operation `CanonicalizeKeyedCollectionKey` takes argument key (an ECMAScript
227/// language value) and returns an ECMAScript language value. It performs the following steps
228/// when called:
229///
230///    1. If key is -0𝔽, return +0𝔽.
231///    2. Return key.
232///
233/// [spec]: https://tc39.es/ecma262/multipage/keyed-collections.html#sec-canonicalizekeyedcollectionkey
234pub(crate) fn canonicalize_keyed_collection_key(value: JsValue) -> JsValue {
235    match value.as_number() {
236        Some(n) if n.is_zero() => JsValue::new(0),
237        _ => value,
238    }
239}
240
241impl Realm {
242    /// Abstract operation [`CreateIntrinsics ( realmRec )`][spec]
243    ///
244    /// [spec]: https://tc39.es/ecma262/#sec-createintrinsics
245    pub(crate) fn initialize(&self) {
246        BuiltInFunctionObject::init(self);
247        OrdinaryObject::init(self);
248        Iterator::init(self);
249        AsyncIterator::init(self);
250        AsyncFromSyncIterator::init(self);
251        IteratorConstructor::init(self);
252        WrapForValidIterator::init(self);
253        IteratorHelper::init(self);
254        Math::init(self);
255        Json::init(self);
256        Array::init(self);
257        ArrayIterator::init(self);
258        Proxy::init(self);
259        ArrayBuffer::init(self);
260        SharedArrayBuffer::init(self);
261        BigInt::init(self);
262        Boolean::init(self);
263        Date::init(self);
264        DataView::init(self);
265        Map::init(self);
266        MapIterator::init(self);
267        IsFinite::init(self);
268        IsNaN::init(self);
269        ParseInt::init(self);
270        ParseFloat::init(self);
271        Number::init(self);
272        Eval::init(self);
273        Set::init(self);
274        SetIterator::init(self);
275        String::init(self);
276        StringIterator::init(self);
277        RegExp::init(self);
278        RegExpStringIterator::init(self);
279        BuiltinTypedArray::init(self);
280        Int8Array::init(self);
281        Uint8Array::init(self);
282        Uint8ClampedArray::init(self);
283        Int16Array::init(self);
284        Uint16Array::init(self);
285        Int32Array::init(self);
286        Uint32Array::init(self);
287        BigInt64Array::init(self);
288        BigUint64Array::init(self);
289        #[cfg(feature = "float16")]
290        typed_array::Float16Array::init(self);
291        Float32Array::init(self);
292        Float64Array::init(self);
293        Symbol::init(self);
294        Error::init(self);
295        RangeError::init(self);
296        ReferenceError::init(self);
297        TypeError::init(self);
298        ThrowTypeError::init(self);
299        SyntaxError::init(self);
300        EvalError::init(self);
301        UriError::init(self);
302        AggregateError::init(self);
303        Reflect::init(self);
304        Generator::init(self);
305        GeneratorFunction::init(self);
306        Promise::init(self);
307        AsyncFunction::init(self);
308        AsyncGenerator::init(self);
309        AsyncGeneratorFunction::init(self);
310        EncodeUri::init(self);
311        EncodeUriComponent::init(self);
312        DecodeUri::init(self);
313        DecodeUriComponent::init(self);
314        WeakRef::init(self);
315        WeakMap::init(self);
316        WeakSet::init(self);
317        Atomics::init(self);
318        FinalizationRegistry::init(self);
319
320        #[cfg(feature = "annex-b")]
321        {
322            escape::Escape::init(self);
323            escape::Unescape::init(self);
324        }
325
326        #[cfg(feature = "intl")]
327        {
328            intl::Intl::init(self);
329            intl::Collator::init(self);
330            intl::ListFormat::init(self);
331            intl::Locale::init(self);
332            intl::DateTimeFormat::init(self);
333            intl::Segmenter::init(self);
334            intl::segmenter::Segments::init(self);
335            intl::segmenter::SegmentIterator::init(self);
336            intl::PluralRules::init(self);
337            intl::NumberFormat::init(self);
338        }
339
340        #[cfg(feature = "temporal")]
341        {
342            temporal::Temporal::init(self);
343            temporal::Now::init(self);
344            temporal::Instant::init(self);
345            temporal::Duration::init(self);
346            temporal::PlainDate::init(self);
347            temporal::PlainTime::init(self);
348            temporal::PlainDateTime::init(self);
349            temporal::PlainMonthDay::init(self);
350            temporal::PlainYearMonth::init(self);
351            temporal::ZonedDateTime::init(self);
352        }
353    }
354}
355
356/// Abstract operation [`SetDefaultGlobalBindings ( realmRec )`][spec].
357///
358/// [spec]: https://tc39.es/ecma262/#sec-setdefaultglobalbindings
359pub(crate) fn set_default_global_bindings(context: &mut Context) -> JsResult<()> {
360    let global_object = context.global_object();
361
362    global_object.define_property_or_throw(
363        js_string!("globalThis"),
364        PropertyDescriptor::builder()
365            .value(context.realm().global_this().clone())
366            .writable(true)
367            .enumerable(false)
368            .configurable(true),
369        context,
370    )?;
371    let restricted = PropertyDescriptor::builder()
372        .writable(false)
373        .enumerable(false)
374        .configurable(false);
375    global_object.define_property_or_throw(
376        js_string!("Infinity"),
377        restricted.clone().value(f64::INFINITY),
378        context,
379    )?;
380    global_object.define_property_or_throw(
381        js_string!("NaN"),
382        restricted.clone().value(f64::NAN),
383        context,
384    )?;
385    global_object.define_property_or_throw(
386        js_string!("undefined"),
387        restricted.value(JsValue::undefined()),
388        context,
389    )?;
390
391    global_binding::<BuiltInFunctionObject>(context)?;
392    global_binding::<OrdinaryObject>(context)?;
393    global_binding::<Math>(context)?;
394    global_binding::<Json>(context)?;
395    global_binding::<Array>(context)?;
396    global_binding::<Proxy>(context)?;
397    global_binding::<ArrayBuffer>(context)?;
398    global_binding::<SharedArrayBuffer>(context)?;
399    global_binding::<BigInt>(context)?;
400    global_binding::<Boolean>(context)?;
401    global_binding::<Date>(context)?;
402    global_binding::<DataView>(context)?;
403    global_binding::<Map>(context)?;
404    global_binding::<IsFinite>(context)?;
405    global_binding::<IsNaN>(context)?;
406    global_binding::<ParseInt>(context)?;
407    global_binding::<ParseFloat>(context)?;
408    global_binding::<Number>(context)?;
409    global_binding::<Eval>(context)?;
410    global_binding::<Set>(context)?;
411    global_binding::<String>(context)?;
412    global_binding::<RegExp>(context)?;
413    global_binding::<BuiltinTypedArray>(context)?;
414    global_binding::<Int8Array>(context)?;
415    global_binding::<Uint8Array>(context)?;
416    global_binding::<Uint8ClampedArray>(context)?;
417    global_binding::<Int16Array>(context)?;
418    global_binding::<Uint16Array>(context)?;
419    global_binding::<Int32Array>(context)?;
420    global_binding::<Uint32Array>(context)?;
421    global_binding::<BigInt64Array>(context)?;
422    global_binding::<BigUint64Array>(context)?;
423    #[cfg(feature = "float16")]
424    global_binding::<typed_array::Float16Array>(context)?;
425    global_binding::<Float32Array>(context)?;
426    global_binding::<Float64Array>(context)?;
427    global_binding::<Symbol>(context)?;
428    global_binding::<Error>(context)?;
429    global_binding::<RangeError>(context)?;
430    global_binding::<ReferenceError>(context)?;
431    global_binding::<TypeError>(context)?;
432    global_binding::<SyntaxError>(context)?;
433    global_binding::<EvalError>(context)?;
434    global_binding::<UriError>(context)?;
435    global_binding::<AggregateError>(context)?;
436    global_binding::<Reflect>(context)?;
437    global_binding::<Promise>(context)?;
438    global_binding::<EncodeUri>(context)?;
439    global_binding::<EncodeUriComponent>(context)?;
440    global_binding::<DecodeUri>(context)?;
441    global_binding::<DecodeUriComponent>(context)?;
442    global_binding::<WeakRef>(context)?;
443    global_binding::<WeakMap>(context)?;
444    global_binding::<WeakSet>(context)?;
445    global_binding::<IteratorConstructor>(context)?;
446    global_binding::<Atomics>(context)?;
447    global_binding::<FinalizationRegistry>(context)?;
448
449    #[cfg(feature = "annex-b")]
450    {
451        global_binding::<escape::Escape>(context)?;
452        global_binding::<escape::Unescape>(context)?;
453    }
454
455    #[cfg(feature = "intl")]
456    global_binding::<intl::Intl>(context)?;
457
458    #[cfg(feature = "temporal")]
459    {
460        global_binding::<temporal::Temporal>(context)?;
461    }
462
463    context.realm().mark_math_pristine();
464
465    Ok(())
466}