boa/syntax/ast/node/declaration/
mod.rs

1//! Declaration nodes
2use crate::{
3    builtins::{iterable::get_iterator, Array},
4    environment::lexical_environment::VariableScope,
5    exec::Executable,
6    gc::{Finalize, Trace},
7    syntax::ast::node::{join_nodes, Identifier, Node},
8    Context, JsResult, JsValue,
9};
10use std::fmt;
11
12#[cfg(feature = "deser")]
13use serde::{Deserialize, Serialize};
14
15pub mod arrow_function_decl;
16pub mod async_function_decl;
17pub mod async_function_expr;
18pub mod function_decl;
19pub mod function_expr;
20
21pub use self::{
22    arrow_function_decl::ArrowFunctionDecl, async_function_decl::AsyncFunctionDecl,
23    async_function_expr::AsyncFunctionExpr, function_decl::FunctionDecl,
24    function_expr::FunctionExpr,
25};
26
27#[cfg(test)]
28mod tests;
29
30#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
31#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
32pub enum DeclarationList {
33    /// The `const` statements are block-scoped, much like variables defined using the `let`
34    /// keyword.
35    ///
36    /// This declaration creates a constant whose scope can be either global or local to the block
37    /// in which it is declared. Global constants do not become properties of the window object,
38    /// unlike var variables.
39    ///
40    /// An initializer for a constant is required. You must specify its value in the same statement
41    /// in which it's declared. (This makes sense, given that it can't be changed later.)
42    ///
43    /// More information:
44    ///  - [ECMAScript reference][spec]
45    ///  - [MDN documentation][mdn]
46    ///
47    /// [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations
48    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
49    /// [identifier]: https://developer.mozilla.org/en-US/docs/Glossary/identifier
50    /// [expression]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions
51    Const(Box<[Declaration]>),
52
53    /// The `let` statement declares a block scope local variable, optionally initializing it to a
54    /// value.
55    ///
56    ///
57    /// `let` allows you to declare variables that are limited to a scope of a block statement, or
58    /// expression on which it is used, unlike the `var` keyword, which defines a variable
59    /// globally, or locally to an entire function regardless of block scope.
60    ///
61    /// Just like const the `let` does not create properties of the window object when declared
62    /// globally (in the top-most scope).
63    ///
64    /// More information:
65    ///  - [ECMAScript reference][spec]
66    ///  - [MDN documentation][mdn]
67    ///
68    /// [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations
69    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
70    Let(Box<[Declaration]>),
71
72    /// The `var` statement declares a variable, optionally initializing it to a value.
73    ///
74    /// var declarations, wherever they occur, are processed before any code is executed. This is
75    /// called hoisting, and is discussed further below.
76    ///
77    /// The scope of a variable declared with var is its current execution context, which is either
78    /// the enclosing function or, for variables declared outside any function, global. If you
79    /// re-declare a JavaScript variable, it will not lose its value.
80    ///
81    /// Assigning a value to an undeclared variable implicitly creates it as a global variable (it
82    /// becomes a property of the global object) when the assignment is executed.
83    ///
84    /// More information:
85    ///  - [ECMAScript reference][spec]
86    ///  - [MDN documentation][mdn]
87    ///
88    /// [spec]: https://tc39.es/ecma262/#prod-VariableStatement
89    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
90    Var(Box<[Declaration]>),
91}
92
93impl Executable for DeclarationList {
94    fn run(&self, context: &mut Context) -> JsResult<JsValue> {
95        for decl in self.as_ref() {
96            use DeclarationList::*;
97            let val = match decl.init() {
98                None if self.is_const() => {
99                    return context.throw_syntax_error("missing = in const declaration")
100                }
101                Some(init) => init.run(context)?,
102                None => JsValue::undefined(),
103            };
104
105            match &decl {
106                Declaration::Identifier { ident, init } => {
107                    if self.is_var() && context.has_binding(ident.as_ref())? {
108                        if init.is_some() {
109                            context.set_mutable_binding(ident.as_ref(), val, context.strict())?;
110                        }
111                        continue;
112                    }
113
114                    match &self {
115                        Const(_) => context.create_immutable_binding(
116                            ident.as_ref(),
117                            false,
118                            VariableScope::Block,
119                        )?,
120                        Let(_) => context.create_mutable_binding(
121                            ident.as_ref(),
122                            false,
123                            VariableScope::Block,
124                        )?,
125                        Var(_) => context.create_mutable_binding(
126                            ident.as_ref(),
127                            false,
128                            VariableScope::Function,
129                        )?,
130                    }
131
132                    context.initialize_binding(ident.as_ref(), val)?;
133                }
134                Declaration::Pattern(p) => {
135                    for (ident, value) in p.run(None, context)? {
136                        if self.is_var() && context.has_binding(ident.as_ref())? {
137                            if !value.is_undefined() {
138                                context.set_mutable_binding(
139                                    ident.as_ref(),
140                                    value,
141                                    context.strict(),
142                                )?;
143                            }
144                            continue;
145                        }
146
147                        match &self {
148                            Const(_) => context.create_immutable_binding(
149                                ident.as_ref(),
150                                false,
151                                VariableScope::Block,
152                            )?,
153                            Let(_) => context.create_mutable_binding(
154                                ident.as_ref(),
155                                false,
156                                VariableScope::Block,
157                            )?,
158                            Var(_) => context.create_mutable_binding(
159                                ident.as_ref(),
160                                false,
161                                VariableScope::Function,
162                            )?,
163                        }
164
165                        context.initialize_binding(ident.as_ref(), value)?;
166                    }
167                }
168            }
169        }
170
171        Ok(JsValue::undefined())
172    }
173}
174
175impl DeclarationList {
176    #[allow(dead_code)]
177    pub(in crate::syntax) fn is_let(&self) -> bool {
178        matches!(self, Self::Let(_))
179    }
180    pub(in crate::syntax) fn is_const(&self) -> bool {
181        matches!(self, Self::Const(_))
182    }
183    pub(in crate::syntax) fn is_var(&self) -> bool {
184        matches!(self, Self::Var(_))
185    }
186}
187
188impl AsRef<[Declaration]> for DeclarationList {
189    fn as_ref(&self) -> &[Declaration] {
190        use DeclarationList::*;
191        match self {
192            Var(list) | Const(list) | Let(list) => list,
193        }
194    }
195}
196
197impl fmt::Display for DeclarationList {
198    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199        if !self.as_ref().is_empty() {
200            use DeclarationList::*;
201            match &self {
202                Let(_) => write!(f, "let ")?,
203                Const(_) => write!(f, "const ")?,
204                Var(_) => write!(f, "var ")?,
205            }
206            join_nodes(f, self.as_ref())
207        } else {
208            Ok(())
209        }
210    }
211}
212
213impl From<DeclarationList> for Node {
214    fn from(list: DeclarationList) -> Self {
215        use DeclarationList::*;
216        match &list {
217            Let(_) => Node::LetDeclList(list),
218            Const(_) => Node::ConstDeclList(list),
219            Var(_) => Node::VarDeclList(list),
220        }
221    }
222}
223
224impl From<Declaration> for Box<[Declaration]> {
225    fn from(d: Declaration) -> Self {
226        Box::new([d])
227    }
228}
229
230/// Declaration represents either an individual binding or a binding pattern.
231///
232/// For `let` and `const` declarations this type represents a [LexicalBinding][spec1]
233///
234/// For `var` declarations this type represents a [VariableDeclaration][spec2]
235///
236/// More information:
237///  - [ECMAScript reference: 14.3 Declarations and the Variable Statement][spec3]
238///
239/// [spec1]: https://tc39.es/ecma262/#prod-LexicalBinding
240/// [spec2]: https://tc39.es/ecma262/#prod-VariableDeclaration
241/// [spec3]:  https://tc39.es/ecma262/#sec-declarations-and-the-variable-statement
242#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
243#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
244pub enum Declaration {
245    Identifier {
246        ident: Identifier,
247        init: Option<Node>,
248    },
249    Pattern(DeclarationPattern),
250}
251
252impl fmt::Display for Declaration {
253    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254        match &self {
255            Self::Identifier { ident, init } => {
256                fmt::Display::fmt(&ident, f)?;
257                if let Some(ref init) = &init {
258                    write!(f, " = {}", init)?;
259                }
260            }
261            Self::Pattern(pattern) => {
262                fmt::Display::fmt(&pattern, f)?;
263            }
264        }
265        Ok(())
266    }
267}
268
269impl Declaration {
270    /// Creates a new variable declaration with a BindingIdentifier.
271    #[inline]
272    pub(in crate::syntax) fn new_with_identifier<N, I>(ident: N, init: I) -> Self
273    where
274        N: Into<Identifier>,
275        I: Into<Option<Node>>,
276    {
277        Self::Identifier {
278            ident: ident.into(),
279            init: init.into(),
280        }
281    }
282
283    /// Creates a new variable declaration with an ObjectBindingPattern.
284    #[inline]
285    pub(in crate::syntax) fn new_with_object_pattern<I>(
286        bindings: Vec<BindingPatternTypeObject>,
287        init: I,
288    ) -> Self
289    where
290        I: Into<Option<Node>>,
291    {
292        Self::Pattern(DeclarationPattern::Object(DeclarationPatternObject::new(
293            bindings,
294            init.into(),
295        )))
296    }
297
298    /// Creates a new variable declaration with an ArrayBindingPattern.
299    #[inline]
300    pub(in crate::syntax) fn new_with_array_pattern<I>(
301        bindings: Vec<BindingPatternTypeArray>,
302        init: I,
303    ) -> Self
304    where
305        I: Into<Option<Node>>,
306    {
307        Self::Pattern(DeclarationPattern::Array(DeclarationPatternArray::new(
308            bindings,
309            init.into(),
310        )))
311    }
312
313    /// Gets the initialization node for the declaration, if any.
314    #[inline]
315    pub(crate) fn init(&self) -> Option<&Node> {
316        match &self {
317            Self::Identifier { init, .. } => init.as_ref(),
318            Self::Pattern(pattern) => pattern.init(),
319        }
320    }
321}
322
323/// DeclarationPattern represents an object or array binding pattern.
324///
325/// This enum mostly wraps the functionality of the specific binding pattern types.
326///
327/// More information:
328///  - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingPattern][spec1]
329///
330/// [spec1]: https://tc39.es/ecma262/#prod-BindingPattern
331#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
332#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
333pub enum DeclarationPattern {
334    Object(DeclarationPatternObject),
335    Array(DeclarationPatternArray),
336}
337
338impl fmt::Display for DeclarationPattern {
339    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
340        match &self {
341            DeclarationPattern::Object(o) => {
342                fmt::Display::fmt(o, f)?;
343            }
344            DeclarationPattern::Array(a) => {
345                fmt::Display::fmt(a, f)?;
346            }
347        }
348        Ok(())
349    }
350}
351
352impl DeclarationPattern {
353    /// Initialize the values of an object/array binding pattern.
354    ///
355    /// This function only calls the specific initialization function for either the object or the array binding pattern.
356    /// For specific documentation and references to the ECMAScript spec, look at the called initialization functions.
357    #[inline]
358    pub(in crate::syntax) fn run(
359        &self,
360        init: Option<JsValue>,
361        context: &mut Context,
362    ) -> JsResult<Vec<(Box<str>, JsValue)>> {
363        match &self {
364            DeclarationPattern::Object(pattern) => pattern.run(init, context),
365            DeclarationPattern::Array(pattern) => pattern.run(init, context),
366        }
367    }
368
369    /// Gets the list of identifiers declared by the binding pattern.
370    ///
371    /// A single binding pattern may declare 0 to n identifiers.
372    #[inline]
373    pub fn idents(&self) -> Vec<&str> {
374        match &self {
375            DeclarationPattern::Object(pattern) => pattern.idents(),
376            DeclarationPattern::Array(pattern) => pattern.idents(),
377        }
378    }
379
380    /// Gets the initialization node for the binding pattern, if any.
381    #[inline]
382    pub fn init(&self) -> Option<&Node> {
383        match &self {
384            DeclarationPattern::Object(pattern) => pattern.init(),
385            DeclarationPattern::Array(pattern) => pattern.init(),
386        }
387    }
388}
389
390/// DeclarationPatternObject represents an object binding pattern.
391///
392/// This struct holds a list of bindings, and an optional initializer for the binding pattern.
393///
394/// More information:
395///  - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - ObjectBindingPattern][spec1]
396///
397/// [spec1]: https://tc39.es/ecma262/#prod-ObjectBindingPattern
398#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
399#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
400pub struct DeclarationPatternObject {
401    bindings: Vec<BindingPatternTypeObject>,
402    init: Option<Node>,
403}
404
405impl fmt::Display for DeclarationPatternObject {
406    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
407        fmt::Display::fmt("{", f)?;
408        for (i, binding) in self.bindings.iter().enumerate() {
409            if i == self.bindings.len() - 1 {
410                write!(f, "{} ", binding)?;
411            } else {
412                write!(f, "{},", binding)?;
413            }
414        }
415        fmt::Display::fmt("}", f)?;
416        if let Some(ref init) = self.init {
417            write!(f, " = {}", init)?;
418        }
419        Ok(())
420    }
421}
422
423impl DeclarationPatternObject {
424    /// Create a new object binding pattern.
425    #[inline]
426    pub(in crate::syntax) fn new(
427        bindings: Vec<BindingPatternTypeObject>,
428        init: Option<Node>,
429    ) -> Self {
430        Self { bindings, init }
431    }
432
433    /// Gets the initialization node for the object binding pattern, if any.
434    #[inline]
435    pub(in crate::syntax) fn init(&self) -> Option<&Node> {
436        self.init.as_ref()
437    }
438
439    /// Initialize the values of an object binding pattern.
440    ///
441    /// More information:
442    ///  - [ECMAScript reference: 8.5.2 Runtime Semantics: BindingInitialization][spec1]
443    ///  - [ECMAScript reference:14.3.3.3 Runtime Semantics: KeyedBindingInitialization][spec2]
444    ///  - [ECMAScript reference:14.3.3.2 Runtime Semantics: RestBindingInitialization][spec3]
445    ///
446    /// [spec1]: https://tc39.es/ecma262/#sec-runtime-semantics-bindinginitialization
447    /// [spec2]: https://tc39.es/ecma262/#sec-runtime-semantics-keyedbindinginitialization
448    /// [spec3]:  https://tc39.es/ecma262/#sec-destructuring-binding-patterns-runtime-semantics-restbindinginitialization
449    pub(in crate::syntax) fn run(
450        &self,
451        init: Option<JsValue>,
452        context: &mut Context,
453    ) -> JsResult<Vec<(Box<str>, JsValue)>> {
454        let value = if let Some(value) = init {
455            value
456        } else if let Some(node) = &self.init {
457            node.run(context)?
458        } else {
459            JsValue::undefined()
460        };
461
462        if value.is_null() {
463            return Err(context.construct_type_error("Cannot destructure 'null' value"));
464        }
465        if value.is_undefined() {
466            return Err(context.construct_type_error("Cannot destructure 'undefined' value"));
467        }
468
469        // 1. Perform ? RequireObjectCoercible(value).
470        let value = value.require_object_coercible(context)?;
471        let mut results = Vec::new();
472
473        // 2. Return the result of performing BindingInitialization for ObjectBindingPattern using value and environment as arguments.
474        for binding in &self.bindings {
475            use BindingPatternTypeObject::*;
476
477            match binding {
478                // ObjectBindingPattern : { }
479                Empty => {
480                    // 1. Return NormalCompletion(empty).
481                }
482                //  SingleNameBinding : BindingIdentifier Initializer[opt]
483                SingleName {
484                    ident,
485                    property_name,
486                    default_init,
487                } => {
488                    // 1. Let bindingId be StringValue of BindingIdentifier.
489                    // 2. Let lhs be ? ResolveBinding(bindingId, environment).
490
491                    // 3. Let v be ? GetV(value, propertyName).
492                    let mut v = value.get_field(property_name.as_ref(), context)?;
493
494                    // 4. If Initializer is present and v is undefined, then
495                    if let Some(init) = default_init {
496                        if v.is_undefined() {
497                            // TODO: a. not implemented yet:
498                            // a. If IsAnonymousFunctionDefinition(Initializer) is true, then
499                            // i. Set v to the result of performing NamedEvaluation for Initializer with argument bindingId.
500
501                            // b. Else,
502                            // i. Let defaultValue be the result of evaluating Initializer.
503                            // ii. Set v to ? GetValue(defaultValue).
504                            v = init.run(context)?;
505                        }
506                    }
507
508                    // 5. If environment is undefined, return ? PutValue(lhs, v).
509                    // 6. Return InitializeReferencedBinding(lhs, v).
510                    results.push((ident.clone(), v));
511                }
512                //  BindingRestProperty : ... BindingIdentifier
513                RestProperty {
514                    ident,
515                    excluded_keys,
516                } => {
517                    // 1. Let lhs be ? ResolveBinding(StringValue of BindingIdentifier, environment).
518
519                    // 2. Let restObj be ! OrdinaryObjectCreate(%Object.prototype%).
520                    let mut rest_obj = context.construct_object();
521
522                    // 3. Perform ? CopyDataProperties(restObj, value, excludedNames).
523                    rest_obj.copy_data_properties(value, excluded_keys.clone(), context)?;
524
525                    // 4. If environment is undefined, return PutValue(lhs, restObj).
526                    // 5. Return InitializeReferencedBinding(lhs, restObj).
527                    results.push((ident.clone(), rest_obj.into()));
528                }
529                //  BindingElement : BindingPattern Initializer[opt]
530                BindingPattern {
531                    ident,
532                    pattern,
533                    default_init,
534                } => {
535                    // 1. Let v be ? GetV(value, propertyName).
536                    let mut v = value.get_field(ident.as_ref(), context)?;
537
538                    // 2. If Initializer is present and v is undefined, then
539                    if let Some(init) = default_init {
540                        if v.is_undefined() {
541                            // a. Let defaultValue be the result of evaluating Initializer.
542                            // b. Set v to ? GetValue(defaultValue).
543                            v = init.run(context)?;
544                        }
545                    }
546
547                    // 3. Return the result of performing BindingInitialization for BindingPattern passing v and environment as arguments.
548                    results.append(&mut pattern.run(Some(v), context)?);
549                }
550            }
551        }
552
553        Ok(results)
554    }
555
556    /// Gets the list of identifiers declared by the object binding pattern.
557    #[inline]
558    pub(in crate::syntax) fn idents(&self) -> Vec<&str> {
559        let mut idents = Vec::new();
560
561        for binding in &self.bindings {
562            use BindingPatternTypeObject::*;
563
564            match binding {
565                Empty => {}
566                SingleName {
567                    ident,
568                    property_name: _,
569                    default_init: _,
570                } => {
571                    idents.push(ident.as_ref());
572                }
573                RestProperty {
574                    ident: property_name,
575                    excluded_keys: _,
576                } => {
577                    idents.push(property_name.as_ref());
578                }
579                BindingPattern {
580                    ident: _,
581                    pattern,
582                    default_init: _,
583                } => {
584                    for ident in pattern.idents() {
585                        idents.push(ident);
586                    }
587                }
588            }
589        }
590
591        idents
592    }
593}
594
595/// DeclarationPatternArray represents an array binding pattern.
596///
597/// This struct holds a list of bindings, and an optional initializer for the binding pattern.
598///
599/// More information:
600///  - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - ArrayBindingPattern][spec1]
601///
602/// [spec1]: https://tc39.es/ecma262/#prod-ArrayBindingPattern
603#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
604#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
605pub struct DeclarationPatternArray {
606    bindings: Vec<BindingPatternTypeArray>,
607    init: Option<Node>,
608}
609
610impl fmt::Display for DeclarationPatternArray {
611    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
612        fmt::Display::fmt("[", f)?;
613        for (i, binding) in self.bindings.iter().enumerate() {
614            if i == self.bindings.len() - 1 {
615                match binding {
616                    BindingPatternTypeArray::Elision => write!(f, "{}, ", binding)?,
617                    _ => write!(f, "{} ", binding)?,
618                }
619            } else {
620                write!(f, "{},", binding)?;
621            }
622        }
623        fmt::Display::fmt("]", f)?;
624        if let Some(ref init) = self.init {
625            write!(f, " = {}", init)?;
626        }
627        Ok(())
628    }
629}
630
631impl DeclarationPatternArray {
632    /// Create a new array binding pattern.
633    #[inline]
634    pub(in crate::syntax) fn new(
635        bindings: Vec<BindingPatternTypeArray>,
636        init: Option<Node>,
637    ) -> Self {
638        Self { bindings, init }
639    }
640
641    /// Gets the initialization node for the array binding pattern, if any.
642    #[inline]
643    pub(in crate::syntax) fn init(&self) -> Option<&Node> {
644        self.init.as_ref()
645    }
646
647    /// Initialize the values of an array binding pattern.
648    ///
649    /// More information:
650    ///  - [ECMAScript reference: 8.5.2 Runtime Semantics: BindingInitialization][spec1]
651    ///  - [ECMAScript reference: 8.5.3 Runtime Semantics: IteratorBindingInitialization][spec2]
652    ///
653    /// [spec1]: https://tc39.es/ecma262/#sec-runtime-semantics-bindinginitialization
654    /// [spec2]: https://tc39.es/ecma262/#sec-runtime-semantics-iteratorbindinginitialization
655    pub(in crate::syntax) fn run(
656        &self,
657        init: Option<JsValue>,
658        context: &mut Context,
659    ) -> JsResult<Vec<(Box<str>, JsValue)>> {
660        let value = if let Some(value) = init {
661            value
662        } else if let Some(node) = &self.init {
663            node.run(context)?
664        } else {
665            JsValue::undefined()
666        };
667
668        if value.is_null() {
669            return Err(context.construct_type_error("Cannot destructure 'null' value"));
670        }
671        if value.is_undefined() {
672            return Err(context.construct_type_error("Cannot destructure 'undefined' value"));
673        }
674
675        // 1. Let iteratorRecord be ? GetIterator(value).
676        let iterator = get_iterator(&value, context)?;
677        let mut result = Vec::new();
678
679        // 2. Let result be IteratorBindingInitialization of ArrayBindingPattern with arguments iteratorRecord and environment.
680        for binding in &self.bindings {
681            use BindingPatternTypeArray::*;
682
683            match binding {
684                // ArrayBindingPattern : [ ]
685                Empty => {
686                    // 1. Return NormalCompletion(empty).
687                }
688                // ArrayBindingPattern : [ Elision ]
689                // Note: This captures all elisions due to our representation of a the binding pattern.
690                Elision => {
691                    // 1. If iteratorRecord.[[Done]] is false, then
692                    // a. Let next be IteratorStep(iteratorRecord).
693                    // b. If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
694                    // c. ReturnIfAbrupt(next).
695                    // d. If next is false, set iteratorRecord.[[Done]] to true.
696                    let _ = iterator.next(context)?;
697
698                    // 2. Return NormalCompletion(empty).
699                }
700                // SingleNameBinding : BindingIdentifier Initializer[opt]
701                SingleName {
702                    ident,
703                    default_init,
704                } => {
705                    // 1. Let bindingId be StringValue of BindingIdentifier.
706                    // 2. Let lhs be ? ResolveBinding(bindingId, environment).
707
708                    let next = iterator.next(context)?;
709
710                    // 3. If iteratorRecord.[[Done]] is false, then
711                    // 4. If iteratorRecord.[[Done]] is true, let v be undefined.
712                    let mut v = if !next.done {
713                        // a. Let next be IteratorStep(iteratorRecord).
714                        // b. If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
715                        // c. ReturnIfAbrupt(next).
716                        // d. If next is false, set iteratorRecord.[[Done]] to true.
717                        // e. Else,
718                        // i. Let v be IteratorValue(next).
719                        // ii. If v is an abrupt completion, set iteratorRecord.[[Done]] to true.
720                        // iii. ReturnIfAbrupt(v).
721                        next.value
722                    } else {
723                        JsValue::undefined()
724                    };
725
726                    // 5. If Initializer is present and v is undefined, then
727                    if let Some(init) = default_init {
728                        if v.is_undefined() {
729                            // TODO: a. not implemented yet:
730                            // a. If IsAnonymousFunctionDefinition(Initializer) is true, then
731                            // i. Set v to the result of performing NamedEvaluation for Initializer with argument bindingId.
732
733                            // b. Else,
734                            // i. Let defaultValue be the result of evaluating Initializer.
735                            // ii. Set v to ? GetValue(defaultValue).
736                            v = init.run(context)?
737                        }
738                    }
739
740                    // 6. If environment is undefined, return ? PutValue(lhs, v).
741                    // 7. Return InitializeReferencedBinding(lhs, v).
742                    result.push((ident.clone(), v));
743                }
744                // BindingElement : BindingPattern Initializer[opt]
745                BindingPattern { pattern } => {
746                    let next = iterator.next(context)?;
747
748                    // 1. If iteratorRecord.[[Done]] is false, then
749                    // 2. If iteratorRecord.[[Done]] is true, let v be undefined.
750                    let v = if !next.done {
751                        // a. Let next be IteratorStep(iteratorRecord).
752                        // b. If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
753                        // c. ReturnIfAbrupt(next).
754                        // d. If next is false, set iteratorRecord.[[Done]] to true.
755                        // e. Else,
756                        // i. Let v be IteratorValue(next).
757                        // ii. If v is an abrupt completion, set iteratorRecord.[[Done]] to true.
758                        // iii. ReturnIfAbrupt(v).
759                        Some(next.value)
760                    } else {
761                        None
762                    };
763
764                    // 3. If Initializer is present and v is undefined, then
765                    // a. Let defaultValue be the result of evaluating Initializer.
766                    // b. Set v to ? GetValue(defaultValue).
767
768                    // 4. Return the result of performing BindingInitialization of BindingPattern with v and environment as the arguments.
769                    result.append(&mut pattern.run(v, context)?);
770                }
771                // BindingRestElement : ... BindingIdentifier
772                SingleNameRest { ident } => {
773                    // 1. Let lhs be ? ResolveBinding(StringValue of BindingIdentifier, environment).
774                    // 2. Let A be ! ArrayCreate(0).
775                    // 3. Let n be 0.
776                    let a = Array::array_create(0, None, context)
777                        .expect("Array creation with 0 length should never fail");
778
779                    // 4. Repeat,
780                    loop {
781                        let next = iterator.next(context)?;
782                        // a. If iteratorRecord.[[Done]] is false, then
783                        // i. Let next be IteratorStep(iteratorRecord).
784                        // ii. If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
785                        // iii. ReturnIfAbrupt(next).
786                        // iv. If next is false, set iteratorRecord.[[Done]] to true.
787
788                        // b. If iteratorRecord.[[Done]] is true, then
789                        if next.done {
790                            // i. If environment is undefined, return ? PutValue(lhs, A).
791                            // ii. Return InitializeReferencedBinding(lhs, A).
792                            break result.push((ident.clone(), a.clone().into()));
793                        }
794
795                        // c. Let nextValue be IteratorValue(next).
796                        // d. If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to true.
797                        // e. ReturnIfAbrupt(nextValue).
798
799                        // f. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), nextValue).
800                        // g. Set n to n + 1.
801                        Array::add_to_array_object(&a.clone().into(), &[next.value], context)?;
802                    }
803                }
804                // BindingRestElement : ... BindingPattern
805                BindingPatternRest { pattern } => {
806                    // 1. Let A be ! ArrayCreate(0).
807                    // 2. Let n be 0.
808                    let a = Array::array_create(0, None, context)
809                        .expect("Array creation with 0 length should never fail");
810
811                    // 3. Repeat,
812                    loop {
813                        // a. If iteratorRecord.[[Done]] is false, then
814                        // i. Let next be IteratorStep(iteratorRecord).
815                        // ii. If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
816                        // iii. ReturnIfAbrupt(next).
817                        // iv. If next is false, set iteratorRecord.[[Done]] to true.
818                        let next = iterator.next(context)?;
819
820                        // b. If iteratorRecord.[[Done]] is true, then
821                        if next.done {
822                            // i. Return the result of performing BindingInitialization of BindingPattern with A and environment as the arguments.
823                            break result
824                                .append(&mut pattern.run(Some(a.clone().into()), context)?);
825                        }
826
827                        // c. Let nextValue be IteratorValue(next).
828                        // d. If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to true.
829                        // e. ReturnIfAbrupt(nextValue).
830                        // f. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), nextValue).
831                        // g. Set n to n + 1.
832                        Array::add_to_array_object(&a.clone().into(), &[next.value], context)?;
833                    }
834                }
835            }
836        }
837
838        // 3. If iteratorRecord.[[Done]] is false, return ? IteratorClose(iteratorRecord, result).
839        // 4. Return result.
840        Ok(result)
841    }
842
843    /// Gets the list of identifiers declared by the array binding pattern.
844    #[inline]
845    pub(in crate::syntax) fn idents(&self) -> Vec<&str> {
846        let mut idents = Vec::new();
847
848        for binding in &self.bindings {
849            use BindingPatternTypeArray::*;
850
851            match binding {
852                Empty => {}
853                Elision => {}
854                SingleName {
855                    ident,
856                    default_init: _,
857                } => {
858                    idents.push(ident.as_ref());
859                }
860                BindingPattern { pattern } | BindingPatternRest { pattern } => {
861                    let mut i = pattern.idents();
862                    idents.append(&mut i)
863                }
864                SingleNameRest { ident } => idents.push(ident),
865            }
866        }
867
868        idents
869    }
870}
871
872/// BindingPatternTypeObject represents the different types of bindings that an object binding pattern may contain.
873///
874/// More information:
875///  - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - ObjectBindingPattern][spec1]
876///
877/// [spec1]: https://tc39.es/ecma262/#prod-ObjectBindingPattern
878#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
879#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
880pub enum BindingPatternTypeObject {
881    /// Empty represents an empty object binding pattern e.g. `{ }`.
882    Empty,
883
884    /// SingleName represents one of the following properties:
885    ///
886    /// - `SingleNameBinding` with an identifier and an optional default initializer.
887    /// - `BindingProperty` with an property name and a `SingleNameBinding` as  the `BindingElement`.
888    ///
889    /// More information:
890    ///  - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - SingleNameBinding][spec1]
891    ///  - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingProperty][spec2]
892    ///
893    /// [spec1]: https://tc39.es/ecma262/#prod-SingleNameBinding
894    /// [spec2]: https://tc39.es/ecma262/#prod-BindingProperty
895    SingleName {
896        ident: Box<str>,
897        property_name: Box<str>,
898        default_init: Option<Node>,
899    },
900
901    /// RestProperty represents a `BindingRestProperty` with an identifier.
902    ///
903    /// It also includes a list of the property keys that should be excluded from the rest,
904    /// because they where already assigned.
905    ///
906    /// More information:
907    ///  - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestProperty][spec1]
908    ///
909    /// [spec1]: https://tc39.es/ecma262/#prod-BindingRestProperty
910    RestProperty {
911        ident: Box<str>,
912        excluded_keys: Vec<Box<str>>,
913    },
914
915    /// BindingPattern represents a `BindingProperty` with a `BindingPattern` as the `BindingElement`.
916    ///
917    /// Additionally to the identifier of the new property and the nested binding pattern,
918    /// this may also include an optional default initializer.
919    ///
920    /// More information:
921    ///  - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingProperty][spec1]
922    ///
923    /// [spec1]: https://tc39.es/ecma262/#prod-BindingProperty
924    BindingPattern {
925        ident: Box<str>,
926        pattern: DeclarationPattern,
927        default_init: Option<Node>,
928    },
929}
930
931impl fmt::Display for BindingPatternTypeObject {
932    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
933        match &self {
934            BindingPatternTypeObject::Empty => {}
935            BindingPatternTypeObject::SingleName {
936                ident,
937                property_name,
938                default_init,
939            } => {
940                if ident == property_name {
941                    write!(f, " {}", ident)?;
942                } else {
943                    write!(f, " {} : {}", property_name, ident)?;
944                }
945                if let Some(ref init) = default_init {
946                    write!(f, " = {}", init)?;
947                }
948            }
949            BindingPatternTypeObject::RestProperty {
950                ident: property_name,
951                excluded_keys: _,
952            } => {
953                write!(f, " ... {}", property_name)?;
954            }
955            BindingPatternTypeObject::BindingPattern {
956                ident: property_name,
957                pattern,
958                default_init,
959            } => {
960                write!(f, " {} : {}", property_name, pattern)?;
961                if let Some(ref init) = default_init {
962                    write!(f, " = {}", init)?;
963                }
964            }
965        }
966        Ok(())
967    }
968}
969
970/// BindingPatternTypeArray represents the different types of bindings that an array binding pattern may contain.
971///
972/// More information:
973///  - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - ArrayBindingPattern][spec1]
974///
975/// [spec1]: https://tc39.es/ecma262/#prod-ArrayBindingPattern
976#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
977#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
978pub enum BindingPatternTypeArray {
979    /// Empty represents an empty array binding pattern e.g. `[ ]`.
980    ///
981    /// This may occur because the `Elision` and `BindingRestElement` in the first type of
982    /// array binding pattern are both optional.
983    ///
984    /// More information:
985    ///  - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - ArrayBindingPattern][spec1]
986    ///
987    /// [spec1]: https://tc39.es/ecma262/#prod-ArrayBindingPattern
988    Empty,
989
990    /// Elision represents the elision of an item in the array binding pattern.
991    ///
992    /// An `Elision` may occur at multiple points in the pattern and may be multiple elisions.
993    /// This variant strictly represents one elision. If there are multiple, this should be used multiple times.
994    ///
995    /// More information:
996    ///  - [ECMAScript reference: 13.2.4 Array Initializer - Elision][spec1]
997    ///
998    /// [spec1]: https://tc39.es/ecma262/#prod-Elision
999    Elision,
1000
1001    /// SingleName represents a `SingleNameBinding` with an identifier and an optional default initializer.
1002    ///
1003    /// More information:
1004    ///  - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - SingleNameBinding][spec1]
1005    ///
1006    /// [spec1]: https://tc39.es/ecma262/#prod-SingleNameBinding
1007    SingleName {
1008        ident: Box<str>,
1009        default_init: Option<Node>,
1010    },
1011
1012    /// BindingPattern represents a `BindingPattern` in a `BindingElement` of an array binding pattern.
1013    ///
1014    /// The pattern and the optional default initializer are both stored in the DeclarationPattern.
1015    ///
1016    /// More information:
1017    ///  - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingElement][spec1]
1018    ///
1019    /// [spec1]: https://tc39.es/ecma262/#prod-BindingElement
1020    BindingPattern { pattern: DeclarationPattern },
1021
1022    /// SingleNameRest represents a `BindingIdentifier` in a `BindingRestElement` of an array binding pattern.
1023    ///
1024    /// More information:
1025    ///  - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestElement][spec1]
1026    ///
1027    /// [spec1]: https://tc39.es/ecma262/#prod-BindingRestElement
1028    SingleNameRest { ident: Box<str> },
1029
1030    /// SingleNameRest represents a `BindingPattern` in a `BindingRestElement` of an array binding pattern.
1031    ///
1032    /// More information:
1033    ///  - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestElement][spec1]
1034    ///
1035    /// [spec1]: https://tc39.es/ecma262/#prod-BindingRestElement
1036    BindingPatternRest { pattern: DeclarationPattern },
1037}
1038
1039impl fmt::Display for BindingPatternTypeArray {
1040    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1041        match &self {
1042            BindingPatternTypeArray::Empty => {}
1043            BindingPatternTypeArray::Elision => {
1044                fmt::Display::fmt(" ", f)?;
1045            }
1046            BindingPatternTypeArray::SingleName {
1047                ident,
1048                default_init,
1049            } => {
1050                write!(f, " {}", ident)?;
1051                if let Some(ref init) = default_init {
1052                    write!(f, " = {}", init)?;
1053                }
1054            }
1055            BindingPatternTypeArray::BindingPattern { pattern } => {
1056                write!(f, " {}", pattern)?;
1057            }
1058            BindingPatternTypeArray::SingleNameRest { ident } => {
1059                write!(f, " ... {}", ident)?;
1060            }
1061            BindingPatternTypeArray::BindingPatternRest { pattern } => {
1062                write!(f, " ... {}", pattern)?;
1063            }
1064        }
1065        Ok(())
1066    }
1067}