From 0982a73d1d8c03ed0b7f3686c2a801c543c4c1f7 Mon Sep 17 00:00:00 2001 From: davidot Date: Mon, 15 Nov 2021 01:53:24 +0100 Subject: LibJS: Parse async generator functions --- Userland/Libraries/LibJS/AST.cpp | 4 +- Userland/Libraries/LibJS/CMakeLists.txt | 2 + Userland/Libraries/LibJS/Forward.h | 49 +++++++++-------- Userland/Libraries/LibJS/Parser.cpp | 32 ++++++----- .../Runtime/AsyncGeneratorFunctionConstructor.cpp | 64 ++++++++++++++++++++++ .../Runtime/AsyncGeneratorFunctionConstructor.h | 28 ++++++++++ .../Runtime/AsyncGeneratorFunctionPrototype.cpp | 33 +++++++++++ .../Runtime/AsyncGeneratorFunctionPrototype.h | 22 ++++++++ .../LibJS/Runtime/ECMAScriptFunctionObject.cpp | 9 +++ .../LibJS/Runtime/FunctionConstructor.cpp | 4 +- Userland/Libraries/LibJS/Runtime/FunctionKind.h | 1 + Userland/Libraries/LibJS/Runtime/GlobalObject.cpp | 7 +++ .../LibJS/Tests/syntax/async-generators.js | 55 +++++++++++++++++++ 13 files changed, 270 insertions(+), 40 deletions(-) create mode 100644 Userland/Libraries/LibJS/Runtime/AsyncGeneratorFunctionConstructor.cpp create mode 100644 Userland/Libraries/LibJS/Runtime/AsyncGeneratorFunctionConstructor.h create mode 100644 Userland/Libraries/LibJS/Runtime/AsyncGeneratorFunctionPrototype.cpp create mode 100644 Userland/Libraries/LibJS/Runtime/AsyncGeneratorFunctionPrototype.h create mode 100644 Userland/Libraries/LibJS/Tests/syntax/async-generators.js diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index 574cd455e8..dbb032dae2 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/Userland/Libraries/LibJS/AST.cpp @@ -1938,7 +1938,9 @@ void BindingPattern::dump(int indent) const void FunctionNode::dump(int indent, String const& class_name) const { print_indent(indent); - outln("{}{}{} '{}'", class_name, m_kind == FunctionKind::Async ? " async" : "", m_kind == FunctionKind::Generator ? "*" : "", name()); + auto is_async = m_kind == FunctionKind::Async || m_kind == FunctionKind::AsyncGenerator; + auto is_generator = m_kind == FunctionKind::Generator || m_kind == FunctionKind::AsyncGenerator; + outln("{}{}{} '{}'", class_name, is_async ? " async" : "", is_generator ? "*" : "", name()); if (m_contains_direct_call_to_eval) { print_indent(indent + 1); outln("\033[31;1m(direct eval)\033[0m"); diff --git a/Userland/Libraries/LibJS/CMakeLists.txt b/Userland/Libraries/LibJS/CMakeLists.txt index 6b54a4034c..07bce78cb7 100644 --- a/Userland/Libraries/LibJS/CMakeLists.txt +++ b/Userland/Libraries/LibJS/CMakeLists.txt @@ -41,6 +41,8 @@ set(SOURCES Runtime/AsyncFunctionConstructor.cpp Runtime/AsyncFunctionDriverWrapper.cpp Runtime/AsyncFunctionPrototype.cpp + Runtime/AsyncGeneratorFunctionConstructor.cpp + Runtime/AsyncGeneratorFunctionPrototype.cpp Runtime/AtomicsObject.cpp Runtime/BigInt.cpp Runtime/BigIntConstructor.cpp diff --git a/Userland/Libraries/LibJS/Forward.h b/Userland/Libraries/LibJS/Forward.h index c589c601ed..84863febcf 100644 --- a/Userland/Libraries/LibJS/Forward.h +++ b/Userland/Libraries/LibJS/Forward.h @@ -13,30 +13,31 @@ JS::ThrowCompletionOr name([[maybe_unused]] JS::VM& vm, [[maybe_unused]] JS::GlobalObject& global_object) // NOTE: Proxy is not included here as it doesn't have a prototype - m_proxy_constructor is initialized separately. -#define JS_ENUMERATE_NATIVE_OBJECTS_EXCLUDING_TEMPLATES \ - __JS_ENUMERATE(AggregateError, aggregate_error, AggregateErrorPrototype, AggregateErrorConstructor, void) \ - __JS_ENUMERATE(Array, array, ArrayPrototype, ArrayConstructor, void) \ - __JS_ENUMERATE(ArrayBuffer, array_buffer, ArrayBufferPrototype, ArrayBufferConstructor, void) \ - __JS_ENUMERATE(AsyncFunction, async_function, AsyncFunctionPrototype, AsyncFunctionConstructor, void) \ - __JS_ENUMERATE(BigIntObject, bigint, BigIntPrototype, BigIntConstructor, void) \ - __JS_ENUMERATE(BooleanObject, boolean, BooleanPrototype, BooleanConstructor, void) \ - __JS_ENUMERATE(DataView, data_view, DataViewPrototype, DataViewConstructor, void) \ - __JS_ENUMERATE(Date, date, DatePrototype, DateConstructor, void) \ - __JS_ENUMERATE(Error, error, ErrorPrototype, ErrorConstructor, void) \ - __JS_ENUMERATE(FinalizationRegistry, finalization_registry, FinalizationRegistryPrototype, FinalizationRegistryConstructor, void) \ - __JS_ENUMERATE(FunctionObject, function, FunctionPrototype, FunctionConstructor, void) \ - __JS_ENUMERATE(GeneratorFunction, generator_function, GeneratorFunctionPrototype, GeneratorFunctionConstructor, void) \ - __JS_ENUMERATE(Map, map, MapPrototype, MapConstructor, void) \ - __JS_ENUMERATE(NumberObject, number, NumberPrototype, NumberConstructor, void) \ - __JS_ENUMERATE(Object, object, ObjectPrototype, ObjectConstructor, void) \ - __JS_ENUMERATE(Promise, promise, PromisePrototype, PromiseConstructor, void) \ - __JS_ENUMERATE(RegExpObject, regexp, RegExpPrototype, RegExpConstructor, void) \ - __JS_ENUMERATE(Set, set, SetPrototype, SetConstructor, void) \ - __JS_ENUMERATE(ShadowRealm, shadow_realm, ShadowRealmPrototype, ShadowRealmConstructor, void) \ - __JS_ENUMERATE(StringObject, string, StringPrototype, StringConstructor, void) \ - __JS_ENUMERATE(SymbolObject, symbol, SymbolPrototype, SymbolConstructor, void) \ - __JS_ENUMERATE(WeakMap, weak_map, WeakMapPrototype, WeakMapConstructor, void) \ - __JS_ENUMERATE(WeakRef, weak_ref, WeakRefPrototype, WeakRefConstructor, void) \ +#define JS_ENUMERATE_NATIVE_OBJECTS_EXCLUDING_TEMPLATES \ + __JS_ENUMERATE(AggregateError, aggregate_error, AggregateErrorPrototype, AggregateErrorConstructor, void) \ + __JS_ENUMERATE(Array, array, ArrayPrototype, ArrayConstructor, void) \ + __JS_ENUMERATE(ArrayBuffer, array_buffer, ArrayBufferPrototype, ArrayBufferConstructor, void) \ + __JS_ENUMERATE(AsyncFunction, async_function, AsyncFunctionPrototype, AsyncFunctionConstructor, void) \ + __JS_ENUMERATE(AsyncGeneratorFunction, async_generator_function, AsyncGeneratorFunctionPrototype, AsyncGeneratorFunctionConstructor, void) \ + __JS_ENUMERATE(BigIntObject, bigint, BigIntPrototype, BigIntConstructor, void) \ + __JS_ENUMERATE(BooleanObject, boolean, BooleanPrototype, BooleanConstructor, void) \ + __JS_ENUMERATE(DataView, data_view, DataViewPrototype, DataViewConstructor, void) \ + __JS_ENUMERATE(Date, date, DatePrototype, DateConstructor, void) \ + __JS_ENUMERATE(Error, error, ErrorPrototype, ErrorConstructor, void) \ + __JS_ENUMERATE(FinalizationRegistry, finalization_registry, FinalizationRegistryPrototype, FinalizationRegistryConstructor, void) \ + __JS_ENUMERATE(FunctionObject, function, FunctionPrototype, FunctionConstructor, void) \ + __JS_ENUMERATE(GeneratorFunction, generator_function, GeneratorFunctionPrototype, GeneratorFunctionConstructor, void) \ + __JS_ENUMERATE(Map, map, MapPrototype, MapConstructor, void) \ + __JS_ENUMERATE(NumberObject, number, NumberPrototype, NumberConstructor, void) \ + __JS_ENUMERATE(Object, object, ObjectPrototype, ObjectConstructor, void) \ + __JS_ENUMERATE(Promise, promise, PromisePrototype, PromiseConstructor, void) \ + __JS_ENUMERATE(RegExpObject, regexp, RegExpPrototype, RegExpConstructor, void) \ + __JS_ENUMERATE(Set, set, SetPrototype, SetConstructor, void) \ + __JS_ENUMERATE(ShadowRealm, shadow_realm, ShadowRealmPrototype, ShadowRealmConstructor, void) \ + __JS_ENUMERATE(StringObject, string, StringPrototype, StringConstructor, void) \ + __JS_ENUMERATE(SymbolObject, symbol, SymbolPrototype, SymbolConstructor, void) \ + __JS_ENUMERATE(WeakMap, weak_map, WeakMapPrototype, WeakMapConstructor, void) \ + __JS_ENUMERATE(WeakRef, weak_ref, WeakRefPrototype, WeakRefConstructor, void) \ __JS_ENUMERATE(WeakSet, weak_set, WeakSetPrototype, WeakSetConstructor, void) #define JS_ENUMERATE_NATIVE_OBJECTS \ diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp index 0dc1ef0e45..3ef3907a99 100644 --- a/Userland/Libraries/LibJS/Parser.cpp +++ b/Userland/Libraries/LibJS/Parser.cpp @@ -1483,18 +1483,23 @@ NonnullRefPtr Parser::parse_object_expression() auto type = m_state.current_token.type(); + if (match(TokenType::Async)) { + auto lookahead_token = next_token(); + + if (lookahead_token.type() != TokenType::ParenOpen && !lookahead_token.trivia_contains_line_terminator()) { + consume(TokenType::Async); + function_kind = FunctionKind::Async; + } + } if (match(TokenType::Asterisk)) { consume(); property_type = ObjectProperty::Type::KeyValue; property_name = parse_property_key(); - function_kind = FunctionKind::Generator; + VERIFY(function_kind == FunctionKind::Regular || function_kind == FunctionKind::Async); + function_kind = function_kind == FunctionKind::Regular ? FunctionKind::Generator : FunctionKind::AsyncGenerator; } else if (match_identifier()) { auto identifier = consume(); - if (identifier.original_value() == "async" && match_property_key() && !m_state.current_token.trivia_contains_line_terminator()) { - property_type = ObjectProperty::Type::KeyValue; - property_name = parse_property_key(); - function_kind = FunctionKind::Async; - } else if (identifier.original_value() == "get"sv && match_property_key()) { + 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()) { @@ -1531,9 +1536,9 @@ NonnullRefPtr Parser::parse_object_expression() parse_options |= FunctionNodeParseOptions::IsGetterFunction; if (property_type == ObjectProperty::Type::Setter) parse_options |= FunctionNodeParseOptions::IsSetterFunction; - if (function_kind == FunctionKind::Generator) + if (function_kind == FunctionKind::Generator || function_kind == FunctionKind::AsyncGenerator) parse_options |= FunctionNodeParseOptions::IsGeneratorFunction; - if (function_kind == FunctionKind::Async) + if (function_kind == FunctionKind::Async || function_kind == FunctionKind::AsyncGenerator) 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)); @@ -2278,12 +2283,13 @@ NonnullRefPtr Parser::parse_function_node(u8 parse_options) TemporaryChange break_context_rollback(m_state.in_break_context, false); TemporaryChange continue_context_rollback(m_state.in_continue_context, false); TemporaryChange class_field_initializer_rollback(m_state.in_class_field_initializer, false); + TemporaryChange class_static_initializer_rollback(m_state.in_class_static_init_block, false); TemporaryChange might_need_arguments_object_rollback(m_state.function_might_need_arguments_object, false); constexpr auto is_function_expression = IsSame; FunctionKind function_kind; if ((parse_options & FunctionNodeParseOptions::IsGeneratorFunction) != 0 && (parse_options & FunctionNodeParseOptions::IsAsyncFunction) != 0) - TODO(); + function_kind = FunctionKind::AsyncGenerator; else if ((parse_options & FunctionNodeParseOptions::IsGeneratorFunction) != 0) function_kind = FunctionKind::Generator; else if ((parse_options & FunctionNodeParseOptions::IsAsyncFunction) != 0) @@ -2298,8 +2304,8 @@ NonnullRefPtr Parser::parse_function_node(u8 parse_options) parse_options = parse_options | FunctionNodeParseOptions::IsAsyncFunction; } consume(TokenType::Function); - if (function_kind == FunctionKind::Regular && match(TokenType::Asterisk)) { - function_kind = FunctionKind::Generator; + if (match(TokenType::Asterisk)) { + function_kind = function_kind == FunctionKind::Regular ? FunctionKind::Generator : FunctionKind::AsyncGenerator; consume(TokenType::Asterisk); parse_options = parse_options | FunctionNodeParseOptions::IsGeneratorFunction; } @@ -2311,8 +2317,8 @@ 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); + TemporaryChange generator_change(m_state.in_generator_function_context, function_kind == FunctionKind::Generator || function_kind == FunctionKind::AsyncGenerator); + TemporaryChange async_change(m_state.in_async_function_context, function_kind == FunctionKind::Async || function_kind == FunctionKind::AsyncGenerator); consume(TokenType::ParenOpen); i32 function_length = -1; diff --git a/Userland/Libraries/LibJS/Runtime/AsyncGeneratorFunctionConstructor.cpp b/Userland/Libraries/LibJS/Runtime/AsyncGeneratorFunctionConstructor.cpp new file mode 100644 index 0000000000..4f03fcee25 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/AsyncGeneratorFunctionConstructor.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021, David Tuin + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include + +namespace JS { + +AsyncGeneratorFunctionConstructor::AsyncGeneratorFunctionConstructor(GlobalObject& global_object) + : NativeFunction(vm().names.AsyncGeneratorFunction.as_string(), *global_object.function_prototype()) +{ +} + +void AsyncGeneratorFunctionConstructor::initialize(GlobalObject& global_object) +{ + auto& vm = this->vm(); + NativeFunction::initialize(global_object); + + // 27.4.2.2 AsyncGeneratorFunction.prototype, https://tc39.es/ecma262/#sec-asyncgeneratorfunction-prototype + define_direct_property(vm.names.prototype, global_object.async_generator_function_prototype(), 0); + + // 27.4.2.1 AsyncGeneratorFunction.length, https://tc39.es/ecma262/#sec-asyncgeneratorfunction-length + define_direct_property(vm.names.length, Value(1), Attribute::Configurable); + + // 27.4.2.2 AsyncGeneratorFunction.prototype, https://tc39.es/ecma262/#sec-asyncgeneratorfunction-prototype + define_direct_property(vm.names.prototype, global_object.async_generator_function_prototype(), 0); +} + +// 27.4.1.1 AsyncGeneratorFunction ( p1, p2, … , pn, body ), https://tc39.es/ecma262/#sec-asyncgeneratorfunction +ThrowCompletionOr AsyncGeneratorFunctionConstructor::call() +{ + return TRY(construct(*this)); +} + +// 27.4.1.1 AsyncGeneratorFunction ( p1, p2, … , pn, body ), https://tc39.es/ecma262/#sec-asyncgeneratorfunction +ThrowCompletionOr AsyncGeneratorFunctionConstructor::construct(FunctionObject& new_target) +{ + auto& vm = this->vm(); + auto function = TRY(FunctionConstructor::create_dynamic_function_node(global_object(), new_target, FunctionKind::AsyncGenerator)); + + OwnPtr local_interpreter; + Interpreter* interpreter = vm.interpreter_if_exists(); + + if (!interpreter) { + local_interpreter = Interpreter::create_with_existing_realm(*realm()); + interpreter = local_interpreter.ptr(); + } + + VM::InterpreterExecutionScope scope(*interpreter); + auto result = function->execute(*interpreter, global_object()); + if (auto* exception = vm.exception()) + return throw_completion(exception->value()); + VERIFY(result.is_object() && is(result.as_object())); + return &result.as_object(); +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/AsyncGeneratorFunctionConstructor.h b/Userland/Libraries/LibJS/Runtime/AsyncGeneratorFunctionConstructor.h new file mode 100644 index 0000000000..4395ced16e --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/AsyncGeneratorFunctionConstructor.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021, David Tuin + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace JS { + +class AsyncGeneratorFunctionConstructor final : public NativeFunction { + JS_OBJECT(AsyncGeneratorFunctionConstructor, NativeFunction); + +public: + explicit AsyncGeneratorFunctionConstructor(GlobalObject&); + virtual void initialize(GlobalObject&) override; + virtual ~AsyncGeneratorFunctionConstructor() override = default; + + virtual ThrowCompletionOr call() override; + virtual ThrowCompletionOr construct(FunctionObject& new_target) override; + +private: + virtual bool has_constructor() const override { return true; } +}; + +} diff --git a/Userland/Libraries/LibJS/Runtime/AsyncGeneratorFunctionPrototype.cpp b/Userland/Libraries/LibJS/Runtime/AsyncGeneratorFunctionPrototype.cpp new file mode 100644 index 0000000000..ccf76ab7c7 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/AsyncGeneratorFunctionPrototype.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021, David Tuin + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace JS { + +AsyncGeneratorFunctionPrototype::AsyncGeneratorFunctionPrototype(GlobalObject& global_object) + : PrototypeObject(*global_object.function_prototype()) +{ +} + +void AsyncGeneratorFunctionPrototype::initialize(GlobalObject& global_object) +{ + auto& vm = this->vm(); + Object::initialize(global_object); + + // The constructor cannot be set at this point since it has not been initialized. + + // 27.4.3.2 AsyncGeneratorFunction.prototype.prototype, https://tc39.es/ecma262/#sec-asyncgeneratorfunction-prototype-prototype + // FIXME: AsyncGenerator does not exist yet. + // define_direct_property(vm.names.prototype, global_object.async_generator_prototype(), Attribute::Configurable); + + // 27.4.3.3 AsyncGeneratorFunction.prototype [ @@toStringTag ], https://tc39.es/ecma262/#sec-asyncgeneratorfunction-prototype-tostringtag + define_direct_property(*vm.well_known_symbol_to_string_tag(), js_string(vm, vm.names.AsyncGeneratorFunction.as_string()), Attribute::Configurable); +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/AsyncGeneratorFunctionPrototype.h b/Userland/Libraries/LibJS/Runtime/AsyncGeneratorFunctionPrototype.h new file mode 100644 index 0000000000..23179d585b --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/AsyncGeneratorFunctionPrototype.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2021, David Tuin + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace JS { + +class AsyncGeneratorFunctionPrototype final : public PrototypeObject { + JS_PROTOTYPE_OBJECT(AsyncGeneratorFunctionPrototype, AsyncGeneratorFunction, AsyncGeneratorFunction); + +public: + explicit AsyncGeneratorFunctionPrototype(GlobalObject&); + virtual void initialize(GlobalObject&) override; + virtual ~AsyncGeneratorFunctionPrototype() override = default; +}; + +} diff --git a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp index 3c4ae45323..3d193f8c7d 100644 --- a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp @@ -41,6 +41,9 @@ ECMAScriptFunctionObject* ECMAScriptFunctionObject::create(GlobalObject& global_ case FunctionKind::Async: prototype = global_object.async_function_prototype(); break; + case FunctionKind::AsyncGenerator: + prototype = global_object.async_generator_function_prototype(); + break; } return global_object.heap().allocate(global_object, move(name), ecmascript_code, move(parameters), m_function_length, parent_scope, private_scope, *prototype, kind, is_strict, might_need_arguments_object, contains_direct_call_to_eval, is_arrow_function); } @@ -106,6 +109,9 @@ void ECMAScriptFunctionObject::initialize(GlobalObject& global_object) break; case FunctionKind::Async: break; + case FunctionKind::AsyncGenerator: + // FIXME: Add the AsyncGeneratorObject and set it as prototype. + break; } define_direct_property(vm.names.prototype, prototype, Attribute::Writable); } @@ -750,6 +756,9 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body() auto& vm = this->vm(); auto* bytecode_interpreter = Bytecode::Interpreter::current(); + if (m_kind == FunctionKind::AsyncGenerator) + return vm.throw_completion(global_object(), ErrorType::NotImplemented, "Async Generator function execution"); + if (bytecode_interpreter) { // FIXME: pass something to evaluate default arguments with TRY(function_declaration_instantiation(nullptr)); diff --git a/Userland/Libraries/LibJS/Runtime/FunctionConstructor.cpp b/Userland/Libraries/LibJS/Runtime/FunctionConstructor.cpp index a39eae0703..ba091e1723 100644 --- a/Userland/Libraries/LibJS/Runtime/FunctionConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/FunctionConstructor.cpp @@ -53,8 +53,8 @@ ThrowCompletionOr> FunctionConstructor::create_dynami parameters_source = parameters_builder.build(); body_source = TRY(vm.argument(vm.argument_count() - 1).to_string(global_object)); } - auto is_generator = kind == FunctionKind::Generator; - auto is_async = kind == FunctionKind::Async; + auto is_generator = kind == FunctionKind::Generator || kind == FunctionKind::AsyncGenerator; + auto is_async = kind == FunctionKind::Async || kind == FunctionKind::AsyncGenerator; auto source = String::formatted("{}function{} anonymous({}\n) {{\n{}\n}}", is_async ? "async " : "", is_generator ? "*" : "", parameters_source, body_source); auto parser = Parser(Lexer(source)); auto function = parser.parse_function_node(); diff --git a/Userland/Libraries/LibJS/Runtime/FunctionKind.h b/Userland/Libraries/LibJS/Runtime/FunctionKind.h index 65fc0c21d2..6a4299a863 100644 --- a/Userland/Libraries/LibJS/Runtime/FunctionKind.h +++ b/Userland/Libraries/LibJS/Runtime/FunctionKind.h @@ -12,6 +12,7 @@ enum class FunctionKind { Generator, Regular, Async, + AsyncGenerator }; } diff --git a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp index 72fcf06ef5..ee3ada697b 100644 --- a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include #include @@ -273,6 +275,11 @@ void GlobalObject::initialize_global_object() // 27.3.3.1 GeneratorFunction.prototype.constructor, https://tc39.es/ecma262/#sec-generatorfunction.prototype.constructor m_generator_function_prototype->define_direct_property(vm.names.constructor, m_generator_function_constructor, Attribute::Configurable); + // The async generator constructor cannot be initialized with add_constructor as it has no global binding + m_async_generator_function_constructor = heap().allocate(*this, *this); + // 27.4.3.1 AsyncGeneratorFunction.prototype.constructor, https://tc39.es/ecma262/#sec-asyncgeneratorfunction-prototype-constructor + m_async_generator_function_prototype->define_direct_property(vm.names.constructor, m_async_generator_function_constructor, Attribute::Configurable); + m_array_prototype_values_function = &m_array_prototype->get_without_side_effects(vm.names.values).as_function(); m_eval_function = &get_without_side_effects(vm.names.eval).as_function(); } diff --git a/Userland/Libraries/LibJS/Tests/syntax/async-generators.js b/Userland/Libraries/LibJS/Tests/syntax/async-generators.js new file mode 100644 index 0000000000..68c9550dbb --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/syntax/async-generators.js @@ -0,0 +1,55 @@ +describe("parsing freestanding generators", () => { + test("simple", () => { + expect(`async function* foo() {}`).toEval(); + expect(`async function *foo() {}`).toEval(); + expect(`async function + *foo() {}`).toEval(); + }); + test("yield & await expression", () => { + expect(`async function* foo() { yield; await 1; }`).toEval(); + expect(`async function* foo() { yield (yield); await (yield); }`).toEval(); + expect(`async function* foo() { yield (yield foo); yield (await foo); }`).toEval(); + + expect(`async function foo() { yield; }`).toEval(); + expect(`async function foo() { yield 3; }`).not.toEval(); + expect(`function* foo() { await 3; }`).not.toEval(); + }); + test("yield-from expression", () => { + expect(`async function* foo() { yield *bar; }`).toEval(); + expect(`async function* foo() { yield *(yield); }`).toEval(); + expect(`async function* foo() { yield + *bar; }`).not.toEval(); + expect(`async function foo() { yield + *bar; }`).toEval(); + }); +}); + +describe("parsing object literal generator functions", () => { + test("simple", () => { + expect(`x = { async *foo() { } }`).toEval(); + expect(`x = { async * foo() { } }`).toEval(); + expect(`x = { async * + foo() { } }`).toEval(); + }); + test("yield & await", () => { + expect(`x = { async foo() { yield; await 3;} }`).toEval(); + expect(`x = { async *foo() { yield; await 3; } }`).toEval(); + expect(`x = { async *foo() { yield 42; await 3; } }`).toEval(); + expect(`x = { async *foo() { yield (yield); await (yield); } }`).toEval(); + expect(`x = { async * + foo() { yield (yield); await 4; } }`).toEval(); + + expect(`x = { async foo() { yield 42; } }`).not.toEval(); + expect(`x = { *foo() { await 42; } }`).not.toEval(); + }); +}); + +describe("parsing classes with generator methods", () => { + test("simple", () => { + expect(`class Foo { async *foo() {} }`).toEval(); + expect(`class Foo { static async *foo() {} }`).toEval(); + expect(`class Foo { async *foo() { yield; } }`).toEval(); + expect(`class Foo { async *foo() { yield 42; } }`).toEval(); + expect(`class Foo { async *constructor() { yield 42; } }`).not.toEval(); + }); +}); -- cgit v1.2.3