diff --git a/boa/src/exec/iteration/mod.rs b/boa/src/exec/iteration/mod.rs index 8835207ec9f..fbbc0421aa7 100644 --- a/boa/src/exec/iteration/mod.rs +++ b/boa/src/exec/iteration/mod.rs @@ -36,8 +36,17 @@ impl Executable for ForLoop { let result = self.body().run(interpreter)?; match interpreter.get_current_state() { - InterpreterState::Break(_label) => { - // TODO break to label. + InterpreterState::Break(label) => { + // If a label is set we want to break the current block and still keep state as Break if the label is a block above + if let Some(stmt_label) = &self.label { + if let Some(brk_label) = label { + // We have a label, but not for the current statement + // break without resetting to executing + if stmt_label != brk_label { + break; + } + } + } // Loops 'consume' breaks. interpreter.set_current_state(InterpreterState::Executing); diff --git a/boa/src/syntax/ast/node/iteration.rs b/boa/src/syntax/ast/node/iteration.rs index d84a12499f3..8a5918dea7b 100644 --- a/boa/src/syntax/ast/node/iteration.rs +++ b/boa/src/syntax/ast/node/iteration.rs @@ -21,11 +21,18 @@ use serde::{Deserialize, Serialize}; pub struct ForLoop { #[cfg_attr(feature = "serde", serde(flatten))] inner: Box, + pub label: Option, } impl ForLoop { /// Creates a new for loop AST node. - pub(in crate::syntax) fn new(init: I, condition: C, final_expr: E, body: B) -> Self + pub(in crate::syntax) fn new( + init: I, + condition: C, + final_expr: E, + body: B, + label: Option, + ) -> Self where I: Into>, C: Into>, @@ -34,6 +41,7 @@ impl ForLoop { { Self { inner: Box::new(InnerForLoop::new(init, condition, final_expr, body)), + label, } } diff --git a/boa/src/syntax/parser/statement/iteration/for_statement.rs b/boa/src/syntax/parser/statement/iteration/for_statement.rs index bd1ed7c6a43..41e2472e359 100644 --- a/boa/src/syntax/parser/statement/iteration/for_statement.rs +++ b/boa/src/syntax/parser/statement/iteration/for_statement.rs @@ -31,11 +31,12 @@ use crate::{ /// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for /// [spec]: https://tc39.es/ecma262/#sec-for-statement -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] pub(in crate::syntax::parser::statement) struct ForStatement { allow_yield: AllowYield, allow_await: AllowAwait, allow_return: AllowReturn, + label: Option, } impl ForStatement { @@ -44,6 +45,7 @@ impl ForStatement { allow_yield: Y, allow_await: A, allow_return: R, + label: Option, ) -> Self where Y: Into, @@ -54,6 +56,7 @@ impl ForStatement { allow_yield: allow_yield.into(), allow_await: allow_await.into(), allow_return: allow_return.into(), + label, } } } @@ -104,6 +107,6 @@ impl TokenParser for ForStatement { Statement::new(self.allow_yield, self.allow_await, self.allow_return).parse(cursor)?; // TODO: do not encapsulate the `for` in a block just to have an inner scope. - Ok(ForLoop::new(init, cond, step, body)) + Ok(ForLoop::new(init, cond, step, body, self.label)) } } diff --git a/boa/src/syntax/parser/statement/labelled_stm/mod.rs b/boa/src/syntax/parser/statement/labelled_stm/mod.rs new file mode 100644 index 00000000000..95bc350f069 --- /dev/null +++ b/boa/src/syntax/parser/statement/labelled_stm/mod.rs @@ -0,0 +1,50 @@ +use super::LabelIdentifier; +use crate::{ + syntax::{ + ast::Punctuator, + parser::{ + cursor::Cursor, error::ParseError, AllowAwait, AllowReturn, AllowYield, TokenParser, + }, + }, + BoaProfiler, +}; +/// Labelled Statement Parsing +/// +/// More information +/// - [MDN documentation][mdn] +/// - [ECMAScript specification][spec] +/// +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/label +/// [spec]: https://tc39.es/ecma262/#sec-labelled-statements +#[derive(Debug, Clone, Copy)] +pub(super) struct Label { + allow_yield: AllowYield, + allow_await: AllowAwait, + allow_return: AllowReturn, +} + +impl Label { + pub(super) fn new(allow_yield: Y, allow_await: A, allow_return: R) -> Self + where + Y: Into, + A: Into, + R: Into, + { + Self { + allow_yield: allow_yield.into(), + allow_await: allow_await.into(), + allow_return: allow_return.into(), + } + } +} + +impl TokenParser for Label { + type Output = String; + + 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)?; + cursor.expect(Punctuator::Colon, "Labelled Statement")?; + Ok(name.to_string()) + } +} diff --git a/boa/src/syntax/parser/statement/mod.rs b/boa/src/syntax/parser/statement/mod.rs index 274871fd439..3b88492ae93 100644 --- a/boa/src/syntax/parser/statement/mod.rs +++ b/boa/src/syntax/parser/statement/mod.rs @@ -14,6 +14,7 @@ mod declaration; mod expression; mod if_stm; mod iteration; +mod labelled_stm; mod return_stm; mod switch; mod throw; @@ -39,6 +40,7 @@ use crate::{ syntax::ast::{node, Keyword, Node, Punctuator, TokenKind}, BoaProfiler, }; +use labelled_stm::Label; /// Statement parsing. /// @@ -95,7 +97,15 @@ impl TokenParser for Statement { fn parse(self, cursor: &mut Cursor<'_>) -> Result { 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)?; + let mut tok = cursor.peek(0).ok_or(ParseError::AbruptEnd)?; + + // Is this statement prefixed with a label? + let label = + Label::new(self.allow_yield, self.allow_await, self.allow_return).try_parse(cursor); + + if label.is_some() { + tok = cursor.peek(0).ok_or(ParseError::AbruptEnd)?; + } match tok.kind { TokenKind::Keyword(Keyword::If) => { @@ -119,7 +129,7 @@ impl TokenParser for Statement { .map(Node::from) } TokenKind::Keyword(Keyword::For) => { - ForStatement::new(self.allow_yield, self.allow_await, self.allow_return) + ForStatement::new(self.allow_yield, self.allow_await, self.allow_return, label) .parse(cursor) .map(Node::from) } @@ -162,6 +172,9 @@ impl TokenParser for Statement { .parse(cursor) .map(Node::from) } + // TokenKind::Identifier(name) => LabelIdentifier::new(self.allow_yield, self.allow_await) + // .parse(cursor) + // .map(Node::from), // TODO: https://tc39.es/ecma262/#prod-LabelledStatement // TokenKind::Punctuator(Punctuator::Semicolon) => { // return Ok(Node::new(NodeBase::Nope, tok.pos)) @@ -350,7 +363,7 @@ impl TokenParser for StatementListItem { /// - [ECMAScript specification][spec] /// /// [spec]: https://tc39.es/ecma262/#prod-LabelIdentifier -type LabelIdentifier = BindingIdentifier; +pub(super) type LabelIdentifier = BindingIdentifier; /// Binding identifier parsing. ///