diff --git a/boa_engine/src/bytecompiler/mod.rs b/boa_engine/src/bytecompiler/mod.rs index 56e05adb23a..794ab9ed87a 100644 --- a/boa_engine/src/bytecompiler/mod.rs +++ b/boa_engine/src/bytecompiler/mod.rs @@ -5,7 +5,7 @@ use crate::{ syntax::ast::{ declaration::{Binding, LexicalDeclaration, VarDeclaration}, expression::{ - access::{PrivatePropertyAccess, PropertyAccess, PropertyAccessField}, + access::{PropertyAccess, PropertyAccessField}, literal::{self, TemplateElement}, operator::{ assign::{AssignOp, AssignTarget}, @@ -178,11 +178,28 @@ enum JumpControlInfoKind { enum Access<'a> { Variable { name: Identifier }, Property { access: &'a PropertyAccess }, - PrivateProperty { access: &'a PrivatePropertyAccess }, - SuperProperty { field: &'a PropertyAccessField }, This, } +impl Access<'_> { + fn from_assign_target(target: &AssignTarget) -> Result, &Pattern> { + match target { + AssignTarget::Identifier(ident) => Ok(Access::Variable { name: *ident }), + AssignTarget::Access(access) => Ok(Access::Property { access }), + AssignTarget::Pattern(pat) => Err(pat), + } + } + + fn from_expression(expr: &Expression) -> Option> { + match expr { + Expression::Identifier(name) => Some(Access::Variable { name: *name }), + Expression::PropertyAccess(access) => Some(Access::Property { access }), + Expression::This => Some(Access::This), + _ => None, + } + } +} + #[derive(Debug)] pub struct ByteCompiler<'b> { code_block: CodeBlock, @@ -637,16 +654,6 @@ impl<'b> ByteCompiler<'b> { } } - #[inline] - fn compile_access(expr: &Expression) -> Option> { - match expr { - Expression::Identifier(name) => Some(Access::Variable { name: *name }), - Expression::PropertyAccess(access) => Some(Access::Property { access }), - Expression::This => Some(Access::This), - _ => None, - } - } - #[inline] fn access_get(&mut self, access: Access<'_>, use_expr: bool) -> JsResult<()> { match access { @@ -655,34 +662,36 @@ impl<'b> ByteCompiler<'b> { let index = self.get_or_insert_binding(binding); self.emit(Opcode::GetName, &[index]); } - Access::Property { access } => match access.field() { - PropertyAccessField::Const(name) => { - let index = self.get_or_insert_name((*name).into()); - self.compile_expr(access.target(), true)?; - self.emit(Opcode::GetPropertyByName, &[index]); - } - PropertyAccessField::Expr(expr) => { - self.compile_expr(expr, true)?; + Access::Property { access } => match access { + PropertyAccess::Simple(access) => match access.field() { + PropertyAccessField::Const(name) => { + let index = self.get_or_insert_name((*name).into()); + self.compile_expr(access.target(), true)?; + self.emit(Opcode::GetPropertyByName, &[index]); + } + PropertyAccessField::Expr(expr) => { + self.compile_expr(expr, true)?; + self.compile_expr(access.target(), true)?; + self.emit(Opcode::GetPropertyByValue, &[]); + } + }, + PropertyAccess::Private(access) => { + let index = self.get_or_insert_name(access.field().into()); self.compile_expr(access.target(), true)?; - self.emit(Opcode::GetPropertyByValue, &[]); - } - }, - Access::PrivateProperty { access } => { - let index = self.get_or_insert_name(access.field().into()); - self.compile_expr(access.target(), true)?; - self.emit(Opcode::GetPrivateField, &[index]); - } - Access::SuperProperty { field } => match field { - PropertyAccessField::Const(field) => { - let index = self.get_or_insert_name((*field).into()); - self.emit_opcode(Opcode::Super); - self.emit(Opcode::GetPropertyByName, &[index]); - } - PropertyAccessField::Expr(expr) => { - self.compile_expr(&**expr, true)?; - self.emit_opcode(Opcode::Super); - self.emit_opcode(Opcode::GetPropertyByValue); + self.emit(Opcode::GetPrivateField, &[index]); } + PropertyAccess::Super(access) => match access.field() { + PropertyAccessField::Const(field) => { + let index = self.get_or_insert_name((*field).into()); + self.emit_opcode(Opcode::Super); + self.emit(Opcode::GetPropertyByName, &[index]); + } + PropertyAccessField::Expr(expr) => { + self.compile_expr(&**expr, true)?; + self.emit_opcode(Opcode::Super); + self.emit_opcode(Opcode::GetPropertyByValue); + } + }, }, Access::This => { self.emit(Opcode::This, &[]); @@ -716,27 +725,72 @@ impl<'b> ByteCompiler<'b> { let index = self.get_or_insert_binding(binding); self.emit(Opcode::SetName, &[index]); } - Access::Property { access } => match access.field() { - PropertyAccessField::Const(name) => { + Access::Property { access } => match access { + PropertyAccess::Simple(access) => match access.field() { + PropertyAccessField::Const(name) => { + self.compile_expr(access.target(), true)?; + let index = self.get_or_insert_name((*name).into()); + self.emit(Opcode::SetPropertyByName, &[index]); + } + PropertyAccessField::Expr(expr) => { + self.compile_expr(expr, true)?; + self.compile_expr(access.target(), true)?; + self.emit(Opcode::SetPropertyByValue, &[]); + } + }, + PropertyAccess::Private(access) => { self.compile_expr(access.target(), true)?; - let index = self.get_or_insert_name((*name).into()); - self.emit(Opcode::SetPropertyByName, &[index]); + self.emit_opcode(Opcode::Swap); + let index = self.get_or_insert_name(access.field().into()); + self.emit(Opcode::AssignPrivateField, &[index]); } - PropertyAccessField::Expr(expr) => { - self.compile_expr(expr, true)?; - self.compile_expr(access.target(), true)?; - self.emit(Opcode::SetPropertyByValue, &[]); + PropertyAccess::Super(access) => match access.field() { + PropertyAccessField::Const(name) => { + self.emit(Opcode::Super, &[]); + let index = self.get_or_insert_name((*name).into()); + self.emit(Opcode::SetPropertyByName, &[index]); + } + PropertyAccessField::Expr(expr) => { + self.compile_expr(expr, true)?; + self.emit(Opcode::Super, &[]); + self.emit(Opcode::SetPropertyByValue, &[]); + } + }, + }, + Access::This => todo!("access_set `this`"), + } + Ok(()) + } + + #[inline] + fn access_delete(&mut self, access: Access<'_>) -> JsResult<()> { + match access { + Access::Property { access } => match access { + PropertyAccess::Simple(access) => match access.field() { + PropertyAccessField::Const(name) => { + let index = self.get_or_insert_name((*name).into()); + self.compile_expr(access.target(), true)?; + self.emit(Opcode::DeletePropertyByName, &[index]); + } + PropertyAccessField::Expr(expr) => { + self.compile_expr(expr, true)?; + self.compile_expr(access.target(), true)?; + self.emit(Opcode::DeletePropertyByValue, &[]); + } + }, + // TODO: throw ReferenceError on super deletion. + PropertyAccess::Super(_) => self.emit(Opcode::PushFalse, &[]), + PropertyAccess::Private(_) => { + unreachable!("deleting private properties should always throw early errors.") } }, - Access::PrivateProperty { access } => { - self.compile_expr(access.target(), true)?; - self.emit_opcode(Opcode::Swap); - let index = self.get_or_insert_name(access.field().into()); - self.emit(Opcode::AssignPrivateField, &[index]); + // TODO: implement delete on references. + Access::Variable { .. } => { + self.emit(Opcode::PushFalse, &[]); + } + Access::This => { + self.emit(Opcode::PushTrue, &[]); } - // TODO: access_set `super` - Access::SuperProperty { field: _field } => {} - Access::This => todo!("access_set `this`"), } Ok(()) } @@ -810,7 +864,7 @@ impl<'b> ByteCompiler<'b> { self.compile_expr(unary.target(), true)?; self.emit(Opcode::Inc, &[]); - let access = Self::compile_access(unary.target()).ok_or_else(|| { + let access = Access::from_expression(unary.target()).ok_or_else(|| { JsNativeError::syntax().with_message("Invalid increment operand") })?; self.access_set(access, None, true)?; @@ -821,7 +875,7 @@ impl<'b> ByteCompiler<'b> { self.emit(Opcode::Dec, &[]); // TODO: promote to an early error. - let access = Self::compile_access(unary.target()).ok_or_else(|| { + let access = Access::from_expression(unary.target()).ok_or_else(|| { JsNativeError::syntax().with_message("Invalid decrement operand") })?; self.access_set(access, None, true)?; @@ -832,7 +886,7 @@ impl<'b> ByteCompiler<'b> { self.emit(Opcode::IncPost, &[]); // TODO: promote to an early error. - let access = Self::compile_access(unary.target()).ok_or_else(|| { + let access = Access::from_expression(unary.target()).ok_or_else(|| { JsNativeError::syntax().with_message("Invalid increment operand") })?; self.access_set(access, None, false)?; @@ -844,39 +898,21 @@ impl<'b> ByteCompiler<'b> { self.emit(Opcode::DecPost, &[]); // TODO: promote to an early error. - let access = Self::compile_access(unary.target()).ok_or_else(|| { + let access = Access::from_expression(unary.target()).ok_or_else(|| { JsNativeError::syntax().with_message("Invalid decrement operand") })?; self.access_set(access, None, false)?; None } - UnaryOp::Delete => match unary.target() { - Expression::PropertyAccess(ref access) => { - match access.field() { - PropertyAccessField::Const(name) => { - let index = self.get_or_insert_name((*name).into()); - self.compile_expr(access.target(), true)?; - self.emit(Opcode::DeletePropertyByName, &[index]); - } - PropertyAccessField::Expr(expr) => { - self.compile_expr(expr, true)?; - self.compile_expr(access.target(), true)?; - self.emit(Opcode::DeletePropertyByValue, &[]); - } - } - None - } - // TODO: implement delete on references. - Expression::Identifier(_) => { - self.emit(Opcode::PushFalse, &[]); - None - } - _ => { + UnaryOp::Delete => { + if let Some(access) = Access::from_expression(unary.target()) { + self.access_delete(access)?; + } else { self.emit(Opcode::PushTrue, &[]); - None } - }, + None + } UnaryOp::Minus => Some(Opcode::Neg), UnaryOp::Plus => Some(Opcode::Pos), UnaryOp::Not => Some(Opcode::LogicalNot), @@ -992,49 +1028,21 @@ impl<'b> ByteCompiler<'b> { } } } - Expression::Assign(assign) if assign.op() == AssignOp::Assign => match assign.lhs() { - AssignTarget::Identifier(name) => self.access_set( - Access::Variable { name: *name }, - Some(assign.rhs()), - use_expr, - )?, - AssignTarget::PrivateProperty(access) => self.access_set( - Access::PrivateProperty { access }, - Some(assign.rhs()), - use_expr, - )?, - AssignTarget::Property(access) => { - self.access_set(Access::Property { access }, Some(assign.rhs()), use_expr)?; - } - AssignTarget::SuperProperty(access) => { - self.access_set( - Access::SuperProperty { - field: access.field(), - }, - Some(assign.rhs()), - use_expr, - )?; - } - AssignTarget::Pattern(pattern) => { - self.compile_expr(assign.rhs(), true)?; - if use_expr { - self.emit_opcode(Opcode::Dup); + Expression::Assign(assign) if assign.op() == AssignOp::Assign => { + match Access::from_assign_target(assign.lhs()) { + Ok(access) => self.access_set(access, Some(assign.rhs()), use_expr)?, + Err(pattern) => { + self.compile_expr(assign.rhs(), true)?; + if use_expr { + self.emit_opcode(Opcode::Dup); + } + self.compile_declaration_pattern(pattern, BindingOpcode::SetName)?; } - self.compile_declaration_pattern(pattern, BindingOpcode::SetName)?; } - }, + } Expression::Assign(assign) => { - let access = match assign.lhs() { - AssignTarget::Identifier(name) => Access::Variable { name: *name }, - AssignTarget::Property(access) => Access::Property { access }, - AssignTarget::PrivateProperty(access) => Access::PrivateProperty { access }, - AssignTarget::SuperProperty(access) => Access::SuperProperty { - field: access.field(), - }, - AssignTarget::Pattern(_) => { - panic!("tried to use an assignment operator on a pattern") - } - }; + let access = Access::from_assign_target(assign.lhs()) + .expect("patterns should throw early errors on complex assignment operators"); self.access_get(access, true)?; let opcode = match assign.op() { AssignOp::Assign => unreachable!(), @@ -1211,15 +1219,6 @@ impl<'b> ByteCompiler<'b> { Expression::PropertyAccess(access) => { self.access_get(Access::Property { access }, use_expr)?; } - Expression::PrivatePropertyAccess(access) => { - self.access_get(Access::PrivateProperty { access }, use_expr)?; - } - Expression::SuperPropertyAccess(access) => self.access_get( - Access::SuperProperty { - field: access.field(), - }, - use_expr, - )?, Expression::Conditional(op) => { self.compile_expr(op.condition(), true)?; let jelse = self.jump_if_false(); @@ -1346,7 +1345,7 @@ impl<'b> ByteCompiler<'b> { } Expression::TaggedTemplate(template) => { match template.tag() { - Expression::PropertyAccess(access) => { + Expression::PropertyAccess(PropertyAccess::Simple(access)) => { self.compile_expr(access.target(), true)?; self.emit(Opcode::Dup, &[]); match access.field() { @@ -1361,6 +1360,12 @@ impl<'b> ByteCompiler<'b> { } } } + Expression::PropertyAccess(PropertyAccess::Private(access)) => { + self.compile_expr(access.target(), true)?; + self.emit(Opcode::Dup, &[]); + let index = self.get_or_insert_name(access.field().into()); + self.emit(Opcode::GetPrivateField, &[index]); + } expr => { self.compile_expr(expr, true)?; self.emit_opcode(Opcode::This); @@ -1653,6 +1658,9 @@ impl<'b> ByteCompiler<'b> { let index = self.get_or_insert_binding(binding); self.emit(Opcode::DefInitVar, &[index]); } + IterableLoopInitializer::Access(access) => { + self.access_set(Access::Property { access }, None, false)?; + } IterableLoopInitializer::Var(declaration) => match declaration { Binding::Identifier(ident) => { self.context.create_mutable_binding(*ident, true); @@ -1771,6 +1779,9 @@ impl<'b> ByteCompiler<'b> { let index = self.get_or_insert_binding(binding); self.emit(Opcode::DefInitVar, &[index]); } + IterableLoopInitializer::Access(access) => { + self.access_set(Access::Property { access }, None, false)?; + } IterableLoopInitializer::Var(declaration) => match declaration { Binding::Identifier(ident) => { self.context.create_mutable_binding(*ident, true); @@ -2344,48 +2355,50 @@ impl<'b> ByteCompiler<'b> { }; match call.function() { - Expression::PropertyAccess(access) => { - self.compile_expr(access.target(), true)?; - if kind == CallKind::Call { - self.emit(Opcode::Dup, &[]); - } - match access.field() { - PropertyAccessField::Const(field) => { - let index = self.get_or_insert_name((*field).into()); - self.emit(Opcode::GetPropertyByName, &[index]); + Expression::PropertyAccess(access) => match access { + PropertyAccess::Simple(access) => { + self.compile_expr(access.target(), true)?; + if kind == CallKind::Call { + self.emit(Opcode::Dup, &[]); } - PropertyAccessField::Expr(field) => { - self.compile_expr(field, true)?; - self.emit(Opcode::Swap, &[]); - self.emit(Opcode::GetPropertyByValue, &[]); + match access.field() { + PropertyAccessField::Const(field) => { + let index = self.get_or_insert_name((*field).into()); + self.emit(Opcode::GetPropertyByName, &[index]); + } + PropertyAccessField::Expr(field) => { + self.compile_expr(field, true)?; + self.emit(Opcode::Swap, &[]); + self.emit(Opcode::GetPropertyByValue, &[]); + } } } - } - Expression::SuperPropertyAccess(access) => { - if kind == CallKind::Call { - self.emit_opcode(Opcode::This); + PropertyAccess::Private(access) => { + self.compile_expr(access.target(), true)?; + if kind == CallKind::Call { + self.emit(Opcode::Dup, &[]); + } + let index = self.get_or_insert_name(access.field().into()); + self.emit(Opcode::GetPrivateField, &[index]); } - self.emit_opcode(Opcode::Super); - match access.field() { - PropertyAccessField::Const(field) => { - let index = self.get_or_insert_name((*field).into()); - self.emit(Opcode::GetPropertyByName, &[index]); + PropertyAccess::Super(access) => { + if kind == CallKind::Call { + self.emit_opcode(Opcode::This); } - PropertyAccessField::Expr(expr) => { - self.compile_expr(expr, true)?; - self.emit_opcode(Opcode::Swap); - self.emit_opcode(Opcode::GetPropertyByValue); + self.emit_opcode(Opcode::Super); + match access.field() { + PropertyAccessField::Const(field) => { + let index = self.get_or_insert_name((*field).into()); + self.emit(Opcode::GetPropertyByName, &[index]); + } + PropertyAccessField::Expr(expr) => { + self.compile_expr(expr, true)?; + self.emit_opcode(Opcode::Swap); + self.emit_opcode(Opcode::GetPropertyByValue); + } } } - } - Expression::PrivatePropertyAccess(access) => { - self.compile_expr(access.target(), true)?; - if kind == CallKind::Call { - self.emit(Opcode::Dup, &[]); - } - let index = self.get_or_insert_name(access.field().into()); - self.emit(Opcode::GetPrivateField, &[index]); - } + }, expr => { self.compile_expr(expr, true)?; if kind == CallKind::Call || kind == CallKind::CallEval { diff --git a/boa_engine/src/syntax/ast/expression/access.rs b/boa_engine/src/syntax/ast/expression/access.rs index 46569bee0ee..75599775353 100644 --- a/boa_engine/src/syntax/ast/expression/access.rs +++ b/boa_engine/src/syntax/ast/expression/access.rs @@ -7,6 +7,10 @@ //! - *Bracket notation* is used when the name of the property is either variable, not a valid //! identifier or a symbol e.g. `arr[var]`, `arr[5]`, `arr[Symbol.iterator]`. //! +//! A property access expression can be represented by a [`SimplePropertyAccess`] (`x.y`), a +//! [`PrivatePropertyAccess`] (`x.#y`) or a [`SuperPropertyAccess`] (`super["y"]`), each of them with +//! slightly different semantics overall. +//! //! [spec]: https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-property-accessors //! [access]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors @@ -60,12 +64,62 @@ impl From for PropertyAccessField { /// See the [module level documentation][self] for more information. #[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] -pub struct PropertyAccess { +pub enum PropertyAccess { + /// A simple property access (`x.prop`). + Simple(SimplePropertyAccess), + /// A property access of a private property (`x.#priv`). + Private(PrivatePropertyAccess), + /// A property access of a `super` reference. (`super["prop"]`). + Super(SuperPropertyAccess), +} + +impl PropertyAccess { + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + match self { + PropertyAccess::Simple(s) => s.contains_arguments(), + PropertyAccess::Private(p) => p.contains_arguments(), + PropertyAccess::Super(s) => s.contains_arguments(), + } + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + match self { + PropertyAccess::Simple(s) => s.contains(symbol), + PropertyAccess::Private(p) => p.contains(symbol), + PropertyAccess::Super(s) => s.contains(symbol), + } + } +} + +impl ToInternedString for PropertyAccess { + #[inline] + fn to_interned_string(&self, interner: &Interner) -> String { + match self { + PropertyAccess::Simple(s) => s.to_interned_string(interner), + PropertyAccess::Private(p) => p.to_interned_string(interner), + PropertyAccess::Super(s) => s.to_interned_string(interner), + } + } +} + +impl From for Expression { + #[inline] + fn from(access: PropertyAccess) -> Self { + Self::PropertyAccess(access) + } +} + +/// A simple property access, where the target object is an [`Expression`]. +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub struct SimplePropertyAccess { target: Box, field: PropertyAccessField, } -impl PropertyAccess { +impl SimplePropertyAccess { /// Gets the target object of the property access. #[inline] pub fn target(&self) -> &Expression { @@ -101,7 +155,7 @@ impl PropertyAccess { } } -impl ToInternedString for PropertyAccess { +impl ToInternedString for SimplePropertyAccess { #[inline] fn to_interned_string(&self, interner: &Interner) -> String { let target = self.target.to_interned_string(interner); @@ -114,17 +168,18 @@ impl ToInternedString for PropertyAccess { } } -impl From for Expression { +impl From for PropertyAccess { #[inline] - fn from(access: PropertyAccess) -> Self { - Self::PropertyAccess(access) + fn from(access: SimplePropertyAccess) -> Self { + Self::Simple(access) } } /// An access expression to a class object's [private fields][mdn]. /// /// Private property accesses differ slightly from plain property accesses, since the accessed -/// property must be prefixed by `#`, and the bracket notation is not allowed e.g. `this.#a`. +/// property must be prefixed by `#`, and the bracket notation is not allowed. For example, +/// `this.#a` is a valid private property access. /// /// This expression corresponds to the [`MemberExpression.PrivateIdentifier`][spec] production. /// @@ -181,10 +236,10 @@ impl ToInternedString for PrivatePropertyAccess { } } -impl From for Expression { +impl From for PropertyAccess { #[inline] fn from(access: PrivatePropertyAccess) -> Self { - Self::PrivatePropertyAccess(access) + Self::Private(access) } } @@ -219,7 +274,7 @@ impl SuperPropertyAccess { #[inline] pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { - self.field.contains(symbol) + symbol == ContainsSymbol::SuperProperty || self.field.contains(symbol) } } @@ -237,9 +292,9 @@ impl ToInternedString for SuperPropertyAccess { } } -impl From for Expression { +impl From for PropertyAccess { #[inline] fn from(access: SuperPropertyAccess) -> Self { - Self::SuperPropertyAccess(access) + Self::Super(access) } } diff --git a/boa_engine/src/syntax/ast/expression/mod.rs b/boa_engine/src/syntax/ast/expression/mod.rs index ec664d638b9..0ca580eee7a 100644 --- a/boa_engine/src/syntax/ast/expression/mod.rs +++ b/boa_engine/src/syntax/ast/expression/mod.rs @@ -12,7 +12,7 @@ use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString}; use self::{ - access::{PrivatePropertyAccess, PropertyAccess, SuperPropertyAccess}, + access::PropertyAccess, literal::{ArrayLiteral, Literal, ObjectLiteral, TemplateLiteral}, operator::{Assign, Binary, Conditional, Unary}, }; @@ -104,12 +104,6 @@ pub enum Expression { /// See [`PropertyAccess`]. PropertyAccess(PropertyAccess), - /// See [`SuperPropertyAccess`] - SuperPropertyAccess(SuperPropertyAccess), - - /// See [`PrivatePropertyAccess]. - PrivatePropertyAccess(PrivatePropertyAccess), - /// See [`New`]. New(New), @@ -176,8 +170,6 @@ impl Expression { Self::AsyncGenerator(asgen) => asgen.to_indented_string(interner, indentation), Self::TemplateLiteral(tem) => tem.to_interned_string(interner), Self::PropertyAccess(prop) => prop.to_interned_string(interner), - Self::SuperPropertyAccess(supp) => supp.to_interned_string(interner), - Self::PrivatePropertyAccess(private) => private.to_interned_string(interner), Self::New(new) => new.to_interned_string(interner), Self::Call(call) => call.to_interned_string(interner), Self::SuperCall(supc) => supc.to_interned_string(interner), @@ -218,8 +210,6 @@ impl Expression { Expression::Class(class) => class.contains_arguments(), Expression::TemplateLiteral(template) => template.contains_arguments(), Expression::PropertyAccess(access) => access.contains_arguments(), - Expression::SuperPropertyAccess(access) => access.contains_arguments(), - Expression::PrivatePropertyAccess(access) => access.contains_arguments(), Expression::New(new) => new.contains_arguments(), Expression::Call(call) => call.contains_arguments(), Expression::SuperCall(call) => call.contains_arguments(), @@ -257,12 +247,7 @@ impl Expression { Expression::ArrowFunction(arrow) => arrow.contains(symbol), Expression::Class(class) => class.contains(symbol), Expression::TemplateLiteral(temp) => temp.contains(symbol), - Expression::PropertyAccess(access) => access.contains(symbol), - Expression::SuperPropertyAccess(_access) if symbol == ContainsSymbol::SuperProperty => { - true - } - Expression::SuperPropertyAccess(access) => access.contains(symbol), - Expression::PrivatePropertyAccess(access) => access.contains(symbol), + Expression::PropertyAccess(prop) => prop.contains(symbol), Expression::New(new) => new.contains(symbol), Expression::Call(call) => call.contains(symbol), Expression::SuperCall(_) if symbol == ContainsSymbol::SuperCall => true, diff --git a/boa_engine/src/syntax/ast/expression/operator/assign/mod.rs b/boa_engine/src/syntax/ast/expression/operator/assign/mod.rs index 3474908ae83..f3ed6b31cf7 100644 --- a/boa_engine/src/syntax/ast/expression/operator/assign/mod.rs +++ b/boa_engine/src/syntax/ast/expression/operator/assign/mod.rs @@ -19,7 +19,7 @@ use boa_interner::{Interner, Sym, ToInternedString}; use crate::syntax::{ ast::{ expression::{ - access::{PrivatePropertyAccess, PropertyAccess, SuperPropertyAccess}, + access::PropertyAccess, identifier::Identifier, literal::{ArrayLiteral, ObjectLiteral}, Expression, @@ -75,9 +75,7 @@ impl Assign { pub(crate) fn contains_arguments(&self) -> bool { (match &*self.lhs { AssignTarget::Identifier(ident) => *ident == Sym::ARGUMENTS, - AssignTarget::Property(access) => access.contains_arguments(), - AssignTarget::PrivateProperty(access) => access.contains_arguments(), - AssignTarget::SuperProperty(access) => access.contains_arguments(), + AssignTarget::Access(access) => access.contains_arguments(), AssignTarget::Pattern(pattern) => pattern.contains_arguments(), } || self.rhs.contains_arguments()) } @@ -86,9 +84,7 @@ impl Assign { pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { (match &*self.lhs { AssignTarget::Identifier(_) => false, - AssignTarget::Property(access) => access.contains(symbol), - AssignTarget::PrivateProperty(access) => access.contains(symbol), - AssignTarget::SuperProperty(access) => access.contains(symbol), + AssignTarget::Access(access) => access.contains(symbol), AssignTarget::Pattern(pattern) => pattern.contains(symbol), } || self.rhs.contains(symbol)) } @@ -123,12 +119,8 @@ pub enum AssignTarget { /// A simple identifier, such as `a`. Identifier(Identifier), /// A property access, such as `a.prop`. - Property(PropertyAccess), - /// A private property access, such as `a.#priv`. - PrivateProperty(PrivatePropertyAccess), - /// A `super` property access, such as `super.prop`. - SuperProperty(SuperPropertyAccess), - /// A pattern assignment target, such as `{a, b, ...c}`. + Access(PropertyAccess), + /// A pattern assignment, such as `{a, b, ...c}`. Pattern(Pattern), } @@ -142,11 +134,7 @@ impl AssignTarget { ) -> Option { match expression { Expression::Identifier(id) => Some(Self::Identifier(*id)), - Expression::PropertyAccess(access) => Some(Self::Property(access.clone())), - Expression::PrivatePropertyAccess(access) => { - Some(Self::PrivateProperty(access.clone())) - } - Expression::SuperPropertyAccess(access) => Some(Self::SuperProperty(access.clone())), + Expression::PropertyAccess(access) => Some(Self::Access(access.clone())), Expression::ObjectLiteral(object) if destructure => { let pattern = object_decl_to_declaration_pattern(object, strict)?; Some(Self::Pattern(pattern.into())) @@ -165,9 +153,7 @@ impl ToInternedString for AssignTarget { fn to_interned_string(&self, interner: &Interner) -> String { match self { AssignTarget::Identifier(id) => id.to_interned_string(interner), - AssignTarget::Property(access) => access.to_interned_string(interner), - AssignTarget::PrivateProperty(access) => access.to_interned_string(interner), - AssignTarget::SuperProperty(access) => access.to_interned_string(interner), + AssignTarget::Access(access) => access.to_interned_string(interner), AssignTarget::Pattern(pattern) => pattern.to_interned_string(interner), } } @@ -189,9 +175,7 @@ pub(crate) fn object_decl_to_declaration_pattern( let mut excluded_keys = Vec::new(); for (i, property) in object.properties().iter().enumerate() { match property { - PropertyDefinition::IdentifierReference(ident) - if strict && ident.sym() == Sym::EVAL => - { + PropertyDefinition::IdentifierReference(ident) if strict && *ident == Sym::EVAL => { return None } PropertyDefinition::IdentifierReference(ident) => { @@ -279,21 +263,18 @@ pub(crate) fn object_decl_to_declaration_pattern( default_init: Some(assign.rhs().clone()), }); } - AssignTarget::Property(field) => { + AssignTarget::Access(access) => { bindings.push(ObjectPatternElement::AssignmentPropertyAccess { name: name.clone(), - access: field.clone(), + access: access.clone(), default_init: Some(assign.rhs().clone()), }); } - AssignTarget::SuperProperty(_) | AssignTarget::PrivateProperty(_) => { - return None - } }, - (_, Expression::PropertyAccess(field)) => { + (_, Expression::PropertyAccess(access)) => { bindings.push(ObjectPatternElement::AssignmentPropertyAccess { name: name.clone(), - access: field.clone(), + access: access.clone(), default_init: None, }); } @@ -402,7 +383,7 @@ pub(crate) fn array_decl_to_declaration_pattern( default_init: Some(assign.rhs().clone()), }); } - AssignTarget::Property(access) => { + AssignTarget::Access(access) => { bindings.push(ArrayPatternElement::PropertyAccess { access: access.clone(), }); @@ -421,7 +402,6 @@ pub(crate) fn array_decl_to_declaration_pattern( }); } }, - AssignTarget::PrivateProperty(_) | AssignTarget::SuperProperty(_) => return None, }, Expression::ArrayLiteral(array) => { let pattern = array_decl_to_declaration_pattern(array, strict)?.into(); diff --git a/boa_engine/src/syntax/ast/pattern.rs b/boa_engine/src/syntax/ast/pattern.rs index c0ea5c1c42f..d70a513ffa6 100644 --- a/boa_engine/src/syntax/ast/pattern.rs +++ b/boa_engine/src/syntax/ast/pattern.rs @@ -22,13 +22,10 @@ //! [spec2]: https://tc39.es/ecma262/#prod-AssignmentPattern //! [destr]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment -use boa_interner::{Interner, ToInternedString}; +use boa_interner::{Interner, Sym, ToInternedString}; use super::{ - expression::{ - access::{PropertyAccess, PropertyAccessField}, - Identifier, - }, + expression::{access::PropertyAccess, Identifier}, property::PropertyName, ContainsSymbol, Expression, }; @@ -374,80 +371,58 @@ impl ObjectPatternElement { pub(crate) fn contains_arguments(&self) -> bool { match self { ObjectPatternElement::SingleName { - name, default_init, .. + name, + ident, + default_init, } => { - if let PropertyName::Computed(node) = name { - if node.contains_arguments() { - return true; - } - } - if let Some(init) = default_init { - if init.contains_arguments() { - return true; - } - } + *ident == Sym::ARGUMENTS + || name.contains_arguments() + || matches!(default_init, Some(init) if init.contains_arguments()) + } + ObjectPatternElement::RestProperty { ident, .. } => *ident == Sym::ARGUMENTS, + ObjectPatternElement::AssignmentPropertyAccess { + name, + access, + default_init, + } => { + name.contains_arguments() + || access.contains_arguments() + || matches!(default_init, Some(init) if init.contains_arguments()) } ObjectPatternElement::AssignmentRestPropertyAccess { access, .. } => { - if access.target().contains_arguments() { - return true; - } + access.contains_arguments() } ObjectPatternElement::Pattern { name, pattern, default_init, } => { - if let PropertyName::Computed(node) = name { - if node.contains_arguments() { - return true; - } - } - if pattern.contains_arguments() { - return true; - } - if let Some(init) = default_init { - if init.contains_arguments() { - return true; - } - } + name.contains_arguments() + || pattern.contains_arguments() + || matches!(default_init, Some(init) if init.contains_arguments()) } - _ => {} } - false } pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { match self { Self::SingleName { - default_init: Some(node), - .. + name, default_init, .. } => { - if node.contains(symbol) { - return true; - } - } - Self::AssignmentRestPropertyAccess { access, .. } => { - if access.target().contains(symbol) { - return true; - } + name.contains(symbol) || matches!(default_init, Some(init) if init.contains(symbol)) } + Self::AssignmentRestPropertyAccess { access, .. } => access.contains(symbol), Self::Pattern { + name, pattern, default_init, - .. } => { - if let Some(node) = default_init { - if node.contains(symbol) { - return true; - } - } - if pattern.contains(symbol) { - return true; - } + name.contains(symbol) + || matches!(default_init, Some(init) if init.contains(symbol)) + || pattern.contains(symbol) } - _ => {} + _ => false, } - false } /// Gets the list of identifiers declared by the object binding pattern. @@ -675,45 +650,27 @@ impl ArrayPatternElement { #[inline] pub(crate) fn contains_arguments(&self) -> bool { match self { + Self::Elision => false, Self::SingleName { - default_init: Some(init), - .. + ident, + default_init, } => { - if init.contains_arguments() { - return true; - } + *ident == Sym::ARGUMENTS + || matches!(default_init, Some(init) if init.contains_arguments()) } + Self::SingleNameRest { ident } => *ident == Sym::ARGUMENTS, Self::PropertyAccess { access } | Self::PropertyAccessRest { access } => { - if access.target().contains_arguments() { - return true; - } - if let PropertyAccessField::Expr(expr) = access.field() { - if expr.contains_arguments() { - return true; - } - } - } - Self::PatternRest { pattern } => { - if pattern.contains_arguments() { - return true; - } + access.contains_arguments() } + Self::PatternRest { pattern } => pattern.contains_arguments(), Self::Pattern { pattern, default_init, } => { - if pattern.contains_arguments() { - return true; - } - if let Some(init) = default_init { - if init.contains_arguments() { - return true; - } - } + pattern.contains_arguments() + || matches!(default_init, Some(init) if init.contains_arguments()) } - _ => {} } - false } /// Returns `true` if the node contains the given token. @@ -724,47 +681,22 @@ impl ArrayPatternElement { /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { match self { - Self::SingleName { - default_init: Some(node), - .. - } => { - if node.contains(symbol) { - return true; - } + Self::Elision | Self::SingleNameRest { .. } => false, + Self::SingleName { default_init, .. } => { + matches!(default_init, Some(init) if init.contains(symbol)) } Self::PropertyAccess { access } | Self::PropertyAccessRest { access } => { - if access.target().contains(symbol) { - return true; - } - - if let PropertyAccessField::Expr(expr) = access.field() { - if expr.contains(symbol) { - return true; - } - } + access.contains(symbol) } Self::Pattern { pattern, default_init, } => { - if pattern.contains(symbol) { - return true; - } - - if let Some(init) = default_init { - if init.contains(symbol) { - return true; - } - } - } - Self::PatternRest { pattern } => { - if pattern.contains(symbol) { - return true; - } + pattern.contains(symbol) + || matches!(default_init, Some(init) if init.contains(symbol)) } - _ => {} + Self::PatternRest { pattern } => pattern.contains(symbol), } - false } /// Gets the list of identifiers in the array pattern element. diff --git a/boa_engine/src/syntax/ast/statement/iteration/mod.rs b/boa_engine/src/syntax/ast/statement/iteration/mod.rs index 39da7bd8648..54c4239ead5 100644 --- a/boa_engine/src/syntax/ast/statement/iteration/mod.rs +++ b/boa_engine/src/syntax/ast/statement/iteration/mod.rs @@ -8,7 +8,11 @@ mod for_loop; mod for_of_loop; mod while_loop; -use crate::syntax::ast::{declaration::Binding, expression::Identifier, pattern::Pattern}; +use crate::syntax::ast::{ + declaration::Binding, + expression::{access::PropertyAccess, Identifier}, + pattern::Pattern, +}; pub use self::{ do_while_loop::DoWhileLoop, @@ -36,9 +40,10 @@ mod tests; #[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub enum IterableLoopInitializer { - // TODO: This should also accept property accessors /// An already declared variable. Identifier(Identifier), + /// A property access. + Access(PropertyAccess), /// A new var declaration. Var(Binding), /// A new let declaration. @@ -69,6 +74,7 @@ impl IterableLoopInitializer { pub(crate) fn contains_arguments(&self) -> bool { match self { Self::Identifier(ident) => *ident == Sym::ARGUMENTS, + Self::Access(access) => access.contains_arguments(), Self::Var(bind) | Self::Let(bind) | Self::Const(bind) => bind.contains_arguments(), Self::Pattern(pattern) => pattern.contains_arguments(), } @@ -81,6 +87,7 @@ impl IterableLoopInitializer { declaration.contains(symbol) } Self::Pattern(pattern) => pattern.contains(symbol), + Self::Access(access) => access.contains(symbol), Self::Identifier(_) => false, } } @@ -91,6 +98,7 @@ impl ToInternedString for IterableLoopInitializer { let (binding, pre) = match self { Self::Identifier(ident) => return ident.to_interned_string(interner), Self::Pattern(pattern) => return pattern.to_interned_string(interner), + Self::Access(access) => return access.to_interned_string(interner), Self::Var(binding) => (binding, "var"), Self::Let(binding) => (binding, "let"), Self::Const(binding) => (binding, "const"), diff --git a/boa_engine/src/syntax/parser/expression/left_hand_side/call.rs b/boa_engine/src/syntax/parser/expression/left_hand_side/call.rs index c56840c8850..45287378078 100644 --- a/boa_engine/src/syntax/parser/expression/left_hand_side/call.rs +++ b/boa_engine/src/syntax/parser/expression/left_hand_side/call.rs @@ -12,7 +12,7 @@ use crate::syntax::{ ast::{ self, expression::{ - access::{PrivatePropertyAccess, PropertyAccess}, + access::{PrivatePropertyAccess, SimplePropertyAccess}, Call, }, Punctuator, @@ -95,25 +95,21 @@ where TokenKind::Punctuator(Punctuator::Dot) => { cursor.next(interner)?.ok_or(ParseError::AbruptEnd)?; // We move the parser forward. - match cursor.next(interner)?.ok_or(ParseError::AbruptEnd)?.kind() { - TokenKind::Identifier(name) => { - lhs = PropertyAccess::new(lhs, *name).into(); - } + let access = match cursor.next(interner)?.ok_or(ParseError::AbruptEnd)?.kind() { + TokenKind::Identifier(name) => SimplePropertyAccess::new(lhs, *name).into(), TokenKind::Keyword((kw, _)) => { - lhs = PropertyAccess::new(lhs, kw.to_sym(interner)).into(); + SimplePropertyAccess::new(lhs, kw.to_sym(interner)).into() } TokenKind::BooleanLiteral(true) => { - lhs = PropertyAccess::new(lhs, Sym::TRUE).into(); + SimplePropertyAccess::new(lhs, Sym::TRUE).into() } TokenKind::BooleanLiteral(false) => { - lhs = PropertyAccess::new(lhs, Sym::FALSE).into(); - } - TokenKind::NullLiteral => { - lhs = PropertyAccess::new(lhs, Sym::NULL).into(); + SimplePropertyAccess::new(lhs, Sym::FALSE).into() } + TokenKind::NullLiteral => SimplePropertyAccess::new(lhs, Sym::NULL).into(), TokenKind::PrivateIdentifier(name) => { cursor.push_used_private_identifier(*name, token.span().start())?; - lhs = PrivatePropertyAccess::new(lhs, *name).into(); + PrivatePropertyAccess::new(lhs, *name).into() } _ => { return Err(ParseError::expected( @@ -123,14 +119,17 @@ where "call expression", )); } - } + }; + + lhs = ast::Expression::PropertyAccess(access); } TokenKind::Punctuator(Punctuator::OpenBracket) => { let _next = cursor.next(interner)?.ok_or(ParseError::AbruptEnd)?; // We move the parser. let idx = Expression::new(None, true, self.allow_yield, self.allow_await) .parse(cursor, interner)?; cursor.expect(Punctuator::CloseBracket, "call expression", interner)?; - lhs = PropertyAccess::new(lhs, idx).into(); + lhs = + ast::Expression::PropertyAccess(SimplePropertyAccess::new(lhs, idx).into()); } TokenKind::TemplateNoSubstitution { .. } | TokenKind::TemplateMiddle { .. } => { lhs = TaggedTemplateLiteral::new( diff --git a/boa_engine/src/syntax/parser/expression/left_hand_side/member.rs b/boa_engine/src/syntax/parser/expression/left_hand_side/member.rs index ad7e3bd9eeb..89ad3c7f072 100644 --- a/boa_engine/src/syntax/parser/expression/left_hand_side/member.rs +++ b/boa_engine/src/syntax/parser/expression/left_hand_side/member.rs @@ -11,7 +11,8 @@ use crate::syntax::{ self, expression::{ access::{ - PrivatePropertyAccess, PropertyAccess, PropertyAccessField, SuperPropertyAccess, + PrivatePropertyAccess, PropertyAccessField, SimplePropertyAccess, + SuperPropertyAccess, }, Call, Identifier, New, }, @@ -141,13 +142,15 @@ where )); } }; - field.into() + ast::Expression::PropertyAccess(field.into()) } TokenKind::Punctuator(Punctuator::OpenBracket) => { let expr = Expression::new(None, true, self.allow_yield, self.allow_await) .parse(cursor, interner)?; cursor.expect(Punctuator::CloseBracket, "super property", interner)?; - ast::Expression::from(SuperPropertyAccess::new(expr.into())) + ast::Expression::PropertyAccess( + SuperPropertyAccess::new(expr.into()).into(), + ) } _ => { return Err(ParseError::unexpected( @@ -173,23 +176,21 @@ where let token = cursor.next(interner)?.ok_or(ParseError::AbruptEnd)?; - match token.kind() { - TokenKind::Identifier(name) => lhs = PropertyAccess::new(lhs, *name).into(), + let access = match token.kind() { + TokenKind::Identifier(name) => SimplePropertyAccess::new(lhs, *name).into(), TokenKind::Keyword((kw, _)) => { - lhs = PropertyAccess::new(lhs, kw.to_sym(interner)).into(); + SimplePropertyAccess::new(lhs, kw.to_sym(interner)).into() } TokenKind::BooleanLiteral(true) => { - lhs = PropertyAccess::new(lhs, Sym::TRUE).into(); + SimplePropertyAccess::new(lhs, Sym::TRUE).into() } TokenKind::BooleanLiteral(false) => { - lhs = PropertyAccess::new(lhs, Sym::FALSE).into(); - } - TokenKind::NullLiteral => { - lhs = PropertyAccess::new(lhs, Sym::NULL).into(); + SimplePropertyAccess::new(lhs, Sym::FALSE).into() } + TokenKind::NullLiteral => SimplePropertyAccess::new(lhs, Sym::NULL).into(), TokenKind::PrivateIdentifier(name) => { cursor.push_used_private_identifier(*name, token.span().start())?; - lhs = PrivatePropertyAccess::new(lhs, *name).into(); + PrivatePropertyAccess::new(lhs, *name).into() } _ => { return Err(ParseError::expected( @@ -199,7 +200,9 @@ where "member expression", )); } - } + }; + + lhs = ast::Expression::PropertyAccess(access); } TokenKind::Punctuator(Punctuator::OpenBracket) => { cursor @@ -208,7 +211,8 @@ where let idx = Expression::new(None, true, self.allow_yield, self.allow_await) .parse(cursor, interner)?; cursor.expect(Punctuator::CloseBracket, "member expression", interner)?; - lhs = PropertyAccess::new(lhs, idx).into(); + lhs = + ast::Expression::PropertyAccess(SimplePropertyAccess::new(lhs, idx).into()); } TokenKind::TemplateNoSubstitution { .. } | TokenKind::TemplateMiddle { .. } => { lhs = TaggedTemplateLiteral::new( diff --git a/boa_engine/src/syntax/parser/expression/left_hand_side/tests.rs b/boa_engine/src/syntax/parser/expression/left_hand_side/tests.rs index 5f57885a894..50df92d5be6 100644 --- a/boa_engine/src/syntax/parser/expression/left_hand_side/tests.rs +++ b/boa_engine/src/syntax/parser/expression/left_hand_side/tests.rs @@ -1,6 +1,6 @@ use crate::syntax::{ ast::{ - expression::{access::PropertyAccess, Call, Identifier}, + expression::{access::SimplePropertyAccess, Call, Identifier}, Expression, Statement, }, parser::tests::check_parser, @@ -13,14 +13,17 @@ macro_rules! check_call_property_identifier { let mut interner = Interner::default(); check_parser( format!("a().{}", $property).as_str(), - vec![Statement::Expression(Expression::from(PropertyAccess::new( - Call::new( - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), - Box::default(), + vec![Statement::Expression(Expression::PropertyAccess( + SimplePropertyAccess::new( + Call::new( + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Box::default(), + ) + .into(), + interner.get_or_intern_static($property, utf16!($property)), ) .into(), - interner.get_or_intern_static($property, utf16!($property)), - ))) + )) .into()], interner, ); @@ -41,10 +44,13 @@ macro_rules! check_member_property_identifier { let mut interner = Interner::default(); check_parser( format!("a.{}", $property).as_str(), - vec![Statement::Expression(Expression::from(PropertyAccess::new( - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), - interner.get_or_intern_static($property, utf16!($property)), - ))) + vec![Statement::Expression(Expression::PropertyAccess( + SimplePropertyAccess::new( + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + interner.get_or_intern_static($property, utf16!($property)), + ) + .into(), + )) .into()], interner, ); diff --git a/boa_engine/src/syntax/parser/expression/primary/mod.rs b/boa_engine/src/syntax/parser/expression/primary/mod.rs index 843726386fc..b160d25c742 100644 --- a/boa_engine/src/syntax/parser/expression/primary/mod.rs +++ b/boa_engine/src/syntax/parser/expression/primary/mod.rs @@ -541,7 +541,7 @@ fn expression_to_formal_parameters( )); } }, - _ => { + AssignTarget::Access(_) => { return Err(ParseError::general( "invalid initialization expression in formal parameter list", span.start(), diff --git a/boa_engine/src/syntax/parser/expression/unary.rs b/boa_engine/src/syntax/parser/expression/unary.rs index 8d454b04523..060a2fb6d7a 100644 --- a/boa_engine/src/syntax/parser/expression/unary.rs +++ b/boa_engine/src/syntax/parser/expression/unary.rs @@ -10,6 +10,7 @@ use crate::syntax::{ ast::{ expression::{ + access::PropertyAccess, operator::{unary::UnaryOp, Unary}, Identifier, }, @@ -87,7 +88,7 @@ where token_start, ))); } - Expression::PrivatePropertyAccess(_) => { + Expression::PropertyAccess(PropertyAccess::Private(_)) => { return Err(ParseError::general( "private fields can not be deleted", position, diff --git a/boa_engine/src/syntax/parser/statement/iteration/for_statement.rs b/boa_engine/src/syntax/parser/statement/iteration/for_statement.rs index ea0d4e9dda9..e08232a3a21 100644 --- a/boa_engine/src/syntax/parser/statement/iteration/for_statement.rs +++ b/boa_engine/src/syntax/parser/statement/iteration/for_statement.rs @@ -377,13 +377,7 @@ fn initializer_to_iterable_loop_initializer( }) .map(|obj| IterableLoopInitializer::Pattern(obj.into())) } - // TODO: implement member initializers - ast::Expression::PropertyAccess(_) - | ast::Expression::SuperPropertyAccess(_) - | ast::Expression::PrivatePropertyAccess(_) => Err(ParseError::Unimplemented { - message: "using a property access as iterable loop initializer is not implemented", - position, - }), + ast::Expression::PropertyAccess(access) => Ok(IterableLoopInitializer::Access(access)), _ => Err(ParseError::lex(LexError::Syntax( "invalid variable for iterable loop".into(), position, diff --git a/boa_engine/src/syntax/parser/statement/iteration/tests.rs b/boa_engine/src/syntax/parser/statement/iteration/tests.rs index efbf6481750..8ecc62df31f 100644 --- a/boa_engine/src/syntax/parser/statement/iteration/tests.rs +++ b/boa_engine/src/syntax/parser/statement/iteration/tests.rs @@ -2,7 +2,7 @@ use crate::syntax::{ ast::{ declaration::{VarDeclaration, Variable}, expression::{ - access::PropertyAccess, + access::SimplePropertyAccess, literal::Literal, operator::{ assign::AssignOp, binary::RelationalOp, unary::UnaryOp, Assign, Binary, Unary, @@ -64,14 +64,16 @@ fn check_do_while_semicolon_insertion() { Statement::Block( vec![StatementListItem::Statement(Statement::Expression( Expression::from(Call::new( - PropertyAccess::new( - Identifier::new( - interner.get_or_intern_static("console", utf16!("console")), + Expression::PropertyAccess( + SimplePropertyAccess::new( + Identifier::new( + interner.get_or_intern_static("console", utf16!("console")), + ) + .into(), + interner.get_or_intern_static("log", utf16!("log")), ) .into(), - interner.get_or_intern_static("log", utf16!("log")), - ) - .into(), + ), vec![Literal::from( interner.get_or_intern_static("hello", utf16!("hello")), ) @@ -94,12 +96,16 @@ fn check_do_while_semicolon_insertion() { )) .into(), Statement::Expression(Expression::from(Call::new( - PropertyAccess::new( - Identifier::new(interner.get_or_intern_static("console", utf16!("console"))) + Expression::PropertyAccess( + SimplePropertyAccess::new( + Identifier::new( + interner.get_or_intern_static("console", utf16!("console")), + ) .into(), - interner.get_or_intern_static("log", utf16!("log")), - ) - .into(), + interner.get_or_intern_static("log", utf16!("log")), + ) + .into(), + ), vec![Literal::from(interner.get_or_intern_static("end", utf16!("end"))).into()] .into(), ))) @@ -131,14 +137,16 @@ fn check_do_while_semicolon_insertion_no_space() { Statement::Block( vec![StatementListItem::Statement(Statement::Expression( Expression::from(Call::new( - PropertyAccess::new( - Identifier::new( - interner.get_or_intern_static("console", utf16!("console")), + Expression::PropertyAccess( + SimplePropertyAccess::new( + Identifier::new( + interner.get_or_intern_static("console", utf16!("console")), + ) + .into(), + interner.get_or_intern_static("log", utf16!("log")), ) .into(), - interner.get_or_intern_static("log", utf16!("log")), - ) - .into(), + ), vec![Literal::from( interner.get_or_intern_static("hello", utf16!("hello")), ) @@ -161,12 +169,16 @@ fn check_do_while_semicolon_insertion_no_space() { )) .into(), Statement::Expression(Expression::from(Call::new( - PropertyAccess::new( - Identifier::new(interner.get_or_intern_static("console", utf16!("console"))) + Expression::PropertyAccess( + SimplePropertyAccess::new( + Identifier::new( + interner.get_or_intern_static("console", utf16!("console")), + ) .into(), - interner.get_or_intern_static("log", utf16!("log")), - ) - .into(), + interner.get_or_intern_static("log", utf16!("log")), + ) + .into(), + ), vec![Literal::from(interner.get_or_intern_static("end", utf16!("end"))).into()] .into(), ))) diff --git a/boa_engine/src/syntax/parser/statement/switch/tests.rs b/boa_engine/src/syntax/parser/statement/switch/tests.rs index dce8b89c0d1..7306769b6d1 100644 --- a/boa_engine/src/syntax/parser/statement/switch/tests.rs +++ b/boa_engine/src/syntax/parser/statement/switch/tests.rs @@ -1,7 +1,7 @@ use crate::syntax::{ ast::{ declaration::{LexicalDeclaration, Variable}, - expression::{access::PropertyAccess, literal::Literal, Call, Identifier}, + expression::{access::SimplePropertyAccess, literal::Literal, Call, Identifier}, statement::{Break, Case, Switch}, Declaration, Expression, Statement, }, @@ -173,7 +173,10 @@ fn check_separated_switch() { Literal::from(5).into(), vec![ Statement::Expression(Expression::from(Call::new( - PropertyAccess::new(Identifier::new(console).into(), log).into(), + Expression::PropertyAccess( + SimplePropertyAccess::new(Identifier::new(console).into(), log) + .into(), + ), vec![Literal::from(5).into()].into(), ))) .into(), @@ -185,7 +188,10 @@ fn check_separated_switch() { Literal::from(10).into(), vec![ Statement::Expression(Expression::from(Call::new( - PropertyAccess::new(Identifier::new(console).into(), log).into(), + Expression::PropertyAccess( + SimplePropertyAccess::new(Identifier::new(console).into(), log) + .into(), + ), vec![Literal::from(10).into()].into(), ))) .into(), @@ -197,7 +203,9 @@ fn check_separated_switch() { .into(), Some( vec![Statement::Expression(Expression::from(Call::new( - PropertyAccess::new(Identifier::new(console).into(), log).into(), + Expression::PropertyAccess( + SimplePropertyAccess::new(Identifier::new(console).into(), log).into(), + ), vec![Literal::from( interner.get_or_intern_static("Default", utf16!("Default")), ) diff --git a/boa_engine/src/syntax/parser/tests.rs b/boa_engine/src/syntax/parser/tests.rs index 1f92e7fc5c9..4d6aa24b654 100644 --- a/boa_engine/src/syntax/parser/tests.rs +++ b/boa_engine/src/syntax/parser/tests.rs @@ -9,7 +9,7 @@ use crate::{ syntax::ast::{ declaration::{Declaration, LexicalDeclaration, VarDeclaration, Variable}, expression::{ - access::PropertyAccess, + access::SimplePropertyAccess, literal::{Literal, ObjectLiteral}, operator::{ assign::AssignOp, @@ -60,15 +60,18 @@ fn check_construct_call_precedence() { check_parser( "new Date().getTime()", vec![Statement::Expression(Expression::from(Call::new( - PropertyAccess::new( - New::from(Call::new( - Identifier::new(interner.get_or_intern_static("Date", utf16!("Date"))).into(), - Box::default(), - )) + Expression::PropertyAccess( + SimplePropertyAccess::new( + New::from(Call::new( + Identifier::new(interner.get_or_intern_static("Date", utf16!("Date"))) + .into(), + Box::default(), + )) + .into(), + interner.get_or_intern_static("getTime", utf16!("getTime")), + ) .into(), - interner.get_or_intern_static("getTime", utf16!("getTime")), - ) - .into(), + ), Box::default(), ))) .into()], diff --git a/boa_engine/src/vm/opcode/mod.rs b/boa_engine/src/vm/opcode/mod.rs index 9fed4fe1b85..a944bf20f21 100644 --- a/boa_engine/src/vm/opcode/mod.rs +++ b/boa_engine/src/vm/opcode/mod.rs @@ -845,7 +845,7 @@ pub enum Opcode { /// Deletes a property by name of an object. /// - /// Like `delete object.key.` + /// Like `delete object.key` /// /// Operands: name_index: `u32` /// @@ -1068,7 +1068,7 @@ pub enum Opcode { /// /// Operands: argument_count: `u32` /// - /// Stack: func, this, argument_1, ... argument_n **=>** result + /// Stack: this, func, argument_1, ... argument_n **=>** result Call, /// Call a function where the arguments contain spreads.