summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAli Mohammad Pur <ali.mpfard@gmail.com>2021-07-11 15:15:38 +0430
committerLinus Groh <mail@linusgroh.de>2021-07-11 21:41:54 +0100
commit77a51442640fdf77348c64331793fc9fc8b973f4 (patch)
tree28b77ccc215129a522dc237c51d6c07743c73e55
parent1a9518ebe313f10091da92e73de76b4ab2f2d875 (diff)
downloadserenity-77a51442640fdf77348c64331793fc9fc8b973f4.zip
LibJS: Add support for binding patterns in catch clauses
`try { ... } catch({a=foo}) {}` is valid, and now we parse and evaluate it correctly :^)
-rw-r--r--Userland/Libraries/LibJS/AST.cpp36
-rw-r--r--Userland/Libraries/LibJS/AST.h11
-rw-r--r--Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp16
-rw-r--r--Userland/Libraries/LibJS/Parser.cpp25
4 files changed, 73 insertions, 15 deletions
diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp
index 6abd3e48dd..b55600f344 100644
--- a/Userland/Libraries/LibJS/AST.cpp
+++ b/Userland/Libraries/LibJS/AST.cpp
@@ -2168,10 +2168,20 @@ void TryStatement::dump(int indent) const
void CatchClause::dump(int indent) const
{
print_indent(indent);
- if (m_parameter.is_null())
- outln("CatchClause");
- else
- outln("CatchClause ({})", m_parameter);
+ m_parameter.visit(
+ [&](FlyString const& parameter) {
+ if (parameter.is_null())
+ outln("CatchClause");
+ else
+ outln("CatchClause ({})", parameter);
+ },
+ [&](NonnullRefPtr<BindingPattern> const& pattern) {
+ outln("CatchClause");
+ print_indent(indent);
+ outln("(Parameter)");
+ pattern->dump(indent + 2);
+ });
+
body().dump(indent + 1);
}
@@ -2191,10 +2201,24 @@ Value TryStatement::execute(Interpreter& interpreter, GlobalObject& global_objec
interpreter.vm().clear_exception();
HashMap<FlyString, Variable> parameters;
- parameters.set(m_handler->parameter(), Variable { exception->value(), DeclarationKind::Var });
+ m_handler->parameter().visit(
+ [&](FlyString const& parameter) {
+ parameters.set(parameter, Variable { exception->value(), DeclarationKind::Var });
+ },
+ [&](NonnullRefPtr<BindingPattern> const& pattern) {
+ pattern->for_each_bound_name([&](auto& name) {
+ parameters.set(name, Variable { Value {}, DeclarationKind::Var });
+ });
+ });
auto* catch_scope = interpreter.heap().allocate<DeclarativeEnvironment>(global_object, move(parameters), interpreter.vm().running_execution_context().lexical_environment);
TemporaryChange<Environment*> scope_change(interpreter.vm().running_execution_context().lexical_environment, catch_scope);
- result = interpreter.execute_statement(global_object, m_handler->body());
+
+ if (auto* pattern = m_handler->parameter().get_pointer<NonnullRefPtr<BindingPattern>>())
+ interpreter.vm().assign(*pattern, exception->value(), global_object, true);
+ if (interpreter.exception())
+ result = js_undefined();
+ else
+ result = interpreter.execute_statement(global_object, m_handler->body());
}
}
diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h
index f3f3ad0673..4c925b48ef 100644
--- a/Userland/Libraries/LibJS/AST.h
+++ b/Userland/Libraries/LibJS/AST.h
@@ -1303,14 +1303,21 @@ public:
{
}
- FlyString const& parameter() const { return m_parameter; }
+ CatchClause(SourceRange source_range, NonnullRefPtr<BindingPattern> parameter, NonnullRefPtr<BlockStatement> body)
+ : ASTNode(source_range)
+ , m_parameter(move(parameter))
+ , m_body(move(body))
+ {
+ }
+
+ auto& parameter() const { return m_parameter; }
BlockStatement const& body() const { return m_body; }
virtual void dump(int indent) const override;
virtual Value execute(Interpreter&, GlobalObject&) const override;
private:
- FlyString m_parameter;
+ Variant<FlyString, NonnullRefPtr<BindingPattern>> m_parameter;
NonnullRefPtr<BlockStatement> m_body;
};
diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp
index 768df2214c..f25875b7ca 100644
--- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp
+++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp
@@ -1215,10 +1215,18 @@ void TryStatement::generate_bytecode(Bytecode::Generator& generator) const
generator.switch_to_basic_block(handler_block);
if (!m_finalizer)
generator.emit<Bytecode::Op::LeaveUnwindContext>();
- if (!m_handler->parameter().is_empty()) {
- // FIXME: We need a separate DeclarativeEnvironment here
- generator.emit<Bytecode::Op::SetVariable>(generator.intern_string(m_handler->parameter()));
- }
+ m_handler->parameter().visit(
+ [&](FlyString const& parameter) {
+ if (parameter.is_empty()) {
+ // FIXME: We need a separate DeclarativeEnvironment here
+ generator.emit<Bytecode::Op::SetVariable>(generator.intern_string(parameter));
+ }
+ },
+ [&](NonnullRefPtr<BindingPattern> const&) {
+ // FIXME: Implement this path when the above DeclrativeEnvironment issue is dealt with.
+ TODO();
+ });
+
m_handler->body().generate_bytecode(generator);
handler_target = Bytecode::Label { handler_block };
if (!generator.is_current_block_terminated()) {
diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp
index 09d652b56b..928ec47f39 100644
--- a/Userland/Libraries/LibJS/Parser.cpp
+++ b/Userland/Libraries/LibJS/Parser.cpp
@@ -2059,15 +2059,34 @@ NonnullRefPtr<CatchClause> Parser::parse_catch_clause()
auto rule_start = push_start();
consume(TokenType::Catch);
- String parameter;
+ FlyString parameter;
+ RefPtr<BindingPattern> pattern_parameter;
+ auto should_expect_parameter = false;
if (match(TokenType::ParenOpen)) {
+ should_expect_parameter = true;
consume();
- parameter = consume(TokenType::Identifier).value();
+ if (match_identifier_name())
+ parameter = parse_identifier()->string();
+ else
+ pattern_parameter = parse_binding_pattern();
consume(TokenType::ParenClose);
}
+ if (should_expect_parameter && parameter.is_empty() && !pattern_parameter)
+ syntax_error("Expected an identifier or a binding pattern");
+
auto body = parse_block_statement();
- return create_ast_node<CatchClause>({ m_state.current_token.filename(), rule_start.position(), position() }, parameter, move(body));
+ if (pattern_parameter) {
+ return create_ast_node<CatchClause>(
+ { m_state.current_token.filename(), rule_start.position(), position() },
+ pattern_parameter.release_nonnull(),
+ move(body));
+ }
+
+ return create_ast_node<CatchClause>(
+ { m_state.current_token.filename(), rule_start.position(), position() },
+ move(parameter),
+ move(body));
}
NonnullRefPtr<IfStatement> Parser::parse_if_statement()