mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 19:07:35 +00:00
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.
This commit is contained in:
parent
03615a7872
commit
d52ea37717
6 changed files with 87 additions and 21 deletions
|
@ -282,9 +282,9 @@ Value ForStatement::execute(Interpreter& interpreter) const
|
||||||
if (interpreter.exception())
|
if (interpreter.exception())
|
||||||
return {};
|
return {};
|
||||||
if (interpreter.should_unwind()) {
|
if (interpreter.should_unwind()) {
|
||||||
if (interpreter.should_unwind_until(ScopeType::Continuable)) {
|
if (interpreter.should_unwind_until(ScopeType::Continuable, m_label)) {
|
||||||
interpreter.stop_unwind();
|
interpreter.stop_unwind();
|
||||||
} else if (interpreter.should_unwind_until(ScopeType::Breakable)) {
|
} else if (interpreter.should_unwind_until(ScopeType::Breakable, m_label)) {
|
||||||
interpreter.stop_unwind();
|
interpreter.stop_unwind();
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
@ -303,9 +303,9 @@ Value ForStatement::execute(Interpreter& interpreter) const
|
||||||
if (interpreter.exception())
|
if (interpreter.exception())
|
||||||
return {};
|
return {};
|
||||||
if (interpreter.should_unwind()) {
|
if (interpreter.should_unwind()) {
|
||||||
if (interpreter.should_unwind_until(ScopeType::Continuable)) {
|
if (interpreter.should_unwind_until(ScopeType::Continuable, m_label)) {
|
||||||
interpreter.stop_unwind();
|
interpreter.stop_unwind();
|
||||||
} else if (interpreter.should_unwind_until(ScopeType::Breakable)) {
|
} else if (interpreter.should_unwind_until(ScopeType::Breakable, m_label)) {
|
||||||
interpreter.stop_unwind();
|
interpreter.stop_unwind();
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
@ -370,9 +370,9 @@ Value ForInStatement::execute(Interpreter& interpreter) const
|
||||||
if (interpreter.exception())
|
if (interpreter.exception())
|
||||||
return {};
|
return {};
|
||||||
if (interpreter.should_unwind()) {
|
if (interpreter.should_unwind()) {
|
||||||
if (interpreter.should_unwind_until(ScopeType::Continuable)) {
|
if (interpreter.should_unwind_until(ScopeType::Continuable, m_label)) {
|
||||||
interpreter.stop_unwind();
|
interpreter.stop_unwind();
|
||||||
} else if (interpreter.should_unwind_until(ScopeType::Breakable)) {
|
} else if (interpreter.should_unwind_until(ScopeType::Breakable, m_label)) {
|
||||||
interpreter.stop_unwind();
|
interpreter.stop_unwind();
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
@ -437,9 +437,9 @@ Value ForOfStatement::execute(Interpreter& interpreter) const
|
||||||
if (interpreter.exception())
|
if (interpreter.exception())
|
||||||
return {};
|
return {};
|
||||||
if (interpreter.should_unwind()) {
|
if (interpreter.should_unwind()) {
|
||||||
if (interpreter.should_unwind_until(ScopeType::Continuable)) {
|
if (interpreter.should_unwind_until(ScopeType::Continuable, m_label)) {
|
||||||
interpreter.stop_unwind();
|
interpreter.stop_unwind();
|
||||||
} else if (interpreter.should_unwind_until(ScopeType::Breakable)) {
|
} else if (interpreter.should_unwind_until(ScopeType::Breakable, m_label)) {
|
||||||
interpreter.stop_unwind();
|
interpreter.stop_unwind();
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1635,7 +1635,7 @@ Value SwitchStatement::execute(Interpreter& interpreter) const
|
||||||
if (interpreter.exception())
|
if (interpreter.exception())
|
||||||
return {};
|
return {};
|
||||||
if (interpreter.should_unwind()) {
|
if (interpreter.should_unwind()) {
|
||||||
if (interpreter.should_unwind_until(ScopeType::Breakable)) {
|
if (interpreter.should_unwind_until(ScopeType::Breakable, m_label)) {
|
||||||
interpreter.stop_unwind();
|
interpreter.stop_unwind();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -1655,13 +1655,13 @@ Value SwitchCase::execute(Interpreter& interpreter) const
|
||||||
|
|
||||||
Value BreakStatement::execute(Interpreter& interpreter) const
|
Value BreakStatement::execute(Interpreter& interpreter) const
|
||||||
{
|
{
|
||||||
interpreter.unwind(ScopeType::Breakable);
|
interpreter.unwind(ScopeType::Breakable, m_target_label);
|
||||||
return js_undefined();
|
return js_undefined();
|
||||||
}
|
}
|
||||||
|
|
||||||
Value ContinueStatement::execute(Interpreter& interpreter) const
|
Value ContinueStatement::execute(Interpreter& interpreter) const
|
||||||
{
|
{
|
||||||
interpreter.unwind(ScopeType::Continuable);
|
interpreter.unwind(ScopeType::Continuable, m_target_label);
|
||||||
return js_undefined();
|
return js_undefined();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,8 +72,11 @@ Value Interpreter::run(const Statement& statement, ArgumentVector arguments, Sco
|
||||||
m_last_value = js_undefined();
|
m_last_value = js_undefined();
|
||||||
for (auto& node : block.children()) {
|
for (auto& node : block.children()) {
|
||||||
m_last_value = node.execute(*this);
|
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;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool did_return = m_unwind_until == ScopeType::Function;
|
bool did_return = m_unwind_until == ScopeType::Function;
|
||||||
|
|
|
@ -91,9 +91,18 @@ public:
|
||||||
|
|
||||||
Heap& heap() { return m_heap; }
|
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; }
|
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; }
|
bool should_unwind() const { return m_unwind_until != ScopeType::None; }
|
||||||
|
|
||||||
Value get_variable(const FlyString& name);
|
Value get_variable(const FlyString& name);
|
||||||
|
@ -189,6 +198,7 @@ private:
|
||||||
Exception* m_exception { nullptr };
|
Exception* m_exception { nullptr };
|
||||||
|
|
||||||
ScopeType m_unwind_until { ScopeType::None };
|
ScopeType m_unwind_until { ScopeType::None };
|
||||||
|
FlyString m_unwind_until_label;
|
||||||
|
|
||||||
Console m_console;
|
Console m_console;
|
||||||
};
|
};
|
||||||
|
|
|
@ -269,9 +269,11 @@ NonnullRefPtr<Statement> Parser::parse_statement()
|
||||||
consume();
|
consume();
|
||||||
return create_ast_node<EmptyStatement>();
|
return create_ast_node<EmptyStatement>();
|
||||||
default:
|
default:
|
||||||
auto result = try_parse_labelled_statement();
|
if (match(TokenType::Identifier)) {
|
||||||
if (!result.is_null())
|
auto result = try_parse_labelled_statement();
|
||||||
return result.release_nonnull();
|
if (!result.is_null())
|
||||||
|
return result.release_nonnull();
|
||||||
|
}
|
||||||
if (match_expression()) {
|
if (match_expression()) {
|
||||||
auto expr = parse_expression(0);
|
auto expr = parse_expression(0);
|
||||||
consume_or_insert_semicolon();
|
consume_or_insert_semicolon();
|
||||||
|
@ -1117,7 +1119,7 @@ NonnullRefPtr<BreakStatement> Parser::parse_break_statement()
|
||||||
consume();
|
consume();
|
||||||
return create_ast_node<BreakStatement>(target_label);
|
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();
|
target_label = consume().value();
|
||||||
consume_or_insert_semicolon();
|
consume_or_insert_semicolon();
|
||||||
return create_ast_node<BreakStatement>(target_label);
|
return create_ast_node<BreakStatement>(target_label);
|
||||||
|
@ -1131,7 +1133,7 @@ NonnullRefPtr<ContinueStatement> Parser::parse_continue_statement()
|
||||||
consume();
|
consume();
|
||||||
return create_ast_node<ContinueStatement>(target_label);
|
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();
|
target_label = consume().value();
|
||||||
consume_or_insert_semicolon();
|
consume_or_insert_semicolon();
|
||||||
return create_ast_node<ContinueStatement>(target_label);
|
return create_ast_node<ContinueStatement>(target_label);
|
||||||
|
|
|
@ -25,9 +25,13 @@ function bar() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function foo() {
|
function foo() {
|
||||||
|
label:
|
||||||
for (var i = 0; i < 4; i++) {
|
for (var i = 0; i < 4; i++) {
|
||||||
break // semicolon inserted here
|
break // semicolon inserted here
|
||||||
continue // semicolon inserted here
|
continue // semicolon inserted here
|
||||||
|
|
||||||
|
break label // semicolon inserted here
|
||||||
|
continue label // semicolon inserted here
|
||||||
}
|
}
|
||||||
|
|
||||||
var j // semicolon inserted here
|
var j // semicolon inserted here
|
||||||
|
@ -39,8 +43,25 @@ function foo() {
|
||||||
1;
|
1;
|
||||||
var curly/* semicolon inserted here */}
|
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 {
|
try {
|
||||||
assert(foo() === undefined);
|
assert(foo() === undefined);
|
||||||
|
assert(baz() === 5);
|
||||||
|
|
||||||
console.log("PASS");
|
console.log("PASS");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -1,12 +1,42 @@
|
||||||
load("test-common.js");
|
load("test-common.js");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
test:
|
test: {
|
||||||
{
|
|
||||||
let o = 1;
|
let o = 1;
|
||||||
assert(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");
|
console.log("PASS");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("FAIL: " + e);
|
console.log("FAIL: " + e);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue