diff --git a/boa/src/context.rs b/boa/src/context.rs index b1e21eb7601..b0585d1289c 100644 --- a/boa/src/context.rs +++ b/boa/src/context.rs @@ -18,7 +18,7 @@ use crate::{ statement_list::RcStatementList, Call, FormalParameter, Identifier, New, StatementList, }, - Const, Node, + Const, Node, NodeKind, Span, }, Parser, }, @@ -336,8 +336,8 @@ impl Context { { // Runs a `new RangeError(message)`. New::from(Call::new( - Identifier::from("RangeError"), - vec![Const::from(message.into()).into()], + Node::new(Identifier::from("RangeError"), Span::default()), + vec![Node::new(Const::from(message.into()), Span::default())], )) .run(self) .expect("Into used as message") @@ -360,8 +360,8 @@ impl Context { { // Runs a `new TypeError(message)`. New::from(Call::new( - Identifier::from("TypeError"), - vec![Const::from(message.into()).into()], + Node::new(Identifier::from("TypeError"), Span::default()), + vec![Node::new(Const::from(message.into()), Span::default())], )) .run(self) .expect("Into used as message") @@ -383,8 +383,8 @@ impl Context { M: Into>, { New::from(Call::new( - Identifier::from("ReferenceError"), - vec![Const::from(message.into()).into()], + Node::new(Identifier::from("ReferenceError"), Span::default()), + vec![Node::new(Const::from(message.into()), Span::default())], )) .run(self) .expect("Into used as message") @@ -406,8 +406,8 @@ impl Context { M: Into>, { New::from(Call::new( - Identifier::from("SyntaxError"), - vec![Const::from(message.into()).into()], + Node::new(Identifier::from("SyntaxError"), Span::default()), + vec![Node::new(Const::from(message.into()), Span::default())], )) .run(self) .expect("Into used as message") @@ -428,8 +428,8 @@ impl Context { M: Into>, { New::from(Call::new( - Identifier::from("EvalError"), - vec![Const::from(message.into()).into()], + Node::new(Identifier::from("EvalError"), Span::default()), + vec![Node::new(Const::from(message.into()), Span::default())], )) .run(self) .expect("Into used as message") @@ -441,8 +441,8 @@ impl Context { M: Into>, { New::from(Call::new( - Identifier::from("URIError"), - vec![Const::from(message.into()).into()], + Node::new(Identifier::from("URIError"), Span::default()), + vec![Node::new(Const::from(message.into()), Span::default())], )) .run(self) .expect("Into used as message") @@ -465,15 +465,14 @@ impl Context { } /// Utility to create a function Value for Function Declarations, Arrow Functions or Function Expressions - pub(crate) fn create_function( + pub(crate) fn create_function

( &mut self, params: P, - body: B, + body: StatementList, flags: FunctionFlags, ) -> Result where P: Into>, - B: Into, { let function_prototype: Value = self.standard_objects().function_object().prototype().into(); @@ -485,7 +484,7 @@ impl Context { let params_len = params.len(); let func = Function::Ordinary { flags, - body: RcStatementList::from(body.into()), + body: RcStatementList::from(body), params, environment: self.get_current_environment().clone(), }; @@ -549,16 +548,16 @@ impl Context { #[inline] pub(crate) fn set_value(&mut self, node: &Node, value: Value) -> Result { - match node { - Node::Identifier(ref name) => { + match node.kind() { + NodeKind::Identifier(ref name) => { self.set_mutable_binding(name.as_ref(), value.clone(), true)?; Ok(value) } - Node::GetConstField(ref get_const_field_node) => Ok(get_const_field_node + NodeKind::GetConstField(ref get_const_field_node) => Ok(get_const_field_node .obj() .run(self)? .set_field(get_const_field_node.field(), value, self)?), - Node::GetField(ref get_field) => { + NodeKind::GetField(ref get_field) => { let field = get_field.field().run(self)?; let key = field.to_property_key(self)?; Ok(get_field.obj().run(self)?.set_field(key, value, self)?) diff --git a/boa/src/syntax/ast/mod.rs b/boa/src/syntax/ast/mod.rs index b768c092460..91deb8ca080 100644 --- a/boa/src/syntax/ast/mod.rs +++ b/boa/src/syntax/ast/mod.rs @@ -10,7 +10,7 @@ pub mod punctuator; pub use self::{ constant::Const, keyword::Keyword, - node::Node, + node::{Node, NodeKind}, position::{Position, Span}, punctuator::Punctuator, }; diff --git a/boa/src/syntax/ast/node/array/mod.rs b/boa/src/syntax/ast/node/array/mod.rs index 8bb7f5ac32e..d6b8b8512d8 100644 --- a/boa/src/syntax/ast/node/array/mod.rs +++ b/boa/src/syntax/ast/node/array/mod.rs @@ -1,6 +1,6 @@ //! Array declaration node. -use super::{join_nodes, Node}; +use super::{join_nodes, Node, NodeKind}; use crate::{ builtins::{iterable, Array}, exec::Executable, @@ -40,8 +40,8 @@ impl Executable for ArrayDecl { let _timer = BoaProfiler::global().start_event("ArrayDecl", "exec"); let array = Array::new_array(context); let mut elements = Vec::new(); - for elem in self.as_ref() { - if let Node::Spread(ref x) = elem { + for elem in self.arr.iter().map(Node::kind) { + if let NodeKind::Spread(ref x) = elem { let val = x.run(context)?; let iterator_record = iterable::get_iterator(context, val)?; // TODO after proper internal Array representation as per https://github.com/boa-dev/boa/pull/811#discussion_r502460858 @@ -89,7 +89,7 @@ impl fmt::Display for ArrayDecl { } } -impl From for Node { +impl From for NodeKind { fn from(arr: ArrayDecl) -> Self { Self::ArrayDecl(arr) } diff --git a/boa/src/syntax/ast/node/await_expr/mod.rs b/boa/src/syntax/ast/node/await_expr/mod.rs index f527dd0f067..ba2fbfb89e7 100644 --- a/boa/src/syntax/ast/node/await_expr/mod.rs +++ b/boa/src/syntax/ast/node/await_expr/mod.rs @@ -1,6 +1,6 @@ //! Await expression node. -use super::Node; +use super::{Node, NodeKind}; use crate::{exec::Executable, BoaProfiler, Context, Result, Value}; use gc::{Finalize, Trace}; use std::fmt; @@ -54,7 +54,7 @@ impl fmt::Display for AwaitExpr { } } -impl From for Node { +impl From for NodeKind { fn from(awaitexpr: AwaitExpr) -> Self { Self::AwaitExpr(awaitexpr) } diff --git a/boa/src/syntax/ast/node/block/mod.rs b/boa/src/syntax/ast/node/block/mod.rs index 39c19f88e1d..e45cd379dd9 100644 --- a/boa/src/syntax/ast/node/block/mod.rs +++ b/boa/src/syntax/ast/node/block/mod.rs @@ -1,6 +1,6 @@ //! Block AST node. -use super::{Node, StatementList}; +use super::{Node, NodeKind, StatementList}; use crate::{ environment::declarative_environment_record::DeclarativeEnvironmentRecord, exec::Executable, @@ -114,7 +114,7 @@ impl fmt::Display for Block { } } -impl From for Node { +impl From for NodeKind { fn from(block: Block) -> Self { Self::Block(block) } diff --git a/boa/src/syntax/ast/node/break_node/mod.rs b/boa/src/syntax/ast/node/break_node/mod.rs index 48b6108f78e..ce990a6c447 100644 --- a/boa/src/syntax/ast/node/break_node/mod.rs +++ b/boa/src/syntax/ast/node/break_node/mod.rs @@ -1,4 +1,4 @@ -use super::Node; +use super::NodeKind; use crate::{ exec::Executable, exec::InterpreterState, @@ -75,8 +75,8 @@ impl fmt::Display for Break { } } -impl From for Node { - fn from(break_smt: Break) -> Node { +impl From for NodeKind { + fn from(break_smt: Break) -> Self { Self::Break(break_smt) } } diff --git a/boa/src/syntax/ast/node/call/mod.rs b/boa/src/syntax/ast/node/call/mod.rs index d6f1b6eba36..9a48c2d5297 100644 --- a/boa/src/syntax/ast/node/call/mod.rs +++ b/boa/src/syntax/ast/node/call/mod.rs @@ -3,7 +3,7 @@ use crate::{ exec::Executable, exec::InterpreterState, gc::{Finalize, Trace}, - syntax::ast::node::{join_nodes, Node}, + syntax::ast::node::{join_nodes, Node, NodeKind}, value::{Type, Value}, BoaProfiler, Context, Result, }; @@ -60,8 +60,8 @@ impl Call { impl Executable for Call { fn run(&self, context: &mut Context) -> Result { let _timer = BoaProfiler::global().start_event("Call", "exec"); - let (this, func) = match self.expr() { - Node::GetConstField(ref get_const_field) => { + let (this, func) = match self.expr().kind() { + NodeKind::GetConstField(ref get_const_field) => { let mut obj = get_const_field.obj().run(context)?; if obj.get_type() != Type::Object { obj = Value::Object(obj.to_object(context)?); @@ -71,7 +71,7 @@ impl Executable for Call { obj.get_field(get_const_field.field(), context)?, ) } - Node::GetField(ref get_field) => { + NodeKind::GetField(ref get_field) => { let mut obj = get_field.obj().run(context)?; if obj.get_type() != Type::Object { obj = Value::Object(obj.to_object(context)?); @@ -89,8 +89,8 @@ impl Executable for Call { ), }; let mut v_args = Vec::with_capacity(self.args().len()); - for arg in self.args() { - if let Node::Spread(ref x) = arg { + for arg in self.args().iter().map(Node::kind) { + if let NodeKind::Spread(ref x) = arg { let val = x.run(context)?; let iterator_record = iterable::get_iterator(context, val)?; loop { @@ -127,7 +127,7 @@ impl fmt::Display for Call { } } -impl From for Node { +impl From for NodeKind { fn from(call: Call) -> Self { Self::Call(call) } diff --git a/boa/src/syntax/ast/node/conditional/conditional_op/mod.rs b/boa/src/syntax/ast/node/conditional/conditional_op/mod.rs index f6c07f14e78..2ebfa97d756 100644 --- a/boa/src/syntax/ast/node/conditional/conditional_op/mod.rs +++ b/boa/src/syntax/ast/node/conditional/conditional_op/mod.rs @@ -1,7 +1,7 @@ use crate::{ exec::Executable, gc::{Finalize, Trace}, - syntax::ast::node::Node, + syntax::ast::node::{Node, NodeKind}, Context, Result, Value, }; use std::fmt; @@ -81,8 +81,8 @@ impl fmt::Display for ConditionalOp { } } -impl From for Node { - fn from(cond_op: ConditionalOp) -> Node { +impl From for NodeKind { + fn from(cond_op: ConditionalOp) -> Self { Self::ConditionalOp(cond_op) } } diff --git a/boa/src/syntax/ast/node/conditional/if_node/mod.rs b/boa/src/syntax/ast/node/conditional/if_node/mod.rs index 63906833a80..ffc63bcf08b 100644 --- a/boa/src/syntax/ast/node/conditional/if_node/mod.rs +++ b/boa/src/syntax/ast/node/conditional/if_node/mod.rs @@ -1,7 +1,7 @@ use crate::{ exec::Executable, gc::{Finalize, Trace}, - syntax::ast::node::Node, + syntax::ast::node::{Node, NodeKind}, Context, Result, Value, }; use std::fmt; @@ -96,8 +96,8 @@ impl fmt::Display for If { } } -impl From for Node { - fn from(if_stm: If) -> Node { +impl From for NodeKind { + fn from(if_stm: If) -> Self { Self::If(if_stm) } } diff --git a/boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs b/boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs index 6646a9542dd..3c5b577202f 100644 --- a/boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs +++ b/boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs @@ -2,7 +2,7 @@ use crate::{ builtins::function::FunctionFlags, exec::Executable, gc::{Finalize, Trace}, - syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList}, + syntax::ast::node::{join_nodes, FormalParameter, Node, NodeKind, StatementList}, Context, Result, Value, }; use std::fmt; @@ -69,8 +69,8 @@ impl ArrowFunctionDecl { impl Executable for ArrowFunctionDecl { fn run(&self, context: &mut Context) -> Result { context.create_function( - self.params().to_vec(), - self.body().to_vec(), + self.params.clone(), + self.body.clone(), FunctionFlags::CALLABLE | FunctionFlags::CONSTRUCTABLE | FunctionFlags::LEXICAL_THIS_MODE, @@ -84,7 +84,7 @@ impl fmt::Display for ArrowFunctionDecl { } } -impl From for Node { +impl From for NodeKind { fn from(decl: ArrowFunctionDecl) -> Self { Self::ArrowFunctionDecl(decl) } diff --git a/boa/src/syntax/ast/node/declaration/async_function_decl/mod.rs b/boa/src/syntax/ast/node/declaration/async_function_decl/mod.rs index 5078b5b2940..7c2ba73ce31 100644 --- a/boa/src/syntax/ast/node/declaration/async_function_decl/mod.rs +++ b/boa/src/syntax/ast/node/declaration/async_function_decl/mod.rs @@ -2,7 +2,7 @@ use crate::{ exec::Executable, - syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList}, + syntax::ast::node::{join_nodes, FormalParameter, Node, NodeKind, StatementList}, BoaProfiler, Context, Result, Value, }; use gc::{Finalize, Trace}; @@ -88,7 +88,7 @@ impl Executable for AsyncFunctionDecl { } } -impl From for Node { +impl From for NodeKind { fn from(decl: AsyncFunctionDecl) -> Self { Self::AsyncFunctionDecl(decl) } diff --git a/boa/src/syntax/ast/node/declaration/async_function_expr/mod.rs b/boa/src/syntax/ast/node/declaration/async_function_expr/mod.rs index 056976f9619..d44f336f6e1 100644 --- a/boa/src/syntax/ast/node/declaration/async_function_expr/mod.rs +++ b/boa/src/syntax/ast/node/declaration/async_function_expr/mod.rs @@ -2,7 +2,7 @@ use crate::{ exec::Executable, - syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList}, + syntax::ast::node::{join_nodes, FormalParameter, Node, NodeKind, StatementList}, Context, Result, Value, }; use gc::{Finalize, Trace}; @@ -91,7 +91,7 @@ impl fmt::Display for AsyncFunctionExpr { } } -impl From for Node { +impl From for NodeKind { fn from(expr: AsyncFunctionExpr) -> Self { Self::AsyncFunctionExpr(expr) } diff --git a/boa/src/syntax/ast/node/declaration/function_decl/mod.rs b/boa/src/syntax/ast/node/declaration/function_decl/mod.rs index e3b68bc7670..3ee5d965662 100644 --- a/boa/src/syntax/ast/node/declaration/function_decl/mod.rs +++ b/boa/src/syntax/ast/node/declaration/function_decl/mod.rs @@ -3,7 +3,7 @@ use crate::{ environment::lexical_environment::VariableScope, exec::Executable, gc::{Finalize, Trace}, - syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList}, + syntax::ast::node::{join_nodes, FormalParameter, Node, NodeKind, StatementList}, BoaProfiler, Context, Result, Value, }; use std::fmt; @@ -87,8 +87,8 @@ impl Executable for FunctionDecl { fn run(&self, context: &mut Context) -> Result { let _timer = BoaProfiler::global().start_event("FunctionDecl", "exec"); let val = context.create_function( - self.parameters().to_vec(), - self.body().to_vec(), + self.parameters.clone(), + self.body.clone(), FunctionFlags::CALLABLE | FunctionFlags::CONSTRUCTABLE, )?; @@ -110,7 +110,7 @@ impl Executable for FunctionDecl { } } -impl From for Node { +impl From for NodeKind { fn from(decl: FunctionDecl) -> Self { Self::FunctionDecl(decl) } diff --git a/boa/src/syntax/ast/node/declaration/function_expr/mod.rs b/boa/src/syntax/ast/node/declaration/function_expr/mod.rs index 6f56d54cf6c..7179fedfff9 100644 --- a/boa/src/syntax/ast/node/declaration/function_expr/mod.rs +++ b/boa/src/syntax/ast/node/declaration/function_expr/mod.rs @@ -2,7 +2,7 @@ use crate::{ builtins::function::FunctionFlags, exec::Executable, gc::{Finalize, Trace}, - syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList}, + syntax::ast::node::{join_nodes, FormalParameter, Node, NodeKind, StatementList}, Context, Result, Value, }; use std::fmt; @@ -87,8 +87,8 @@ impl FunctionExpr { impl Executable for FunctionExpr { fn run(&self, context: &mut Context) -> Result { let val = context.create_function( - self.parameters().to_vec(), - self.body().to_vec(), + self.parameters.clone(), + self.body.clone(), FunctionFlags::CALLABLE | FunctionFlags::CONSTRUCTABLE, )?; @@ -106,7 +106,7 @@ impl fmt::Display for FunctionExpr { } } -impl From for Node { +impl From for NodeKind { fn from(expr: FunctionExpr) -> Self { Self::FunctionExpr(expr) } diff --git a/boa/src/syntax/ast/node/declaration/mod.rs b/boa/src/syntax/ast/node/declaration/mod.rs index 4f375ceb6d0..9e9a583b32d 100644 --- a/boa/src/syntax/ast/node/declaration/mod.rs +++ b/boa/src/syntax/ast/node/declaration/mod.rs @@ -3,7 +3,7 @@ use crate::{ environment::lexical_environment::VariableScope, exec::Executable, gc::{Finalize, Trace}, - syntax::ast::node::{join_nodes, Identifier, Node}, + syntax::ast::node::{join_nodes, Identifier, Node, NodeKind}, Context, Result, Value, }; use std::fmt; @@ -171,13 +171,13 @@ impl fmt::Display for DeclarationList { } } -impl From for Node { +impl From for NodeKind { fn from(list: DeclarationList) -> Self { use DeclarationList::*; match &list { - Let(_) => Node::LetDeclList(list), - Const(_) => Node::ConstDeclList(list), - Var(_) => Node::VarDeclList(list), + Let(_) => Self::LetDeclList(list), + Const(_) => Self::ConstDeclList(list), + Var(_) => Self::VarDeclList(list), } } } diff --git a/boa/src/syntax/ast/node/field/get_const_field/mod.rs b/boa/src/syntax/ast/node/field/get_const_field/mod.rs index c5b1de5dbe7..59c0eb0a663 100644 --- a/boa/src/syntax/ast/node/field/get_const_field/mod.rs +++ b/boa/src/syntax/ast/node/field/get_const_field/mod.rs @@ -1,7 +1,7 @@ use crate::{ exec::Executable, gc::{Finalize, Trace}, - syntax::ast::node::Node, + syntax::ast::node::{Node, NodeKind}, value::{Type, Value}, Context, Result, }; @@ -79,7 +79,7 @@ impl fmt::Display for GetConstField { } } -impl From for Node { +impl From for NodeKind { fn from(get_const_field: GetConstField) -> Self { Self::GetConstField(get_const_field) } diff --git a/boa/src/syntax/ast/node/field/get_field/mod.rs b/boa/src/syntax/ast/node/field/get_field/mod.rs index 4fc5fbcb2da..c028aebd52d 100644 --- a/boa/src/syntax/ast/node/field/get_field/mod.rs +++ b/boa/src/syntax/ast/node/field/get_field/mod.rs @@ -1,7 +1,7 @@ use crate::{ exec::Executable, gc::{Finalize, Trace}, - syntax::ast::node::Node, + syntax::ast::node::{Node, NodeKind}, value::{Type, Value}, Context, Result, }; @@ -79,7 +79,7 @@ impl fmt::Display for GetField { } } -impl From for Node { +impl From for NodeKind { fn from(get_field: GetField) -> Self { Self::GetField(get_field) } diff --git a/boa/src/syntax/ast/node/identifier/mod.rs b/boa/src/syntax/ast/node/identifier/mod.rs index ca595d460e8..e4d4aa81764 100644 --- a/boa/src/syntax/ast/node/identifier/mod.rs +++ b/boa/src/syntax/ast/node/identifier/mod.rs @@ -3,7 +3,7 @@ use crate::{ exec::Executable, gc::{Finalize, Trace}, - syntax::ast::node::Node, + syntax::ast::node::NodeKind, Context, Result, Value, }; use std::fmt; @@ -61,7 +61,7 @@ where } } -impl From for Node { +impl From for NodeKind { fn from(local: Identifier) -> Self { Self::Identifier(local) } diff --git a/boa/src/syntax/ast/node/iteration/continue_node/mod.rs b/boa/src/syntax/ast/node/iteration/continue_node/mod.rs index f42864339dd..60a81900294 100644 --- a/boa/src/syntax/ast/node/iteration/continue_node/mod.rs +++ b/boa/src/syntax/ast/node/iteration/continue_node/mod.rs @@ -1,7 +1,7 @@ use crate::{ exec::{Executable, InterpreterState}, gc::{Finalize, Trace}, - syntax::ast::node::Node, + syntax::ast::node::NodeKind, Context, Result, Value, }; use std::fmt; @@ -69,8 +69,8 @@ impl fmt::Display for Continue { } } -impl From for Node { - fn from(cont: Continue) -> Node { +impl From for NodeKind { + fn from(cont: Continue) -> Self { Self::Continue(cont) } } diff --git a/boa/src/syntax/ast/node/iteration/do_while_loop/mod.rs b/boa/src/syntax/ast/node/iteration/do_while_loop/mod.rs index 19a828d6588..05ce0424e7d 100644 --- a/boa/src/syntax/ast/node/iteration/do_while_loop/mod.rs +++ b/boa/src/syntax/ast/node/iteration/do_while_loop/mod.rs @@ -1,7 +1,7 @@ use crate::{ exec::{Executable, InterpreterState}, gc::{Finalize, Trace}, - syntax::ast::node::Node, + syntax::ast::node::{Node, NodeKind}, Context, Result, Value, }; use std::fmt; @@ -104,7 +104,7 @@ impl fmt::Display for DoWhileLoop { } } -impl From for Node { +impl From for NodeKind { fn from(do_while: DoWhileLoop) -> Self { Self::DoWhileLoop(do_while) } diff --git a/boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs b/boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs index 1b2e53988bb..9fcc76c6e2f 100644 --- a/boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs +++ b/boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs @@ -6,7 +6,7 @@ use crate::{ }, exec::{Executable, InterpreterState}, gc::{Finalize, Trace}, - syntax::ast::node::Node, + syntax::ast::node::{Node, NodeKind}, BoaProfiler, Context, Result, Value, }; use std::fmt; @@ -71,8 +71,8 @@ impl fmt::Display for ForInLoop { } } -impl From for Node { - fn from(for_in: ForInLoop) -> Node { +impl From for NodeKind { + fn from(for_in: ForInLoop) -> Self { Self::ForInLoop(for_in) } } @@ -106,8 +106,8 @@ impl Executable for ForInLoop { } let next_result = iterator_result.value(); - match self.variable() { - Node::Identifier(ref name) => { + match self.variable().kind() { + NodeKind::Identifier(ref name) => { if context.has_binding(name.as_ref()) { // Binding already exists context.set_mutable_binding(name.as_ref(), next_result.clone(), true)?; @@ -120,7 +120,7 @@ impl Executable for ForInLoop { context.initialize_binding(name.as_ref(), next_result.clone())?; } } - Node::VarDeclList(ref list) => match list.as_ref() { + NodeKind::VarDeclList(ref list) => match list.as_ref() { [var] => { if var.init().is_some() { return context.throw_syntax_error("a declaration in the head of a for-in loop can't have an initializer"); @@ -143,7 +143,7 @@ impl Executable for ForInLoop { ) } }, - Node::LetDeclList(ref list) => match list.as_ref() { + NodeKind::LetDeclList(ref list) => match list.as_ref() { [var] => { if var.init().is_some() { return context.throw_syntax_error("a declaration in the head of a for-in loop can't have an initializer"); @@ -162,7 +162,7 @@ impl Executable for ForInLoop { ) } }, - Node::ConstDeclList(ref list) => match list.as_ref() { + NodeKind::ConstDeclList(ref list) => match list.as_ref() { [var] => { if var.init().is_some() { return context.throw_syntax_error("a declaration in the head of a for-in loop can't have an initializer"); @@ -181,7 +181,7 @@ impl Executable for ForInLoop { ) } }, - Node::Assign(_) => { + NodeKind::Assign(_) => { return context.throw_syntax_error( "a declaration in the head of a for-in loop can't have an initializer", ); diff --git a/boa/src/syntax/ast/node/iteration/for_loop/mod.rs b/boa/src/syntax/ast/node/iteration/for_loop/mod.rs index dd690273549..cfa6b7d1c6e 100644 --- a/boa/src/syntax/ast/node/iteration/for_loop/mod.rs +++ b/boa/src/syntax/ast/node/iteration/for_loop/mod.rs @@ -2,7 +2,7 @@ use crate::{ environment::declarative_environment_record::DeclarativeEnvironmentRecord, exec::{Executable, InterpreterState}, gc::{Finalize, Trace}, - syntax::ast::node::Node, + syntax::ast::node::{Node, NodeKind}, BoaProfiler, Context, Result, Value, }; use std::fmt; @@ -153,7 +153,7 @@ impl fmt::Display for ForLoop { } } -impl From for Node { +impl From for NodeKind { fn from(for_loop: ForLoop) -> Self { Self::ForLoop(for_loop) } diff --git a/boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs b/boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs index 9db251e91ae..e53ccf20e9f 100644 --- a/boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs +++ b/boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs @@ -6,7 +6,7 @@ use crate::{ }, exec::{Executable, InterpreterState}, gc::{Finalize, Trace}, - syntax::ast::node::Node, + syntax::ast::node::{Node, NodeKind}, BoaProfiler, Context, Result, Value, }; use std::fmt; @@ -71,8 +71,8 @@ impl fmt::Display for ForOfLoop { } } -impl From for Node { - fn from(for_of: ForOfLoop) -> Node { +impl From for NodeKind { + fn from(for_of: ForOfLoop) -> Self { Self::ForOfLoop(for_of) } } @@ -96,8 +96,8 @@ impl Executable for ForOfLoop { } let next_result = iterator_result.value(); - match self.variable() { - Node::Identifier(ref name) => { + match self.variable().kind() { + NodeKind::Identifier(ref name) => { if context.has_binding(name.as_ref()) { // Binding already exists context.set_mutable_binding(name.as_ref(), next_result.clone(), true)?; @@ -110,7 +110,7 @@ impl Executable for ForOfLoop { context.initialize_binding(name.as_ref(), next_result.clone())?; } } - Node::VarDeclList(ref list) => match list.as_ref() { + NodeKind::VarDeclList(ref list) => match list.as_ref() { [var] => { if var.init().is_some() { return context.throw_syntax_error("a declaration in the head of a for-of loop can't have an initializer"); @@ -133,7 +133,7 @@ impl Executable for ForOfLoop { ) } }, - Node::LetDeclList(ref list) => match list.as_ref() { + NodeKind::LetDeclList(ref list) => match list.as_ref() { [var] => { if var.init().is_some() { return context.throw_syntax_error("a declaration in the head of a for-of loop can't have an initializer"); @@ -153,7 +153,7 @@ impl Executable for ForOfLoop { ) } }, - Node::ConstDeclList(ref list) => match list.as_ref() { + NodeKind::ConstDeclList(ref list) => match list.as_ref() { [var] => { if var.init().is_some() { return context.throw_syntax_error("a declaration in the head of a for-of loop can't have an initializer"); @@ -172,7 +172,7 @@ impl Executable for ForOfLoop { ) } }, - Node::Assign(_) => { + NodeKind::Assign(_) => { return context.throw_syntax_error( "a declaration in the head of a for-of loop can't have an initializer", ); diff --git a/boa/src/syntax/ast/node/iteration/while_loop/mod.rs b/boa/src/syntax/ast/node/iteration/while_loop/mod.rs index bc4c3d23bde..ef0738ee01d 100644 --- a/boa/src/syntax/ast/node/iteration/while_loop/mod.rs +++ b/boa/src/syntax/ast/node/iteration/while_loop/mod.rs @@ -1,7 +1,7 @@ use crate::{ exec::{Executable, InterpreterState}, gc::{Finalize, Trace}, - syntax::ast::node::Node, + syntax::ast::node::{Node, NodeKind}, Context, Result, Value, }; use std::fmt; @@ -99,7 +99,7 @@ impl fmt::Display for WhileLoop { } } -impl From for Node { +impl From for NodeKind { fn from(while_loop: WhileLoop) -> Self { Self::WhileLoop(while_loop) } diff --git a/boa/src/syntax/ast/node/mod.rs b/boa/src/syntax/ast/node/mod.rs index 6f38bb22ed0..c3b3fc1e486 100644 --- a/boa/src/syntax/ast/node/mod.rs +++ b/boa/src/syntax/ast/node/mod.rs @@ -46,7 +46,7 @@ pub use self::{ throw::Throw, try_node::{Catch, Finally, Try}, }; -use super::Const; +use super::{Const, Span}; use crate::{ exec::Executable, gc::{empty_trace, Finalize, Trace}, @@ -60,9 +60,70 @@ use std::{ #[cfg(feature = "deser")] use serde::{Deserialize, Serialize}; +/// An AST node. #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] -pub enum Node { +pub struct Node { + kind: NodeKind, + span: Span, +} + +impl Node { + /// Creates a new node. + #[inline] + pub fn new(kind: K, span: Span) -> Self + where + K: Into, + { + Self { + kind: kind.into(), + span, + } + } + + /// Retrieves the kind of node. + #[inline] + pub fn kind(&self) -> &NodeKind { + &self.kind + } + + /// Retrieves the span of the node. + #[inline] + pub fn span(&self) -> Span { + self.span + } + + /// Implements the display formatting with indentation. + #[inline] + fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result { + self.kind.display(f, indentation) + } + + /// Returns a node ordering based on the hoistability of each node. + #[inline] + pub(crate) fn hoistable_order(a: &Node, b: &Node) -> Ordering { + NodeKind::hoistable_order(a.kind(), b.kind()) + } +} + +impl Executable for Node { + #[inline] + fn run(&self, context: &mut Context) -> Result { + self.kind.run(context) + } +} + +impl Display for Node { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.kind.display(f, 0) + } +} + +/// The kind of node, with all the relevant information. +#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[derive(Clone, Debug, Trace, Finalize, PartialEq)] +pub enum NodeKind { /// Array declaration node. [More information](./array/struct.ArrayDecl.html). ArrayDecl(ArrayDecl), @@ -210,30 +271,44 @@ pub enum Node { Empty, } -impl Display for Node { +impl Display for NodeKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.display(f, 0) } } -impl From for Node { +impl From for NodeKind { fn from(c: Const) -> Self { Self::Const(c) } } -impl Node { +impl NodeKind { /// Returns a node ordering based on the hoistability of each node. - pub(crate) fn hoistable_order(a: &Node, b: &Node) -> Ordering { + pub(crate) fn hoistable_order(a: &NodeKind, b: &NodeKind) -> Ordering { match (a, b) { - (Node::FunctionDecl(_), Node::FunctionDecl(_)) => Ordering::Equal, - (_, Node::FunctionDecl(_)) => Ordering::Greater, - (Node::FunctionDecl(_), _) => Ordering::Less, - + (NodeKind::FunctionDecl(_), NodeKind::FunctionDecl(_)) => Ordering::Equal, + (_, NodeKind::FunctionDecl(_)) => Ordering::Greater, + (NodeKind::FunctionDecl(_), _) => Ordering::Less, (_, _) => Ordering::Equal, } } + /// Returns true if as per the [spec][spec] the node can be assigned a value. + /// + /// [spec]: https://tc39.es/ecma262/#sec-assignment-operators-static-semantics-early-errors + pub(crate) fn is_assignable(&self) -> bool { + matches!( + self, + Self::GetConstField(_) + | Self::GetField(_) + | Self::Assign(_) + | Self::Call(_) + | Self::Identifier(_) + | Self::Object(_) + ) + } + /// Creates a `This` AST node. pub fn this() -> Self { Self::This @@ -291,63 +366,63 @@ impl Node { } } -impl Executable for Node { +impl Executable for NodeKind { fn run(&self, context: &mut Context) -> Result { let _timer = BoaProfiler::global().start_event("Executable", "exec"); match *self { - Node::AsyncFunctionDecl(ref decl) => decl.run(context), - Node::AsyncFunctionExpr(ref function_expr) => function_expr.run(context), - Node::AwaitExpr(ref expr) => expr.run(context), - Node::Call(ref call) => call.run(context), - Node::Const(Const::Null) => Ok(Value::null()), - Node::Const(Const::Num(num)) => Ok(Value::rational(num)), - Node::Const(Const::Int(num)) => Ok(Value::integer(num)), - Node::Const(Const::BigInt(ref num)) => Ok(Value::from(num.clone())), - Node::Const(Const::Undefined) => Ok(Value::Undefined), + Self::AsyncFunctionDecl(ref decl) => decl.run(context), + Self::AsyncFunctionExpr(ref function_expr) => function_expr.run(context), + Self::AwaitExpr(ref expr) => expr.run(context), + Self::Call(ref call) => call.run(context), + Self::Const(Const::Null) => Ok(Value::null()), + Self::Const(Const::Num(num)) => Ok(Value::rational(num)), + Self::Const(Const::Int(num)) => Ok(Value::integer(num)), + Self::Const(Const::BigInt(ref num)) => Ok(Value::from(num.clone())), + Self::Const(Const::Undefined) => Ok(Value::Undefined), // we can't move String from Const into value, because const is a garbage collected value // Which means Drop() get's called on Const, but str will be gone at that point. // Do Const values need to be garbage collected? We no longer need them once we've generated Values - Node::Const(Const::String(ref value)) => Ok(Value::string(value.to_string())), - Node::Const(Const::Bool(value)) => Ok(Value::boolean(value)), - Node::Block(ref block) => block.run(context), - Node::Identifier(ref identifier) => identifier.run(context), - Node::GetConstField(ref get_const_field_node) => get_const_field_node.run(context), - Node::GetField(ref get_field) => get_field.run(context), - Node::WhileLoop(ref while_loop) => while_loop.run(context), - Node::DoWhileLoop(ref do_while) => do_while.run(context), - Node::ForLoop(ref for_loop) => for_loop.run(context), - Node::ForOfLoop(ref for_of_loop) => for_of_loop.run(context), - Node::ForInLoop(ref for_in_loop) => for_in_loop.run(context), - Node::If(ref if_smt) => if_smt.run(context), - Node::ConditionalOp(ref op) => op.run(context), - Node::Switch(ref switch) => switch.run(context), - Node::Object(ref obj) => obj.run(context), - Node::ArrayDecl(ref arr) => arr.run(context), + Self::Const(Const::String(ref value)) => Ok(Value::string(value.to_string())), + Self::Const(Const::Bool(value)) => Ok(Value::boolean(value)), + Self::Block(ref block) => block.run(context), + Self::Identifier(ref identifier) => identifier.run(context), + Self::GetConstField(ref get_const_field_node) => get_const_field_node.run(context), + Self::GetField(ref get_field) => get_field.run(context), + Self::WhileLoop(ref while_loop) => while_loop.run(context), + Self::DoWhileLoop(ref do_while) => do_while.run(context), + Self::ForLoop(ref for_loop) => for_loop.run(context), + Self::ForOfLoop(ref for_of_loop) => for_of_loop.run(context), + Self::ForInLoop(ref for_in_loop) => for_in_loop.run(context), + Self::If(ref if_smt) => if_smt.run(context), + Self::ConditionalOp(ref op) => op.run(context), + Self::Switch(ref switch) => switch.run(context), + Self::Object(ref obj) => obj.run(context), + Self::ArrayDecl(ref arr) => arr.run(context), // - Node::FunctionDecl(ref decl) => decl.run(context), + Self::FunctionDecl(ref decl) => decl.run(context), // - Node::FunctionExpr(ref function_expr) => function_expr.run(context), - Node::ArrowFunctionDecl(ref decl) => decl.run(context), - Node::BinOp(ref op) => op.run(context), - Node::UnaryOp(ref op) => op.run(context), - Node::New(ref call) => call.run(context), - Node::Return(ref ret) => ret.run(context), - Node::TaggedTemplate(ref template) => template.run(context), - Node::TemplateLit(ref template) => template.run(context), - Node::Throw(ref throw) => throw.run(context), - Node::Assign(ref op) => op.run(context), - Node::VarDeclList(ref decl) => decl.run(context), - Node::LetDeclList(ref decl) => decl.run(context), - Node::ConstDeclList(ref decl) => decl.run(context), - Node::Spread(ref spread) => spread.run(context), - Node::This => { + Self::FunctionExpr(ref function_expr) => function_expr.run(context), + Self::ArrowFunctionDecl(ref decl) => decl.run(context), + Self::BinOp(ref op) => op.run(context), + Self::UnaryOp(ref op) => op.run(context), + Self::New(ref call) => call.run(context), + Self::Return(ref ret) => ret.run(context), + Self::TaggedTemplate(ref template) => template.run(context), + Self::TemplateLit(ref template) => template.run(context), + Self::Throw(ref throw) => throw.run(context), + Self::Assign(ref op) => op.run(context), + Self::VarDeclList(ref decl) => decl.run(context), + Self::LetDeclList(ref decl) => decl.run(context), + Self::ConstDeclList(ref decl) => decl.run(context), + Self::Spread(ref spread) => spread.run(context), + Self::This => { // Will either return `this` binding or undefined context.get_this_binding() } - Node::Try(ref try_node) => try_node.run(context), - Node::Break(ref break_node) => break_node.run(context), - Node::Continue(ref continue_node) => continue_node.run(context), - Node::Empty => Ok(Value::Undefined), + Self::Try(ref try_node) => try_node.run(context), + Self::Break(ref break_node) => break_node.run(context), + Self::Continue(ref continue_node) => continue_node.run(context), + Self::Empty => Ok(Value::Undefined), } } } diff --git a/boa/src/syntax/ast/node/new/mod.rs b/boa/src/syntax/ast/node/new/mod.rs index 37a0100551f..ab4704df4e0 100644 --- a/boa/src/syntax/ast/node/new/mod.rs +++ b/boa/src/syntax/ast/node/new/mod.rs @@ -2,7 +2,7 @@ use crate::{ builtins::iterable, exec::Executable, gc::{Finalize, Trace}, - syntax::ast::node::{Call, Node}, + syntax::ast::node::{Call, Node, NodeKind}, value::Value, BoaProfiler, Context, Result, }; @@ -51,7 +51,7 @@ impl Executable for New { let func_object = self.expr().run(context)?; let mut v_args = Vec::with_capacity(self.args().len()); for arg in self.args() { - if let Node::Spread(ref x) = arg { + if let NodeKind::Spread(ref x) = arg.kind() { let val = x.run(context)?; let iterator_record = iterable::get_iterator(context, val)?; loop { @@ -88,7 +88,7 @@ impl fmt::Display for New { } } -impl From for Node { +impl From for NodeKind { fn from(new: New) -> Self { Self::New(new) } diff --git a/boa/src/syntax/ast/node/object/mod.rs b/boa/src/syntax/ast/node/object/mod.rs index 4919c937755..07b2bb1d8a3 100644 --- a/boa/src/syntax/ast/node/object/mod.rs +++ b/boa/src/syntax/ast/node/object/mod.rs @@ -4,7 +4,7 @@ use crate::{ exec::Executable, gc::{Finalize, Trace}, property::{AccessorDescriptor, Attribute, DataDescriptor, PropertyDescriptor}, - syntax::ast::node::{MethodDefinitionKind, Node, PropertyDefinition}, + syntax::ast::node::{MethodDefinitionKind, NodeKind, PropertyDefinition}, Context, Result, Value, }; use std::fmt; @@ -157,7 +157,7 @@ where } } -impl From for Node { +impl From for NodeKind { fn from(obj: Object) -> Self { Self::Object(obj) } diff --git a/boa/src/syntax/ast/node/operator/assign/mod.rs b/boa/src/syntax/ast/node/operator/assign/mod.rs index 0d362ec8d12..fd4a9fc3a15 100644 --- a/boa/src/syntax/ast/node/operator/assign/mod.rs +++ b/boa/src/syntax/ast/node/operator/assign/mod.rs @@ -2,7 +2,7 @@ use crate::{ environment::lexical_environment::VariableScope, exec::Executable, gc::{Finalize, Trace}, - syntax::ast::node::Node, + syntax::ast::node::{Node, NodeKind}, BoaProfiler, Context, Result, Value, }; use std::fmt; @@ -56,8 +56,8 @@ impl Executable for Assign { fn run(&self, context: &mut Context) -> Result { let _timer = BoaProfiler::global().start_event("Assign", "exec"); let val = self.rhs().run(context)?; - match self.lhs() { - Node::Identifier(ref name) => { + match self.lhs().kind() { + NodeKind::Identifier(ref name) => { if context.has_binding(name.as_ref()) { // Binding already exists context.set_mutable_binding(name.as_ref(), val.clone(), true)?; @@ -70,11 +70,11 @@ impl Executable for Assign { context.initialize_binding(name.as_ref(), val.clone())?; } } - Node::GetConstField(ref get_const_field) => { + NodeKind::GetConstField(ref get_const_field) => { let val_obj = get_const_field.obj().run(context)?; val_obj.set_field(get_const_field.field(), val.clone(), context)?; } - Node::GetField(ref get_field) => { + NodeKind::GetField(ref get_field) => { let object = get_field.obj().run(context)?; let field = get_field.field().run(context)?; let key = field.to_property_key(context)?; @@ -92,7 +92,7 @@ impl fmt::Display for Assign { } } -impl From for Node { +impl From for NodeKind { fn from(op: Assign) -> Self { Self::Assign(op) } diff --git a/boa/src/syntax/ast/node/operator/bin_op/mod.rs b/boa/src/syntax/ast/node/operator/bin_op/mod.rs index 230a18e3cbc..9b919fed942 100644 --- a/boa/src/syntax/ast/node/operator/bin_op/mod.rs +++ b/boa/src/syntax/ast/node/operator/bin_op/mod.rs @@ -3,7 +3,7 @@ use crate::{ gc::{Finalize, Trace}, symbol::WellKnownSymbols, syntax::ast::{ - node::Node, + node::{Node, NodeKind}, op::{self, AssignOp, BitOp, CompOp, LogOp, NumOp}, }, Context, Result, Value, @@ -204,15 +204,15 @@ impl Executable for BinOp { } } }), - op::BinOp::Assign(op) => match self.lhs() { - Node::Identifier(ref name) => { + op::BinOp::Assign(op) => match self.lhs().kind() { + NodeKind::Identifier(ref name) => { let v_a = context.get_binding_value(name.as_ref())?; let value = Self::run_assign(op, v_a, self.rhs(), context)?; context.set_mutable_binding(name.as_ref(), value.clone(), true)?; Ok(value) } - Node::GetConstField(ref get_const_field) => { + NodeKind::GetConstField(ref get_const_field) => { let v_r_a = get_const_field.obj().run(context)?; let v_a = v_r_a.get_field(get_const_field.field(), context)?; let value = Self::run_assign(op, v_a, self.rhs(), context)?; @@ -285,7 +285,7 @@ impl fmt::Display for BinOp { } } -impl From for Node { +impl From for NodeKind { fn from(op: BinOp) -> Self { Self::BinOp(op) } diff --git a/boa/src/syntax/ast/node/operator/unary_op/mod.rs b/boa/src/syntax/ast/node/operator/unary_op/mod.rs index 327b08cedff..a67307919cd 100644 --- a/boa/src/syntax/ast/node/operator/unary_op/mod.rs +++ b/boa/src/syntax/ast/node/operator/unary_op/mod.rs @@ -1,7 +1,10 @@ use crate::{ exec::Executable, gc::{Finalize, Trace}, - syntax::ast::{node::Node, op}, + syntax::ast::{ + node::{Node, NodeKind}, + op, + }, Context, Result, Value, }; use std::fmt; @@ -94,15 +97,15 @@ impl Executable for UnaryOp { self.target().run(context)?; Value::undefined() } - op::UnaryOp::Delete => match *self.target() { - Node::GetConstField(ref get_const_field) => Value::boolean( + op::UnaryOp::Delete => match self.target().kind() { + NodeKind::GetConstField(ref get_const_field) => Value::boolean( get_const_field .obj() .run(context)? .to_object(context)? .delete(&get_const_field.field().into()), ), - Node::GetField(ref get_field) => { + NodeKind::GetField(ref get_field) => { let obj = get_field.obj().run(context)?; let field = &get_field.field().run(context)?; let res = obj @@ -110,15 +113,15 @@ impl Executable for UnaryOp { .delete(&field.to_property_key(context)?); return Ok(Value::boolean(res)); } - Node::Identifier(_) => Value::boolean(false), - Node::ArrayDecl(_) - | Node::Block(_) - | Node::Const(_) - | Node::FunctionDecl(_) - | Node::FunctionExpr(_) - | Node::New(_) - | Node::Object(_) - | Node::UnaryOp(_) => Value::boolean(true), + NodeKind::Identifier(_) => Value::boolean(false), + NodeKind::ArrayDecl(_) + | NodeKind::Block(_) + | NodeKind::Const(_) + | NodeKind::FunctionDecl(_) + | NodeKind::FunctionExpr(_) + | NodeKind::New(_) + | NodeKind::Object(_) + | NodeKind::UnaryOp(_) => Value::boolean(true), _ => return context.throw_syntax_error(format!("wrong delete argument {}", self)), }, op::UnaryOp::TypeOf => Value::from(self.target().run(context)?.get_type().as_str()), @@ -132,7 +135,7 @@ impl fmt::Display for UnaryOp { } } -impl From for Node { +impl From for NodeKind { fn from(op: UnaryOp) -> Self { Self::UnaryOp(op) } diff --git a/boa/src/syntax/ast/node/return_smt/mod.rs b/boa/src/syntax/ast/node/return_smt/mod.rs index 6201624b52c..1bd0ea45d9d 100644 --- a/boa/src/syntax/ast/node/return_smt/mod.rs +++ b/boa/src/syntax/ast/node/return_smt/mod.rs @@ -1,7 +1,7 @@ use crate::{ exec::{Executable, InterpreterState}, gc::{Finalize, Trace}, - syntax::ast::node::Node, + syntax::ast::node::{Node, NodeKind}, Context, Result, Value, }; use std::fmt; @@ -71,9 +71,9 @@ impl Executable for Return { } } -impl From for Node { - fn from(return_smt: Return) -> Node { - Node::Return(return_smt) +impl From for NodeKind { + fn from(return_smt: Return) -> Self { + Self::Return(return_smt) } } diff --git a/boa/src/syntax/ast/node/spread/mod.rs b/boa/src/syntax/ast/node/spread/mod.rs index b79292ebab3..abe6887462b 100644 --- a/boa/src/syntax/ast/node/spread/mod.rs +++ b/boa/src/syntax/ast/node/spread/mod.rs @@ -1,7 +1,7 @@ use crate::{ exec::Executable, gc::{Finalize, Trace}, - syntax::ast::node::Node, + syntax::ast::node::{Node, NodeKind}, Context, Result, Value, }; use std::fmt; @@ -64,8 +64,8 @@ impl fmt::Display for Spread { } } -impl From for Node { - fn from(spread: Spread) -> Node { +impl From for NodeKind { + fn from(spread: Spread) -> Self { Self::Spread(spread) } } diff --git a/boa/src/syntax/ast/node/statement_list/mod.rs b/boa/src/syntax/ast/node/statement_list/mod.rs index 871c29e316c..bfcee2e26c4 100644 --- a/boa/src/syntax/ast/node/statement_list/mod.rs +++ b/boa/src/syntax/ast/node/statement_list/mod.rs @@ -3,7 +3,7 @@ use crate::{ exec::{Executable, InterpreterState}, gc::{empty_trace, Finalize, Trace}, - syntax::ast::node::Node, + syntax::ast::{Node, NodeKind, Span}, BoaProfiler, Context, Result, Value, }; use std::{collections::HashSet, fmt, ops::Deref, rc::Rc}; @@ -25,16 +25,34 @@ use crate::vm::{compilation::CodeGen, Compiler}; #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct StatementList { - #[cfg_attr(feature = "deser", serde(flatten))] items: Box<[Node]>, + span: Span, } impl StatementList { + /// Creates a new statement list + pub(crate) fn new(items: N, span: Span) -> Self + where + N: Into>, + { + let items = items.into(); + debug_assert_ne!(items.len(), 0, "empty statement list created"); + + Self { items, span } + } + /// Gets the list of items. + #[inline] pub fn items(&self) -> &[Node] { &self.items } + /// Gets the span of the statement list. + #[inline] + pub fn span(&self) -> Span { + self.span + } + /// Implements the display formatting with indentation. pub(in crate::syntax::ast::node) fn display( &self, @@ -43,12 +61,15 @@ impl StatementList { ) -> fmt::Result { let indent = " ".repeat(indentation); // Print statements - for node in self.items.iter() { + for node in self.items.iter().map(Node::kind) { f.write_str(&indent)?; node.display(f, indentation + 1)?; match node { - Node::Block(_) | Node::If(_) | Node::Switch(_) | Node::WhileLoop(_) => {} + NodeKind::Block(_) + | NodeKind::If(_) + | NodeKind::Switch(_) + | NodeKind::WhileLoop(_) => {} _ => write!(f, ";")?, } writeln!(f)?; @@ -59,7 +80,9 @@ impl StatementList { pub fn lexically_declared_names(&self) -> HashSet<&str> { let mut set = HashSet::new(); for stmt in self.items() { - if let Node::LetDeclList(decl_list) | Node::ConstDeclList(decl_list) = stmt { + if let NodeKind::LetDeclList(decl_list) | NodeKind::ConstDeclList(decl_list) = + stmt.kind() + { for decl in decl_list.as_ref() { if !set.insert(decl.name()) { // It is a Syntax Error if the LexicallyDeclaredNames of StatementList contains any duplicate entries. @@ -73,19 +96,22 @@ impl StatementList { } pub fn function_declared_names(&self) -> HashSet<&str> { - let mut set = HashSet::new(); - for stmt in self.items() { - if let Node::FunctionDecl(decl) = stmt { - set.insert(decl.name()); - } - } - set + self.items + .iter() + .filter_map(|node| { + if let NodeKind::FunctionDecl(decl) = node.kind() { + Some(decl.name()) + } else { + None + } + }) + .collect::>() } pub fn var_declared_names(&self) -> HashSet<&str> { let mut set = HashSet::new(); for stmt in self.items() { - if let Node::VarDeclList(decl_list) = stmt { + if let NodeKind::VarDeclList(decl_list) = stmt.kind() { for decl in decl_list.as_ref() { set.insert(decl.name()); } @@ -144,15 +170,6 @@ impl CodeGen for StatementList { } } -impl From for StatementList -where - T: Into>, -{ - fn from(stm: T) -> Self { - Self { items: stm.into() } - } -} - impl fmt::Display for StatementList { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.display(f, 0) diff --git a/boa/src/syntax/ast/node/switch/mod.rs b/boa/src/syntax/ast/node/switch/mod.rs index f62de5ae109..0330fccf3f6 100644 --- a/boa/src/syntax/ast/node/switch/mod.rs +++ b/boa/src/syntax/ast/node/switch/mod.rs @@ -3,7 +3,7 @@ use crate::{ exec::{Executable, InterpreterState}, gc::{Finalize, Trace}, - syntax::ast::node::Node, + syntax::ast::node::{Node, NodeKind}, Context, Result, Value, }; use std::fmt; @@ -205,7 +205,7 @@ impl fmt::Display for Switch { } } -impl From for Node { +impl From for NodeKind { fn from(switch: Switch) -> Self { Self::Switch(switch) } diff --git a/boa/src/syntax/ast/node/template/mod.rs b/boa/src/syntax/ast/node/template/mod.rs index f44fbd487cc..b355445cc64 100644 --- a/boa/src/syntax/ast/node/template/mod.rs +++ b/boa/src/syntax/ast/node/template/mod.rs @@ -1,8 +1,13 @@ //! Template literal node. -use super::Node; -use crate::{builtins::Array, exec::Executable, value::Type, BoaProfiler, Context, Result, Value}; -use gc::{Finalize, Trace}; +use super::{Node, NodeKind}; +use crate::{ + builtins::Array, + exec::Executable, + gc::{Finalize, Trace}, + value::Type, + BoaProfiler, Context, Result, Value, +}; #[cfg(feature = "deser")] use serde::{Deserialize, Serialize}; @@ -109,8 +114,8 @@ impl Executable for TaggedTemplate { } template_object.set_field("raw", raw_array, context)?; - let (this, func) = match *self.tag { - Node::GetConstField(ref get_const_field) => { + let (this, func) = match self.tag.kind() { + NodeKind::GetConstField(ref get_const_field) => { let mut obj = get_const_field.obj().run(context)?; if obj.get_type() != Type::Object { obj = Value::Object(obj.to_object(context)?); @@ -120,7 +125,7 @@ impl Executable for TaggedTemplate { obj.get_field(get_const_field.field(), context)?, ) } - Node::GetField(ref get_field) => { + NodeKind::GetField(ref get_field) => { let obj = get_field.obj().run(context)?; let field = get_field.field().run(context)?; ( @@ -150,9 +155,9 @@ impl fmt::Display for TaggedTemplate { } } -impl From for Node { +impl From for NodeKind { fn from(template: TaggedTemplate) -> Self { - Node::TaggedTemplate(template) + Self::TaggedTemplate(template) } } diff --git a/boa/src/syntax/ast/node/throw/mod.rs b/boa/src/syntax/ast/node/throw/mod.rs index 486b5129439..82060b7f294 100644 --- a/boa/src/syntax/ast/node/throw/mod.rs +++ b/boa/src/syntax/ast/node/throw/mod.rs @@ -1,7 +1,7 @@ use crate::{ exec::Executable, gc::{Finalize, Trace}, - syntax::ast::node::Node, + syntax::ast::node::{Node, NodeKind}, Context, Result, Value, }; use std::fmt; @@ -58,8 +58,8 @@ impl fmt::Display for Throw { } } -impl From for Node { - fn from(trw: Throw) -> Node { +impl From for NodeKind { + fn from(trw: Throw) -> Self { Self::Throw(trw) } } diff --git a/boa/src/syntax/ast/node/try_node/mod.rs b/boa/src/syntax/ast/node/try_node/mod.rs index cf56f56f0f5..add90243fbb 100644 --- a/boa/src/syntax/ast/node/try_node/mod.rs +++ b/boa/src/syntax/ast/node/try_node/mod.rs @@ -5,7 +5,7 @@ use crate::{ }, exec::Executable, gc::{Finalize, Trace}, - syntax::ast::node::{Block, Identifier, Node}, + syntax::ast::node::{Block, Identifier, NodeKind}, BoaProfiler, Context, Result, Value, }; use std::fmt; @@ -141,7 +141,7 @@ impl fmt::Display for Try { } } -impl From for Node { +impl From for NodeKind { fn from(try_catch: Try) -> Self { Self::Try(try_catch) } diff --git a/boa/src/syntax/ast/position.rs b/boa/src/syntax/ast/position.rs index ca9fc8c422d..89d67b0059d 100644 --- a/boa/src/syntax/ast/position.rs +++ b/boa/src/syntax/ast/position.rs @@ -5,6 +5,8 @@ use std::{cmp::Ordering, fmt, num::NonZeroU32}; #[cfg(feature = "deser")] use serde::{Deserialize, Serialize}; +use crate::gc::{empty_trace, Finalize, Trace}; + /// A position in the JavaScript source code. /// /// Stores both the column number and the line number. @@ -15,7 +17,7 @@ use serde::{Deserialize, Serialize}; /// ## Similar Implementations /// [V8: Location](https://cs.chromium.org/chromium/src/v8/src/parsing/scanner.h?type=cs&q=isValid+Location&g=0&l=216) #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Finalize)] pub struct Position { /// Line number. line_number: NonZeroU32, @@ -23,6 +25,10 @@ pub struct Position { column_number: NonZeroU32, } +unsafe impl Trace for Position { + empty_trace!(); +} + impl Position { /// Creates a new `Position`. #[inline] @@ -47,6 +53,16 @@ impl Position { } } +impl Default for Position { + #[inline] + fn default() -> Self { + Self { + line_number: NonZeroU32::new(1).unwrap(), + column_number: NonZeroU32::new(1).unwrap(), + } + } +} + impl fmt::Display for Position { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", self.line_number, self.column_number) @@ -57,12 +73,16 @@ impl fmt::Display for Position { /// /// Stores a start position and an end position. #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Finalize)] pub struct Span { start: Position, end: Position, } +unsafe impl Trace for Span { + empty_trace!(); +} + impl Span { /// Creates a new `Span`. #[inline] @@ -97,6 +117,7 @@ impl Span { } impl From for Span { + #[inline] fn from(pos: Position) -> Self { Self { start: pos, @@ -105,6 +126,16 @@ impl From for Span { } } +impl Default for Span { + #[inline] + fn default() -> Self { + Self { + start: Position::default(), + end: Position::default(), + } + } +} + impl PartialOrd for Span { fn partial_cmp(&self, other: &Self) -> Option { if self == other { diff --git a/boa/src/syntax/parser/cursor/mod.rs b/boa/src/syntax/parser/cursor/mod.rs index 61ddcb55a81..b3c53bf84fb 100644 --- a/boa/src/syntax/parser/cursor/mod.rs +++ b/boa/src/syntax/parser/cursor/mod.rs @@ -113,16 +113,19 @@ where /// /// [spec]: https://tc39.es/ecma262/#sec-automatic-semicolon-insertion #[inline] - pub(super) fn expect_semicolon(&mut self, context: &'static str) -> Result<(), ParseError> { + pub(super) fn expect_semicolon( + &mut self, + context: &'static str, + ) -> Result, ParseError> { match self.peek_semicolon()? { SemicolonResult::Found(Some(tk)) => match *tk.kind() { TokenKind::Punctuator(Punctuator::Semicolon) | TokenKind::LineTerminator => { - let _ = self.buffered_lexer.next(false)?; - Ok(()) + let tk = self.buffered_lexer.next(false)?; + Ok(tk) } - _ => Ok(()), + _ => Ok(None), }, - SemicolonResult::Found(None) => Ok(()), + SemicolonResult::Found(None) => Ok(None), SemicolonResult::NotFound(tk) => Err(ParseError::expected( vec![TokenKind::Punctuator(Punctuator::Semicolon)], tk.clone(), diff --git a/boa/src/syntax/parser/expression/assignment/arrow_function.rs b/boa/src/syntax/parser/expression/assignment/arrow_function.rs index 5528c1c8b9b..cc35156f904 100644 --- a/boa/src/syntax/parser/expression/assignment/arrow_function.rs +++ b/boa/src/syntax/parser/expression/assignment/arrow_function.rs @@ -12,7 +12,7 @@ use crate::{ syntax::{ ast::{ node::{ArrowFunctionDecl, FormalParameter, Node, Return, StatementList}, - Punctuator, + Punctuator, Span, }, lexer::{Error as LexError, Position, TokenKind}, parser::{ @@ -66,11 +66,13 @@ impl TokenParser for ArrowFunction where R: Read, { - type Output = ArrowFunctionDecl; + type Output = (ArrowFunctionDecl, Span); fn parse(self, cursor: &mut Cursor) -> Result { let _timer = BoaProfiler::global().start_event("ArrowFunction", "Parsing"); + let next_token = cursor.peek(0)?.ok_or(ParseError::AbruptEnd)?; + let span_start = next_token.span().start(); let params = if let TokenKind::Punctuator(Punctuator::OpenParen) = &next_token.kind() { // CoverParenthesizedExpressionAndArrowParameterList @@ -80,7 +82,7 @@ where cursor.expect(Punctuator::CloseParen, "arrow function")?; params } else { - let param = BindingIdentifier::new(self.allow_yield, self.allow_await) + let (param, _span) = BindingIdentifier::new(self.allow_yield, self.allow_await) .parse(cursor) .context("arrow function")?; Box::new([FormalParameter::new(param, None, false)]) @@ -109,7 +111,9 @@ where } } - Ok(ArrowFunctionDecl::new(params, body)) + let span = Span::new(span_start, body.span().end()); + + Ok((ArrowFunctionDecl::new(params, body), span)) } } @@ -138,18 +142,22 @@ where type Output = StatementList; fn parse(self, cursor: &mut Cursor) -> Result { - match cursor.peek(0)?.ok_or(ParseError::AbruptEnd)?.kind() { + let next = cursor.peek(0)?.ok_or(ParseError::AbruptEnd)?; + match next.kind() { TokenKind::Punctuator(Punctuator::OpenBlock) => { let _ = cursor.next(); let body = FunctionBody::new(false, false).parse(cursor)?; cursor.expect(Punctuator::CloseBlock, "arrow function")?; Ok(body) } - _ => Ok(StatementList::from(vec![Return::new( - ExpressionBody::new(self.allow_in, false).parse(cursor)?, - None, - ) - .into()])), + _ => { + let expr = ExpressionBody::new(self.allow_in, false).parse(cursor)?; + + Ok(StatementList::new( + vec![Node::new(Return::new(expr, None), expr.span())], + expr.span(), + )) + } } } } diff --git a/boa/src/syntax/parser/expression/assignment/conditional.rs b/boa/src/syntax/parser/expression/assignment/conditional.rs index aa9998e4151..7c0102c1370 100644 --- a/boa/src/syntax/parser/expression/assignment/conditional.rs +++ b/boa/src/syntax/parser/expression/assignment/conditional.rs @@ -10,7 +10,7 @@ use crate::syntax::lexer::TokenKind; use crate::{ syntax::{ - ast::{node::ConditionalOp, Node, Punctuator}, + ast::{node::ConditionalOp, Node, Punctuator, Span}, parser::{ expression::{AssignmentExpression, ShortCircuitExpression}, AllowAwait, AllowIn, AllowYield, Cursor, ParseResult, TokenParser, @@ -79,7 +79,12 @@ where let else_clause = AssignmentExpression::new(self.allow_in, self.allow_yield, self.allow_await) .parse(cursor)?; - return Ok(ConditionalOp::new(lhs, then_clause, else_clause).into()); + + let span = Span::new(lhs.span().start(), else_clause.span().end()); + return Ok(Node::new( + ConditionalOp::new(lhs, then_clause, else_clause), + span, + )); } } diff --git a/boa/src/syntax/parser/expression/assignment/exponentiation.rs b/boa/src/syntax/parser/expression/assignment/exponentiation.rs index e9eac4c9dd9..a17d86512ce 100644 --- a/boa/src/syntax/parser/expression/assignment/exponentiation.rs +++ b/boa/src/syntax/parser/expression/assignment/exponentiation.rs @@ -14,7 +14,7 @@ use crate::{ ast::{ node::{BinOp, Node}, op::NumOp, - Keyword, Punctuator, + Keyword, Punctuator, Span, }, parser::{ expression::{unary::UnaryExpression, update::UpdateExpression}, @@ -92,7 +92,12 @@ where if let Some(tok) = cursor.peek(0)? { if let TokenKind::Punctuator(Punctuator::Exp) = tok.kind() { cursor.next()?.expect("** token vanished"); // Consume the token. - return Ok(BinOp::new(NumOp::Exp, lhs, self.parse(cursor)?).into()); + + let span = Span::new(lhs.span().start(), tok.span().end()); + return Ok(Node::new( + BinOp::new(NumOp::Exp, lhs, self.parse(cursor)?), + span, + )); } } Ok(lhs) diff --git a/boa/src/syntax/parser/expression/assignment/mod.rs b/boa/src/syntax/parser/expression/assignment/mod.rs index be30d52eebd..7958728274f 100644 --- a/boa/src/syntax/parser/expression/assignment/mod.rs +++ b/boa/src/syntax/parser/expression/assignment/mod.rs @@ -16,8 +16,8 @@ use crate::syntax::lexer::{Error as LexError, InputElement, TokenKind}; use crate::{ syntax::{ ast::{ - node::{Assign, BinOp, Node}, - Keyword, Punctuator, + node::{Assign, BinOp, Node, NodeKind}, + Keyword, Punctuator, Span, }, parser::{AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser}, }, @@ -90,13 +90,11 @@ where | TokenKind::Keyword(Keyword::Await) => { if let Ok(tok) = cursor.peek_expect_no_lineterminator(1, "assignment expression") { if tok.kind() == &TokenKind::Punctuator(Punctuator::Arrow) { - return ArrowFunction::new( - self.allow_in, - self.allow_yield, - self.allow_await, - ) - .parse(cursor) - .map(Node::ArrowFunctionDecl); + let (decl, span) = + ArrowFunction::new(self.allow_in, self.allow_yield, self.allow_await) + .parse(cursor)?; + + return Ok(Node::new(NodeKind::ArrowFunctionDecl(decl), span)); } } } @@ -110,37 +108,43 @@ where // otherwise it is an expression of the form (b). if let Some(t) = cursor.peek(2)? { if t.kind() == &TokenKind::Punctuator(Punctuator::Arrow) { - return ArrowFunction::new( + let (decl, span) = ArrowFunction::new( self.allow_in, self.allow_yield, self.allow_await, ) - .parse(cursor) - .map(Node::ArrowFunctionDecl); + .parse(cursor)?; + + return Ok(Node::new(NodeKind::ArrowFunctionDecl(decl), span)); } } } TokenKind::Punctuator(Punctuator::Spread) => { - return ArrowFunction::new( + let (decl, span) = ArrowFunction::new( self.allow_in, self.allow_yield, self.allow_await, ) - .parse(cursor) - .map(Node::ArrowFunctionDecl); + .parse(cursor)?; + + return Ok(Node::new(NodeKind::ArrowFunctionDecl(decl), span)); } TokenKind::Identifier(_) => { if let Some(t) = cursor.peek(2)? { match *t.kind() { TokenKind::Punctuator(Punctuator::Comma) => { // This must be an argument list and therefore (a, b) => {} - return ArrowFunction::new( + let (decl, span) = ArrowFunction::new( self.allow_in, self.allow_yield, self.allow_await, ) - .parse(cursor) - .map(Node::ArrowFunctionDecl); + .parse(cursor)?; + + return Ok(Node::new( + NodeKind::ArrowFunctionDecl(decl), + span, + )); } TokenKind::Punctuator(Punctuator::CloseParen) => { // Need to check if the token after the close paren is an arrow, if so then this is an ArrowFunction @@ -148,13 +152,17 @@ where if let Some(t) = cursor.peek(3)? { if t.kind() == &TokenKind::Punctuator(Punctuator::Arrow) { - return ArrowFunction::new( + let (decl, span) = ArrowFunction::new( self.allow_in, self.allow_yield, self.allow_await, ) - .parse(cursor) - .map(Node::ArrowFunctionDecl); + .parse(cursor)?; + + return Ok(Node::new( + NodeKind::ArrowFunctionDecl(decl), + span, + )); } } } @@ -181,8 +189,11 @@ where match tok.kind() { TokenKind::Punctuator(Punctuator::Assign) => { cursor.next()?.expect("= token vanished"); // Consume the token. - if is_assignable(&lhs) { - lhs = Assign::new(lhs, self.parse(cursor)?).into(); + if lhs.kind().is_assignable() { + let expr = self.parse(cursor)?; + let span = Span::new(lhs.span().start(), expr.span().end()); + + lhs = Node::new(Assign::new(lhs, expr), span); } else { return Err(ParseError::lex(LexError::Syntax( "Invalid left-hand side in assignment".into(), @@ -192,11 +203,13 @@ where } TokenKind::Punctuator(p) if p.as_binop().is_some() && p != &Punctuator::Comma => { cursor.next()?.expect("token vanished"); // Consume the token. - if is_assignable(&lhs) { + if lhs.kind().is_assignable() { let binop = p.as_binop().expect("binop disappeared"); let expr = self.parse(cursor)?; - lhs = BinOp::new(binop, lhs, expr).into(); + let span = Span::new(lhs.span().start(), expr.span().end()); + + lhs = Node::new(BinOp::new(binop, lhs, expr), span); } else { return Err(ParseError::lex(LexError::Syntax( "Invalid left-hand side in assignment".into(), @@ -211,19 +224,3 @@ where Ok(lhs) } } - -/// Returns true if as per spec[spec] the node can be assigned a value. -/// -/// [spec]: https://tc39.es/ecma262/#sec-assignment-operators-static-semantics-early-errors -#[inline] -pub(crate) fn is_assignable(node: &Node) -> bool { - matches!( - node, - Node::GetConstField(_) - | Node::GetField(_) - | Node::Assign(_) - | Node::Call(_) - | Node::Identifier(_) - | Node::Object(_) - ) -} diff --git a/boa/src/syntax/parser/mod.rs b/boa/src/syntax/parser/mod.rs index f6b677a77cb..a3eed4d30af 100644 --- a/boa/src/syntax/parser/mod.rs +++ b/boa/src/syntax/parser/mod.rs @@ -8,10 +8,12 @@ mod statement; #[cfg(test)] mod tests; +use self::cursor::Cursor; pub use self::error::{ParseError, ParseResult}; -use crate::syntax::{ast::node::StatementList, lexer::TokenKind}; - -use cursor::Cursor; +use crate::syntax::{ + ast::{node::StatementList, Position}, + lexer::TokenKind, +}; use std::io::Read; @@ -132,7 +134,7 @@ where } ScriptBody.parse(cursor) } - None => Ok(StatementList::from(Vec::new())), + None => Ok(StatementList::new(Vec::new(), Position::new(0, 0).into())), } } } diff --git a/boa/src/syntax/parser/statement/block/mod.rs b/boa/src/syntax/parser/statement/block/mod.rs index a568e47ea26..5dfce2af43d 100644 --- a/boa/src/syntax/parser/statement/block/mod.rs +++ b/boa/src/syntax/parser/statement/block/mod.rs @@ -16,7 +16,7 @@ use crate::syntax::lexer::TokenKind; use crate::{ profiler::BoaProfiler, syntax::{ - ast::{node, Punctuator}, + ast::{node, Punctuator, Span}, parser::{AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser}, }, }; @@ -73,11 +73,17 @@ where fn parse(self, cursor: &mut Cursor) -> Result { let _timer = BoaProfiler::global().start_event("Block", "Parsing"); - cursor.expect(Punctuator::OpenBlock, "block")?; + + let start_token = cursor.expect(Punctuator::OpenBlock, "block")?; if let Some(tk) = cursor.peek(0)? { if tk.kind() == &TokenKind::Punctuator(Punctuator::CloseBlock) { cursor.next()?.expect("} token vanished"); - return Ok(node::Block::from(vec![])); + + let span = Span::new(start_token.span().start(), tk.span().end()); + return Ok(node::Block::from(node::StatementList::new( + Vec::new(), + span, + ))); } } diff --git a/boa/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs b/boa/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs index 0c9f3c5e972..2c205d62863 100644 --- a/boa/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs +++ b/boa/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs @@ -2,7 +2,7 @@ mod tests; use crate::syntax::{ - ast::{node::AsyncFunctionDecl, Keyword, Punctuator}, + ast::{node::AsyncFunctionDecl, Keyword, Punctuator, Span}, lexer::TokenKind, parser::{ function::FormalParameters, @@ -48,10 +48,10 @@ impl TokenParser for AsyncFunctionDeclaration where R: Read, { - type Output = AsyncFunctionDecl; + type Output = (AsyncFunctionDecl, Span); fn parse(self, cursor: &mut Cursor) -> Result { - cursor.expect(Keyword::Async, "async function declaration")?; + let start_token = cursor.expect(Keyword::Async, "async function declaration")?; cursor.peek_expect_no_lineterminator(0, "async function declaration")?; cursor.expect(Keyword::Function, "async function declaration")?; let tok = cursor.peek(0)?; @@ -84,7 +84,7 @@ where let body = FunctionBody::new(false, true).parse(cursor)?; - cursor.expect(Punctuator::CloseBlock, "async function declaration")?; + let end_token = cursor.expect(Punctuator::CloseBlock, "async function declaration")?; // It is a Syntax Error if any element of the BoundNames of FormalParameters // also occurs in the LexicallyDeclaredNames of FunctionBody. @@ -104,6 +104,8 @@ where } } - Ok(AsyncFunctionDecl::new(name, params, body)) + let span = Span::new(start_token.span().start(), end_token.span().end()); + + Ok((AsyncFunctionDecl::new(name, params, body), span)) } } diff --git a/boa/src/syntax/parser/statement/declaration/hoistable/function_decl/mod.rs b/boa/src/syntax/parser/statement/declaration/hoistable/function_decl/mod.rs index a2f3c765b43..d66f3a7917e 100644 --- a/boa/src/syntax/parser/statement/declaration/hoistable/function_decl/mod.rs +++ b/boa/src/syntax/parser/statement/declaration/hoistable/function_decl/mod.rs @@ -2,7 +2,7 @@ mod tests; use crate::syntax::{ - ast::{node::FunctionDecl, Keyword, Punctuator}, + ast::{node::FunctionDecl, Keyword, Punctuator, Span}, parser::{ function::FormalParameters, function::FunctionBody, @@ -48,10 +48,10 @@ impl TokenParser for FunctionDeclaration where R: Read, { - type Output = FunctionDecl; + type Output = (FunctionDecl, Span); fn parse(self, cursor: &mut Cursor) -> Result { - cursor.expect(Keyword::Function, "function declaration")?; + let start_token = cursor.expect(Keyword::Function, "function declaration")?; // TODO: If self.is_default, then this can be empty. let name = BindingIdentifier::new(self.allow_yield, self.allow_await).parse(cursor)?; @@ -65,7 +65,7 @@ where let body = FunctionBody::new(self.allow_yield, self.allow_await).parse(cursor)?; - cursor.expect(Punctuator::CloseBlock, "function declaration")?; + let end_token = cursor.expect(Punctuator::CloseBlock, "function declaration")?; // It is a Syntax Error if any element of the BoundNames of FormalParameters // also occurs in the LexicallyDeclaredNames of FunctionBody. @@ -85,6 +85,8 @@ where } } - Ok(FunctionDecl::new(name, params, body)) + let span = Span::new(start_token.span().start(), end_token.span().end()); + + Ok((FunctionDecl::new(name, params, body), span)) } } diff --git a/boa/src/syntax/parser/statement/declaration/hoistable/mod.rs b/boa/src/syntax/parser/statement/declaration/hoistable/mod.rs index bfb104d67d6..a2de90caacc 100644 --- a/boa/src/syntax/parser/statement/declaration/hoistable/mod.rs +++ b/boa/src/syntax/parser/statement/declaration/hoistable/mod.rs @@ -65,16 +65,18 @@ where let _timer = BoaProfiler::global().start_event("HoistableDeclaration", "Parsing"); let tok = cursor.peek(0)?.ok_or(ParseError::AbruptEnd)?; + let start = tok.span().start(); + match tok.kind() { TokenKind::Keyword(Keyword::Function) => { FunctionDeclaration::new(self.allow_yield, self.allow_await, self.is_default) .parse(cursor) - .map(Node::from) + .map(|(kind, span)| Node::new(kind, span)) } TokenKind::Keyword(Keyword::Async) => { AsyncFunctionDeclaration::new(self.allow_yield, self.allow_await, false) .parse(cursor) - .map(Node::from) + .map(|(kind, span)| Node::new(kind, span)) } _ => unreachable!("unknown token found: {:?}", tok), } diff --git a/boa/src/syntax/parser/statement/iteration/for_statement.rs b/boa/src/syntax/parser/statement/iteration/for_statement.rs index 71b0ea3c17e..6958fedd918 100644 --- a/boa/src/syntax/parser/statement/iteration/for_statement.rs +++ b/boa/src/syntax/parser/statement/iteration/for_statement.rs @@ -78,7 +78,7 @@ where Some( VariableDeclarationList::new(false, self.allow_yield, self.allow_await) .parse(cursor) - .map(Node::from)?, + .map(|(kind, span)| Node::new(kind, span))?, ) } TokenKind::Keyword(Keyword::Let) | TokenKind::Keyword(Keyword::Const) => { diff --git a/boa/src/syntax/parser/statement/labelled_stm/mod.rs b/boa/src/syntax/parser/statement/labelled_stm/mod.rs index 0e970846c07..ba9375ac454 100644 --- a/boa/src/syntax/parser/statement/labelled_stm/mod.rs +++ b/boa/src/syntax/parser/statement/labelled_stm/mod.rs @@ -2,9 +2,8 @@ use std::io::Read; use super::{LabelIdentifier, Statement}; use crate::{ - syntax::ast::Node, syntax::{ - ast::Punctuator, + ast::{Node, NodeKind, Punctuator}, parser::{ cursor::Cursor, error::ParseError, AllowAwait, AllowReturn, AllowYield, TokenParser, }, @@ -49,7 +48,9 @@ where fn parse(self, cursor: &mut Cursor) -> Result { let _timer = BoaProfiler::global().start_event("Label", "Parsing"); - let name = LabelIdentifier::new(self.allow_yield, self.allow_await).parse(cursor)?; + + let (name, name_span) = + LabelIdentifier::new(self.allow_yield, self.allow_await).parse(cursor)?; cursor.expect(Punctuator::Colon, "Labelled Statement")?; let mut stmt = Statement::new(self.allow_yield, self.allow_await, self.allow_return).parse(cursor)?; @@ -60,12 +61,12 @@ where } fn set_label_for_node(stmt: &mut Node, name: Box) { - match stmt { - Node::ForLoop(ref mut for_loop) => for_loop.set_label(name), - Node::ForOfLoop(ref mut for_of_loop) => for_of_loop.set_label(name), - Node::ForInLoop(ref mut for_in_loop) => for_in_loop.set_label(name), - Node::DoWhileLoop(ref mut do_while_loop) => do_while_loop.set_label(name), - Node::WhileLoop(ref mut while_loop) => while_loop.set_label(name), + match stmt.kind() { + NodeKind::ForLoop(ref mut for_loop) => for_loop.set_label(name), + NodeKind::ForOfLoop(ref mut for_of_loop) => for_of_loop.set_label(name), + NodeKind::ForInLoop(ref mut for_in_loop) => for_in_loop.set_label(name), + NodeKind::DoWhileLoop(ref mut do_while_loop) => do_while_loop.set_label(name), + NodeKind::WhileLoop(ref mut while_loop) => while_loop.set_label(name), _ => (), } } diff --git a/boa/src/syntax/parser/statement/mod.rs b/boa/src/syntax/parser/statement/mod.rs index d77eb58bde0..c414241933f 100644 --- a/boa/src/syntax/parser/statement/mod.rs +++ b/boa/src/syntax/parser/statement/mod.rs @@ -40,7 +40,7 @@ use super::{AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser use crate::{ syntax::{ - ast::{node, Keyword, Node, Punctuator}, + ast::{node, Keyword, Node, NodeKind, Punctuator, Span}, lexer::{Error as LexError, InputElement, Position, TokenKind}, parser::expression::await_expr::AwaitExpression, }, @@ -106,7 +106,7 @@ where { type Output = Node; - fn parse(self, cursor: &mut Cursor) -> Result { + fn parse(self, cursor: &mut Cursor) -> ParseResult { let _timer = BoaProfiler::global().start_event("Statement", "Parsing"); // TODO: add BreakableStatement and divide Whiles, fors and so on to another place. let tok = cursor.peek(0)?.ok_or(ParseError::AbruptEnd)?; @@ -114,37 +114,37 @@ where match tok.kind() { TokenKind::Keyword(Keyword::Await) => AwaitExpression::new(self.allow_yield) .parse(cursor) - .map(Node::from), + .map(|(kind, span)| Node::new(kind, span)), TokenKind::Keyword(Keyword::If) => { IfStatement::new(self.allow_yield, self.allow_await, self.allow_return) .parse(cursor) - .map(Node::from) + .map(|(kind, span)| Node::new(kind, span)) } TokenKind::Keyword(Keyword::Var) => { VariableStatement::new(self.allow_yield, self.allow_await) .parse(cursor) - .map(Node::from) + .map(|(kind, span)| Node::new(kind, span)) } TokenKind::Keyword(Keyword::While) => { WhileStatement::new(self.allow_yield, self.allow_await, self.allow_return) .parse(cursor) - .map(Node::from) + .map(|(kind, span)| Node::new(kind, span)) } TokenKind::Keyword(Keyword::Do) => { DoWhileStatement::new(self.allow_yield, self.allow_await, self.allow_return) .parse(cursor) - .map(Node::from) + .map(|(kind, span)| Node::new(kind, span)) } TokenKind::Keyword(Keyword::For) => { ForStatement::new(self.allow_yield, self.allow_await, self.allow_return) .parse(cursor) - .map(Node::from) + .map(|(kind, span)| Node::new(kind, span)) } TokenKind::Keyword(Keyword::Return) => { if self.allow_return.0 { ReturnStatement::new(self.allow_yield, self.allow_await) .parse(cursor) - .map(Node::from) + .map(|(kind, span)| Node::new(kind, span)) } else { Err(ParseError::unexpected(tok.clone(), "statement")) } @@ -152,37 +152,37 @@ where TokenKind::Keyword(Keyword::Break) => { BreakStatement::new(self.allow_yield, self.allow_await) .parse(cursor) - .map(Node::from) + .map(|(kind, span)| Node::new(kind, span)) } TokenKind::Keyword(Keyword::Continue) => { ContinueStatement::new(self.allow_yield, self.allow_await) .parse(cursor) - .map(Node::from) + .map(|(kind, span)| Node::new(kind, span)) } TokenKind::Keyword(Keyword::Try) => { TryStatement::new(self.allow_yield, self.allow_await, self.allow_return) .parse(cursor) - .map(Node::from) + .map(|(kind, span)| Node::new(kind, span)) } TokenKind::Keyword(Keyword::Throw) => { ThrowStatement::new(self.allow_yield, self.allow_await) .parse(cursor) - .map(Node::from) + .map(|(kind, span)| Node::new(kind, span)) } TokenKind::Keyword(Keyword::Switch) => { SwitchStatement::new(self.allow_yield, self.allow_await, self.allow_return) .parse(cursor) - .map(Node::from) + .map(|(kind, span)| Node::new(kind, span)) } TokenKind::Punctuator(Punctuator::OpenBlock) => { BlockStatement::new(self.allow_yield, self.allow_await, self.allow_return) .parse(cursor) - .map(Node::from) + .map(|(kind, span)| Node::new(kind, span)) } TokenKind::Punctuator(Punctuator::Semicolon) => { // parse the EmptyStatement cursor.next().expect("semicolon disappeared"); - Ok(Node::Empty) + Ok(Node::new(NodeKind::Empty, tok.span())) } TokenKind::Identifier(_) => { // Labelled Statement check @@ -200,7 +200,7 @@ where self.allow_return, ) .parse(cursor) - .map(Node::from); + .map(|(kind, span)| Node::new(kind, span)); } ExpressionStatement::new(self.allow_yield, self.allow_await).parse(cursor) @@ -254,7 +254,7 @@ impl TokenParser for StatementList where R: Read, { - type Output = node::StatementList; + type Output = Option; /// The function parses a node::StatementList using the StatementList's /// break_nodes to know when to terminate. @@ -297,9 +297,9 @@ where let mut var_declared_names: HashSet<&str> = HashSet::new(); // TODO: Use more helpful positions in errors when spans are added to Nodes - for item in &items { + for item in items.iter().map(Node::kind) { match item { - Node::LetDeclList(decl_list) | Node::ConstDeclList(decl_list) => { + NodeKind::LetDeclList(decl_list) | NodeKind::ConstDeclList(decl_list) => { for decl in decl_list.as_ref() { // if name in VarDeclaredNames or can't be added to // LexicallyDeclaredNames, raise an error @@ -316,7 +316,7 @@ where } } } - Node::VarDeclList(decl_list) => { + NodeKind::VarDeclList(decl_list) => { for decl in decl_list.as_ref() { // if name in LexicallyDeclaredNames, raise an error if lexically_declared_names.contains(decl.name()) { @@ -339,7 +339,14 @@ where items.sort_by(Node::hoistable_order); - Ok(items.into()) + if items.is_empty() { + Ok(None) + } else { + let start = items.first().expect("item disappeared").span().start(); + let end = items.last().expect("item disappeared").span().end(); + + Ok(Some(node::StatementList::new(items, Span::new(start, end)))) + } } } @@ -449,7 +456,7 @@ impl TokenParser for BindingIdentifier where R: Read, { - type Output = Box; + type Output = (Box, Span); /// Strict mode parsing as per . fn parse(self, cursor: &mut Cursor) -> Result { @@ -457,7 +464,7 @@ where let next_token = cursor.next()?.ok_or(ParseError::AbruptEnd)?; - match next_token.kind() { + let ident = match next_token.kind() { TokenKind::Identifier(ref s) => Ok(s.clone()), TokenKind::Keyword(k @ Keyword::Yield) if !self.allow_yield.0 => { if cursor.strict_mode() { @@ -484,6 +491,8 @@ where next_token, "binding identifier", )), - } + }?; + + Ok((ident, next_token.span())) } } diff --git a/boa/src/syntax/parser/statement/variable/mod.rs b/boa/src/syntax/parser/statement/variable/mod.rs index db4adabad56..90ccb5b870e 100644 --- a/boa/src/syntax/parser/statement/variable/mod.rs +++ b/boa/src/syntax/parser/statement/variable/mod.rs @@ -4,7 +4,7 @@ use crate::{ syntax::{ ast::{ node::{Declaration, DeclarationList}, - Keyword, Punctuator, + Keyword, Punctuator, Span, }, lexer::TokenKind, parser::{ @@ -52,18 +52,25 @@ impl TokenParser for VariableStatement where R: Read, { - type Output = DeclarationList; + type Output = (DeclarationList, Span); fn parse(self, cursor: &mut Cursor) -> Result { let _timer = BoaProfiler::global().start_event("VariableStatement", "Parsing"); - cursor.expect(Keyword::Var, "variable statement")?; - let decl_list = + let start_token = cursor.expect(Keyword::Var, "variable statement")?; + + let (decl_list, list_span) = VariableDeclarationList::new(true, self.allow_yield, self.allow_await).parse(cursor)?; - cursor.expect_semicolon("variable statement")?; + let end_token = cursor.expect_semicolon("variable statement")?; + let end_pos = if let Some(tk) = end_token { + tk.span().end() + } else { + list_span.end() + }; + let span = Span::new(start_token.span().start(), end_pos); - Ok(decl_list) + Ok((decl_list, span)) } } @@ -106,15 +113,17 @@ impl TokenParser for VariableDeclarationList where R: Read, { - type Output = DeclarationList; + type Output = (DeclarationList, Span); fn parse(self, cursor: &mut Cursor) -> Result { let mut list = Vec::new(); + let start_pos = cursor.pos(); loop { + let decl = VariableDeclaration::new(self.allow_in, self.allow_yield, self.allow_await) + .parse(cursor)? list.push( - VariableDeclaration::new(self.allow_in, self.allow_yield, self.allow_await) - .parse(cursor)?, + decl ); match cursor.peek_semicolon()? { @@ -127,7 +136,11 @@ where } } - Ok(DeclarationList::Var(list.into())) + let end_pos = cursor.pos(); + + let span = Span::new(start_pos, end_pos); + + Ok((DeclarationList::Var(list.into()), span)) } } @@ -164,7 +177,7 @@ impl TokenParser for VariableDeclaration where R: Read, { - type Output = Declaration; + type Output = (Declaration, Span); fn parse(self, cursor: &mut Cursor) -> Result { // TODO: BindingPattern