mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 20:37:34 +00:00
LibJS: Add support for binding patterns in catch clauses
`try { ... } catch({a=foo}) {}` is valid, and now we parse and evaluate it correctly :^)
This commit is contained in:
parent
1a9518ebe3
commit
77a5144264
4 changed files with 73 additions and 15 deletions
|
@ -2168,10 +2168,20 @@ void TryStatement::dump(int indent) const
|
||||||
void CatchClause::dump(int indent) const
|
void CatchClause::dump(int indent) const
|
||||||
{
|
{
|
||||||
print_indent(indent);
|
print_indent(indent);
|
||||||
if (m_parameter.is_null())
|
m_parameter.visit(
|
||||||
outln("CatchClause");
|
[&](FlyString const& parameter) {
|
||||||
else
|
if (parameter.is_null())
|
||||||
outln("CatchClause ({})", m_parameter);
|
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);
|
body().dump(indent + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2191,10 +2201,24 @@ Value TryStatement::execute(Interpreter& interpreter, GlobalObject& global_objec
|
||||||
interpreter.vm().clear_exception();
|
interpreter.vm().clear_exception();
|
||||||
|
|
||||||
HashMap<FlyString, Variable> parameters;
|
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);
|
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);
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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; }
|
BlockStatement const& body() const { return m_body; }
|
||||||
|
|
||||||
virtual void dump(int indent) const override;
|
virtual void dump(int indent) const override;
|
||||||
virtual Value execute(Interpreter&, GlobalObject&) const override;
|
virtual Value execute(Interpreter&, GlobalObject&) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FlyString m_parameter;
|
Variant<FlyString, NonnullRefPtr<BindingPattern>> m_parameter;
|
||||||
NonnullRefPtr<BlockStatement> m_body;
|
NonnullRefPtr<BlockStatement> m_body;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1215,10 +1215,18 @@ void TryStatement::generate_bytecode(Bytecode::Generator& generator) const
|
||||||
generator.switch_to_basic_block(handler_block);
|
generator.switch_to_basic_block(handler_block);
|
||||||
if (!m_finalizer)
|
if (!m_finalizer)
|
||||||
generator.emit<Bytecode::Op::LeaveUnwindContext>();
|
generator.emit<Bytecode::Op::LeaveUnwindContext>();
|
||||||
if (!m_handler->parameter().is_empty()) {
|
m_handler->parameter().visit(
|
||||||
// FIXME: We need a separate DeclarativeEnvironment here
|
[&](FlyString const& parameter) {
|
||||||
generator.emit<Bytecode::Op::SetVariable>(generator.intern_string(m_handler->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);
|
m_handler->body().generate_bytecode(generator);
|
||||||
handler_target = Bytecode::Label { handler_block };
|
handler_target = Bytecode::Label { handler_block };
|
||||||
if (!generator.is_current_block_terminated()) {
|
if (!generator.is_current_block_terminated()) {
|
||||||
|
|
|
@ -2059,15 +2059,34 @@ NonnullRefPtr<CatchClause> Parser::parse_catch_clause()
|
||||||
auto rule_start = push_start();
|
auto rule_start = push_start();
|
||||||
consume(TokenType::Catch);
|
consume(TokenType::Catch);
|
||||||
|
|
||||||
String parameter;
|
FlyString parameter;
|
||||||
|
RefPtr<BindingPattern> pattern_parameter;
|
||||||
|
auto should_expect_parameter = false;
|
||||||
if (match(TokenType::ParenOpen)) {
|
if (match(TokenType::ParenOpen)) {
|
||||||
|
should_expect_parameter = true;
|
||||||
consume();
|
consume();
|
||||||
parameter = consume(TokenType::Identifier).value();
|
if (match_identifier_name())
|
||||||
|
parameter = parse_identifier()->string();
|
||||||
|
else
|
||||||
|
pattern_parameter = parse_binding_pattern();
|
||||||
consume(TokenType::ParenClose);
|
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();
|
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()
|
NonnullRefPtr<IfStatement> Parser::parse_if_statement()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue