From 681787de7656a31f4f010f93b8319e444b7014fa Mon Sep 17 00:00:00 2001 From: Idan Horowitz Date: Tue, 9 Nov 2021 20:39:22 +0200 Subject: 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. --- Userland/Libraries/LibJS/Parser.cpp | 126 +++++++++++++++++++++++++++++------- 1 file changed, 102 insertions(+), 24 deletions(-) (limited to 'Userland/Libraries/LibJS/Parser.cpp') diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp index f2f275dd44..11a2a85467 100644 --- a/Userland/Libraries/LibJS/Parser.cpp +++ b/Userland/Libraries/LibJS/Parser.cpp @@ -502,6 +502,8 @@ NonnullRefPtr Parser::parse_program(bool starts_in_strict_mode) NonnullRefPtr 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(); switch (m_state.current_token.type()) { case TokenType::Class: return parse_class_declaration(); @@ -572,7 +574,7 @@ NonnullRefPtr 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 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 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 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 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 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 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 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 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 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(parse_options); if (is_constructor) { constructor = move(function); @@ -1065,7 +1089,7 @@ NonnullRefPtr 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(*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({ 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({ 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() }; case TokenType::BracketOpen: @@ -1409,10 +1439,14 @@ NonnullRefPtr 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 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(parse_options); properties.append(create_ast_node({ 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 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 Parser::parse_function_body(Vector Parser::parse_function_body(Vector 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; - 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 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 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( + { 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( { m_state.current_token.filename(), rule_start.position(), position() }, consume().value()); @@ -2913,7 +2975,7 @@ NonnullRefPtr 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 Parser::parse_if_statement() auto& function_declaration = static_cast(*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 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(); 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(); -- cgit v1.2.3