Skip to content

Commit

Permalink
Merge fb11b15 into e775743
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonwilliams authored Sep 20, 2020
2 parents e775743 + fb11b15 commit 46c28ba
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 6 deletions.
18 changes: 16 additions & 2 deletions boa/src/exec/iteration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,22 @@ impl Executable for ForLoop {
let result = self.body().run(interpreter)?;

match interpreter.executor().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 executings
if stmt_label.to_string() != *brk_label {
break;
} else {
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
break;
}
}
}

// Loops 'consume' breaks.
interpreter
Expand Down
21 changes: 20 additions & 1 deletion boa/src/exec/iteration/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ fn for_loop_return() {
}
}
}
foo();
"#;

Expand Down Expand Up @@ -191,3 +191,22 @@ fn do_while_loop_continue() {
"#;
assert_eq!(&exec(scenario), "[ 1, 2 ]");
}

#[test]
fn for_loop_break_label() {
let scenario = r#"
var str = "";
loop1: for (let i = 0; i < 5; i++) {
loop2: for (let b = 0; b < 5; b++) {
if (b === 2) {
break loop1;
}
str = str + b;
}
str = str + i;
}
str;
"#;
assert_eq!(&exec(scenario), "\"01\"")
}
6 changes: 6 additions & 0 deletions boa/src/syntax/ast/node/iteration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use serde::{Deserialize, Serialize};
pub struct ForLoop {
#[cfg_attr(feature = "serde", serde(flatten))]
inner: Box<InnerForLoop>,
pub(crate) label: Option<Box<str>>,
}

impl ForLoop {
Expand All @@ -34,6 +35,7 @@ impl ForLoop {
{
Self {
inner: Box::new(InnerForLoop::new(init, condition, final_expr, body)),
label: None,
}
}

Expand Down Expand Up @@ -76,6 +78,10 @@ impl ForLoop {

write!(f, "}}")
}

pub fn set_label(&mut self, label: Box<str>) {
self.label = Some(label);
}
}

impl fmt::Display for ForLoop {
Expand Down
66 changes: 66 additions & 0 deletions boa/src/syntax/parser/statement/labelled_stm/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use std::io::Read;

use super::{LabelIdentifier, Statement};
use crate::{
syntax::ast::Node,
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 LabelledStatement {
allow_yield: AllowYield,
allow_await: AllowAwait,
allow_return: AllowReturn,
}

impl LabelledStatement {
pub(super) fn new<Y, A, R>(allow_yield: Y, allow_await: A, allow_return: R) -> Self
where
Y: Into<AllowYield>,
A: Into<AllowAwait>,
R: Into<AllowReturn>,
{
Self {
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
allow_return: allow_return.into(),
}
}
}

impl<R> TokenParser<R> for LabelledStatement
where
R: Read,
{
type Output = Node;

fn parse(self, cursor: &mut Cursor<R>) -> Result<Self::Output, ParseError> {
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")?;
let mut stmt =
Statement::new(self.allow_yield, self.allow_await, self.allow_return).parse(cursor)?;

set_label_for_node(&mut stmt, name);
Ok(stmt)
}
}

fn set_label_for_node(stmt: &mut Node, name: Box<str>) {
if let Node::ForLoop(ref mut for_loop) = stmt {
for_loop.set_label(name)
}
}
20 changes: 17 additions & 3 deletions boa/src/syntax/parser/statement/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ mod declaration;
mod expression;
mod if_stm;
mod iteration;
mod labelled_stm;
mod return_stm;
mod switch;
mod throw;
Expand Down Expand Up @@ -42,6 +43,7 @@ use crate::{
syntax::ast::{node, Keyword, Node, Punctuator},
BoaProfiler,
};
use labelled_stm::LabelledStatement;

use std::io::Read;

Expand Down Expand Up @@ -103,7 +105,7 @@ where
fn parse(self, cursor: &mut Cursor<R>) -> Result<Self::Output, ParseError> {
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 tok = cursor.peek(0)?.ok_or(ParseError::AbruptEnd)?.to_owned();

match tok.kind() {
TokenKind::Keyword(Keyword::If) => {
Expand Down Expand Up @@ -170,7 +172,19 @@ where
.parse(cursor)
.map(Node::from)
}
// TODO: https://tc39.es/ecma262/#prod-LabelledStatement
// Create guard to check if the next token is a `:` then we know we're sitting on a label
// if not fall back to ExpressionStatement
TokenKind::Identifier(_)
if matches!(
cursor.peek(1)?.ok_or(ParseError::AbruptEnd)?.kind(),
TokenKind::Punctuator(Punctuator::Colon)
) =>
{
LabelledStatement::new(self.allow_yield, self.allow_await, self.allow_return)
.parse(cursor)
.map(Node::from)
}

_ => ExpressionStatement::new(self.allow_yield, self.allow_await).parse(cursor),
}
}
Expand Down Expand Up @@ -367,7 +381,7 @@ where
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-LabelIdentifier
type LabelIdentifier = BindingIdentifier;
pub(super) type LabelIdentifier = BindingIdentifier;

/// Binding identifier parsing.
///
Expand Down

0 comments on commit 46c28ba

Please sign in to comment.