mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 10:58:12 +00:00
LibJS: Add support for async functions
This commit adds support for the most bare bones version of async functions, support for async generator functions, async arrow functions and await expressions are TODO.
This commit is contained in:
parent
05c3320da3
commit
681787de76
17 changed files with 399 additions and 36 deletions
|
@ -502,6 +502,8 @@ NonnullRefPtr<Program> Parser::parse_program(bool starts_in_strict_mode)
|
|||
NonnullRefPtr<Declaration> Parser::parse_declaration()
|
||||
{
|
||||
auto rule_start = push_start();
|
||||
if (m_state.current_token.type() == TokenType::Async && next_token().type() == TokenType::Function)
|
||||
return parse_function_node<FunctionDeclaration>();
|
||||
switch (m_state.current_token.type()) {
|
||||
case TokenType::Class:
|
||||
return parse_class_declaration();
|
||||
|
@ -572,7 +574,7 @@ NonnullRefPtr<Statement> Parser::parse_statement(AllowLabelledFunction allow_lab
|
|||
return result.release_nonnull();
|
||||
}
|
||||
if (match_expression()) {
|
||||
if (match(TokenType::Function) || match(TokenType::Class))
|
||||
if (match(TokenType::Function) || (match(TokenType::Async) && next_token().type() == TokenType::Function) || match(TokenType::Class))
|
||||
syntax_error(String::formatted("{} declaration not allowed in single-statement context", m_state.current_token.name()));
|
||||
if (match(TokenType::Let) && next_token().type() == TokenType::BracketOpen)
|
||||
syntax_error(String::formatted("let followed by [ is not allowed in single-statement context"));
|
||||
|
@ -748,7 +750,7 @@ RefPtr<Statement> Parser::try_parse_labelled_statement(AllowLabelledFunction all
|
|||
return {};
|
||||
}
|
||||
|
||||
if (m_state.current_token.value() == "await"sv && m_program_type == Program::Type::Module) {
|
||||
if (m_state.current_token.value() == "await"sv && (m_program_type == Program::Type::Module || m_state.in_async_function_context)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -792,6 +794,8 @@ RefPtr<Statement> Parser::try_parse_labelled_statement(AllowLabelledFunction all
|
|||
m_state.current_scope_pusher->add_declaration(function_declaration);
|
||||
if (function_declaration->kind() == FunctionKind::Generator)
|
||||
syntax_error("Generator functions cannot be defined in labelled statements");
|
||||
if (function_declaration->kind() == FunctionKind::Async)
|
||||
syntax_error("Async functions cannot be defined in labelled statements");
|
||||
|
||||
labelled_statement = move(function_declaration);
|
||||
} else {
|
||||
|
@ -899,6 +903,7 @@ NonnullRefPtr<ClassExpression> Parser::parse_class_expression(bool expect_class_
|
|||
bool is_static = false;
|
||||
bool is_constructor = false;
|
||||
bool is_generator = false;
|
||||
bool is_async = false;
|
||||
auto method_kind = ClassMethod::Kind::Method;
|
||||
|
||||
if (match(TokenType::Semicolon)) {
|
||||
|
@ -906,6 +911,11 @@ NonnullRefPtr<ClassExpression> Parser::parse_class_expression(bool expect_class_
|
|||
continue;
|
||||
}
|
||||
|
||||
if (match(TokenType::Async)) {
|
||||
consume();
|
||||
is_async = true;
|
||||
}
|
||||
|
||||
if (match(TokenType::Asterisk)) {
|
||||
consume();
|
||||
is_generator = true;
|
||||
|
@ -913,10 +923,14 @@ NonnullRefPtr<ClassExpression> Parser::parse_class_expression(bool expect_class_
|
|||
|
||||
StringView name;
|
||||
if (match_property_key() || match(TokenType::PrivateIdentifier)) {
|
||||
if (!is_generator && m_state.current_token.original_value() == "static"sv) {
|
||||
if (!is_generator && !is_async && m_state.current_token.original_value() == "static"sv) {
|
||||
if (match(TokenType::Identifier)) {
|
||||
consume();
|
||||
is_static = true;
|
||||
if (match(TokenType::Async)) {
|
||||
consume();
|
||||
is_async = true;
|
||||
}
|
||||
if (match(TokenType::Asterisk)) {
|
||||
consume();
|
||||
is_generator = true;
|
||||
|
@ -995,12 +1009,17 @@ NonnullRefPtr<ClassExpression> Parser::parse_class_expression(bool expect_class_
|
|||
// It is a Syntax Error if PropName of MethodDefinition is "prototype".
|
||||
if (is_static && name == "prototype"sv)
|
||||
syntax_error("Classes may not have a static property named 'prototype'");
|
||||
} else if ((match(TokenType::ParenOpen) || match(TokenType::Equals)) && (is_static || method_kind != ClassMethod::Kind::Method)) {
|
||||
} else if ((match(TokenType::ParenOpen) || match(TokenType::Equals)) && (is_static || is_async || method_kind != ClassMethod::Kind::Method)) {
|
||||
switch (method_kind) {
|
||||
case ClassMethod::Kind::Method:
|
||||
VERIFY(is_static);
|
||||
name = "static";
|
||||
is_static = false;
|
||||
if (is_async) {
|
||||
name = "async";
|
||||
is_async = false;
|
||||
} else {
|
||||
VERIFY(is_static);
|
||||
name = "static";
|
||||
is_static = false;
|
||||
}
|
||||
break;
|
||||
case ClassMethod::Kind::Getter:
|
||||
name = "get";
|
||||
|
@ -1022,6 +1041,7 @@ NonnullRefPtr<ClassExpression> Parser::parse_class_expression(bool expect_class_
|
|||
TemporaryChange continue_context_rollback(m_state.in_continue_context, false);
|
||||
TemporaryChange function_context_rollback(m_state.in_function_context, false);
|
||||
TemporaryChange generator_function_context_rollback(m_state.in_generator_function_context, false);
|
||||
TemporaryChange async_function_context_rollback(m_state.in_async_function_context, false);
|
||||
TemporaryChange in_class_field_initializer_rollback(m_state.in_class_field_initializer, true);
|
||||
|
||||
ScopePusher static_init_scope = ScopePusher::static_init_block_scope(*this, *static_init_block);
|
||||
|
@ -1042,6 +1062,8 @@ NonnullRefPtr<ClassExpression> Parser::parse_class_expression(bool expect_class_
|
|||
syntax_error("Classes may not have more than one constructor");
|
||||
if (is_generator)
|
||||
syntax_error("Class constructor may not be a generator");
|
||||
if (is_async)
|
||||
syntax_error("Class constructor may not be async");
|
||||
|
||||
is_constructor = true;
|
||||
}
|
||||
|
@ -1057,6 +1079,8 @@ NonnullRefPtr<ClassExpression> Parser::parse_class_expression(bool expect_class_
|
|||
parse_options |= FunctionNodeParseOptions::IsSetterFunction;
|
||||
if (is_generator)
|
||||
parse_options |= FunctionNodeParseOptions::IsGeneratorFunction;
|
||||
if (is_async)
|
||||
parse_options |= FunctionNodeParseOptions::IsAsyncFunction;
|
||||
auto function = parse_function_node<FunctionExpression>(parse_options);
|
||||
if (is_constructor) {
|
||||
constructor = move(function);
|
||||
|
@ -1065,7 +1089,7 @@ NonnullRefPtr<ClassExpression> Parser::parse_class_expression(bool expect_class_
|
|||
} else {
|
||||
syntax_error("No key for class method");
|
||||
}
|
||||
} else if (is_generator) {
|
||||
} else if (is_generator || is_async) {
|
||||
expected("ParenOpen");
|
||||
consume();
|
||||
} else if (property_key.is_null()) {
|
||||
|
@ -1156,6 +1180,8 @@ Parser::PrimaryExpressionParseResult Parser::parse_primary_expression()
|
|||
auto& function = static_cast<FunctionExpression&>(*expression);
|
||||
if (function.kind() == FunctionKind::Generator && function.name() == "yield"sv)
|
||||
syntax_error("function is not allowed to be called 'yield' in this context", function.source_range().start);
|
||||
if (function.kind() == FunctionKind::Async && function.name() == "await"sv)
|
||||
syntax_error("function is not allowed to be called 'await' in this context", function.source_range().start);
|
||||
}
|
||||
return { move(expression) };
|
||||
}
|
||||
|
@ -1170,7 +1196,7 @@ Parser::PrimaryExpressionParseResult Parser::parse_primary_expression()
|
|||
syntax_error("'super' keyword unexpected here");
|
||||
return { create_ast_node<SuperExpression>({ m_state.current_token.filename(), rule_start.position(), position() }) };
|
||||
case TokenType::EscapedKeyword:
|
||||
if (m_state.strict_mode || (m_state.current_token.value() != "let"sv && (m_state.in_generator_function_context || m_state.current_token.value() != "yield"sv)))
|
||||
if (m_state.strict_mode || (m_state.current_token.value() != "let"sv && (m_state.in_generator_function_context || m_state.current_token.value() != "yield"sv) && (m_state.in_async_function_context || m_state.current_token.value() != "await"sv)))
|
||||
syntax_error("Keyword must not contain escaped characters");
|
||||
[[fallthrough]];
|
||||
case TokenType::Identifier: {
|
||||
|
@ -1201,6 +1227,10 @@ Parser::PrimaryExpressionParseResult Parser::parse_primary_expression()
|
|||
return { create_ast_node<NullLiteral>({ m_state.current_token.filename(), rule_start.position(), position() }) };
|
||||
case TokenType::CurlyOpen:
|
||||
return { parse_object_expression() };
|
||||
case TokenType::Async:
|
||||
if (next_token().type() != TokenType::Function)
|
||||
goto read_as_identifier;
|
||||
[[fallthrough]];
|
||||
case TokenType::Function:
|
||||
return { parse_function_node<FunctionExpression>() };
|
||||
case TokenType::BracketOpen:
|
||||
|
@ -1409,10 +1439,14 @@ NonnullRefPtr<ObjectExpression> Parser::parse_object_expression()
|
|||
consume();
|
||||
property_type = ObjectProperty::Type::KeyValue;
|
||||
property_name = parse_property_key();
|
||||
function_kind = FunctionKind ::Generator;
|
||||
function_kind = FunctionKind::Generator;
|
||||
} else if (match_identifier()) {
|
||||
auto identifier = consume();
|
||||
if (identifier.original_value() == "get"sv && match_property_key()) {
|
||||
if (identifier.original_value() == "async" && match_property_key()) {
|
||||
property_type = ObjectProperty::Type::KeyValue;
|
||||
property_name = parse_property_key();
|
||||
function_kind = FunctionKind::Async;
|
||||
} else if (identifier.original_value() == "get"sv && match_property_key()) {
|
||||
property_type = ObjectProperty::Type::Getter;
|
||||
property_name = parse_property_key();
|
||||
} else if (identifier.original_value() == "set"sv && match_property_key()) {
|
||||
|
@ -1451,6 +1485,8 @@ NonnullRefPtr<ObjectExpression> Parser::parse_object_expression()
|
|||
parse_options |= FunctionNodeParseOptions::IsSetterFunction;
|
||||
if (function_kind == FunctionKind::Generator)
|
||||
parse_options |= FunctionNodeParseOptions::IsGeneratorFunction;
|
||||
if (function_kind == FunctionKind::Async)
|
||||
parse_options |= FunctionNodeParseOptions::IsAsyncFunction;
|
||||
auto function = parse_function_node<FunctionExpression>(parse_options);
|
||||
properties.append(create_ast_node<ObjectProperty>({ m_state.current_token.filename(), rule_start.position(), position() }, *property_name, function, property_type, true));
|
||||
} else if (match(TokenType::Colon)) {
|
||||
|
@ -1892,6 +1928,7 @@ RefPtr<BindingPattern> Parser::synthesize_binding_pattern(Expression const& expr
|
|||
parser.m_state.in_function_context = m_state.in_function_context;
|
||||
parser.m_state.in_formal_parameter_context = m_state.in_formal_parameter_context;
|
||||
parser.m_state.in_generator_function_context = m_state.in_generator_function_context;
|
||||
parser.m_state.in_async_function_context = m_state.in_async_function_context;
|
||||
parser.m_state.in_arrow_function_context = m_state.in_arrow_function_context;
|
||||
parser.m_state.in_break_context = m_state.in_break_context;
|
||||
parser.m_state.in_continue_context = m_state.in_continue_context;
|
||||
|
@ -2116,6 +2153,9 @@ NonnullRefPtr<FunctionBody> Parser::parse_function_body(Vector<FunctionDeclarati
|
|||
if (function_kind == FunctionKind::Generator && parameter_name == "yield"sv)
|
||||
syntax_error("Parameter name 'yield' not allowed in this context");
|
||||
|
||||
if (function_kind == FunctionKind::Async && parameter_name == "await"sv)
|
||||
syntax_error("Parameter name 'await' not allowed in this context");
|
||||
|
||||
for (auto& previous_name : parameter_names) {
|
||||
if (previous_name == parameter_name) {
|
||||
syntax_error(String::formatted("Duplicate parameter '{}' not allowed in strict mode", parameter_name));
|
||||
|
@ -2129,6 +2169,9 @@ NonnullRefPtr<FunctionBody> Parser::parse_function_body(Vector<FunctionDeclarati
|
|||
if (function_kind == FunctionKind::Generator && bound_name == "yield"sv)
|
||||
syntax_error("Parameter name 'yield' not allowed in this context");
|
||||
|
||||
if (function_kind == FunctionKind::Async && bound_name == "await"sv)
|
||||
syntax_error("Parameter name 'await' not allowed in this context");
|
||||
|
||||
for (auto& previous_name : parameter_names) {
|
||||
if (previous_name == bound_name) {
|
||||
syntax_error(String::formatted("Duplicate parameter '{}' not allowed in strict mode", bound_name));
|
||||
|
@ -2173,16 +2216,27 @@ NonnullRefPtr<FunctionNodeType> Parser::parse_function_node(u8 parse_options)
|
|||
TemporaryChange might_need_arguments_object_rollback(m_state.function_might_need_arguments_object, false);
|
||||
|
||||
constexpr auto is_function_expression = IsSame<FunctionNodeType, FunctionExpression>;
|
||||
auto function_kind = (parse_options & FunctionNodeParseOptions::IsGeneratorFunction) != 0 ? FunctionKind::Generator : FunctionKind::Regular;
|
||||
FunctionKind function_kind;
|
||||
if ((parse_options & FunctionNodeParseOptions::IsGeneratorFunction) != 0 && (parse_options & FunctionNodeParseOptions::IsAsyncFunction) != 0)
|
||||
TODO();
|
||||
else if ((parse_options & FunctionNodeParseOptions::IsGeneratorFunction) != 0)
|
||||
function_kind = FunctionKind::Generator;
|
||||
else if ((parse_options & FunctionNodeParseOptions::IsAsyncFunction) != 0)
|
||||
function_kind = FunctionKind::Async;
|
||||
else
|
||||
function_kind = FunctionKind::Regular;
|
||||
String name;
|
||||
if (parse_options & FunctionNodeParseOptions::CheckForFunctionAndName) {
|
||||
if (function_kind == FunctionKind::Regular && match(TokenType::Async) && !next_token().trivia_contains_line_terminator()) {
|
||||
function_kind = FunctionKind::Async;
|
||||
consume(TokenType::Async);
|
||||
parse_options = parse_options | FunctionNodeParseOptions::IsAsyncFunction;
|
||||
}
|
||||
consume(TokenType::Function);
|
||||
if (function_kind == FunctionKind::Regular) {
|
||||
function_kind = match(TokenType::Asterisk) ? FunctionKind::Generator : FunctionKind::Regular;
|
||||
if (function_kind == FunctionKind::Generator) {
|
||||
consume(TokenType::Asterisk);
|
||||
parse_options = parse_options | FunctionNodeParseOptions::IsGeneratorFunction;
|
||||
}
|
||||
if (function_kind == FunctionKind::Regular && match(TokenType::Asterisk)) {
|
||||
function_kind = FunctionKind::Generator;
|
||||
consume(TokenType::Asterisk);
|
||||
parse_options = parse_options | FunctionNodeParseOptions::IsGeneratorFunction;
|
||||
}
|
||||
|
||||
if (FunctionNodeType::must_have_name() || match_identifier())
|
||||
|
@ -2193,6 +2247,7 @@ NonnullRefPtr<FunctionNodeType> Parser::parse_function_node(u8 parse_options)
|
|||
check_identifier_name_for_assignment_validity(name);
|
||||
}
|
||||
TemporaryChange generator_change(m_state.in_generator_function_context, function_kind == FunctionKind::Generator);
|
||||
TemporaryChange async_change(m_state.in_async_function_context, function_kind == FunctionKind::Async);
|
||||
|
||||
consume(TokenType::ParenOpen);
|
||||
i32 function_length = -1;
|
||||
|
@ -2557,6 +2612,13 @@ NonnullRefPtr<VariableDeclaration> Parser::parse_variable_declaration(bool for_l
|
|||
if (m_state.strict_mode)
|
||||
syntax_error("Identifier must not be a reserved word in strict mode ('yield')");
|
||||
|
||||
target = create_ast_node<Identifier>(
|
||||
{ m_state.current_token.filename(), rule_start.position(), position() },
|
||||
consume().value());
|
||||
} else if (!m_state.in_async_function_context && match(TokenType::Async)) {
|
||||
if (m_program_type == Program::Type::Module)
|
||||
syntax_error("Identifier must not be a reserved word in modules ('async')");
|
||||
|
||||
target = create_ast_node<Identifier>(
|
||||
{ m_state.current_token.filename(), rule_start.position(), position() },
|
||||
consume().value());
|
||||
|
@ -2913,7 +2975,7 @@ NonnullRefPtr<CatchClause> Parser::parse_catch_clause()
|
|||
if (match(TokenType::ParenOpen)) {
|
||||
should_expect_parameter = true;
|
||||
consume();
|
||||
if (match_identifier_name() && (!match(TokenType::Yield) || !m_state.in_generator_function_context))
|
||||
if (match_identifier_name() && (!match(TokenType::Yield) || !m_state.in_generator_function_context) && (!match(TokenType::Async) || !m_state.in_async_function_context))
|
||||
parameter = consume().value();
|
||||
else
|
||||
pattern_parameter = parse_binding_pattern(AllowDuplicates::No, AllowMemberExpressions::No);
|
||||
|
@ -2978,6 +3040,8 @@ NonnullRefPtr<IfStatement> Parser::parse_if_statement()
|
|||
auto& function_declaration = static_cast<FunctionDeclaration const&>(*declaration);
|
||||
if (function_declaration.kind() == FunctionKind::Generator)
|
||||
syntax_error("Generator functions can only be declared in top-level or within a block");
|
||||
if (function_declaration.kind() == FunctionKind::Async)
|
||||
syntax_error("Async functions can only be declared in top-level or within a block");
|
||||
block->append(move(declaration));
|
||||
return block;
|
||||
};
|
||||
|
@ -3162,6 +3226,7 @@ bool Parser::match_expression() const
|
|||
|| type == TokenType::BracketOpen
|
||||
|| type == TokenType::ParenOpen
|
||||
|| type == TokenType::Function
|
||||
|| type == TokenType::Async
|
||||
|| type == TokenType::This
|
||||
|| type == TokenType::Super
|
||||
|| type == TokenType::RegexLiteral
|
||||
|
@ -3280,7 +3345,8 @@ bool Parser::match_declaration() const
|
|||
return type == TokenType::Function
|
||||
|| type == TokenType::Class
|
||||
|| type == TokenType::Const
|
||||
|| type == TokenType::Let;
|
||||
|| type == TokenType::Let
|
||||
|| (type == TokenType::Async && next_token().type() == TokenType::Function);
|
||||
}
|
||||
|
||||
Token Parser::next_token() const
|
||||
|
@ -3327,13 +3393,14 @@ bool Parser::match_identifier() const
|
|||
if (m_state.current_token.value() == "yield"sv)
|
||||
return !m_state.strict_mode && !m_state.in_generator_function_context;
|
||||
if (m_state.current_token.value() == "await"sv)
|
||||
return m_program_type != Program::Type::Module;
|
||||
return m_program_type != Program::Type::Module && !m_state.in_async_function_context;
|
||||
return true;
|
||||
}
|
||||
|
||||
return m_state.current_token.type() == TokenType::Identifier
|
||||
|| m_state.current_token.type() == TokenType::Async
|
||||
|| (m_state.current_token.type() == TokenType::Let && !m_state.strict_mode)
|
||||
|| (m_state.current_token.type() == TokenType::Await && m_program_type != Program::Type::Module)
|
||||
|| (m_state.current_token.type() == TokenType::Await && m_program_type != Program::Type::Module && !m_state.in_async_function_context)
|
||||
|| (m_state.current_token.type() == TokenType::Yield && !m_state.in_generator_function_context && !m_state.strict_mode); // See note in Parser::parse_identifier().
|
||||
}
|
||||
|
||||
|
@ -3414,6 +3481,15 @@ Token Parser::consume_identifier()
|
|||
return consume();
|
||||
}
|
||||
|
||||
if (match(TokenType::Await)) {
|
||||
if (m_program_type == Program::Type::Module || m_state.in_async_function_context)
|
||||
syntax_error("Identifier must not be a reserved word in modules ('await')");
|
||||
return consume();
|
||||
}
|
||||
|
||||
if (match(TokenType::Async))
|
||||
return consume();
|
||||
|
||||
expected("Identifier");
|
||||
return consume();
|
||||
}
|
||||
|
@ -3453,6 +3529,9 @@ Token Parser::consume_identifier_reference()
|
|||
return consume();
|
||||
}
|
||||
|
||||
if (match(TokenType::Async))
|
||||
return consume();
|
||||
|
||||
expected(Token::name(TokenType::Identifier));
|
||||
return consume();
|
||||
}
|
||||
|
@ -3722,11 +3801,10 @@ NonnullRefPtr<ExportStatement> Parser::parse_export_statement(Program& program)
|
|||
auto class_expression = parse_class_expression(false);
|
||||
local_name = class_expression->name();
|
||||
expression = move(class_expression);
|
||||
} else if (match(TokenType::Function)) {
|
||||
} else if (match(TokenType::Function) || (match(TokenType::Async) && next_token().type() == TokenType::Function)) {
|
||||
auto func_expr = parse_function_node<FunctionExpression>();
|
||||
local_name = func_expr->name();
|
||||
expression = move(func_expr);
|
||||
// TODO: Allow async function
|
||||
} else if (match_expression()) {
|
||||
expression = parse_expression(2);
|
||||
consume_or_insert_semicolon();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue