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}