Browse Source

Replace `contains` and friends with visitors (#2403)

This Pull Request replaces `contains`, `contains_arguments`, `has_direct_super` and `function_contains_super` with visitors. (~1000 removed lines!)

Also, the new visitor implementation caught a bug where we weren't setting the home object of async functions, generators and async generators for methods of classes, which caused a stack overflow on `super` calls, and I think that's pretty cool!

Next is `var_declared_names`, `lexically_declared_names` and friends, which will be on another PR.
pull/2393/head
José Julián Espina 2 years ago
parent
commit
49a58675cc
  1. 37
      boa_ast/src/declaration/mod.rs
  2. 70
      boa_ast/src/declaration/variable.rs
  3. 68
      boa_ast/src/expression/access.rs
  4. 11
      boa_ast/src/expression/await.rs
  5. 22
      boa_ast/src/expression/call.rs
  6. 15
      boa_ast/src/expression/literal/array.rs
  7. 13
      boa_ast/src/expression/literal/object.rs
  8. 18
      boa_ast/src/expression/literal/template.rs
  9. 89
      boa_ast/src/expression/mod.rs
  10. 12
      boa_ast/src/expression/new.rs
  11. 25
      boa_ast/src/expression/operator/assign/mod.rs
  12. 18
      boa_ast/src/expression/operator/binary/mod.rs
  13. 22
      boa_ast/src/expression/operator/conditional.rs
  14. 12
      boa_ast/src/expression/operator/unary/mod.rs
  15. 41
      boa_ast/src/expression/optional.rs
  16. 11
      boa_ast/src/expression/spread.rs
  17. 11
      boa_ast/src/expression/tagged_template.rs
  18. 11
      boa_ast/src/expression/yield.rs
  19. 22
      boa_ast/src/function/arrow_function.rs
  20. 63
      boa_ast/src/function/class.rs
  21. 50
      boa_ast/src/function/parameters.rs
  22. 27
      boa_ast/src/lib.rs
  23. 262
      boa_ast/src/operations.rs
  24. 176
      boa_ast/src/pattern.rs
  25. 85
      boa_ast/src/property.rs
  26. 18
      boa_ast/src/statement/block.rs
  27. 23
      boa_ast/src/statement/if.rs
  28. 5
      boa_ast/src/statement/iteration/break.rs
  29. 5
      boa_ast/src/statement/iteration/continue.rs
  30. 19
      boa_ast/src/statement/iteration/do_while_loop.rs
  31. 15
      boa_ast/src/statement/iteration/for_in_loop.rs
  32. 37
      boa_ast/src/statement/iteration/for_loop.rs
  33. 13
      boa_ast/src/statement/iteration/for_of_loop.rs
  34. 26
      boa_ast/src/statement/iteration/mod.rs
  35. 19
      boa_ast/src/statement/iteration/while_loop.rs
  36. 36
      boa_ast/src/statement/labelled.rs
  37. 64
      boa_ast/src/statement/mod.rs
  38. 16
      boa_ast/src/statement/return.rs
  39. 39
      boa_ast/src/statement/switch.rs
  40. 17
      boa_ast/src/statement/throw.rs
  41. 53
      boa_ast/src/statement/try.rs
  42. 64
      boa_ast/src/statement_list.rs
  43. 10
      boa_engine/src/builtins/array/mod.rs
  44. 44
      boa_engine/src/builtins/function/mod.rs
  45. 14
      boa_engine/src/builtins/intl/mod.rs
  46. 6
      boa_engine/src/builtins/json/mod.rs
  47. 2
      boa_engine/src/builtins/object/mod.rs
  48. 55
      boa_engine/src/builtins/uri/mod.rs
  49. 8
      boa_engine/src/bytecompiler/mod.rs
  50. 2
      boa_engine/src/object/jsobject.rs
  51. 4
      boa_engine/src/object/operations.rs
  52. 2
      boa_engine/src/syntax/lexer/cursor.rs
  53. 16
      boa_engine/src/syntax/parser/expression/primary/async_function_expression/mod.rs
  54. 20
      boa_engine/src/syntax/parser/expression/primary/async_generator_expression/mod.rs
  55. 16
      boa_engine/src/syntax/parser/expression/primary/function_expression/mod.rs
  56. 16
      boa_engine/src/syntax/parser/expression/primary/generator_expression/mod.rs
  57. 3
      boa_engine/src/syntax/parser/expression/primary/mod.rs
  58. 67
      boa_engine/src/syntax/parser/expression/primary/object_initializer/mod.rs
  59. 57
      boa_engine/src/syntax/parser/mod.rs
  60. 24
      boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/mod.rs
  61. 12
      boa_engine/src/syntax/parser/statement/declaration/hoistable/mod.rs
  62. 4
      boa_engine/src/value/mod.rs
  63. 12
      boa_engine/src/vm/code_block.rs
  64. 2
      boa_tester/src/exec/mod.rs
  65. 2
      boa_tester/src/results.rs

37
boa_ast/src/declaration/mod.rs

@ -17,7 +17,6 @@
use super::{ use super::{
expression::Identifier, expression::Identifier,
function::{AsyncFunction, AsyncGenerator, Class, Function, Generator}, function::{AsyncFunction, AsyncGenerator, Class, Function, Generator},
ContainsSymbol,
}; };
use boa_interner::{Interner, ToIndentedString, ToInternedString}; use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
@ -116,42 +115,6 @@ impl Declaration {
} }
} }
} }
/// Returns true if the node contains a identifier reference named 'arguments'.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments
// TODO: replace with a visitor
pub(crate) fn contains_arguments(&self) -> bool {
match self {
Self::Function(_)
| Self::Generator(_)
| Self::AsyncGenerator(_)
| Self::AsyncFunction(_) => false,
Self::Lexical(decl) => decl.contains_arguments(),
Self::Class(class) => class.contains_arguments(),
}
}
/// Returns `true` if the node contains the given token.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains
// TODO: replace with a visitor
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
match self {
Self::Function(_)
| Self::Generator(_)
| Self::AsyncGenerator(_)
| Self::AsyncFunction(_) => false,
Self::Class(class) => class.contains(symbol),
Self::Lexical(decl) => decl.contains(symbol),
}
}
} }
impl ToIndentedString for Declaration { impl ToIndentedString for Declaration {

70
boa_ast/src/declaration/variable.rs

@ -9,7 +9,7 @@ use crate::{
expression::{Expression, Identifier}, expression::{Expression, Identifier},
join_nodes, join_nodes,
pattern::Pattern, pattern::Pattern,
ContainsSymbol, Statement, Statement,
}; };
use boa_interner::{Interner, ToInternedString}; use boa_interner::{Interner, ToInternedString};
@ -48,17 +48,6 @@ use super::Declaration;
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct VarDeclaration(pub VariableList); pub struct VarDeclaration(pub VariableList);
impl VarDeclaration {
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.0.as_ref().iter().any(Variable::contains_arguments)
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.0.as_ref().iter().any(|decl| decl.contains(symbol))
}
}
impl From<VarDeclaration> for Statement { impl From<VarDeclaration> for Statement {
fn from(var: VarDeclaration) -> Self { fn from(var: VarDeclaration) -> Self {
Statement::Var(var) Statement::Var(var)
@ -124,22 +113,6 @@ impl LexicalDeclaration {
LexicalDeclaration::Const(list) | LexicalDeclaration::Let(list) => list, LexicalDeclaration::Const(list) | LexicalDeclaration::Let(list) => list,
} }
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.variable_list()
.as_ref()
.iter()
.any(Variable::contains_arguments)
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.variable_list()
.as_ref()
.iter()
.any(|decl| decl.contains(symbol))
}
} }
impl From<LexicalDeclaration> for Declaration { impl From<LexicalDeclaration> for Declaration {
@ -332,33 +305,6 @@ impl Variable {
pub fn idents(&self) -> Vec<Identifier> { pub fn idents(&self) -> Vec<Identifier> {
self.binding.idents() self.binding.idents()
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
if let Some(ref node) = self.init {
if node.contains_arguments() {
return true;
}
}
self.binding.contains_arguments()
}
/// Returns `true` if the variable declaration contains the given token.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains
#[inline]
#[must_use]
pub fn contains(&self, symbol: ContainsSymbol) -> bool {
if let Some(ref node) = self.init {
if node.contains(symbol) {
return true;
}
}
self.binding.contains(symbol)
}
} }
impl VisitWith for Variable { impl VisitWith for Variable {
@ -413,20 +359,6 @@ impl From<Pattern> for Binding {
} }
impl Binding { impl Binding {
pub(crate) fn contains_arguments(&self) -> bool {
matches!(self, Binding::Pattern(ref pattern) if pattern.contains_arguments())
}
/// Returns `true` if the node contains the given token.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
matches!(self, Binding::Pattern(ref pattern) if pattern.contains(symbol))
}
/// Gets the list of declared identifiers. /// Gets the list of declared identifiers.
pub(crate) fn idents(&self) -> Vec<Identifier> { pub(crate) fn idents(&self) -> Vec<Identifier> {
match self { match self {

68
boa_ast/src/expression/access.rs

@ -14,9 +14,9 @@
//! [spec]: https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-property-accessors //! [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 //! [access]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors
use crate::expression::Expression;
use crate::try_break; use crate::try_break;
use crate::visitor::{VisitWith, Visitor, VisitorMut}; use crate::visitor::{VisitWith, Visitor, VisitorMut};
use crate::{expression::Expression, ContainsSymbol};
use boa_interner::{Interner, Sym, ToInternedString}; use boa_interner::{Interner, Sym, ToInternedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
@ -32,22 +32,6 @@ pub enum PropertyAccessField {
Expr(Box<Expression>), Expr(Box<Expression>),
} }
impl PropertyAccessField {
pub(crate) fn contains_arguments(&self) -> bool {
match self {
PropertyAccessField::Const(_) => false,
PropertyAccessField::Expr(expr) => expr.contains_arguments(),
}
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
match self {
PropertyAccessField::Const(_) => false,
PropertyAccessField::Expr(expr) => expr.contains(symbol),
}
}
}
impl From<Sym> for PropertyAccessField { impl From<Sym> for PropertyAccessField {
#[inline] #[inline]
fn from(id: Sym) -> Self { fn from(id: Sym) -> Self {
@ -98,26 +82,6 @@ pub enum PropertyAccess {
Super(SuperPropertyAccess), 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 { impl ToInternedString for PropertyAccess {
#[inline] #[inline]
fn to_interned_string(&self, interner: &Interner) -> String { fn to_interned_string(&self, interner: &Interner) -> String {
@ -194,16 +158,6 @@ impl SimplePropertyAccess {
field: field.into(), field: field.into(),
} }
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.target.contains_arguments() || self.field.contains_arguments()
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.target.contains(symbol) || self.field.contains(symbol)
}
} }
impl ToInternedString for SimplePropertyAccess { impl ToInternedString for SimplePropertyAccess {
@ -285,16 +239,6 @@ impl PrivatePropertyAccess {
pub fn field(&self) -> Sym { pub fn field(&self) -> Sym {
self.field self.field
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.target.contains_arguments()
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.target.contains(symbol)
}
} }
impl ToInternedString for PrivatePropertyAccess { impl ToInternedString for PrivatePropertyAccess {
@ -359,16 +303,6 @@ impl SuperPropertyAccess {
pub fn field(&self) -> &PropertyAccessField { pub fn field(&self) -> &PropertyAccessField {
&self.field &self.field
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.field.contains_arguments()
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
symbol == ContainsSymbol::SuperProperty || self.field.contains(symbol)
}
} }
impl ToInternedString for SuperPropertyAccess { impl ToInternedString for SuperPropertyAccess {

11
boa_ast/src/expression/await.rs

@ -1,6 +1,5 @@
//! Await expression Expression. //! Await expression Expression.
use crate::ContainsSymbol;
use core::ops::ControlFlow; use core::ops::ControlFlow;
use super::Expression; use super::Expression;
@ -29,16 +28,6 @@ impl Await {
pub fn target(&self) -> &Expression { pub fn target(&self) -> &Expression {
&self.target &self.target
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.target.contains_arguments()
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.target.contains(symbol)
}
} }
impl<T> From<T> for Await impl<T> From<T> for Await

22
boa_ast/src/expression/call.rs

@ -1,6 +1,6 @@
use crate::join_nodes;
use crate::try_break; use crate::try_break;
use crate::visitor::{VisitWith, Visitor, VisitorMut}; use crate::visitor::{VisitWith, Visitor, VisitorMut};
use crate::{join_nodes, ContainsSymbol};
use boa_interner::{Interner, ToInternedString}; use boa_interner::{Interner, ToInternedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
@ -51,16 +51,6 @@ impl Call {
pub fn args(&self) -> &[Expression] { pub fn args(&self) -> &[Expression] {
&self.args &self.args
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.function.contains_arguments() || self.args.iter().any(Expression::contains_arguments)
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.function.contains(symbol) || self.args.iter().any(|expr| expr.contains(symbol))
}
} }
impl ToInternedString for Call { impl ToInternedString for Call {
@ -133,16 +123,6 @@ impl SuperCall {
pub fn arguments(&self) -> &[Expression] { pub fn arguments(&self) -> &[Expression] {
&self.args &self.args
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.args.iter().any(Expression::contains_arguments)
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.args.iter().any(|expr| expr.contains(symbol))
}
} }
impl ToInternedString for SuperCall { impl ToInternedString for SuperCall {

15
boa_ast/src/expression/literal/array.rs

@ -1,10 +1,10 @@
//! Array declaration Expression. //! Array declaration Expression.
use crate::expression::operator::assign::AssignTarget; use crate::expression::operator::assign::AssignTarget;
use crate::expression::Expression;
use crate::pattern::{ArrayPattern, ArrayPatternElement, Pattern}; use crate::pattern::{ArrayPattern, ArrayPatternElement, Pattern};
use crate::try_break; use crate::try_break;
use crate::visitor::{VisitWith, Visitor, VisitorMut}; use crate::visitor::{VisitWith, Visitor, VisitorMut};
use crate::{expression::Expression, ContainsSymbol};
use boa_interner::{Interner, Sym, ToInternedString}; use boa_interner::{Interner, Sym, ToInternedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
@ -151,19 +151,6 @@ impl ArrayLiteral {
} }
Some(ArrayPattern::new(bindings.into())) Some(ArrayPattern::new(bindings.into()))
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.arr
.iter()
.flatten()
.any(Expression::contains_arguments)
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.arr.iter().flatten().any(|expr| expr.contains(symbol))
}
} }
impl AsRef<[Option<Expression>]> for ArrayLiteral { impl AsRef<[Option<Expression>]> for ArrayLiteral {

13
boa_ast/src/expression/literal/object.rs

@ -8,7 +8,6 @@ use crate::{
property::{MethodDefinition, PropertyDefinition, PropertyName}, property::{MethodDefinition, PropertyDefinition, PropertyName},
try_break, try_break,
visitor::{VisitWith, Visitor, VisitorMut}, visitor::{VisitWith, Visitor, VisitorMut},
ContainsSymbol,
}; };
use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString}; use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
@ -205,18 +204,6 @@ impl ObjectLiteral {
Some(ObjectPattern::new(bindings.into())) Some(ObjectPattern::new(bindings.into()))
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.properties
.iter()
.any(PropertyDefinition::contains_arguments)
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.properties.iter().any(|prop| prop.contains(symbol))
}
} }
impl ToIndentedString for ObjectLiteral { impl ToIndentedString for ObjectLiteral {

18
boa_ast/src/expression/literal/template.rs

@ -9,7 +9,7 @@ use crate::{
expression::Expression, expression::Expression,
try_break, try_break,
visitor::{VisitWith, Visitor, VisitorMut}, visitor::{VisitWith, Visitor, VisitorMut},
ContainsSymbol, ToStringEscaped, ToStringEscaped,
}; };
/// Template literals are string literals allowing embedded expressions. /// Template literals are string literals allowing embedded expressions.
@ -61,22 +61,6 @@ impl TemplateLiteral {
pub fn elements(&self) -> &[TemplateElement] { pub fn elements(&self) -> &[TemplateElement] {
&self.elements &self.elements
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.elements.iter().any(|e| match e {
TemplateElement::String(_) => false,
TemplateElement::Expr(expr) => expr.contains_arguments(),
})
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.elements.iter().any(|e| match e {
TemplateElement::String(_) => false,
TemplateElement::Expr(expr) => expr.contains(symbol),
})
}
} }
impl ToInternedString for TemplateLiteral { impl ToInternedString for TemplateLiteral {

89
boa_ast/src/expression/mod.rs

@ -9,7 +9,7 @@
//! [primary]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#primary_expressions //! [primary]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#primary_expressions
//! [lhs]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#left-hand-side_expressions //! [lhs]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#left-hand-side_expressions
use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString}; use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
use self::{ use self::{
@ -21,7 +21,7 @@ use self::{
use super::{ use super::{
function::FormalParameterList, function::FormalParameterList,
function::{ArrowFunction, AsyncFunction, AsyncGenerator, Class, Function, Generator}, function::{ArrowFunction, AsyncFunction, AsyncGenerator, Class, Function, Generator},
ContainsSymbol, Statement, Statement,
}; };
mod r#await; mod r#await;
@ -190,91 +190,6 @@ impl Expression {
Self::FormalParameterList(_) => unreachable!(), Self::FormalParameterList(_) => unreachable!(),
} }
} }
/// Returns true if the expression contains a identifier reference named 'arguments'.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments
// TODO: replace with a visitor
#[inline]
#[must_use]
pub fn contains_arguments(&self) -> bool {
match self {
Expression::Identifier(ident) => *ident == Sym::ARGUMENTS,
Expression::Function(_)
| Expression::Generator(_)
| Expression::AsyncFunction(_)
| Expression::AsyncGenerator(_)
| Expression::Literal(_)
| Expression::This
| Expression::NewTarget => false,
Expression::ArrayLiteral(array) => array.contains_arguments(),
Expression::ObjectLiteral(object) => object.contains_arguments(),
Expression::Spread(spread) => spread.contains_arguments(),
Expression::ArrowFunction(arrow) => arrow.contains_arguments(),
Expression::Class(class) => class.contains_arguments(),
Expression::TemplateLiteral(template) => template.contains_arguments(),
Expression::PropertyAccess(access) => access.contains_arguments(),
Expression::New(new) => new.contains_arguments(),
Expression::Call(call) => call.contains_arguments(),
Expression::SuperCall(call) => call.contains_arguments(),
Expression::Optional(opt) => opt.contains_arguments(),
Expression::TaggedTemplate(tag) => tag.contains_arguments(),
Expression::Assign(assign) => assign.contains_arguments(),
Expression::Unary(unary) => unary.contains_arguments(),
Expression::Binary(binary) => binary.contains_arguments(),
Expression::Conditional(cond) => cond.contains_arguments(),
Expression::Await(r#await) => r#await.contains_arguments(),
Expression::Yield(r#yield) => r#yield.contains_arguments(),
// TODO: remove variant
Expression::FormalParameterList(_) => unreachable!(),
}
}
/// Returns `true` if the node contains the given token.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains
// TODO: replace with a visitor
#[must_use]
pub fn contains(&self, symbol: ContainsSymbol) -> bool {
match self {
Expression::This => symbol == ContainsSymbol::This,
Expression::Identifier(_)
| Expression::Literal(_)
| Expression::Function(_)
| Expression::Generator(_)
| Expression::AsyncFunction(_)
| Expression::AsyncGenerator(_) => false,
Expression::ArrayLiteral(array) => array.contains(symbol),
Expression::ObjectLiteral(obj) => obj.contains(symbol),
Expression::Spread(spread) => spread.contains(symbol),
Expression::ArrowFunction(arrow) => arrow.contains(symbol),
Expression::Class(class) => class.contains(symbol),
Expression::TemplateLiteral(temp) => temp.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,
Expression::SuperCall(expr) => expr.contains(symbol),
Expression::Optional(opt) => opt.contains(symbol),
Expression::TaggedTemplate(temp) => temp.contains(symbol),
Expression::NewTarget => symbol == ContainsSymbol::NewTarget,
Expression::Assign(assign) => assign.contains(symbol),
Expression::Unary(unary) => unary.contains(symbol),
Expression::Binary(binary) => binary.contains(symbol),
Expression::Conditional(cond) => cond.contains(symbol),
Expression::Await(_) if symbol == ContainsSymbol::AwaitExpression => true,
Expression::Await(r#await) => r#await.contains(symbol),
Expression::Yield(_) if symbol == ContainsSymbol::YieldExpression => true,
Expression::Yield(r#yield) => r#yield.contains(symbol),
Expression::FormalParameterList(_) => unreachable!(),
}
}
} }
impl From<Expression> for Statement { impl From<Expression> for Statement {

12
boa_ast/src/expression/new.rs

@ -1,5 +1,5 @@
use crate::expression::Call;
use crate::visitor::{VisitWith, Visitor, VisitorMut}; use crate::visitor::{VisitWith, Visitor, VisitorMut};
use crate::{expression::Call, ContainsSymbol};
use boa_interner::{Interner, ToInternedString}; use boa_interner::{Interner, ToInternedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
@ -46,16 +46,6 @@ impl New {
pub fn call(&self) -> &Call { pub fn call(&self) -> &Call {
&self.call &self.call
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.call.contains_arguments()
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.call.contains(symbol)
}
} }
impl From<Call> for New { impl From<Call> for New {

25
boa_ast/src/expression/operator/assign/mod.rs

@ -15,14 +15,13 @@ mod op;
use core::ops::ControlFlow; use core::ops::ControlFlow;
pub use op::*; pub use op::*;
use boa_interner::{Interner, Sym, ToInternedString}; use boa_interner::{Interner, ToInternedString};
use crate::try_break;
use crate::visitor::{VisitWith, Visitor, VisitorMut};
use crate::{ use crate::{
expression::{access::PropertyAccess, identifier::Identifier, Expression}, expression::{access::PropertyAccess, identifier::Identifier, Expression},
pattern::Pattern, pattern::Pattern,
ContainsSymbol, try_break,
visitor::{VisitWith, Visitor, VisitorMut},
}; };
/// An assignment operator expression. /// An assignment operator expression.
@ -67,24 +66,6 @@ impl Assign {
pub fn rhs(&self) -> &Expression { pub fn rhs(&self) -> &Expression {
&self.rhs &self.rhs
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
(match &*self.lhs {
AssignTarget::Identifier(ident) => *ident == Sym::ARGUMENTS,
AssignTarget::Access(access) => access.contains_arguments(),
AssignTarget::Pattern(pattern) => pattern.contains_arguments(),
} || self.rhs.contains_arguments())
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
(match &*self.lhs {
AssignTarget::Identifier(_) => false,
AssignTarget::Access(access) => access.contains(symbol),
AssignTarget::Pattern(pattern) => pattern.contains(symbol),
} || self.rhs.contains(symbol))
}
} }
impl ToInternedString for Assign { impl ToInternedString for Assign {

18
boa_ast/src/expression/operator/binary/mod.rs

@ -21,9 +21,11 @@ pub use op::*;
use boa_interner::{Interner, ToInternedString}; use boa_interner::{Interner, ToInternedString};
use crate::try_break; use crate::{
use crate::visitor::{VisitWith, Visitor, VisitorMut}; expression::Expression,
use crate::{expression::Expression, ContainsSymbol}; try_break,
visitor::{VisitWith, Visitor, VisitorMut},
};
/// Binary operations require two operands, one before the operator and one after the operator. /// Binary operations require two operands, one before the operator and one after the operator.
/// ///
@ -67,16 +69,6 @@ impl Binary {
pub fn rhs(&self) -> &Expression { pub fn rhs(&self) -> &Expression {
&self.rhs &self.rhs
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.lhs.contains_arguments() || self.rhs.contains_arguments()
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.lhs.contains(symbol) || self.rhs.contains(symbol)
}
} }
impl ToInternedString for Binary { impl ToInternedString for Binary {

22
boa_ast/src/expression/operator/conditional.rs

@ -1,6 +1,8 @@
use crate::try_break; use crate::{
use crate::visitor::{VisitWith, Visitor, VisitorMut}; expression::Expression,
use crate::{expression::Expression, ContainsSymbol}; try_break,
visitor::{VisitWith, Visitor, VisitorMut},
};
use boa_interner::{Interner, ToInternedString}; use boa_interner::{Interner, ToInternedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
@ -58,20 +60,6 @@ impl Conditional {
if_false: Box::new(if_false), if_false: Box::new(if_false),
} }
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.condition.contains_arguments()
|| self.if_true.contains_arguments()
|| self.if_false.contains_arguments()
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.condition.contains(symbol)
|| self.if_true.contains(symbol)
|| self.if_false.contains(symbol)
}
} }
impl ToInternedString for Conditional { impl ToInternedString for Conditional {

12
boa_ast/src/expression/operator/unary/mod.rs

@ -18,8 +18,8 @@ pub use op::*;
use boa_interner::{Interner, ToInternedString}; use boa_interner::{Interner, ToInternedString};
use crate::expression::Expression;
use crate::visitor::{VisitWith, Visitor, VisitorMut}; use crate::visitor::{VisitWith, Visitor, VisitorMut};
use crate::{expression::Expression, ContainsSymbol};
/// A unary expression is an operation with only one operand. /// A unary expression is an operation with only one operand.
/// ///
@ -59,16 +59,6 @@ impl Unary {
pub fn target(&self) -> &Expression { pub fn target(&self) -> &Expression {
self.target.as_ref() self.target.as_ref()
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.target.contains_arguments()
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.target.contains(symbol)
}
} }
impl ToInternedString for Unary { impl ToInternedString for Unary {

41
boa_ast/src/expression/optional.rs

@ -1,9 +1,9 @@
use boa_interner::{Interner, Sym, ToInternedString}; use boa_interner::{Interner, Sym, ToInternedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
use crate::join_nodes;
use crate::try_break; use crate::try_break;
use crate::visitor::{VisitWith, Visitor, VisitorMut}; use crate::visitor::{VisitWith, Visitor, VisitorMut};
use crate::{join_nodes, ContainsSymbol};
use super::{access::PropertyAccessField, Expression}; use super::{access::PropertyAccessField, Expression};
@ -28,25 +28,6 @@ pub enum OptionalOperationKind {
}, },
} }
impl OptionalOperationKind {
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
match self {
OptionalOperationKind::SimplePropertyAccess { field } => field.contains_arguments(),
OptionalOperationKind::PrivatePropertyAccess { .. } => false,
OptionalOperationKind::Call { args } => args.iter().any(Expression::contains_arguments),
}
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
match self {
OptionalOperationKind::SimplePropertyAccess { field } => field.contains(symbol),
OptionalOperationKind::PrivatePropertyAccess { .. } => false,
OptionalOperationKind::Call { args } => args.iter().any(|e| e.contains(symbol)),
}
}
}
impl VisitWith for OptionalOperationKind { impl VisitWith for OptionalOperationKind {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy> fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where where
@ -119,16 +100,6 @@ impl OptionalOperation {
pub fn shorted(&self) -> bool { pub fn shorted(&self) -> bool {
self.shorted self.shorted
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.kind.contains_arguments()
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.kind.contains(symbol)
}
} }
impl ToInternedString for OptionalOperation { impl ToInternedString for OptionalOperation {
@ -258,16 +229,6 @@ impl Optional {
pub fn chain(&self) -> &[OptionalOperation] { pub fn chain(&self) -> &[OptionalOperation] {
self.chain.as_ref() self.chain.as_ref()
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.target.contains_arguments()
|| self.chain.iter().any(OptionalOperation::contains_arguments)
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.target.contains(symbol) || self.chain.iter().any(|item| item.contains(symbol))
}
} }
impl From<Optional> for Expression { impl From<Optional> for Expression {

11
boa_ast/src/expression/spread.rs

@ -2,7 +2,6 @@ use boa_interner::{Interner, ToInternedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
use crate::visitor::{VisitWith, Visitor, VisitorMut}; use crate::visitor::{VisitWith, Visitor, VisitorMut};
use crate::ContainsSymbol;
use super::Expression; use super::Expression;
@ -45,16 +44,6 @@ impl Spread {
target: Box::new(target), target: Box::new(target),
} }
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.target.contains_arguments()
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.target.contains(symbol)
}
} }
impl ToInternedString for Spread { impl ToInternedString for Spread {

11
boa_ast/src/expression/tagged_template.rs

@ -3,7 +3,6 @@ use core::ops::ControlFlow;
use crate::try_break; use crate::try_break;
use crate::visitor::{VisitWith, Visitor, VisitorMut}; use crate::visitor::{VisitWith, Visitor, VisitorMut};
use crate::ContainsSymbol;
use super::Expression; use super::Expression;
@ -69,16 +68,6 @@ impl TaggedTemplate {
pub fn exprs(&self) -> &[Expression] { pub fn exprs(&self) -> &[Expression] {
&self.exprs &self.exprs
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.tag.contains_arguments() || self.exprs.iter().any(Expression::contains_arguments)
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.tag.contains(symbol) || self.exprs.iter().any(|expr| expr.contains(symbol))
}
} }
impl ToInternedString for TaggedTemplate { impl ToInternedString for TaggedTemplate {

11
boa_ast/src/expression/yield.rs

@ -2,7 +2,6 @@ use boa_interner::{Interner, ToInternedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
use crate::visitor::{VisitWith, Visitor, VisitorMut}; use crate::visitor::{VisitWith, Visitor, VisitorMut};
use crate::ContainsSymbol;
use super::Expression; use super::Expression;
@ -44,16 +43,6 @@ impl Yield {
delegate, delegate,
} }
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
matches!(self.target, Some(ref expr) if expr.contains_arguments())
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
matches!(self.target, Some(ref expr) if expr.contains(symbol))
}
} }
impl From<Yield> for Expression { impl From<Yield> for Expression {

22
boa_ast/src/function/arrow_function.rs

@ -2,7 +2,7 @@ use crate::try_break;
use crate::visitor::{VisitWith, Visitor, VisitorMut}; use crate::visitor::{VisitWith, Visitor, VisitorMut};
use crate::{ use crate::{
expression::{Expression, Identifier}, expression::{Expression, Identifier},
join_nodes, ContainsSymbol, StatementList, join_nodes, StatementList,
}; };
use boa_interner::{Interner, ToIndentedString}; use boa_interner::{Interner, ToIndentedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
@ -64,26 +64,6 @@ impl ArrowFunction {
pub fn body(&self) -> &StatementList { pub fn body(&self) -> &StatementList {
&self.body &self.body
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.parameters.contains_arguments() || self.body.contains_arguments()
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
if ![
ContainsSymbol::NewTarget,
ContainsSymbol::SuperProperty,
ContainsSymbol::SuperCall,
ContainsSymbol::This,
]
.contains(&symbol)
{
return false;
}
self.parameters.contains(symbol) || self.body.contains(symbol)
}
} }
impl ToIndentedString for ArrowFunction { impl ToIndentedString for ArrowFunction {

63
boa_ast/src/function/class.rs

@ -8,7 +8,7 @@ use crate::{
property::{MethodDefinition, PropertyName}, property::{MethodDefinition, PropertyName},
try_break, try_break,
visitor::{VisitWith, Visitor, VisitorMut}, visitor::{VisitWith, Visitor, VisitorMut},
ContainsSymbol, Declaration, StatementList, StatementListItem, ToStringEscaped, Declaration, StatementList, ToStringEscaped,
}; };
use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString}; use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString};
@ -75,26 +75,6 @@ impl Class {
pub fn elements(&self) -> &[ClassElement] { pub fn elements(&self) -> &[ClassElement] {
&self.elements &self.elements
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
matches!(self.name, Some(ref ident) if *ident == Sym::ARGUMENTS)
|| matches!(self.super_ref, Some(ref expr) if expr.contains_arguments())
|| self.elements.iter().any(ClassElement::contains_arguments)
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
if symbol == ContainsSymbol::ClassBody && !self.elements.is_empty() {
return true;
}
if symbol == ContainsSymbol::ClassHeritage {
return self.super_ref.is_some();
}
matches!(self.super_ref, Some(ref expr) if expr.contains(symbol))
|| self.elements.iter().any(|elem| elem.contains(symbol))
}
} }
impl ToIndentedString for Class { impl ToIndentedString for Class {
@ -450,47 +430,6 @@ pub enum ClassElement {
StaticBlock(StatementList), StaticBlock(StatementList),
} }
impl ClassElement {
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
match self {
// Skipping function since they must not have names
Self::MethodDefinition(name, _) | Self::StaticMethodDefinition(name, _) => {
name.contains_arguments()
}
Self::FieldDefinition(name, Some(init))
| Self::StaticFieldDefinition(name, Some(init)) => {
name.contains_arguments() || init.contains_arguments()
}
Self::PrivateFieldDefinition(_, Some(init))
| Self::PrivateStaticFieldDefinition(_, Some(init)) => init.contains_arguments(),
Self::StaticBlock(statement_list) => statement_list
.statements()
.iter()
.any(StatementListItem::contains_arguments),
_ => false,
}
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
match self {
// Skipping function since they must not have names
Self::MethodDefinition(name, _) | Self::StaticMethodDefinition(name, _) => {
name.contains(symbol)
}
Self::FieldDefinition(name, Some(init))
| Self::StaticFieldDefinition(name, Some(init)) => {
name.contains(symbol) || init.contains(symbol)
}
Self::PrivateFieldDefinition(_, Some(init))
| Self::PrivateStaticFieldDefinition(_, Some(init)) => init.contains(symbol),
Self::StaticBlock(_statement_list) => false,
_ => false,
}
}
}
impl VisitWith for ClassElement { impl VisitWith for ClassElement {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy> fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where where

50
boa_ast/src/function/parameters.rs

@ -4,7 +4,6 @@ use crate::{
pattern::Pattern, pattern::Pattern,
try_break, try_break,
visitor::{VisitWith, Visitor, VisitorMut}, visitor::{VisitWith, Visitor, VisitorMut},
ContainsSymbol,
}; };
use bitflags::bitflags; use bitflags::bitflags;
use boa_interner::{Interner, Sym, ToInternedString}; use boa_interner::{Interner, Sym, ToInternedString};
@ -124,46 +123,6 @@ impl FormalParameterList {
pub fn has_arguments(&self) -> bool { pub fn has_arguments(&self) -> bool {
self.flags.contains(FormalParameterListFlags::HAS_ARGUMENTS) self.flags.contains(FormalParameterListFlags::HAS_ARGUMENTS)
} }
/// Check if the any of the parameters contains a yield expression.
#[must_use]
pub fn contains_yield_expression(&self) -> bool {
for parameter in self.parameters.iter() {
if parameter
.variable()
.contains(ContainsSymbol::YieldExpression)
{
return true;
}
}
false
}
/// Check if the any of the parameters contains a await expression.
#[must_use]
pub fn contains_await_expression(&self) -> bool {
for parameter in self.parameters.iter() {
if parameter
.variable()
.contains(ContainsSymbol::AwaitExpression)
{
return true;
}
}
false
}
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.parameters
.iter()
.any(FormalParameter::contains_arguments)
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.parameters.iter().any(|param| param.contains(symbol))
}
} }
impl From<Vec<FormalParameter>> for FormalParameterList { impl From<Vec<FormalParameter>> for FormalParameterList {
@ -301,15 +260,6 @@ impl FormalParameter {
pub fn is_identifier(&self) -> bool { pub fn is_identifier(&self) -> bool {
matches!(&self.variable.binding(), Binding::Identifier(_)) matches!(&self.variable.binding(), Binding::Identifier(_))
} }
pub(crate) fn contains_arguments(&self) -> bool {
self.variable.contains_arguments()
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.variable.contains(symbol)
}
} }
impl ToInternedString for FormalParameter { impl ToInternedString for FormalParameter {

27
boa_ast/src/lib.rs

@ -63,6 +63,7 @@ pub mod declaration;
pub mod expression; pub mod expression;
pub mod function; pub mod function;
pub mod keyword; pub mod keyword;
pub mod operations;
pub mod pattern; pub mod pattern;
pub mod property; pub mod property;
pub mod statement; pub mod statement;
@ -80,32 +81,6 @@ pub use self::{
statement_list::{StatementList, StatementListItem}, statement_list::{StatementList, StatementListItem},
}; };
/// Represents all the possible symbols searched for by the [`Contains`][contains] operation.
///
/// [contains]: https://tc39.es/ecma262/#sec-syntax-directed-operations-contains
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum ContainsSymbol {
/// A super property access (`super.prop`).
SuperProperty,
/// A super constructor call (`super(args)`).
SuperCall,
/// A yield expression (`yield 5`).
YieldExpression,
/// An await expression (`await 4`).
AwaitExpression,
/// The new target expression (`new.target`).
NewTarget,
/// The body of a class definition.
ClassBody,
/// The super class of a class definition.
ClassHeritage,
/// A this expression (`this`).
This,
/// A method definition.
MethodDefinition,
}
/// Utility to join multiple Nodes into a single string. /// Utility to join multiple Nodes into a single string.
fn join_nodes<N>(interner: &Interner, nodes: &[N]) -> String fn join_nodes<N>(interner: &Interner, nodes: &[N]) -> String
where where

262
boa_ast/src/operations.rs

@ -0,0 +1,262 @@
//! Definitions of various **Syntax-Directed Operations** used in the [spec].
//!
//! [spec]: https://tc39.es/ecma262/#sec-syntax-directed-operations
use core::ops::ControlFlow;
use boa_interner::Sym;
use crate::{
expression::{access::SuperPropertyAccess, Await, Identifier, SuperCall, Yield},
function::{
ArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, Function, Generator,
},
property::{MethodDefinition, PropertyDefinition},
visitor::{VisitWith, Visitor},
Expression,
};
/// Represents all the possible symbols searched for by the [`Contains`][contains] operation.
///
/// [contains]: https://tc39.es/ecma262/#sec-syntax-directed-operations-contains
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum ContainsSymbol {
/// A node with the `super` keyword (`super(args)` or `super.prop`).
Super,
/// A super property access (`super.prop`).
SuperProperty,
/// A super constructor call (`super(args)`).
SuperCall,
/// A yield expression (`yield 5`).
YieldExpression,
/// An await expression (`await 4`).
AwaitExpression,
/// The new target expression (`new.target`).
NewTarget,
/// The body of a class definition.
ClassBody,
/// The super class of a class definition.
ClassHeritage,
/// A this expression (`this`).
This,
/// A method definition.
MethodDefinition,
}
/// Returns `true` if the node contains the given symbol.
///
/// This is equivalent to the [`Contains`][spec] syntax operation in the spec.
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains
#[must_use]
pub fn contains<N>(node: &N, symbol: ContainsSymbol) -> bool
where
N: VisitWith,
{
/// Visitor used by the function to search for a specific symbol in a node.
#[derive(Debug, Clone, Copy)]
struct ContainsVisitor(ContainsSymbol);
impl<'ast> Visitor<'ast> for ContainsVisitor {
type BreakTy = ();
fn visit_function(&mut self, _: &'ast Function) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_async_function(&mut self, _: &'ast AsyncFunction) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_generator(&mut self, _: &'ast Generator) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_async_generator(&mut self, _: &'ast AsyncGenerator) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_class(&mut self, node: &'ast Class) -> ControlFlow<Self::BreakTy> {
if !node.elements().is_empty() && self.0 == ContainsSymbol::ClassBody {
return ControlFlow::Break(());
}
if node.super_ref().is_some() && self.0 == ContainsSymbol::ClassHeritage {
return ControlFlow::Break(());
}
node.visit_with(self)
}
// `ComputedPropertyContains`: https://tc39.es/ecma262/#sec-static-semantics-computedpropertycontains
fn visit_class_element(&mut self, node: &'ast ClassElement) -> ControlFlow<Self::BreakTy> {
match node {
ClassElement::MethodDefinition(name, _)
| ClassElement::StaticMethodDefinition(name, _)
| ClassElement::FieldDefinition(name, _)
| ClassElement::StaticFieldDefinition(name, _) => name.visit_with(self),
_ => ControlFlow::Continue(()),
}
}
fn visit_property_definition(
&mut self,
node: &'ast PropertyDefinition,
) -> ControlFlow<Self::BreakTy> {
if let PropertyDefinition::MethodDefinition(name, _) = node {
if self.0 == ContainsSymbol::MethodDefinition {
return ControlFlow::Break(());
}
return name.visit_with(self);
}
node.visit_with(self)
}
fn visit_arrow_function(
&mut self,
node: &'ast ArrowFunction,
) -> ControlFlow<Self::BreakTy> {
if ![
ContainsSymbol::NewTarget,
ContainsSymbol::SuperProperty,
ContainsSymbol::SuperCall,
ContainsSymbol::Super,
ContainsSymbol::This,
]
.contains(&self.0)
{
return ControlFlow::Continue(());
}
node.visit_with(self)
}
fn visit_super_property_access(
&mut self,
node: &'ast SuperPropertyAccess,
) -> ControlFlow<Self::BreakTy> {
if [ContainsSymbol::SuperProperty, ContainsSymbol::Super].contains(&self.0) {
return ControlFlow::Break(());
}
node.visit_with(self)
}
fn visit_super_call(&mut self, node: &'ast SuperCall) -> ControlFlow<Self::BreakTy> {
if [ContainsSymbol::SuperCall, ContainsSymbol::Super].contains(&self.0) {
return ControlFlow::Break(());
}
node.visit_with(self)
}
fn visit_yield(&mut self, node: &'ast Yield) -> ControlFlow<Self::BreakTy> {
if self.0 == ContainsSymbol::YieldExpression {
return ControlFlow::Break(());
}
node.visit_with(self)
}
fn visit_await(&mut self, node: &'ast Await) -> ControlFlow<Self::BreakTy> {
if self.0 == ContainsSymbol::AwaitExpression {
return ControlFlow::Break(());
}
node.visit_with(self)
}
fn visit_expression(&mut self, node: &'ast Expression) -> ControlFlow<Self::BreakTy> {
if node == &Expression::This && self.0 == ContainsSymbol::This {
return ControlFlow::Break(());
}
if node == &Expression::NewTarget && self.0 == ContainsSymbol::NewTarget {
return ControlFlow::Break(());
}
node.visit_with(self)
}
}
node.visit_with(&mut ContainsVisitor(symbol)).is_break()
}
/// Returns true if the node contains an identifier reference with name `arguments`.
///
/// This is equivalent to the [`ContainsArguments`][spec] syntax operation in the spec.
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments
#[must_use]
pub fn contains_arguments<N>(node: &N) -> bool
where
N: VisitWith,
{
/// Visitor used by the function to search for an identifier with the name `arguments`.
#[derive(Debug, Clone, Copy)]
struct ContainsArgsVisitor;
impl<'ast> Visitor<'ast> for ContainsArgsVisitor {
type BreakTy = ();
fn visit_identifier(&mut self, node: &'ast Identifier) -> ControlFlow<Self::BreakTy> {
if node.sym() == Sym::ARGUMENTS {
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
}
fn visit_function(&mut self, _: &'ast Function) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_async_function(&mut self, _: &'ast AsyncFunction) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_generator(&mut self, _: &'ast Generator) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_async_generator(&mut self, _: &'ast AsyncGenerator) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_class_element(&mut self, node: &'ast ClassElement) -> ControlFlow<Self::BreakTy> {
match node {
ClassElement::MethodDefinition(name, _)
| ClassElement::StaticMethodDefinition(name, _) => return name.visit_with(self),
_ => {}
}
node.visit_with(self)
}
fn visit_property_definition(
&mut self,
node: &'ast PropertyDefinition,
) -> ControlFlow<Self::BreakTy> {
if let PropertyDefinition::MethodDefinition(name, _) = node {
name.visit_with(self)
} else {
node.visit_with(self)
}
}
}
node.visit_with(&mut ContainsArgsVisitor).is_break()
}
/// Returns `true` if `method` has a super call in its parameters or body.
///
/// This is equivalent to the [`HasDirectSuper`][spec] syntax operation in the spec.
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-hasdirectsuper
#[must_use]
pub fn has_direct_super(method: &MethodDefinition) -> bool {
match method {
MethodDefinition::Get(f) | MethodDefinition::Set(f) | MethodDefinition::Ordinary(f) => {
contains(f, ContainsSymbol::SuperCall)
}
MethodDefinition::Generator(f) => contains(f, ContainsSymbol::SuperCall),
MethodDefinition::AsyncGenerator(f) => contains(f, ContainsSymbol::SuperCall),
MethodDefinition::Async(f) => contains(f, ContainsSymbol::SuperCall),
}
}

176
boa_ast/src/pattern.rs

@ -22,16 +22,15 @@
//! [spec2]: https://tc39.es/ecma262/#prod-AssignmentPattern //! [spec2]: https://tc39.es/ecma262/#prod-AssignmentPattern
//! [destr]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment //! [destr]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
use crate::try_break; use crate::{
use crate::visitor::{VisitWith, Visitor, VisitorMut};
use boa_interner::{Interner, Sym, ToInternedString};
use core::ops::ControlFlow;
use super::{
expression::{access::PropertyAccess, Identifier}, expression::{access::PropertyAccess, Identifier},
property::PropertyName, property::PropertyName,
ContainsSymbol, Expression, try_break,
visitor::{VisitWith, Visitor, VisitorMut},
Expression,
}; };
use boa_interner::{Interner, ToInternedString};
use core::ops::ControlFlow;
/// An object or array pattern binding or assignment. /// An object or array pattern binding or assignment.
/// ///
@ -89,34 +88,6 @@ impl Pattern {
Pattern::Array(pattern) => pattern.idents(), Pattern::Array(pattern) => pattern.idents(),
} }
} }
/// Returns true if the node contains a identifier reference named 'arguments'.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
match self {
Pattern::Object(object) => object.contains_arguments(),
Pattern::Array(array) => array.contains_arguments(),
}
}
/// Returns `true` if the node contains the given token.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
match self {
Pattern::Object(object) => object.contains(symbol),
Pattern::Array(array) => array.contains(symbol),
}
}
} }
impl VisitWith for Pattern { impl VisitWith for Pattern {
@ -215,16 +186,6 @@ impl ObjectPattern {
Some(ObjectPatternElement::RestProperty { .. }) Some(ObjectPatternElement::RestProperty { .. })
) )
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.0.iter().any(ObjectPatternElement::contains_arguments)
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.0.iter().any(|e| e.contains(symbol))
}
} }
impl VisitWith for ObjectPattern { impl VisitWith for ObjectPattern {
@ -311,15 +272,6 @@ impl ArrayPattern {
.flat_map(ArrayPatternElement::idents) .flat_map(ArrayPatternElement::idents)
.collect() .collect()
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.0.iter().any(ArrayPatternElement::contains_arguments)
}
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.0.iter().any(|e| e.contains(symbol))
}
} }
impl VisitWith for ArrayPattern { impl VisitWith for ArrayPattern {
@ -443,64 +395,6 @@ pub enum ObjectPatternElement {
} }
impl ObjectPatternElement { impl ObjectPatternElement {
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
match self {
ObjectPatternElement::SingleName {
name,
ident,
default_init,
} => {
*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, .. } => {
access.contains_arguments()
}
ObjectPatternElement::Pattern {
name,
pattern,
default_init,
} => {
name.contains_arguments()
|| pattern.contains_arguments()
|| matches!(default_init, Some(init) if init.contains_arguments())
}
}
}
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
match self {
Self::SingleName {
name, default_init, ..
} => {
name.contains(symbol) || matches!(default_init, Some(init) if init.contains(symbol))
}
Self::AssignmentRestPropertyAccess { access, .. } => access.contains(symbol),
Self::Pattern {
name,
pattern,
default_init,
} => {
name.contains(symbol)
|| matches!(default_init, Some(init) if init.contains(symbol))
|| pattern.contains(symbol)
}
_ => false,
}
}
/// Gets the list of identifiers declared by the object binding pattern. /// Gets the list of identifiers declared by the object binding pattern.
#[inline] #[inline]
pub(crate) fn idents(&self) -> Vec<Identifier> { pub(crate) fn idents(&self) -> Vec<Identifier> {
@ -853,64 +747,6 @@ pub enum ArrayPatternElement {
} }
impl ArrayPatternElement { impl ArrayPatternElement {
/// Returns true if the node contains a identifier reference named 'arguments'.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
match self {
Self::Elision => false,
Self::SingleName {
ident,
default_init,
} => {
*ident == Sym::ARGUMENTS
|| matches!(default_init, Some(init) if init.contains_arguments())
}
Self::SingleNameRest { ident } => *ident == Sym::ARGUMENTS,
Self::PropertyAccess { access } | Self::PropertyAccessRest { access } => {
access.contains_arguments()
}
Self::PatternRest { pattern } => pattern.contains_arguments(),
Self::Pattern {
pattern,
default_init,
} => {
pattern.contains_arguments()
|| matches!(default_init, Some(init) if init.contains_arguments())
}
}
}
/// Returns `true` if the node contains the given token.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
match self {
Self::Elision | Self::SingleNameRest { .. } => false,
Self::SingleName { default_init, .. } => {
matches!(default_init, Some(init) if init.contains(symbol))
}
Self::PropertyAccess { access } | Self::PropertyAccessRest { access } => {
access.contains(symbol)
}
Self::Pattern {
pattern,
default_init,
} => {
pattern.contains(symbol)
|| matches!(default_init, Some(init) if init.contains(symbol))
}
Self::PatternRest { pattern } => pattern.contains(symbol),
}
}
/// Gets the list of identifiers in the array pattern element. /// Gets the list of identifiers in the array pattern element.
#[inline] #[inline]
pub(crate) fn idents(&self) -> Vec<Identifier> { pub(crate) fn idents(&self) -> Vec<Identifier> {

85
boa_ast/src/property.rs

@ -7,8 +7,8 @@ use core::ops::ControlFlow;
use super::{ use super::{
expression::{literal::Literal, Identifier}, expression::{literal::Literal, Identifier},
function::{AsyncFunction, AsyncGenerator, FormalParameterList, Function, Generator}, function::{AsyncFunction, AsyncGenerator, Function, Generator},
ContainsSymbol, Expression, StatementList, Expression,
}; };
/// Describes the definition of a property within an object literal. /// Describes the definition of a property within an object literal.
@ -79,43 +79,6 @@ pub enum PropertyDefinition {
CoverInitializedName(Identifier, Expression), CoverInitializedName(Identifier, Expression),
} }
impl PropertyDefinition {
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
match self {
PropertyDefinition::IdentifierReference(ident) => *ident == Sym::ARGUMENTS,
PropertyDefinition::Property(name, expr) => {
name.contains_arguments() || expr.contains_arguments()
}
// Skipping definition since functions are excluded from the search
PropertyDefinition::MethodDefinition(name, _) => name.contains_arguments(),
PropertyDefinition::SpreadObject(expr) => expr.contains_arguments(),
PropertyDefinition::CoverInitializedName(ident, expr) => {
*ident == Sym::ARGUMENTS || expr.contains_arguments()
}
}
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
match self {
PropertyDefinition::IdentifierReference(_) => false,
PropertyDefinition::Property(name, expr) => {
name.contains(symbol) || expr.contains(symbol)
}
// Skipping definition since functions are excluded from the search
PropertyDefinition::MethodDefinition(_, _)
if symbol == ContainsSymbol::MethodDefinition =>
{
true
}
PropertyDefinition::MethodDefinition(name, _) => name.contains(symbol),
PropertyDefinition::SpreadObject(expr) => expr.contains(symbol),
PropertyDefinition::CoverInitializedName(_ident, expr) => expr.contains(symbol),
}
}
}
impl VisitWith for PropertyDefinition { impl VisitWith for PropertyDefinition {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy> fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where where
@ -248,34 +211,6 @@ pub enum MethodDefinition {
Async(AsyncFunction), Async(AsyncFunction),
} }
impl MethodDefinition {
/// Gets the body of the method.
#[must_use]
pub fn body(&self) -> &StatementList {
match self {
MethodDefinition::Get(expr)
| MethodDefinition::Set(expr)
| MethodDefinition::Ordinary(expr) => expr.body(),
MethodDefinition::Generator(expr) => expr.body(),
MethodDefinition::AsyncGenerator(expr) => expr.body(),
MethodDefinition::Async(expr) => expr.body(),
}
}
/// Gets the parameters of the method.
#[must_use]
pub fn parameters(&self) -> &FormalParameterList {
match self {
MethodDefinition::Get(expr)
| MethodDefinition::Set(expr)
| MethodDefinition::Ordinary(expr) => expr.parameters(),
MethodDefinition::Generator(expr) => expr.parameters(),
MethodDefinition::AsyncGenerator(expr) => expr.parameters(),
MethodDefinition::Async(expr) => expr.parameters(),
}
}
}
impl VisitWith for MethodDefinition { impl VisitWith for MethodDefinition {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy> fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where where
@ -362,22 +297,6 @@ impl PropertyName {
PropertyName::Computed(_) => None, PropertyName::Computed(_) => None,
} }
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
match self {
PropertyName::Literal(_) => false,
PropertyName::Computed(expr) => expr.contains_arguments(),
}
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
match self {
PropertyName::Literal(_) => false,
PropertyName::Computed(expr) => expr.contains(symbol),
}
}
} }
impl ToInternedString for PropertyName { impl ToInternedString for PropertyName {

18
boa_ast/src/statement/block.rs

@ -1,8 +1,10 @@
//! Block AST node. //! Block AST node.
use super::Statement; use crate::{
use crate::visitor::{VisitWith, Visitor, VisitorMut}; expression::Identifier,
use crate::{expression::Identifier, ContainsSymbol, StatementList}; visitor::{VisitWith, Visitor, VisitorMut},
Statement, StatementList,
};
use boa_interner::{Interner, ToIndentedString}; use boa_interner::{Interner, ToIndentedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
@ -42,16 +44,6 @@ impl Block {
pub fn lexically_declared_names(&self) -> Vec<(Identifier, bool)> { pub fn lexically_declared_names(&self) -> Vec<(Identifier, bool)> {
self.statements.lexically_declared_names() self.statements.lexically_declared_names()
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.statements.contains_arguments()
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.statements.contains(symbol)
}
} }
impl<T> From<T> for Block impl<T> From<T> for Block

23
boa_ast/src/statement/if.rs

@ -1,8 +1,11 @@
//! If statement //! If statement
use crate::try_break; use crate::{
use crate::visitor::{VisitWith, Visitor, VisitorMut}; expression::Expression,
use crate::{expression::Expression, statement::Statement, ContainsSymbol}; statement::Statement,
try_break,
visitor::{VisitWith, Visitor, VisitorMut},
};
use boa_interner::{Interner, ToIndentedString, ToInternedString}; use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
@ -60,20 +63,6 @@ impl If {
else_node: else_node.map(Box::new), else_node: else_node.map(Box::new),
} }
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.condition.contains_arguments()
|| self.body.contains_arguments()
|| matches!(self.else_node, Some(ref stmt) if stmt.contains_arguments())
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.condition.contains(symbol)
|| self.body.contains(symbol)
|| matches!(self.else_node, Some(ref stmt) if stmt.contains(symbol))
}
} }
impl ToIndentedString for If { impl ToIndentedString for If {

5
boa_ast/src/statement/iteration/break.rs

@ -36,11 +36,6 @@ impl Break {
pub fn label(&self) -> Option<Sym> { pub fn label(&self) -> Option<Sym> {
self.label self.label
} }
#[inline]
pub(crate) fn contains_arguments(self) -> bool {
matches!(self.label, Some(label) if label == Sym::ARGUMENTS)
}
} }
impl ToInternedString for Break { impl ToInternedString for Break {

5
boa_ast/src/statement/iteration/continue.rs

@ -34,11 +34,6 @@ impl Continue {
pub fn label(&self) -> Option<Sym> { pub fn label(&self) -> Option<Sym> {
self.label self.label
} }
#[inline]
pub(crate) fn contains_arguments(self) -> bool {
matches!(self.label, Some(label) if label == Sym::ARGUMENTS)
}
} }
impl ToInternedString for Continue { impl ToInternedString for Continue {

19
boa_ast/src/statement/iteration/do_while_loop.rs

@ -1,6 +1,9 @@
use crate::try_break; use crate::{
use crate::visitor::{VisitWith, Visitor, VisitorMut}; expression::Expression,
use crate::{expression::Expression, statement::Statement, ContainsSymbol}; statement::Statement,
try_break,
visitor::{VisitWith, Visitor, VisitorMut},
};
use boa_interner::{Interner, ToIndentedString, ToInternedString}; use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
@ -46,16 +49,6 @@ impl DoWhileLoop {
condition, condition,
} }
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.body.contains_arguments() || self.condition.contains_arguments()
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.body.contains(symbol) || self.condition.contains(symbol)
}
} }
impl ToIndentedString for DoWhileLoop { impl ToIndentedString for DoWhileLoop {

15
boa_ast/src/statement/iteration/for_in_loop.rs

@ -3,7 +3,6 @@ use crate::visitor::{VisitWith, Visitor, VisitorMut};
use crate::{ use crate::{
expression::Expression, expression::Expression,
statement::{iteration::IterableLoopInitializer, Statement}, statement::{iteration::IterableLoopInitializer, Statement},
ContainsSymbol,
}; };
use boa_interner::{Interner, ToIndentedString, ToInternedString}; use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
@ -55,20 +54,6 @@ impl ForInLoop {
pub fn body(&self) -> &Statement { pub fn body(&self) -> &Statement {
&self.body &self.body
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.initializer.contains_arguments()
|| self.target.contains_arguments()
|| self.body.contains_arguments()
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.initializer.contains(symbol)
|| self.target.contains(symbol)
|| self.body.contains(symbol)
}
} }
impl ToIndentedString for ForInLoop { impl ToIndentedString for ForInLoop {

37
boa_ast/src/statement/iteration/for_loop.rs

@ -4,7 +4,7 @@ use crate::{
declaration::{LexicalDeclaration, VarDeclaration, Variable}, declaration::{LexicalDeclaration, VarDeclaration, Variable},
expression::Identifier, expression::Identifier,
statement::Statement, statement::Statement,
ContainsSymbol, Expression, Expression,
}; };
use boa_interner::{Interner, ToIndentedString, ToInternedString}; use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
@ -68,24 +68,6 @@ impl ForLoop {
pub fn body(&self) -> &Statement { pub fn body(&self) -> &Statement {
self.inner.body() self.inner.body()
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
let inner = &self.inner;
matches!(inner.init, Some(ref init) if init.contains_arguments())
|| matches!(inner.condition, Some(ref expr) if expr.contains_arguments())
|| matches!(inner.final_expr, Some(ref expr) if expr.contains_arguments())
|| inner.body.contains_arguments()
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
let inner = &self.inner;
matches!(inner.init, Some(ref init) if init.contains(symbol))
|| matches!(inner.condition, Some(ref expr) if expr.contains(symbol))
|| matches!(inner.final_expr, Some(ref expr) if expr.contains(symbol))
|| inner.body.contains(symbol)
}
} }
impl ToIndentedString for ForLoop { impl ToIndentedString for ForLoop {
@ -242,23 +224,6 @@ impl ForLoopInitializer {
_ => Vec::new(), _ => Vec::new(),
} }
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
match self {
Self::Var(var) => var.contains_arguments(),
Self::Lexical(lex) => lex.contains_arguments(),
Self::Expression(expr) => expr.contains_arguments(),
}
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
match self {
Self::Var(var) => var.contains(symbol),
Self::Lexical(lex) => lex.contains(symbol),
Self::Expression(expr) => expr.contains(symbol),
}
}
} }
impl ToInternedString for ForLoopInitializer { impl ToInternedString for ForLoopInitializer {

13
boa_ast/src/statement/iteration/for_of_loop.rs

@ -3,7 +3,6 @@ use crate::visitor::{VisitWith, Visitor, VisitorMut};
use crate::{ use crate::{
expression::Expression, expression::Expression,
statement::{iteration::IterableLoopInitializer, Statement}, statement::{iteration::IterableLoopInitializer, Statement},
ContainsSymbol,
}; };
use boa_interner::{Interner, ToIndentedString, ToInternedString}; use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
@ -74,18 +73,6 @@ impl ForOfLoop {
pub fn r#await(&self) -> bool { pub fn r#await(&self) -> bool {
self.r#await self.r#await
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.init.contains_arguments()
|| self.iterable.contains_arguments()
|| self.body.contains_arguments()
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.init.contains(symbol) || self.iterable.contains(symbol) || self.body.contains(symbol)
}
} }
impl ToIndentedString for ForOfLoop { impl ToIndentedString for ForOfLoop {

26
boa_ast/src/statement/iteration/mod.rs

@ -25,9 +25,7 @@ pub use self::{
while_loop::WhileLoop, while_loop::WhileLoop,
}; };
use crate::visitor::{VisitWith, Visitor, VisitorMut}; use crate::visitor::{VisitWith, Visitor, VisitorMut};
use boa_interner::{Interner, Sym, ToInternedString}; use boa_interner::{Interner, ToInternedString};
use super::ContainsSymbol;
/// A `for-in`, `for-of` and `for-await-of` loop initializer. /// A `for-in`, `for-of` and `for-await-of` loop initializer.
/// ///
@ -69,28 +67,6 @@ impl IterableLoopInitializer {
_ => Vec::new(), _ => Vec::new(),
} }
} }
#[inline]
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(),
}
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
match self {
Self::Var(declaration) | Self::Let(declaration) | Self::Const(declaration) => {
declaration.contains(symbol)
}
Self::Pattern(pattern) => pattern.contains(symbol),
Self::Access(access) => access.contains(symbol),
Self::Identifier(_) => false,
}
}
} }
impl ToInternedString for IterableLoopInitializer { impl ToInternedString for IterableLoopInitializer {

19
boa_ast/src/statement/iteration/while_loop.rs

@ -1,6 +1,9 @@
use crate::try_break; use crate::{
use crate::visitor::{VisitWith, Visitor, VisitorMut}; expression::Expression,
use crate::{expression::Expression, statement::Statement, ContainsSymbol}; statement::Statement,
try_break,
visitor::{VisitWith, Visitor, VisitorMut},
};
use boa_interner::{Interner, ToIndentedString, ToInternedString}; use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
@ -46,16 +49,6 @@ impl WhileLoop {
pub fn body(&self) -> &Statement { pub fn body(&self) -> &Statement {
&self.body &self.body
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.condition.contains_arguments() || self.body.contains_arguments()
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.condition.contains(symbol) || self.body.contains(symbol)
}
} }
impl ToIndentedString for WhileLoop { impl ToIndentedString for WhileLoop {

36
boa_ast/src/statement/labelled.rs

@ -1,7 +1,9 @@
use super::Statement; use crate::{
use crate::try_break; function::Function,
use crate::visitor::{VisitWith, Visitor, VisitorMut}; try_break,
use crate::{function::Function, ContainsSymbol}; visitor::{VisitWith, Visitor, VisitorMut},
Statement,
};
use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString}; use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
@ -30,22 +32,6 @@ impl LabelledItem {
LabelledItem::Statement(stmt) => stmt.to_indented_string(interner, indentation), LabelledItem::Statement(stmt) => stmt.to_indented_string(interner, indentation),
} }
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
match self {
LabelledItem::Function(_) => false,
LabelledItem::Statement(stmt) => stmt.contains_arguments(),
}
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
match self {
LabelledItem::Function(_) => false,
LabelledItem::Statement(stmt) => stmt.contains(symbol),
}
}
} }
impl ToInternedString for LabelledItem { impl ToInternedString for LabelledItem {
@ -130,16 +116,6 @@ impl Labelled {
self.item.to_indented_string(interner, indentation) self.item.to_indented_string(interner, indentation)
) )
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.label == Sym::ARGUMENTS || self.item.contains_arguments()
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.item.contains(symbol)
}
} }
impl ToInternedString for Labelled { impl ToInternedString for Labelled {

64
boa_ast/src/statement/mod.rs

@ -37,7 +37,6 @@ use rustc_hash::FxHashSet;
use super::{ use super::{
declaration::{Binding, VarDeclaration}, declaration::{Binding, VarDeclaration},
expression::{Expression, Identifier}, expression::{Expression, Identifier},
ContainsSymbol,
}; };
/// The `Statement` Parse Node. /// The `Statement` Parse Node.
@ -226,65 +225,16 @@ impl Statement {
} }
} }
/// Returns true if the node contains a identifier reference named 'arguments'. /// Abstract operation [`IsLabelledFunction`][spec].
/// ///
/// More information: /// This recursively checks if this `Statement` is a labelled function, since adding
/// - [ECMAScript specification][spec] /// several labels in a function should not change the return value of the abstract operation:
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments /// ```Javascript
// TODO: replace with a visitor /// l1: l2: l3: l4: function f(){ }
pub(crate) fn contains_arguments(&self) -> bool { /// ```
match self {
Self::Empty => false,
Self::Block(block) => block.contains_arguments(),
Self::Var(var) => var.contains_arguments(),
Self::Expression(expr) => expr.contains_arguments(),
Self::If(r#if) => r#if.contains_arguments(),
Self::DoWhileLoop(dowhile) => dowhile.contains_arguments(),
Self::WhileLoop(whileloop) => whileloop.contains_arguments(),
Self::ForLoop(forloop) => forloop.contains_arguments(),
Self::ForInLoop(forin) => forin.contains_arguments(),
Self::ForOfLoop(forof) => forof.contains_arguments(),
Self::Switch(switch) => switch.contains_arguments(),
Self::Continue(r#continue) => r#continue.contains_arguments(),
Self::Break(r#break) => r#break.contains_arguments(),
Self::Return(r#return) => r#return.contains_arguments(),
Self::Labelled(labelled) => labelled.contains_arguments(),
Self::Throw(throw) => throw.contains_arguments(),
Self::Try(r#try) => r#try.contains_arguments(),
}
}
/// Returns `true` if the node contains the given token.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains
// TODO: replace with a visitor
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
match self {
Self::Empty | Self::Continue(_) | Self::Break(_) => false,
Self::Block(block) => block.contains(symbol),
Self::Var(var) => var.contains(symbol),
Self::Expression(expr) => expr.contains(symbol),
Self::If(r#if) => r#if.contains(symbol),
Self::DoWhileLoop(dowhile) => dowhile.contains(symbol),
Self::WhileLoop(whileloop) => whileloop.contains(symbol),
Self::ForLoop(forloop) => forloop.contains(symbol),
Self::ForInLoop(forin) => forin.contains(symbol),
Self::ForOfLoop(forof) => forof.contains(symbol),
Self::Switch(switch) => switch.contains(symbol),
Self::Return(r#return) => r#return.contains(symbol),
Self::Labelled(labelled) => labelled.contains(symbol),
Self::Throw(throw) => throw.contains(symbol),
Self::Try(r#try) => r#try.contains(symbol),
}
}
/// `IsLabelledFunction` static operation, as defined by the [spec].
/// ///
/// Returns `true` if this `Statement` is a labelled function. /// This should return `true` for that snippet.
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-islabelledfunction /// [spec]: https://tc39.es/ecma262/#sec-islabelledfunction
#[inline] #[inline]

16
boa_ast/src/statement/return.rs

@ -1,5 +1,8 @@
use crate::visitor::{VisitWith, Visitor, VisitorMut}; use crate::{
use crate::{expression::Expression, statement::Statement, ContainsSymbol}; expression::Expression,
statement::Statement,
visitor::{VisitWith, Visitor, VisitorMut},
};
use boa_interner::{Interner, ToInternedString}; use boa_interner::{Interner, ToInternedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
@ -38,15 +41,6 @@ impl Return {
pub fn new(expression: Option<Expression>) -> Self { pub fn new(expression: Option<Expression>) -> Self {
Self { target: expression } Self { target: expression }
} }
pub(crate) fn contains_arguments(&self) -> bool {
matches!(self.target, Some(ref expr) if expr.contains_arguments())
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
matches!(self.target, Some(ref expr) if expr.contains(symbol))
}
} }
impl From<Return> for Statement { impl From<Return> for Statement {

39
boa_ast/src/statement/switch.rs

@ -1,13 +1,15 @@
//! Switch node. //! Switch node.
//! //!
use crate::try_break; use crate::{
use crate::visitor::{VisitWith, Visitor, VisitorMut}; expression::Expression,
use crate::{expression::Expression, statement::Statement, StatementList}; statement::Statement,
try_break,
visitor::{VisitWith, Visitor, VisitorMut},
StatementList,
};
use boa_interner::{Interner, ToIndentedString, ToInternedString}; use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
use super::ContainsSymbol;
/// A case clause inside a [`Switch`] statement, as defined by the [spec]. /// A case clause inside a [`Switch`] statement, as defined by the [spec].
/// ///
/// Even though every [`Case`] body is a [`StatementList`], it doesn't create a new lexical /// Even though every [`Case`] body is a [`StatementList`], it doesn't create a new lexical
@ -44,21 +46,6 @@ impl Case {
pub fn body(&self) -> &StatementList { pub fn body(&self) -> &StatementList {
&self.body &self.body
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.condition.contains_arguments() || self.body.contains_arguments()
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.condition.contains(symbol)
|| self
.body
.statements()
.iter()
.any(|stmt| stmt.contains(symbol))
}
} }
impl VisitWith for Case { impl VisitWith for Case {
@ -135,18 +122,6 @@ impl Switch {
pub fn default(&self) -> Option<&StatementList> { pub fn default(&self) -> Option<&StatementList> {
self.default.as_ref() self.default.as_ref()
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.val.contains_arguments()
|| self.cases.iter().any(Case::contains_arguments)
|| matches!(self.default, Some(ref stmts) if stmts.contains_arguments())
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.val.contains(symbol) || self.cases.iter().any(|case| case.contains(symbol))
}
} }
impl ToIndentedString for Switch { impl ToIndentedString for Switch {

17
boa_ast/src/statement/throw.rs

@ -1,5 +1,8 @@
use crate::visitor::{VisitWith, Visitor, VisitorMut}; use crate::{
use crate::{statement::Statement, ContainsSymbol, Expression}; statement::Statement,
visitor::{VisitWith, Visitor, VisitorMut},
Expression,
};
use boa_interner::{Interner, ToInternedString}; use boa_interner::{Interner, ToInternedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
@ -35,16 +38,6 @@ impl Throw {
pub fn new(target: Expression) -> Self { pub fn new(target: Expression) -> Self {
Self { target } Self { target }
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.target.contains_arguments()
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.target.contains(symbol)
}
} }
impl ToInternedString for Throw { impl ToInternedString for Throw {

53
boa_ast/src/statement/try.rs

@ -5,13 +5,10 @@ use crate::visitor::{VisitWith, Visitor, VisitorMut};
use crate::{ use crate::{
declaration::Binding, declaration::Binding,
statement::{Block, Statement}, statement::{Block, Statement},
StatementListItem,
}; };
use boa_interner::{Interner, ToIndentedString, ToInternedString}; use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
use super::ContainsSymbol;
/// The `try...catch` statement marks a block of statements to try and specifies a response /// The `try...catch` statement marks a block of statements to try and specifies a response
/// should an exception be thrown. /// should an exception be thrown.
/// ///
@ -78,20 +75,6 @@ impl Try {
ErrorHandler::Catch(_) => None, ErrorHandler::Catch(_) => None,
} }
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.block.contains_arguments()
|| matches!(self.catch(), Some(catch) if catch.contains_arguments())
|| matches!(self.finally(), Some(finally) if finally.contains_arguments())
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.block.contains(symbol)
|| matches!(self.catch(), Some(catch) if catch.contains(symbol))
|| matches!(self.finally(), Some(finally) if finally.contains(symbol))
}
} }
impl ToIndentedString for Try { impl ToIndentedString for Try {
@ -181,24 +164,6 @@ impl Catch {
pub fn block(&self) -> &Block { pub fn block(&self) -> &Block {
&self.block &self.block
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.block
.statement_list()
.statements()
.iter()
.any(StatementListItem::contains_arguments)
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.block
.statement_list()
.statements()
.iter()
.any(|stmt| stmt.contains(symbol))
}
} }
impl ToIndentedString for Catch { impl ToIndentedString for Catch {
@ -252,24 +217,6 @@ impl Finally {
pub fn block(&self) -> &Block { pub fn block(&self) -> &Block {
&self.block &self.block
} }
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.block
.statement_list()
.statements()
.iter()
.any(StatementListItem::contains_arguments)
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.block
.statement_list()
.statements()
.iter()
.any(|stmt| stmt.contains(symbol))
}
} }
impl ToIndentedString for Finally { impl ToIndentedString for Finally {

64
boa_ast/src/statement_list.rs

@ -1,9 +1,12 @@
//! Statement list node. //! Statement list node.
use super::{declaration::Binding, Declaration}; use super::{declaration::Binding, Declaration};
use crate::try_break; use crate::{
use crate::visitor::{VisitWith, Visitor, VisitorMut}; expression::Identifier,
use crate::{expression::Identifier, statement::Statement, ContainsSymbol}; statement::Statement,
try_break,
visitor::{VisitWith, Visitor, VisitorMut},
};
use boa_interner::{Interner, ToIndentedString}; use boa_interner::{Interner, ToIndentedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
@ -48,36 +51,6 @@ impl StatementListItem {
StatementListItem::Declaration(_) => {} StatementListItem::Declaration(_) => {}
} }
} }
/// Returns true if the node contains a identifier reference named 'arguments'.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments
#[inline]
#[must_use]
pub fn contains_arguments(&self) -> bool {
match self {
StatementListItem::Statement(stmt) => stmt.contains_arguments(),
StatementListItem::Declaration(decl) => decl.contains_arguments(),
}
}
/// Returns `true` if the node contains the given token.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains
#[inline]
#[must_use]
pub fn contains(&self, symbol: ContainsSymbol) -> bool {
match self {
StatementListItem::Statement(stmt) => stmt.contains(symbol),
StatementListItem::Declaration(decl) => decl.contains(symbol),
}
}
} }
impl ToIndentedString for StatementListItem { impl ToIndentedString for StatementListItem {
@ -252,31 +225,6 @@ impl StatementList {
names names
} }
/// Returns true if the node contains a identifier reference named 'arguments'.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments
#[inline]
pub fn contains_arguments(&self) -> bool {
self.statements
.iter()
.any(StatementListItem::contains_arguments)
}
/// Returns `true` if the node contains the given token.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains
#[inline]
#[must_use]
pub fn contains(&self, symbol: ContainsSymbol) -> bool {
self.statements.iter().any(|stmt| stmt.contains(symbol))
}
} }
impl From<Box<[StatementListItem]>> for StatementList { impl From<Box<[StatementListItem]>> for StatementList {

10
boa_engine/src/builtins/array/mod.rs

@ -315,7 +315,7 @@ impl Array {
} }
// 4. Return ? IsArray(O). // 4. Return ? IsArray(O).
o.is_array_abstract(context) o.is_array_abstract()
} }
/// `get Array [ @@species ]` /// `get Array [ @@species ]`
@ -345,7 +345,7 @@ impl Array {
) -> JsResult<JsObject> { ) -> JsResult<JsObject> {
// 1. Let isArray be ? IsArray(originalArray). // 1. Let isArray be ? IsArray(originalArray).
// 2. If isArray is false, return ? ArrayCreate(length). // 2. If isArray is false, return ? ArrayCreate(length).
if !original_array.is_array_abstract(context)? { if !original_array.is_array_abstract()? {
return Self::array_create(length, None, context); return Self::array_create(length, None, context);
} }
// 3. Let C be ? Get(originalArray, "constructor"). // 3. Let C be ? Get(originalArray, "constructor").
@ -566,10 +566,10 @@ impl Array {
pub(crate) fn is_array( pub(crate) fn is_array(
_: &JsValue, _: &JsValue,
args: &[JsValue], args: &[JsValue],
context: &mut Context, _context: &mut Context,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
// 1. Return ? IsArray(arg). // 1. Return ? IsArray(arg).
args.get_or_undefined(0).is_array(context).map(Into::into) args.get_or_undefined(0).is_array().map(Into::into)
} }
/// `Array.of(...items)` /// `Array.of(...items)`
@ -1788,7 +1788,7 @@ impl Array {
// iv. If depth > 0, then // iv. If depth > 0, then
if depth > 0 { if depth > 0 {
// 1. Set shouldFlatten to ? IsArray(element). // 1. Set shouldFlatten to ? IsArray(element).
should_flatten = element.is_array(context)?; should_flatten = element.is_array()?;
} }
// v. If shouldFlatten is true // v. If shouldFlatten is true

44
boa_engine/src/builtins/function/mod.rs

@ -30,7 +30,11 @@ use crate::{
value::IntegerOrInfinity, value::IntegerOrInfinity,
Context, JsResult, JsString, JsValue, Context, JsResult, JsString, JsValue,
}; };
use boa_ast::{function::FormalParameterList, StatementList}; use boa_ast::{
function::FormalParameterList,
operations::{contains, ContainsSymbol},
StatementList,
};
use boa_gc::{self, custom_trace, Finalize, Gc, Trace}; use boa_gc::{self, custom_trace, Finalize, Gc, Trace};
use boa_interner::Sym; use boa_interner::Sym;
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -240,15 +244,21 @@ pub enum Function {
Async { Async {
code: Gc<crate::vm::CodeBlock>, code: Gc<crate::vm::CodeBlock>,
environments: DeclarativeEnvironmentStack, environments: DeclarativeEnvironmentStack,
/// The `[[HomeObject]]` internal slot.
home_object: Option<JsObject>,
promise_capability: PromiseCapability, promise_capability: PromiseCapability,
}, },
Generator { Generator {
code: Gc<crate::vm::CodeBlock>, code: Gc<crate::vm::CodeBlock>,
environments: DeclarativeEnvironmentStack, environments: DeclarativeEnvironmentStack,
/// The `[[HomeObject]]` internal slot.
home_object: Option<JsObject>,
}, },
AsyncGenerator { AsyncGenerator {
code: Gc<crate::vm::CodeBlock>, code: Gc<crate::vm::CodeBlock>,
environments: DeclarativeEnvironmentStack, environments: DeclarativeEnvironmentStack,
/// The `[[HomeObject]]` internal slot.
home_object: Option<JsObject>,
}, },
} }
@ -266,15 +276,17 @@ unsafe impl Trace for Function {
mark(elem); mark(elem);
} }
} }
Self::Async { code, environments, promise_capability } => { Self::Async { code, environments, home_object, promise_capability } => {
mark(code); mark(code);
mark(environments); mark(environments);
mark(home_object);
mark(promise_capability); mark(promise_capability);
} }
Self::Generator { code, environments } Self::Generator { code, environments, home_object}
| Self::AsyncGenerator { code, environments } => { | Self::AsyncGenerator { code, environments, home_object} => {
mark(code); mark(code);
mark(environments); mark(environments);
mark(home_object);
} }
} }
}} }}
@ -312,17 +324,23 @@ impl Function {
/// Returns a reference to the function `[[HomeObject]]` slot if present. /// Returns a reference to the function `[[HomeObject]]` slot if present.
pub(crate) fn get_home_object(&self) -> Option<&JsObject> { pub(crate) fn get_home_object(&self) -> Option<&JsObject> {
if let Self::Ordinary { home_object, .. } = self { match self {
home_object.as_ref() Self::Ordinary { home_object, .. }
} else { | Self::Async { home_object, .. }
None | Self::Generator { home_object, .. }
| Self::AsyncGenerator { home_object, .. } => home_object.as_ref(),
_ => None,
} }
} }
/// Sets the `[[HomeObject]]` slot if present. /// Sets the `[[HomeObject]]` slot if present.
pub(crate) fn set_home_object(&mut self, object: JsObject) { pub(crate) fn set_home_object(&mut self, object: JsObject) {
if let Self::Ordinary { home_object, .. } = self { match self {
*home_object = Some(object); Self::Ordinary { home_object, .. }
| Self::Async { home_object, .. }
| Self::Generator { home_object, .. }
| Self::AsyncGenerator { home_object, .. } => *home_object = Some(object),
_ => {}
} }
} }
@ -515,7 +533,7 @@ impl BuiltInFunctionObject {
} }
}; };
if generator && parameters.contains_yield_expression() { if generator && contains(&parameters, ContainsSymbol::YieldExpression) {
return Err(JsNativeError::syntax().with_message( return Err(JsNativeError::syntax().with_message(
"yield expression is not allowed in formal parameter list of generator function", "yield expression is not allowed in formal parameter list of generator function",
).into()); ).into());
@ -525,14 +543,14 @@ impl BuiltInFunctionObject {
}; };
// It is a Syntax Error if FormalParameters Contains YieldExpression is true. // It is a Syntax Error if FormalParameters Contains YieldExpression is true.
if generator && r#async && parameters.contains_yield_expression() { if generator && r#async && contains(&parameters, ContainsSymbol::YieldExpression) {
return Err(JsNativeError::syntax() return Err(JsNativeError::syntax()
.with_message("yield expression not allowed in async generator parameters") .with_message("yield expression not allowed in async generator parameters")
.into()); .into());
} }
// It is a Syntax Error if FormalParameters Contains AwaitExpression is true. // It is a Syntax Error if FormalParameters Contains AwaitExpression is true.
if generator && r#async && parameters.contains_await_expression() { if generator && r#async && contains(&parameters, ContainsSymbol::AwaitExpression) {
return Err(JsNativeError::syntax() return Err(JsNativeError::syntax()
.with_message("await expression not allowed in async generator parameters") .with_message("await expression not allowed in async generator parameters")
.into()); .into());

14
boa_engine/src/builtins/intl/mod.rs

@ -184,7 +184,7 @@ fn lookup_matcher(
// Assignment deferred. See return statement below. // Assignment deferred. See return statement below.
// ii. If locale and noExtensionsLocale are not the same String value, then // ii. If locale and noExtensionsLocale are not the same String value, then
let maybe_ext = if locale_str.eq(&no_extensions_locale) { let maybe_ext = if locale_str.eq(&no_extensions_locale) {
"".into() String::new()
} else { } else {
// 1. Let extension be the String value consisting of the substring of the Unicode // 1. Let extension be the String value consisting of the substring of the Unicode
// locale extension sequence within locale. // locale extension sequence within locale.
@ -205,7 +205,7 @@ fn lookup_matcher(
// 5. Return result. // 5. Return result.
MatcherRecord { MatcherRecord {
locale: default_locale(canonicalizer).to_string(), locale: default_locale(canonicalizer).to_string(),
extension: "".into(), extension: String::new(),
} }
} }
@ -312,7 +312,7 @@ fn unicode_extension_components(extension: &str) -> UniExtRecord {
// ii. Set keyword to the Record { [[Key]]: subtag, [[Value]]: "" }. // ii. Set keyword to the Record { [[Key]]: subtag, [[Value]]: "" }.
keyword = Some(Keyword { keyword = Some(Keyword {
key: subtag.into(), key: subtag.into(),
value: "".into(), value: String::new(),
}); });
// f. Else, // f. Else,
} else { } else {
@ -562,9 +562,9 @@ fn resolve_locale(
// 5. Let result be a new Record. // 5. Let result be a new Record.
let mut result = ResolveLocaleRecord { let mut result = ResolveLocaleRecord {
locale: "".into(), locale: String::new(),
properties: FxHashMap::default(), properties: FxHashMap::default(),
data_locale: "".into(), data_locale: String::new(),
}; };
// 6. Set result.[[dataLocale]] to foundLocale. // 6. Set result.[[dataLocale]] to foundLocale.
@ -607,7 +607,7 @@ fn resolve_locale(
}; };
// g. Let supportedExtensionAddition be "". // g. Let supportedExtensionAddition be "".
let mut supported_extension_addition = "".into(); let mut supported_extension_addition = String::new();
// h. If r has an [[extension]] field, then // h. If r has an [[extension]] field, then
if !r.extension.is_empty() { if !r.extension.is_empty() {
@ -682,7 +682,7 @@ fn resolve_locale(
value = options_value; value = options_value;
// b. Let supportedExtensionAddition be "". // b. Let supportedExtensionAddition be "".
supported_extension_addition = "".into(); supported_extension_addition = String::new();
} }
} }
} }

6
boa_engine/src/builtins/json/mod.rs

@ -234,7 +234,7 @@ impl Json {
if let Some(obj) = val.as_object() { if let Some(obj) = val.as_object() {
// a. Let isArray be ? IsArray(val). // a. Let isArray be ? IsArray(val).
// b. If isArray is true, then // b. If isArray is true, then
if obj.is_array_abstract(context)? { if obj.is_array_abstract()? {
// i. Let I be 0. // i. Let I be 0.
// ii. Let len be ? LengthOfArrayLike(val). // ii. Let len be ? LengthOfArrayLike(val).
// iii. Repeat, while I < len, // iii. Repeat, while I < len,
@ -339,7 +339,7 @@ impl Json {
} else { } else {
// i. Let isArray be ? IsArray(replacer). // i. Let isArray be ? IsArray(replacer).
// ii. If isArray is true, then // ii. If isArray is true, then
if replacer_obj.is_array_abstract(context)? { if replacer_obj.is_array_abstract()? {
// 1. Set PropertyList to a new empty List. // 1. Set PropertyList to a new empty List.
let mut property_set = indexmap::IndexSet::new(); let mut property_set = indexmap::IndexSet::new();
@ -562,7 +562,7 @@ impl Json {
// a. Let isArray be ? IsArray(value). // a. Let isArray be ? IsArray(value).
// b. If isArray is true, return ? SerializeJSONArray(state, value). // b. If isArray is true, return ? SerializeJSONArray(state, value).
// c. Return ? SerializeJSONObject(state, value). // c. Return ? SerializeJSONObject(state, value).
return if obj.is_array_abstract(context)? { return if obj.is_array_abstract()? {
Ok(Some(Self::serialize_json_array(state, obj, context)?)) Ok(Some(Self::serialize_json_array(state, obj, context)?))
} else { } else {
Ok(Some(Self::serialize_json_object(state, obj, context)?)) Ok(Some(Self::serialize_json_object(state, obj, context)?))

2
boa_engine/src/builtins/object/mod.rs

@ -780,7 +780,7 @@ impl Object {
// 4. Let isArray be ? IsArray(O). // 4. Let isArray be ? IsArray(O).
// 5. If isArray is true, let builtinTag be "Array". // 5. If isArray is true, let builtinTag be "Array".
let builtin_tag = if o.is_array_abstract(context)? { let builtin_tag = if o.is_array_abstract()? {
utf16!("Array") utf16!("Array")
} else { } else {
// 6. Else if O has a [[ParameterMap]] internal slot, let builtinTag be "Arguments". // 6. Else if O has a [[ParameterMap]] internal slot, let builtinTag be "Arguments".

55
boa_engine/src/builtins/uri/mod.rs

@ -345,7 +345,7 @@ where
k += 2; k += 2;
// vi. Let n be the number of leading 1 bits in B. // vi. Let n be the number of leading 1 bits in B.
let n = leading_one_bits(b); let n = b.leading_ones() as usize;
// vii. If n = 0, then // vii. If n = 0, then
if n == 0 { if n == 0 {
@ -453,63 +453,10 @@ fn decode_hex_byte(high: u16, low: u16) -> Option<u8> {
} }
} }
/// Counts the number of leading 1 bits in a given byte.
#[inline]
fn leading_one_bits(byte: u8) -> usize {
// This uses a value table for speed
if byte == u8::MAX {
8
} else if byte == 0b1111_1110 {
7
} else if byte & 0b1111_1100 == 0b1111_1100 {
6
} else if byte & 0b1111_1000 == 0b1111_1000 {
5
} else if byte & 0b1111_0000 == 0b1111_0000 {
4
} else if byte & 0b1110_0000 == 0b1110_0000 {
3
} else if byte & 0b1100_0000 == 0b1100_0000 {
2
} else if byte & 0b1000_0000 == 0b1000_0000 {
1
} else {
0
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
/// Checks if the `leading_one_bits()` function works as expected.
#[test]
fn ut_leading_one_bits() {
assert_eq!(leading_one_bits(0b1111_1111), 8);
assert_eq!(leading_one_bits(0b1111_1110), 7);
assert_eq!(leading_one_bits(0b1111_1100), 6);
assert_eq!(leading_one_bits(0b1111_1101), 6);
assert_eq!(leading_one_bits(0b1111_1011), 5);
assert_eq!(leading_one_bits(0b1111_1000), 5);
assert_eq!(leading_one_bits(0b1111_0000), 4);
assert_eq!(leading_one_bits(0b1111_0111), 4);
assert_eq!(leading_one_bits(0b1110_0000), 3);
assert_eq!(leading_one_bits(0b1110_1111), 3);
assert_eq!(leading_one_bits(0b1100_0000), 2);
assert_eq!(leading_one_bits(0b1101_1111), 2);
assert_eq!(leading_one_bits(0b1000_0000), 1);
assert_eq!(leading_one_bits(0b1011_1111), 1);
assert_eq!(leading_one_bits(0b0000_0000), 0);
assert_eq!(leading_one_bits(0b0111_1111), 0);
}
/// Checks that the `decode_byte()` function works as expected. /// Checks that the `decode_byte()` function works as expected.
#[test] #[test]
fn ut_decode_byte() { fn ut_decode_byte() {

8
boa_engine/src/bytecompiler/mod.rs

@ -346,17 +346,17 @@ impl<'b> ByteCompiler<'b> {
#[inline] #[inline]
fn emit_u64(&mut self, value: u64) { fn emit_u64(&mut self, value: u64) {
self.code_block.code.extend(&value.to_ne_bytes()); self.code_block.code.extend(value.to_ne_bytes());
} }
#[inline] #[inline]
fn emit_u32(&mut self, value: u32) { fn emit_u32(&mut self, value: u32) {
self.code_block.code.extend(&value.to_ne_bytes()); self.code_block.code.extend(value.to_ne_bytes());
} }
#[inline] #[inline]
fn emit_u16(&mut self, value: u16) { fn emit_u16(&mut self, value: u16) {
self.code_block.code.extend(&value.to_ne_bytes()); self.code_block.code.extend(value.to_ne_bytes());
} }
#[inline] #[inline]
@ -696,7 +696,7 @@ impl<'b> ByteCompiler<'b> {
} }
PropertyAccessField::Expr(expr) => { PropertyAccessField::Expr(expr) => {
self.emit_opcode(Opcode::Super); self.emit_opcode(Opcode::Super);
self.compile_expr(&**expr, true)?; self.compile_expr(expr, true)?;
self.emit_opcode(Opcode::GetPropertyByValue); self.emit_opcode(Opcode::GetPropertyByValue);
} }
}, },

2
boa_engine/src/object/jsobject.rs

@ -728,7 +728,7 @@ Cannot both specify accessors and a value or writable attribute",
impl AsRef<boa_gc::Cell<Object>> for JsObject { impl AsRef<boa_gc::Cell<Object>> for JsObject {
#[inline] #[inline]
fn as_ref(&self) -> &boa_gc::Cell<Object> { fn as_ref(&self) -> &boa_gc::Cell<Object> {
&*self.inner &self.inner
} }
} }

4
boa_engine/src/object/operations.rs

@ -627,7 +627,7 @@ impl JsObject {
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-isarray /// [spec]: https://tc39.es/ecma262/#sec-isarray
pub(crate) fn is_array_abstract(&self, context: &mut Context) -> JsResult<bool> { pub(crate) fn is_array_abstract(&self) -> JsResult<bool> {
// Note: The spec specifies this function for JsValue. // Note: The spec specifies this function for JsValue.
// It is implemented for JsObject for convenience. // It is implemented for JsObject for convenience.
@ -644,7 +644,7 @@ impl JsObject {
let (target, _) = proxy.try_data()?; let (target, _) = proxy.try_data()?;
// c. Return ? IsArray(target). // c. Return ? IsArray(target).
return target.is_array_abstract(context); return target.is_array_abstract();
} }
// 4. Return false. // 4. Return false.

2
boa_engine/src/syntax/lexer/cursor.rs

@ -338,7 +338,7 @@ where
#[inline] #[inline]
fn increment(&mut self, n: u32) -> Result<(), Error> { fn increment(&mut self, n: u32) -> Result<(), Error> {
for _ in 0..n { for _ in 0..n {
if None == self.next_byte()? { if (self.next_byte()?).is_none() {
break; break;
} }
} }

16
boa_engine/src/syntax/parser/expression/primary/async_function_expression/mod.rs

@ -6,11 +6,15 @@ use crate::syntax::{
parser::{ parser::{
expression::BindingIdentifier, expression::BindingIdentifier,
function::{FormalParameters, FunctionBody}, function::{FormalParameters, FunctionBody},
function_contains_super, name_in_lexically_declared_names, AllowYield, Cursor, ParseError, name_in_lexically_declared_names, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
ParseResult, TokenParser,
}, },
}; };
use boa_ast::{expression::Identifier, function::AsyncFunction, Keyword, Position, Punctuator}; use boa_ast::{
expression::Identifier,
function::AsyncFunction,
operations::{contains, ContainsSymbol},
Keyword, Position, Punctuator,
};
use boa_interner::{Interner, Sym}; use boa_interner::{Interner, Sym};
use boa_profiler::Profiler; use boa_profiler::Profiler;
use std::io::Read; use std::io::Read;
@ -130,13 +134,15 @@ where
params_start_position, params_start_position,
)?; )?;
if function_contains_super(&body, &params) { let function = AsyncFunction::new(name, params, body);
if contains(&function, ContainsSymbol::Super) {
return Err(ParseError::lex(LexError::Syntax( return Err(ParseError::lex(LexError::Syntax(
"invalid super usage".into(), "invalid super usage".into(),
params_start_position, params_start_position,
))); )));
} }
Ok(AsyncFunction::new(name, params, body)) Ok(function)
} }
} }

20
boa_engine/src/syntax/parser/expression/primary/async_generator_expression/mod.rs

@ -15,11 +15,15 @@ use crate::syntax::{
parser::{ parser::{
expression::BindingIdentifier, expression::BindingIdentifier,
function::{FormalParameters, FunctionBody}, function::{FormalParameters, FunctionBody},
function_contains_super, name_in_lexically_declared_names, Cursor, ParseError, ParseResult, name_in_lexically_declared_names, Cursor, ParseError, ParseResult, TokenParser,
TokenParser,
}, },
}; };
use boa_ast::{expression::Identifier, function::AsyncGenerator, Keyword, Position, Punctuator}; use boa_ast::{
expression::Identifier,
function::AsyncGenerator,
operations::{contains, ContainsSymbol},
Keyword, Position, Punctuator,
};
use boa_interner::{Interner, Sym}; use boa_interner::{Interner, Sym};
use boa_profiler::Profiler; use boa_profiler::Profiler;
use std::io::Read; use std::io::Read;
@ -102,7 +106,7 @@ where
let params = FormalParameters::new(true, true).parse(cursor, interner)?; let params = FormalParameters::new(true, true).parse(cursor, interner)?;
// It is a Syntax Error if FormalParameters Contains YieldExpression is true. // It is a Syntax Error if FormalParameters Contains YieldExpression is true.
if params.contains_yield_expression() { if contains(&params, ContainsSymbol::YieldExpression) {
return Err(ParseError::lex(LexError::Syntax( return Err(ParseError::lex(LexError::Syntax(
"yield expression not allowed in async generator expression parameters".into(), "yield expression not allowed in async generator expression parameters".into(),
params_start_position, params_start_position,
@ -110,7 +114,7 @@ where
} }
// It is a Syntax Error if FormalParameters Contains AwaitExpression is true. // It is a Syntax Error if FormalParameters Contains AwaitExpression is true.
if params.contains_await_expression() { if contains(&params, ContainsSymbol::AwaitExpression) {
return Err(ParseError::lex(LexError::Syntax( return Err(ParseError::lex(LexError::Syntax(
"await expression not allowed in async generator expression parameters".into(), "await expression not allowed in async generator expression parameters".into(),
params_start_position, params_start_position,
@ -162,7 +166,9 @@ where
params_start_position, params_start_position,
)?; )?;
if function_contains_super(&body, &params) { let function = AsyncGenerator::new(name, params, body);
if contains(&function, ContainsSymbol::Super) {
return Err(ParseError::lex(LexError::Syntax( return Err(ParseError::lex(LexError::Syntax(
"invalid super usage".into(), "invalid super usage".into(),
params_start_position, params_start_position,
@ -170,6 +176,6 @@ where
} }
//implement the below AsyncGeneratorExpr in ast::node //implement the below AsyncGeneratorExpr in ast::node
Ok(AsyncGenerator::new(name, params, body)) Ok(function)
} }
} }

16
boa_engine/src/syntax/parser/expression/primary/function_expression/mod.rs

@ -15,11 +15,15 @@ use crate::syntax::{
parser::{ parser::{
expression::BindingIdentifier, expression::BindingIdentifier,
function::{FormalParameters, FunctionBody}, function::{FormalParameters, FunctionBody},
function_contains_super, name_in_lexically_declared_names, Cursor, ParseError, ParseResult, name_in_lexically_declared_names, Cursor, ParseError, ParseResult, TokenParser,
TokenParser,
}, },
}; };
use boa_ast::{expression::Identifier, function::Function, Keyword, Position, Punctuator}; use boa_ast::{
expression::Identifier,
function::Function,
operations::{contains, ContainsSymbol},
Keyword, Position, Punctuator,
};
use boa_interner::{Interner, Sym}; use boa_interner::{Interner, Sym};
use boa_profiler::Profiler; use boa_profiler::Profiler;
use std::io::Read; use std::io::Read;
@ -124,13 +128,15 @@ where
params_start_position, params_start_position,
)?; )?;
if function_contains_super(&body, &params) { let function = Function::new(name, params, body);
if contains(&function, ContainsSymbol::Super) {
return Err(ParseError::lex(LexError::Syntax( return Err(ParseError::lex(LexError::Syntax(
"invalid super usage".into(), "invalid super usage".into(),
params_start_position, params_start_position,
))); )));
} }
Ok(Function::new(name, params, body)) Ok(function)
} }
} }

16
boa_engine/src/syntax/parser/expression/primary/generator_expression/mod.rs

@ -15,11 +15,15 @@ use crate::syntax::{
parser::{ parser::{
expression::BindingIdentifier, expression::BindingIdentifier,
function::{FormalParameters, FunctionBody}, function::{FormalParameters, FunctionBody},
function_contains_super, name_in_lexically_declared_names, Cursor, ParseError, ParseResult, name_in_lexically_declared_names, Cursor, ParseError, ParseResult, TokenParser,
TokenParser,
}, },
}; };
use boa_ast::{expression::Identifier, function::Generator, Position, Punctuator}; use boa_ast::{
expression::Identifier,
function::Generator,
operations::{contains, ContainsSymbol},
Position, Punctuator,
};
use boa_interner::{Interner, Sym}; use boa_interner::{Interner, Sym};
use boa_profiler::Profiler; use boa_profiler::Profiler;
use std::io::Read; use std::io::Read;
@ -126,13 +130,15 @@ where
params_start_position, params_start_position,
)?; )?;
if function_contains_super(&body, &params) { let function = Generator::new(name, params, body);
if contains(&function, ContainsSymbol::Super) {
return Err(ParseError::lex(LexError::Syntax( return Err(ParseError::lex(LexError::Syntax(
"invalid super usage".into(), "invalid super usage".into(),
params_start_position, params_start_position,
))); )));
} }
Ok(Generator::new(name, params, body)) Ok(function)
} }
} }

3
boa_engine/src/syntax/parser/expression/primary/mod.rs

@ -46,6 +46,7 @@ use boa_ast::{
Call, Identifier, New, Call, Identifier, New,
}, },
function::{FormalParameter, FormalParameterList}, function::{FormalParameter, FormalParameterList},
operations::{contains, ContainsSymbol},
pattern::{ArrayPatternElement, ObjectPatternElement, Pattern}, pattern::{ArrayPatternElement, ObjectPatternElement, Pattern},
Keyword, Punctuator, Span, Keyword, Punctuator, Span,
}; };
@ -467,7 +468,7 @@ where
} }
} }
if parameters.contains_yield_expression() { if contains(&parameters, ContainsSymbol::YieldExpression) {
return Err(ParseError::general( return Err(ParseError::general(
"yield expression is not allowed in formal parameter list of arrow function", "yield expression is not allowed in formal parameter list of arrow function",
start_span.start(), start_span.start(),

67
boa_engine/src/syntax/parser/expression/primary/object_initializer/mod.rs

@ -15,8 +15,8 @@ use crate::syntax::{
parser::{ parser::{
expression::{identifiers::IdentifierReference, AssignmentExpression}, expression::{identifiers::IdentifierReference, AssignmentExpression},
function::{FormalParameter, FormalParameters, FunctionBody, UniqueFormalParameters}, function::{FormalParameter, FormalParameters, FunctionBody, UniqueFormalParameters},
function_contains_super, has_direct_super, name_in_lexically_declared_names, AllowAwait, name_in_lexically_declared_names, AllowAwait, AllowIn, AllowYield, Cursor, ParseError,
AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser, ParseResult, TokenParser,
}, },
}; };
use boa_ast::{ use boa_ast::{
@ -25,6 +25,7 @@ use boa_ast::{
Identifier, Identifier,
}, },
function::{AsyncFunction, AsyncGenerator, FormalParameterList, Function, Generator}, function::{AsyncFunction, AsyncGenerator, FormalParameterList, Function, Generator},
operations::{contains, has_direct_super, ContainsSymbol},
property::{self, MethodDefinition}, property::{self, MethodDefinition},
Expression, Keyword, Punctuator, Expression, Keyword, Punctuator,
}; };
@ -191,7 +192,7 @@ where
.parse(cursor, interner)?; .parse(cursor, interner)?;
// It is a Syntax Error if HasDirectSuper of MethodDefinition is true. // It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
if has_direct_super(method.body(), method.parameters()) { if has_direct_super(&method) {
return Err(ParseError::general("invalid super usage", position)); return Err(ParseError::general("invalid super usage", position));
} }
@ -227,7 +228,7 @@ where
}; };
// It is a Syntax Error if HasDirectSuper of MethodDefinition is true. // It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
if has_direct_super(method.body(), method.parameters()) { if has_direct_super(&method) {
return Err(ParseError::general("invalid super usage", position)); return Err(ParseError::general("invalid super usage", position));
} }
@ -254,7 +255,7 @@ where
GeneratorMethod::new(self.allow_yield, self.allow_await).parse(cursor, interner)?; GeneratorMethod::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;
// It is a Syntax Error if HasDirectSuper of MethodDefinition is true. // It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
if has_direct_super(method.body(), method.parameters()) { if has_direct_super(&method) {
return Err(ParseError::general("invalid super usage", position)); return Err(ParseError::general("invalid super usage", position));
} }
@ -324,19 +325,20 @@ where
"get method definition", "get method definition",
interner, interner,
)?; )?;
let method = MethodDefinition::Get(Function::new(
None,
FormalParameterList::default(),
body,
));
// It is a Syntax Error if HasDirectSuper of MethodDefinition is true. // It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
if has_direct_super(&body, &FormalParameterList::default()) { if has_direct_super(&method) {
return Err(ParseError::general("invalid super usage", position)); return Err(ParseError::general("invalid super usage", position));
} }
Ok(property::PropertyDefinition::MethodDefinition( Ok(property::PropertyDefinition::MethodDefinition(
property_name, property_name,
MethodDefinition::Get(Function::new( method,
None,
FormalParameterList::default(),
body,
)),
)) ))
} }
// MethodDefinition[?Yield, ?Await] -> set ClassElementName[?Yield, ?Await] ( PropertySetParameterList ) { FunctionBody[~Yield, ~Await] } // MethodDefinition[?Yield, ?Await] -> set ClassElementName[?Yield, ?Await] ( PropertySetParameterList ) { FunctionBody[~Yield, ~Await] }
@ -383,8 +385,10 @@ where
))); )));
} }
let method = MethodDefinition::Set(Function::new(None, parameters, body));
// It is a Syntax Error if HasDirectSuper of MethodDefinition is true. // It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
if has_direct_super(&body, &parameters) { if has_direct_super(&method) {
return Err(ParseError::general( return Err(ParseError::general(
"invalid super usage", "invalid super usage",
params_start_position, params_start_position,
@ -393,7 +397,7 @@ where
Ok(property::PropertyDefinition::MethodDefinition( Ok(property::PropertyDefinition::MethodDefinition(
property_name, property_name,
MethodDefinition::Set(Function::new(None, parameters, body)), method,
)) ))
} }
// MethodDefinition[?Yield, ?Await] -> ClassElementName[?Yield, ?Await] ( UniqueFormalParameters[~Yield, ~Await] ) { FunctionBody[~Yield, ~Await] } // MethodDefinition[?Yield, ?Await] -> ClassElementName[?Yield, ?Await] ( UniqueFormalParameters[~Yield, ~Await] ) { FunctionBody[~Yield, ~Await] }
@ -462,8 +466,10 @@ where
} }
} }
let method = MethodDefinition::Ordinary(Function::new(None, params, body));
// It is a Syntax Error if HasDirectSuper of MethodDefinition is true. // It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
if has_direct_super(&body, &params) { if has_direct_super(&method) {
return Err(ParseError::general( return Err(ParseError::general(
"invalid super usage", "invalid super usage",
params_start_position, params_start_position,
@ -472,7 +478,7 @@ where
Ok(property::PropertyDefinition::MethodDefinition( Ok(property::PropertyDefinition::MethodDefinition(
property_name, property_name,
MethodDefinition::Ordinary(Function::new(None, params, body)), method,
)) ))
} }
} }
@ -723,17 +729,16 @@ where
body_start, body_start,
)?; )?;
if function_contains_super(&body, &params) { let method = MethodDefinition::Generator(Generator::new(None, params, body));
if contains(&method, ContainsSymbol::Super) {
return Err(ParseError::lex(LexError::Syntax( return Err(ParseError::lex(LexError::Syntax(
"invalid super usage".into(), "invalid super usage".into(),
body_start, body_start,
))); )));
} }
Ok(( Ok((class_element_name, method))
class_element_name,
MethodDefinition::Generator(Generator::new(None, params, body)),
))
} }
} }
@ -789,7 +794,7 @@ where
let params = UniqueFormalParameters::new(true, true).parse(cursor, interner)?; let params = UniqueFormalParameters::new(true, true).parse(cursor, interner)?;
// It is a Syntax Error if FormalParameters Contains YieldExpression is true. // It is a Syntax Error if FormalParameters Contains YieldExpression is true.
if params.contains_yield_expression() { if contains(&params, ContainsSymbol::YieldExpression) {
return Err(ParseError::lex(LexError::Syntax( return Err(ParseError::lex(LexError::Syntax(
"yield expression not allowed in async generator method definition parameters" "yield expression not allowed in async generator method definition parameters"
.into(), .into(),
@ -798,7 +803,7 @@ where
} }
// It is a Syntax Error if FormalParameters Contains AwaitExpression is true. // It is a Syntax Error if FormalParameters Contains AwaitExpression is true.
if params.contains_await_expression() { if contains(&params, ContainsSymbol::AwaitExpression) {
return Err(ParseError::lex(LexError::Syntax( return Err(ParseError::lex(LexError::Syntax(
"await expression not allowed in async generator method definition parameters" "await expression not allowed in async generator method definition parameters"
.into(), .into(),
@ -838,17 +843,16 @@ where
body_start, body_start,
)?; )?;
if function_contains_super(&body, &params) { let method = MethodDefinition::AsyncGenerator(AsyncGenerator::new(None, params, body));
if contains(&method, ContainsSymbol::Super) {
return Err(ParseError::lex(LexError::Syntax( return Err(ParseError::lex(LexError::Syntax(
"invalid super usage".into(), "invalid super usage".into(),
body_start, body_start,
))); )));
} }
Ok(( Ok((name, method))
name,
MethodDefinition::AsyncGenerator(AsyncGenerator::new(None, params, body)),
))
} }
} }
@ -924,17 +928,16 @@ where
body_start, body_start,
)?; )?;
if function_contains_super(&body, &params) { let method = MethodDefinition::Async(AsyncFunction::new(None, params, body));
if contains(&method, ContainsSymbol::Super) {
return Err(ParseError::lex(LexError::Syntax( return Err(ParseError::lex(LexError::Syntax(
"invalid super usage".into(), "invalid super usage".into(),
body_start, body_start,
))); )));
} }
Ok(( Ok((class_element_name, method))
class_element_name,
MethodDefinition::Async(AsyncFunction::new(None, params, body)),
))
} }
} }

57
boa_engine/src/syntax/parser/mod.rs

@ -29,7 +29,10 @@ use std::io::Read;
pub use self::error::{ParseError, ParseResult}; pub use self::error::{ParseError, ParseResult};
use boa_ast::{ use boa_ast::{
expression::Identifier, function::FormalParameterList, ContainsSymbol, Position, StatementList, expression::Identifier,
function::FormalParameterList,
operations::{contains, contains_arguments, ContainsSymbol},
Position, StatementList,
}; };
/// Trait implemented by parsers. /// Trait implemented by parsers.
@ -199,25 +202,25 @@ impl<R> Parser<R> {
_ => Flags::default(), _ => Flags::default(),
}; };
if !flags.in_function && body.contains(ContainsSymbol::NewTarget) { if !flags.in_function && contains(&body, ContainsSymbol::NewTarget) {
return Err(ParseError::general( return Err(ParseError::general(
"invalid `new.target` expression inside eval", "invalid `new.target` expression inside eval",
Position::new(1, 1), Position::new(1, 1),
)); ));
} }
if !flags.in_method && body.contains(ContainsSymbol::SuperProperty) { if !flags.in_method && contains(&body, ContainsSymbol::SuperProperty) {
return Err(ParseError::general( return Err(ParseError::general(
"invalid `super` reference inside eval", "invalid `super` reference inside eval",
Position::new(1, 1), Position::new(1, 1),
)); ));
} }
if !flags.in_derived_constructor && body.contains(ContainsSymbol::SuperCall) { if !flags.in_derived_constructor && contains(&body, ContainsSymbol::SuperCall) {
return Err(ParseError::general( return Err(ParseError::general(
"invalid `super` call inside eval", "invalid `super` call inside eval",
Position::new(1, 1), Position::new(1, 1),
)); ));
} }
if flags.in_class_field_initializer && body.contains_arguments() { if flags.in_class_field_initializer && contains_arguments(&body) {
return Err(ParseError::general( return Err(ParseError::general(
"invalid `arguments` reference inside eval", "invalid `arguments` reference inside eval",
Position::new(1, 1), Position::new(1, 1),
@ -401,9 +404,7 @@ where
// It is a Syntax Error if StatementList Contains super unless the source text containing super is eval // It is a Syntax Error if StatementList Contains super unless the source text containing super is eval
// code that is being processed by a direct eval. // code that is being processed by a direct eval.
// Additional early error rules for super within direct eval are defined in 19.2.1.1. // Additional early error rules for super within direct eval are defined in 19.2.1.1.
if node.contains(ContainsSymbol::SuperCall) if contains(node, ContainsSymbol::Super) {
|| node.contains(ContainsSymbol::SuperProperty)
{
return Err(ParseError::general( return Err(ParseError::general(
"invalid super usage", "invalid super usage",
Position::new(1, 1), Position::new(1, 1),
@ -413,7 +414,7 @@ where
// It is a Syntax Error if StatementList Contains NewTarget unless the source text containing NewTarget // It is a Syntax Error if StatementList Contains NewTarget unless the source text containing NewTarget
// is eval code that is being processed by a direct eval. // is eval code that is being processed by a direct eval.
// Additional early error rules for NewTarget in direct eval are defined in 19.2.1.1. // Additional early error rules for NewTarget in direct eval are defined in 19.2.1.1.
if node.contains(ContainsSymbol::NewTarget) { if contains(node, ContainsSymbol::NewTarget) {
return Err(ParseError::general( return Err(ParseError::general(
"invalid new.target usage", "invalid new.target usage",
Position::new(1, 1), Position::new(1, 1),
@ -426,44 +427,6 @@ where
} }
} }
// Checks if a function contains a super call or super property access.
fn function_contains_super(body: &StatementList, parameters: &FormalParameterList) -> bool {
for param in parameters.as_ref() {
if param.variable().contains(ContainsSymbol::SuperCall)
|| param.variable().contains(ContainsSymbol::SuperProperty)
{
return true;
}
}
for node in body.statements() {
if node.contains(ContainsSymbol::SuperCall) || node.contains(ContainsSymbol::SuperProperty)
{
return true;
}
}
false
}
/// Returns `true` if the function parameters or body contain a direct `super` call.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-hasdirectsuper
pub fn has_direct_super(body: &StatementList, parameters: &FormalParameterList) -> bool {
for param in parameters.as_ref() {
if param.variable().contains(ContainsSymbol::SuperCall) {
return true;
}
}
for node in body.statements() {
if node.contains(ContainsSymbol::SuperCall) {
return true;
}
}
false
}
/// Helper to check if any parameter names are declared in the given list. /// Helper to check if any parameter names are declared in the given list.
fn name_in_lexically_declared_names( fn name_in_lexically_declared_names(
parameter_list: &FormalParameterList, parameter_list: &FormalParameterList,

24
boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/mod.rs

@ -9,7 +9,6 @@ use crate::syntax::{
GeneratorMethod, LeftHandSideExpression, PropertyName, GeneratorMethod, LeftHandSideExpression, PropertyName,
}, },
function::{FormalParameters, FunctionBody, UniqueFormalParameters, FUNCTION_BREAK_TOKENS}, function::{FormalParameters, FunctionBody, UniqueFormalParameters, FUNCTION_BREAK_TOKENS},
function_contains_super, has_direct_super,
statement::StatementList, statement::StatementList,
AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, ParseResult, TokenParser, AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
}, },
@ -18,8 +17,9 @@ use boa_ast::{
self as ast, self as ast,
expression::Identifier, expression::Identifier,
function::{self, Class, FormalParameterList, Function}, function::{self, Class, FormalParameterList, Function},
operations::{contains, contains_arguments, has_direct_super, ContainsSymbol},
property::{ClassElementName, MethodDefinition}, property::{ClassElementName, MethodDefinition},
ContainsSymbol, Declaration, Expression, Keyword, Punctuator, Declaration, Expression, Keyword, Punctuator,
}; };
use boa_interner::{Interner, Sym}; use boa_interner::{Interner, Sym};
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
@ -171,7 +171,7 @@ where
if super_ref.is_none() { if super_ref.is_none() {
if let Some(constructor) = &constructor { if let Some(constructor) = &constructor {
if function_contains_super(constructor.body(), constructor.parameters()) { if contains(constructor, ContainsSymbol::Super) {
return Err(ParseError::lex(LexError::Syntax( return Err(ParseError::lex(LexError::Syntax(
"invalid super usage".into(), "invalid super usage".into(),
body_start, body_start,
@ -309,7 +309,7 @@ where
match &element { match &element {
function::ClassElement::PrivateMethodDefinition(name, method) => { function::ClassElement::PrivateMethodDefinition(name, method) => {
// It is a Syntax Error if PropName of MethodDefinition is not "constructor" and HasDirectSuper of MethodDefinition is true. // It is a Syntax Error if PropName of MethodDefinition is not "constructor" and HasDirectSuper of MethodDefinition is true.
if has_direct_super(method.body(), method.parameters()) { if has_direct_super(method) {
return Err(ParseError::lex(LexError::Syntax( return Err(ParseError::lex(LexError::Syntax(
"invalid super usage".into(), "invalid super usage".into(),
position, position,
@ -367,7 +367,7 @@ where
} }
function::ClassElement::PrivateStaticMethodDefinition(name, method) => { function::ClassElement::PrivateStaticMethodDefinition(name, method) => {
// It is a Syntax Error if HasDirectSuper of MethodDefinition is true. // It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
if has_direct_super(method.body(), method.parameters()) { if has_direct_super(method) {
return Err(ParseError::lex(LexError::Syntax( return Err(ParseError::lex(LexError::Syntax(
"invalid super usage".into(), "invalid super usage".into(),
position, position,
@ -425,7 +425,7 @@ where
} }
function::ClassElement::PrivateFieldDefinition(name, init) => { function::ClassElement::PrivateFieldDefinition(name, init) => {
if let Some(node) = init { if let Some(node) = init {
if node.contains(ContainsSymbol::SuperCall) { if contains(node, ContainsSymbol::SuperCall) {
return Err(ParseError::lex(LexError::Syntax( return Err(ParseError::lex(LexError::Syntax(
"invalid super usage".into(), "invalid super usage".into(),
position, position,
@ -444,7 +444,7 @@ where
} }
function::ClassElement::PrivateStaticFieldDefinition(name, init) => { function::ClassElement::PrivateStaticFieldDefinition(name, init) => {
if let Some(node) = init { if let Some(node) = init {
if node.contains(ContainsSymbol::SuperCall) { if contains(node, ContainsSymbol::SuperCall) {
return Err(ParseError::lex(LexError::Syntax( return Err(ParseError::lex(LexError::Syntax(
"invalid super usage".into(), "invalid super usage".into(),
position, position,
@ -467,7 +467,7 @@ where
// It is a Syntax Error if PropName of MethodDefinition is not "constructor" and HasDirectSuper of MethodDefinition is true. // It is a Syntax Error if PropName of MethodDefinition is not "constructor" and HasDirectSuper of MethodDefinition is true.
// ClassElement : static MethodDefinition: // ClassElement : static MethodDefinition:
// It is a Syntax Error if HasDirectSuper of MethodDefinition is true. // It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
if has_direct_super(method.body(), method.parameters()) { if has_direct_super(method) {
return Err(ParseError::lex(LexError::Syntax( return Err(ParseError::lex(LexError::Syntax(
"invalid super usage".into(), "invalid super usage".into(),
position, position,
@ -476,7 +476,7 @@ where
} }
function::ClassElement::FieldDefinition(_, Some(node)) function::ClassElement::FieldDefinition(_, Some(node))
| function::ClassElement::StaticFieldDefinition(_, Some(node)) => { | function::ClassElement::StaticFieldDefinition(_, Some(node)) => {
if node.contains(ContainsSymbol::SuperCall) { if contains(node, ContainsSymbol::SuperCall) {
return Err(ParseError::lex(LexError::Syntax( return Err(ParseError::lex(LexError::Syntax(
"invalid super usage".into(), "invalid super usage".into(),
position, position,
@ -1270,7 +1270,7 @@ where
| function::ClassElement::StaticFieldDefinition(_, Some(node)) | function::ClassElement::StaticFieldDefinition(_, Some(node))
| function::ClassElement::PrivateFieldDefinition(_, Some(node)) | function::ClassElement::PrivateFieldDefinition(_, Some(node))
| function::ClassElement::PrivateStaticFieldDefinition(_, Some(node)) => { | function::ClassElement::PrivateStaticFieldDefinition(_, Some(node)) => {
if node.contains_arguments() { if contains_arguments(node) {
return Err(ParseError::general( return Err(ParseError::general(
"'arguments' not allowed in class field definition", "'arguments' not allowed in class field definition",
position, position,
@ -1282,13 +1282,13 @@ where
// It is a Syntax Error if ClassStaticBlockStatementList Contains SuperCall is true. // It is a Syntax Error if ClassStaticBlockStatementList Contains SuperCall is true.
function::ClassElement::StaticBlock(block) => { function::ClassElement::StaticBlock(block) => {
for node in block.statements() { for node in block.statements() {
if node.contains_arguments() { if contains_arguments(node) {
return Err(ParseError::general( return Err(ParseError::general(
"'arguments' not allowed in class static block", "'arguments' not allowed in class static block",
position, position,
)); ));
} }
if node.contains(ContainsSymbol::SuperCall) { if contains(node, ContainsSymbol::SuperCall) {
return Err(ParseError::general("invalid super usage", position)); return Err(ParseError::general("invalid super usage", position));
} }
} }

12
boa_engine/src/syntax/parser/statement/declaration/hoistable/mod.rs

@ -24,14 +24,16 @@ use crate::syntax::{
parser::{ parser::{
expression::BindingIdentifier, expression::BindingIdentifier,
function::{FormalParameters, FunctionBody}, function::{FormalParameters, FunctionBody},
function_contains_super, name_in_lexically_declared_names, name_in_lexically_declared_names,
statement::LexError, statement::LexError,
AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, ParseResult, TokenParser, AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
}, },
}; };
use boa_ast::{ use boa_ast::{
expression::Identifier, function::FormalParameterList, Declaration, Keyword, Position, expression::Identifier,
Punctuator, StatementList, function::FormalParameterList,
operations::{contains, ContainsSymbol},
Declaration, Keyword, Position, Punctuator, StatementList,
}; };
use boa_interner::{Interner, Sym}; use boa_interner::{Interner, Sym};
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -134,7 +136,7 @@ trait CallableDeclaration {
fn body_allow_await(&self) -> bool; fn body_allow_await(&self) -> bool;
} }
// This is a helper function to not duplicate code in the individual callable deceleration parsers. // This is a helper function to not duplicate code in the individual callable declaration parsers.
#[inline] #[inline]
fn parse_callable_declaration<R: Read, C: CallableDeclaration>( fn parse_callable_declaration<R: Read, C: CallableDeclaration>(
c: &C, c: &C,
@ -216,7 +218,7 @@ fn parse_callable_declaration<R: Read, C: CallableDeclaration>(
params_start_position, params_start_position,
)?; )?;
if function_contains_super(&body, &params) { if contains(&body, ContainsSymbol::Super) || contains(&params, ContainsSymbol::Super) {
return Err(ParseError::lex(LexError::Syntax( return Err(ParseError::lex(LexError::Syntax(
"invalid super usage".into(), "invalid super usage".into(),
params_start_position, params_start_position,

4
boa_engine/src/value/mod.rs

@ -992,13 +992,13 @@ impl JsValue {
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-isarray /// [spec]: https://tc39.es/ecma262/#sec-isarray
pub(crate) fn is_array(&self, context: &mut Context) -> JsResult<bool> { pub(crate) fn is_array(&self) -> JsResult<bool> {
// Note: The spec specifies this function for JsValue. // Note: The spec specifies this function for JsValue.
// The main part of the function is implemented for JsObject. // The main part of the function is implemented for JsObject.
// 1. If Type(argument) is not Object, return false. // 1. If Type(argument) is not Object, return false.
if let Some(object) = self.as_object() { if let Some(object) = self.as_object() {
object.is_array_abstract(context) object.is_array_abstract()
} }
// 4. Return false. // 4. Return false.
else { else {

12
boa_engine/src/vm/code_block.rs

@ -521,6 +521,7 @@ pub(crate) fn create_function_object(
Function::Async { Function::Async {
code, code,
environments: context.realm.environments.clone(), environments: context.realm.environments.clone(),
home_object: None,
promise_capability, promise_capability,
} }
} else { } else {
@ -626,6 +627,7 @@ pub(crate) fn create_generator_function_object(
let function = Function::AsyncGenerator { let function = Function::AsyncGenerator {
code, code,
environments: context.realm.environments.clone(), environments: context.realm.environments.clone(),
home_object: None,
}; };
JsObject::from_proto_and_data( JsObject::from_proto_and_data(
function_prototype, function_prototype,
@ -635,6 +637,7 @@ pub(crate) fn create_generator_function_object(
let function = Function::Generator { let function = Function::Generator {
code, code,
environments: context.realm.environments.clone(), environments: context.realm.environments.clone(),
home_object: None,
}; };
JsObject::from_proto_and_data(function_prototype, ObjectData::generator_function(function)) JsObject::from_proto_and_data(function_prototype, ObjectData::generator_function(function))
}; };
@ -831,6 +834,7 @@ impl JsObject {
code, code,
environments, environments,
promise_capability, promise_capability,
..
} => { } => {
let code = code.clone(); let code = code.clone();
let mut environments = environments.clone(); let mut environments = environments.clone();
@ -949,7 +953,9 @@ impl JsObject {
Ok(promise.into()) Ok(promise.into())
} }
Function::Generator { code, environments } => { Function::Generator {
code, environments, ..
} => {
let code = code.clone(); let code = code.clone();
let mut environments = environments.clone(); let mut environments = environments.clone();
drop(object); drop(object);
@ -1084,7 +1090,9 @@ impl JsObject {
Ok(generator.into()) Ok(generator.into())
} }
Function::AsyncGenerator { code, environments } => { Function::AsyncGenerator {
code, environments, ..
} => {
let code = code.clone(); let code = code.clone();
let mut environments = environments.clone(); let mut environments = environments.clone();
drop(object); drop(object);

2
boa_tester/src/exec/mod.rs

@ -379,7 +379,7 @@ impl Test {
for include in self.includes.iter() { for include in self.includes.iter() {
context context
.eval( .eval(
&harness harness
.includes .includes
.get(include) .get(include)
.ok_or_else(|| format!("could not find the {include} include file."))? .ok_or_else(|| format!("could not find the {include} include file."))?

2
boa_tester/src/results.rs

@ -187,7 +187,7 @@ fn update_gh_pages_repo(path: &Path, verbose: u8) {
// We run the command to pull the gh-pages branch: git -C ../gh-pages/ pull origin // We run the command to pull the gh-pages branch: git -C ../gh-pages/ pull origin
Command::new("git") Command::new("git")
.args(&["-C", "../gh-pages", "pull", "--ff-only"]) .args(["-C", "../gh-pages", "pull", "--ff-only"])
.output() .output()
.expect("could not update GitHub Pages"); .expect("could not update GitHub Pages");

Loading…
Cancel
Save