diff --git a/boa/src/bytecompiler.rs b/boa/src/bytecompiler.rs index 7c126d39aa1..c22c06188b2 100644 --- a/boa/src/bytecompiler.rs +++ b/boa/src/bytecompiler.rs @@ -589,7 +589,7 @@ impl ByteCompiler { Node::VarDeclList(list) => { for decl in list.as_ref() { match decl { - Declaration::Identifier(ident, _) => { + Declaration::Identifier { ident, .. } => { let index = self.get_or_insert_name(ident.as_ref()); self.emit(Opcode::DefVar, &[index]); @@ -615,7 +615,7 @@ impl ByteCompiler { Node::LetDeclList(list) => { for decl in list.as_ref() { match decl { - Declaration::Identifier(ident, _) => { + Declaration::Identifier { ident, .. } => { let index = self.get_or_insert_name(ident.as_ref()); self.emit(Opcode::DefLet, &[index]); @@ -641,7 +641,7 @@ impl ByteCompiler { Node::ConstDeclList(list) => { for decl in list.as_ref() { match decl { - Declaration::Identifier(ident, _) => { + Declaration::Identifier { ident, .. } => { let index = self.get_or_insert_name(ident.as_ref()); self.emit(Opcode::DefConst, &[index]); diff --git a/boa/src/object/gcobject.rs b/boa/src/object/gcobject.rs index 17e43a46cb6..dd5c8191008 100644 --- a/boa/src/object/gcobject.rs +++ b/boa/src/object/gcobject.rs @@ -923,7 +923,9 @@ impl GcObject { } // 4. Let from be ! ToObject(source). - let from = source.to_object(context)?; + let from = source + .to_object(context) + .expect("function ToObject should never complete abruptly here"); // 5. Let keys be ? from.[[OwnPropertyKeys]](). // 6. For each element nextKey of keys, do @@ -937,7 +939,8 @@ impl GcObject { // i. If SameValue(e, nextKey) is true, then if *e == key { // 1. Set excluded to true. - excluded = true + excluded = true; + break; } } // c. If excluded is false, then @@ -956,7 +959,7 @@ impl GcObject { key, DataDescriptor::new(prop_value, Attribute::all()), context, - )?; + ).expect("function CreateDataPropertyOrThrow should never complete abruptly here"); } } } diff --git a/boa/src/syntax/ast/node/declaration/mod.rs b/boa/src/syntax/ast/node/declaration/mod.rs index 2ef4d7bdbf8..0883721142c 100644 --- a/boa/src/syntax/ast/node/declaration/mod.rs +++ b/boa/src/syntax/ast/node/declaration/mod.rs @@ -103,33 +103,33 @@ impl Executable for DeclarationList { }; match &decl { - Declaration::Identifier(name, init) => { - if self.is_var() && context.has_binding(name.as_ref()) { + Declaration::Identifier { ident, init } => { + if self.is_var() && context.has_binding(ident.as_ref()) { if init.is_some() { - context.set_mutable_binding(name.as_ref(), val, true)?; + context.set_mutable_binding(ident.as_ref(), val, true)?; } continue; } match &self { Const(_) => context.create_immutable_binding( - name.to_string(), + ident.to_string(), false, VariableScope::Block, )?, Let(_) => context.create_mutable_binding( - name.to_string(), + ident.to_string(), false, VariableScope::Block, )?, Var(_) => context.create_mutable_binding( - name.to_string(), + ident.to_string(), false, VariableScope::Function, )?, } - context.initialize_binding(name.as_ref(), val)?; + context.initialize_binding(ident.as_ref(), val)?; } Declaration::Pattern(p) => { for (ident, value) in p.run(None, context)? { @@ -223,18 +223,32 @@ impl From for Box<[Declaration]> { } } -/// Individual declaration. +/// Declaration represents either an individual binding or a binding pattern. +/// +/// For `let` and `const` declarations this type represents a [LexicalBinding][spec1] +/// +/// For `var` declarations this type represents a [VariableDeclaration][spec2] +/// +/// More information: +/// - [ECMAScript reference: 14.3 Declarations and the Variable Statement][spec3] +/// +/// [spec1]: https://tc39.es/ecma262/#prod-LexicalBinding +/// [spec2]: https://tc39.es/ecma262/#prod-VariableDeclaration +/// [spec3]: https://tc39.es/ecma262/#sec-declarations-and-the-variable-statement #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub enum Declaration { - Identifier(Identifier, Option), + Identifier { + ident: Identifier, + init: Option, + }, Pattern(DeclarationPattern), } impl fmt::Display for Declaration { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self { - Self::Identifier(ident, init) => { + Self::Identifier { ident, init } => { fmt::Display::fmt(&ident, f)?; if let Some(ref init) = &init { write!(f, " = {}", init)?; @@ -250,15 +264,20 @@ impl fmt::Display for Declaration { impl Declaration { /// Creates a new variable declaration with a BindingIdentifier. - pub(in crate::syntax) fn new_with_identifier(name: N, init: I) -> Self + #[inline] + pub(in crate::syntax) fn new_with_identifier(ident: N, init: I) -> Self where N: Into, I: Into>, { - Self::Identifier(name.into(), init.into()) + Self::Identifier { + ident: ident.into(), + init: init.into(), + } } /// Creates a new variable declaration with an ObjectBindingPattern. + #[inline] pub(in crate::syntax) fn new_with_object_pattern( bindings: Vec, init: I, @@ -273,6 +292,7 @@ impl Declaration { } /// Creates a new variable declaration with an ArrayBindingPattern. + #[inline] pub(in crate::syntax) fn new_with_array_pattern( bindings: Vec, init: I, @@ -286,15 +306,24 @@ impl Declaration { ))) } - /// Gets the initialization node for the variable, if any. + /// Gets the initialization node for the declaration, if any. + #[inline] pub fn init(&self) -> Option<&Node> { match &self { - Self::Identifier(_, init) => init.as_ref(), + Self::Identifier { init, .. } => init.as_ref(), Self::Pattern(pattern) => pattern.init(), } } } +/// DeclarationPattern represents an object or array binding pattern. +/// +/// This enum mostly wraps the functionality of the specific binding pattern types. +/// +/// More information: +/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingPattern][spec1] +/// +/// [spec1]: https://tc39.es/ecma262/#prod-BindingPattern #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub enum DeclarationPattern { @@ -317,6 +346,11 @@ impl fmt::Display for DeclarationPattern { } impl DeclarationPattern { + /// Initialize the values of an object/array binding pattern. + /// + /// This function only calls the specific initialization function for either the object or the array binding pattern. + /// For specific documentation and references to the ECMAScript spec, look at the called initialization functions. + #[inline] pub(in crate::syntax) fn run( &self, init: Option, @@ -328,6 +362,10 @@ impl DeclarationPattern { } } + /// Gets the list of identifiers declared by the binding pattern. + /// + /// A single binding pattern may declare 0 to n identifiers. + #[inline] pub fn idents(&self) -> Vec<&str> { match &self { DeclarationPattern::Object(pattern) => pattern.idents(), @@ -335,6 +373,8 @@ impl DeclarationPattern { } } + /// Gets the initialization node for the binding pattern, if any. + #[inline] pub fn init(&self) -> Option<&Node> { match &self { DeclarationPattern::Object(pattern) => pattern.init(), @@ -343,6 +383,14 @@ impl DeclarationPattern { } } +/// DeclarationPatternObject represents an object binding pattern. +/// +/// This struct holds a list of bindings, and an optional initializer for the binding pattern. +/// +/// More information: +/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - ObjectBindingPattern][spec1] +/// +/// [spec1]: https://tc39.es/ecma262/#prod-ObjectBindingPattern #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct DeclarationPatternObject { @@ -369,6 +417,8 @@ impl fmt::Display for DeclarationPatternObject { } impl DeclarationPatternObject { + /// Create a new object binding pattern. + #[inline] pub(in crate::syntax) fn new( bindings: Vec, init: Option, @@ -376,6 +426,8 @@ impl DeclarationPatternObject { Self { bindings, init } } + /// Gets the initialization node for the object binding pattern, if any. + #[inline] pub(in crate::syntax) fn init(&self) -> Option<&Node> { self.init.as_ref() } @@ -455,7 +507,7 @@ impl DeclarationPatternObject { } // BindingRestProperty : ... BindingIdentifier RestProperty { - property_name, + ident, excluded_keys, } => { // 1. Let lhs be ? ResolveBinding(StringValue of BindingIdentifier, environment). @@ -468,16 +520,16 @@ impl DeclarationPatternObject { // 4. If environment is undefined, return PutValue(lhs, restObj). // 5. Return InitializeReferencedBinding(lhs, restObj). - results.push((property_name.clone(), rest_obj.into())); + results.push((ident.clone(), rest_obj.into())); } // BindingElement : BindingPattern Initializer[opt] BindingPattern { - property_name, + ident, pattern, default_init, } => { // 1. Let v be ? GetV(value, propertyName). - let mut v = value.get_field(property_name.as_ref(), context)?; + let mut v = value.get_field(ident.as_ref(), context)?; // 2. If Initializer is present and v is undefined, then if let Some(init) = default_init { @@ -497,6 +549,8 @@ impl DeclarationPatternObject { Ok(results) } + /// Gets the list of identifiers declared by the object binding pattern. + #[inline] pub(in crate::syntax) fn idents(&self) -> Vec<&str> { let mut idents = Vec::new(); @@ -513,13 +567,13 @@ impl DeclarationPatternObject { idents.push(ident.as_ref()); } RestProperty { - property_name, + ident: property_name, excluded_keys: _, } => { idents.push(property_name.as_ref()); } BindingPattern { - property_name: _, + ident: _, pattern, default_init: _, } => { @@ -534,6 +588,14 @@ impl DeclarationPatternObject { } } +/// DeclarationPatternArray represents an array binding pattern. +/// +/// This struct holds a list of bindings, and an optional initializer for the binding pattern. +/// +/// More information: +/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - ArrayBindingPattern][spec1] +/// +/// [spec1]: https://tc39.es/ecma262/#prod-ArrayBindingPattern #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct DeclarationPatternArray { @@ -563,6 +625,8 @@ impl fmt::Display for DeclarationPatternArray { } impl DeclarationPatternArray { + /// Create a new array binding pattern. + #[inline] pub(in crate::syntax) fn new( bindings: Vec, init: Option, @@ -570,6 +634,8 @@ impl DeclarationPatternArray { Self { bindings, init } } + /// Gets the initialization node for the array binding pattern, if any. + #[inline] pub(in crate::syntax) fn init(&self) -> Option<&Node> { self.init.as_ref() } @@ -767,6 +833,8 @@ impl DeclarationPatternArray { Ok(result) } + /// Gets the list of identifiers declared by the array binding pattern. + #[inline] pub(in crate::syntax) fn idents(&self) -> Vec<&str> { let mut idents = Vec::new(); @@ -794,21 +862,60 @@ impl DeclarationPatternArray { } } +/// BindingPatternTypeObject represents the different types of bindings that an object binding pattern may contain. +/// +/// More information: +/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - ObjectBindingPattern][spec1] +/// +/// [spec1]: https://tc39.es/ecma262/#prod-ObjectBindingPattern #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub enum BindingPatternTypeObject { + /// Empty represents an empty object binding pattern e.g. `{ }`. Empty, + + /// SingleName represents one of the following properties: + /// + /// - `SingleNameBinding` with an identifier and an optional default initializer. + /// - `BindingProperty` with an property name and a `SingleNameBinding` as the `BindingElement`. + /// + /// More information: + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - SingleNameBinding][spec1] + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingProperty][spec2] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-SingleNameBinding + /// [spec2]: https://tc39.es/ecma262/#prod-BindingProperty SingleName { ident: Box, property_name: Box, default_init: Option, }, + + /// RestProperty represents a `BindingRestProperty` with an identifier. + /// + /// It also includes a list of the property keys that should be excluded from the rest, + /// because they where already assigned. + /// + /// More information: + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestProperty][spec1] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-BindingRestProperty RestProperty { - property_name: Box, + ident: Box, excluded_keys: Vec>, }, + + /// BindingPattern represents a `BindingProperty` with a `BindingPattern` as the `BindingElement`. + /// + /// Additionally to the identifier of the new property and the nested binding pattern, + /// this may also include an optional default initializer. + /// + /// More information: + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingProperty][spec1] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-BindingProperty BindingPattern { - property_name: Box, + ident: Box, pattern: DeclarationPattern, default_init: Option, }, @@ -833,13 +940,13 @@ impl fmt::Display for BindingPatternTypeObject { } } BindingPatternTypeObject::RestProperty { - property_name, + ident: property_name, excluded_keys: _, } => { write!(f, " ... {}", property_name)?; } BindingPatternTypeObject::BindingPattern { - property_name, + ident: property_name, pattern, default_init, } => { @@ -853,24 +960,73 @@ impl fmt::Display for BindingPatternTypeObject { } } +/// BindingPatternTypeArray represents the different types of bindings that an array binding pattern may contain. +/// +/// More information: +/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - ArrayBindingPattern][spec1] +/// +/// [spec1]: https://tc39.es/ecma262/#prod-ArrayBindingPattern #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub enum BindingPatternTypeArray { + /// Empty represents an empty array binding pattern e.g. `[ ]`. + /// + /// This may occur because the `Elision` and `BindingRestElement` in the first type of + /// array binding pattern are both optional. + /// + /// More information: + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - ArrayBindingPattern][spec1] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-ArrayBindingPattern Empty, + + /// Elision represents the elision of an item in the array binding pattern. + /// + /// An `Elision` may occur at multiple points in the pattern and may be multiple elisions. + /// This variant strictly represents one elision. If there are multiple, this should be used multiple times. + /// + /// More information: + /// - [ECMAScript reference: 13.2.4 Array Initializer - Elision][spec1] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-Elision Elision, + + /// SingleName represents a `SingleNameBinding` with an identifier and an optional default initializer. + /// + /// More information: + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - SingleNameBinding][spec1] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-SingleNameBinding SingleName { ident: Box, default_init: Option, }, - BindingPattern { - pattern: DeclarationPattern, - }, - SingleNameRest { - ident: Box, - }, - BindingPatternRest { - pattern: DeclarationPattern, - }, + + /// BindingPattern represents a `BindingPattern` in a `BindingElement` of an array binding pattern. + /// + /// The pattern and the optional default initializer are both stored in the DeclarationPattern. + /// + /// More information: + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingElement][spec1] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-BindingElement + BindingPattern { pattern: DeclarationPattern }, + + /// SingleNameRest represents a `BindingIdentifier` in a `BindingRestElement` of an array binding pattern. + /// + /// More information: + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestElement][spec1] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-BindingRestElement + SingleNameRest { ident: Box }, + + /// SingleNameRest represents a `BindingPattern` in a `BindingRestElement` of an array binding pattern. + /// + /// More information: + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestElement][spec1] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-BindingRestElement + BindingPatternRest { pattern: DeclarationPattern }, } impl fmt::Display for BindingPatternTypeArray { diff --git a/boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs b/boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs index 4cf1c47f93a..2b9a1b04ffc 100644 --- a/boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs +++ b/boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs @@ -131,7 +131,7 @@ impl Executable for ForInLoop { } match &var { - Declaration::Identifier(ident, _) => { + Declaration::Identifier { ident, .. } => { if context.has_binding(ident.as_ref()) { context.set_mutable_binding( ident.as_ref(), @@ -176,7 +176,7 @@ impl Executable for ForInLoop { } match &var { - Declaration::Identifier(ident, _) => { + Declaration::Identifier { ident, .. } => { context.create_mutable_binding( ident.to_string(), false, @@ -209,7 +209,7 @@ impl Executable for ForInLoop { } match &var { - Declaration::Identifier(ident, _) => { + Declaration::Identifier { ident, .. } => { context.create_immutable_binding( ident.to_string(), false, diff --git a/boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs b/boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs index 3db9894c5f8..9e6ccbe9e5a 100644 --- a/boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs +++ b/boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs @@ -119,7 +119,7 @@ impl Executable for ForOfLoop { } match &var { - Declaration::Identifier(ident, _) => { + Declaration::Identifier { ident, .. } => { if context.has_binding(ident.as_ref()) { context.set_mutable_binding( ident.as_ref(), @@ -164,7 +164,7 @@ impl Executable for ForOfLoop { } match &var { - Declaration::Identifier(ident, _) => { + Declaration::Identifier { ident, .. } => { context.create_mutable_binding( ident.to_string(), false, @@ -197,7 +197,7 @@ impl Executable for ForOfLoop { } match &var { - Declaration::Identifier(ident, _) => { + Declaration::Identifier { ident, .. } => { context.create_immutable_binding( ident.to_string(), false, diff --git a/boa/src/syntax/ast/node/statement_list/mod.rs b/boa/src/syntax/ast/node/statement_list/mod.rs index b2a3103e44c..08c595478ed 100644 --- a/boa/src/syntax/ast/node/statement_list/mod.rs +++ b/boa/src/syntax/ast/node/statement_list/mod.rs @@ -60,7 +60,7 @@ impl StatementList { // It is a Syntax Error if the LexicallyDeclaredNames of StatementList contains any duplicate entries. // https://tc39.es/ecma262/#sec-block-static-semantics-early-errors match decl { - Declaration::Identifier(ident, _) => { + Declaration::Identifier { ident, .. } => { if !set.insert(ident.as_ref()) { unreachable!("Redeclaration of {}", ident.as_ref()); } @@ -95,7 +95,7 @@ impl StatementList { if let Node::VarDeclList(decl_list) = stmt { for decl in decl_list.as_ref() { match decl { - Declaration::Identifier(ident, _) => { + Declaration::Identifier { ident, .. } => { set.insert(ident.as_ref()); } Declaration::Pattern(p) => { diff --git a/boa/src/syntax/parser/statement/declaration/lexical.rs b/boa/src/syntax/parser/statement/declaration/lexical.rs index 3ea26db014a..aca9b7bd1aa 100644 --- a/boa/src/syntax/parser/statement/declaration/lexical.rs +++ b/boa/src/syntax/parser/statement/declaration/lexical.rs @@ -160,7 +160,7 @@ where if self.is_const { if self.const_init_required { let init_is_some = match &decl { - Declaration::Identifier(_, init) if init.is_some() => true, + Declaration::Identifier { init, .. } if init.is_some() => true, Declaration::Pattern(p) if p.init().is_some() => true, _ => false, }; diff --git a/boa/src/syntax/parser/statement/mod.rs b/boa/src/syntax/parser/statement/mod.rs index b7fdf2c8e6d..6e42f34e86d 100644 --- a/boa/src/syntax/parser/statement/mod.rs +++ b/boa/src/syntax/parser/statement/mod.rs @@ -316,7 +316,7 @@ where // if name in VarDeclaredNames or can't be added to // LexicallyDeclaredNames, raise an error match decl { - node::Declaration::Identifier(ident, _) => { + node::Declaration::Identifier { ident, .. } => { if var_declared_names.contains(ident.as_ref()) || !lexically_declared_names.insert(ident.as_ref()) { @@ -355,7 +355,7 @@ where Node::VarDeclList(decl_list) => { for decl in decl_list.as_ref() { match decl { - node::Declaration::Identifier(ident, _) => { + node::Declaration::Identifier { ident, .. } => { // if name in LexicallyDeclaredNames, raise an error if lexically_declared_names.contains(ident.as_ref()) { return Err(ParseError::lex(LexError::Syntax( @@ -663,7 +663,7 @@ where .parse(cursor)?; patterns.push( BindingPatternTypeObject::BindingPattern { - property_name, + ident: property_name, pattern: DeclarationPattern::Object( DeclarationPatternObject::new( bindings, None, @@ -676,7 +676,7 @@ where _ => { patterns.push( BindingPatternTypeObject::BindingPattern { - property_name, + ident: property_name, pattern: DeclarationPattern::Object( DeclarationPatternObject::new( bindings, None, @@ -708,7 +708,7 @@ where .parse(cursor)?; patterns.push( BindingPatternTypeObject::BindingPattern { - property_name, + ident: property_name, pattern: DeclarationPattern::Array( DeclarationPatternArray::new( bindings, None, @@ -721,7 +721,7 @@ where _ => { patterns.push( BindingPatternTypeObject::BindingPattern { - property_name, + ident: property_name, pattern: DeclarationPattern::Array( DeclarationPatternArray::new( bindings, None, @@ -796,12 +796,12 @@ where if let Some(rest) = rest_property_name { if patterns.is_empty() { Ok(vec![BindingPatternTypeObject::RestProperty { - property_name: rest, + ident: rest, excluded_keys: property_names, }]) } else { patterns.push(BindingPatternTypeObject::RestProperty { - property_name: rest, + ident: rest, excluded_keys: property_names, }); Ok(patterns)