diff options
author | Ali Mohammad Pur <ali.mpfard@gmail.com> | 2021-07-11 15:15:38 +0430 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2021-07-11 21:41:54 +0100 |
commit | 77a51442640fdf77348c64331793fc9fc8b973f4 (patch) | |
tree | 28b77ccc215129a522dc237c51d6c07743c73e55 /Userland/Libraries | |
parent | 1a9518ebe313f10091da92e73de76b4ab2f2d875 (diff) | |
download | serenity-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 :^)
Diffstat (limited to 'Userland/Libraries')
-rw-r--r-- | Userland/Libraries/LibJS/AST.cpp | 36 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/AST.h | 11 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp | 16 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Parser.cpp | 25 |
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() |