summaryrefslogtreecommitdiff
path: root/Libraries/LibJS
diff options
context:
space:
mode:
authorMatthew Olsson <matthewcolsson@gmail.com>2020-05-28 13:36:59 -0700
committerAndreas Kling <kling@serenityos.org>2020-05-29 16:20:32 +0200
commitd52ea37717eb80388606d2e5945599d00ac0688d (patch)
treed575e1f80ac654ffc62be73bfbab918aeab8993d /Libraries/LibJS
parent03615a7872849dc63f0495e779b6ce6f6c09f11c (diff)
downloadserenity-d52ea37717eb80388606d2e5945599d00ac0688d.zip
LibJS: Integrate labels into the Interpreter
The interpreter now considers a statement or block's label when considering whether or not to break. All statements can be labelled.
Diffstat (limited to 'Libraries/LibJS')
-rw-r--r--Libraries/LibJS/AST.cpp22
-rw-r--r--Libraries/LibJS/Interpreter.cpp5
-rw-r--r--Libraries/LibJS/Interpreter.h14
-rw-r--r--Libraries/LibJS/Parser.cpp12
-rw-r--r--Libraries/LibJS/Tests/automatic-semicolon-insertion.js21
-rw-r--r--Libraries/LibJS/Tests/labels.js34
6 files changed, 87 insertions, 21 deletions
diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp
index 55db8784d8..f3cad3a386 100644
--- a/Libraries/LibJS/AST.cpp
+++ b/Libraries/LibJS/AST.cpp
@@ -282,9 +282,9 @@ Value ForStatement::execute(Interpreter& interpreter) const
if (interpreter.exception())
return {};
if (interpreter.should_unwind()) {
- if (interpreter.should_unwind_until(ScopeType::Continuable)) {
+ if (interpreter.should_unwind_until(ScopeType::Continuable, m_label)) {
interpreter.stop_unwind();
- } else if (interpreter.should_unwind_until(ScopeType::Breakable)) {
+ } else if (interpreter.should_unwind_until(ScopeType::Breakable, m_label)) {
interpreter.stop_unwind();
break;
} else {
@@ -303,9 +303,9 @@ Value ForStatement::execute(Interpreter& interpreter) const
if (interpreter.exception())
return {};
if (interpreter.should_unwind()) {
- if (interpreter.should_unwind_until(ScopeType::Continuable)) {
+ if (interpreter.should_unwind_until(ScopeType::Continuable, m_label)) {
interpreter.stop_unwind();
- } else if (interpreter.should_unwind_until(ScopeType::Breakable)) {
+ } else if (interpreter.should_unwind_until(ScopeType::Breakable, m_label)) {
interpreter.stop_unwind();
break;
} else {
@@ -370,9 +370,9 @@ Value ForInStatement::execute(Interpreter& interpreter) const
if (interpreter.exception())
return {};
if (interpreter.should_unwind()) {
- if (interpreter.should_unwind_until(ScopeType::Continuable)) {
+ if (interpreter.should_unwind_until(ScopeType::Continuable, m_label)) {
interpreter.stop_unwind();
- } else if (interpreter.should_unwind_until(ScopeType::Breakable)) {
+ } else if (interpreter.should_unwind_until(ScopeType::Breakable, m_label)) {
interpreter.stop_unwind();
break;
} else {
@@ -437,9 +437,9 @@ Value ForOfStatement::execute(Interpreter& interpreter) const
if (interpreter.exception())
return {};
if (interpreter.should_unwind()) {
- if (interpreter.should_unwind_until(ScopeType::Continuable)) {
+ if (interpreter.should_unwind_until(ScopeType::Continuable, m_label)) {
interpreter.stop_unwind();
- } else if (interpreter.should_unwind_until(ScopeType::Breakable)) {
+ } else if (interpreter.should_unwind_until(ScopeType::Breakable, m_label)) {
interpreter.stop_unwind();
break;
} else {
@@ -1635,7 +1635,7 @@ Value SwitchStatement::execute(Interpreter& interpreter) const
if (interpreter.exception())
return {};
if (interpreter.should_unwind()) {
- if (interpreter.should_unwind_until(ScopeType::Breakable)) {
+ if (interpreter.should_unwind_until(ScopeType::Breakable, m_label)) {
interpreter.stop_unwind();
return {};
}
@@ -1655,13 +1655,13 @@ Value SwitchCase::execute(Interpreter& interpreter) const
Value BreakStatement::execute(Interpreter& interpreter) const
{
- interpreter.unwind(ScopeType::Breakable);
+ interpreter.unwind(ScopeType::Breakable, m_target_label);
return js_undefined();
}
Value ContinueStatement::execute(Interpreter& interpreter) const
{
- interpreter.unwind(ScopeType::Continuable);
+ interpreter.unwind(ScopeType::Continuable, m_target_label);
return js_undefined();
}
diff --git a/Libraries/LibJS/Interpreter.cpp b/Libraries/LibJS/Interpreter.cpp
index efee9b940a..28b52900f6 100644
--- a/Libraries/LibJS/Interpreter.cpp
+++ b/Libraries/LibJS/Interpreter.cpp
@@ -72,8 +72,11 @@ Value Interpreter::run(const Statement& statement, ArgumentVector arguments, Sco
m_last_value = js_undefined();
for (auto& node : block.children()) {
m_last_value = node.execute(*this);
- if (m_unwind_until != ScopeType::None)
+ if (should_unwind()) {
+ if (should_unwind_until(ScopeType::Breakable, block.label()))
+ stop_unwind();
break;
+ }
}
bool did_return = m_unwind_until == ScopeType::Function;
diff --git a/Libraries/LibJS/Interpreter.h b/Libraries/LibJS/Interpreter.h
index e0ffbb6722..659d7e67f8 100644
--- a/Libraries/LibJS/Interpreter.h
+++ b/Libraries/LibJS/Interpreter.h
@@ -91,9 +91,18 @@ public:
Heap& heap() { return m_heap; }
- void unwind(ScopeType type) { m_unwind_until = type; }
+ void unwind(ScopeType type, FlyString label = {})
+ {
+ m_unwind_until = type;
+ m_unwind_until_label = label;
+ }
void stop_unwind() { m_unwind_until = ScopeType::None; }
- bool should_unwind_until(ScopeType type) const { return m_unwind_until == type; }
+ bool should_unwind_until(ScopeType type, FlyString label) const
+ {
+ if (m_unwind_until_label.is_null())
+ return m_unwind_until == type;
+ return m_unwind_until == type && m_unwind_until_label == label;
+ }
bool should_unwind() const { return m_unwind_until != ScopeType::None; }
Value get_variable(const FlyString& name);
@@ -189,6 +198,7 @@ private:
Exception* m_exception { nullptr };
ScopeType m_unwind_until { ScopeType::None };
+ FlyString m_unwind_until_label;
Console m_console;
};
diff --git a/Libraries/LibJS/Parser.cpp b/Libraries/LibJS/Parser.cpp
index 86c7c70f1d..651bfe2ec4 100644
--- a/Libraries/LibJS/Parser.cpp
+++ b/Libraries/LibJS/Parser.cpp
@@ -269,9 +269,11 @@ NonnullRefPtr<Statement> Parser::parse_statement()
consume();
return create_ast_node<EmptyStatement>();
default:
- auto result = try_parse_labelled_statement();
- if (!result.is_null())
- return result.release_nonnull();
+ if (match(TokenType::Identifier)) {
+ auto result = try_parse_labelled_statement();
+ if (!result.is_null())
+ return result.release_nonnull();
+ }
if (match_expression()) {
auto expr = parse_expression(0);
consume_or_insert_semicolon();
@@ -1117,7 +1119,7 @@ NonnullRefPtr<BreakStatement> Parser::parse_break_statement()
consume();
return create_ast_node<BreakStatement>(target_label);
}
- if (match_identifier_name() && !m_parser_state.m_current_token.trivia().contains('\n'))
+ if (match(TokenType::Identifier) && !m_parser_state.m_current_token.trivia().contains('\n'))
target_label = consume().value();
consume_or_insert_semicolon();
return create_ast_node<BreakStatement>(target_label);
@@ -1131,7 +1133,7 @@ NonnullRefPtr<ContinueStatement> Parser::parse_continue_statement()
consume();
return create_ast_node<ContinueStatement>(target_label);
}
- if (match_identifier_name() && !m_parser_state.m_current_token.trivia().contains('\n'))
+ if (match(TokenType::Identifier) && !m_parser_state.m_current_token.trivia().contains('\n'))
target_label = consume().value();
consume_or_insert_semicolon();
return create_ast_node<ContinueStatement>(target_label);
diff --git a/Libraries/LibJS/Tests/automatic-semicolon-insertion.js b/Libraries/LibJS/Tests/automatic-semicolon-insertion.js
index 0673e2bf69..f3c4d3bc00 100644
--- a/Libraries/LibJS/Tests/automatic-semicolon-insertion.js
+++ b/Libraries/LibJS/Tests/automatic-semicolon-insertion.js
@@ -25,9 +25,13 @@ function bar() {
}
function foo() {
+ label:
for (var i = 0; i < 4; i++) {
break // semicolon inserted here
continue // semicolon inserted here
+
+ break label // semicolon inserted here
+ continue label // semicolon inserted here
}
var j // semicolon inserted here
@@ -39,8 +43,25 @@ function foo() {
1;
var curly/* semicolon inserted here */}
+function baz() {
+ let counter = 0;
+ let outer;
+
+ outer:
+ for (let i = 0; i < 5; ++i) {
+ for (let j = 0; j < 5; ++j) {
+ continue // semicolon inserted here
+ outer // semicolon inserted here
+ }
+ counter++;
+ }
+
+ return counter;
+}
+
try {
assert(foo() === undefined);
+ assert(baz() === 5);
console.log("PASS");
} catch (e) {
diff --git a/Libraries/LibJS/Tests/labels.js b/Libraries/LibJS/Tests/labels.js
index 21150b296f..4388cac823 100644
--- a/Libraries/LibJS/Tests/labels.js
+++ b/Libraries/LibJS/Tests/labels.js
@@ -1,12 +1,42 @@
load("test-common.js");
try {
- test:
- {
+ test: {
let o = 1;
assert(o === 1);
+ break test;
+ assertNotReached();
}
+ outer: {
+ {
+ break outer;
+ }
+ assertNotReached();
+ }
+
+ let counter = 0;
+ outer:
+ for (a of [1, 2, 3]) {
+ for (b of [4, 5, 6]) {
+ if (a === 2 && b === 5)
+ break outer;
+ counter++;
+ }
+ }
+ assert(counter === 4);
+
+ let counter = 0;
+ outer:
+ for (a of [1, 2, 3]) {
+ for (b of [4, 5, 6]) {
+ if (b === 6)
+ continue outer;
+ counter++;
+ }
+ }
+ assert(counter === 6);
+
console.log("PASS");
} catch (e) {
console.log("FAIL: " + e);