diff options
author | davidot <davidot@serenityos.org> | 2021-11-15 01:53:24 +0100 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2021-11-21 21:46:39 +0000 |
commit | 0982a73d1d8c03ed0b7f3686c2a801c543c4c1f7 (patch) | |
tree | 9b28f9fa1c1907583575eb4a759fdd4c49a0fc3e | |
parent | 5d0f666f22db855fb6ecede5e6a570ba66efbec5 (diff) | |
download | serenity-0982a73d1d8c03ed0b7f3686c2a801c543c4c1f7.zip |
LibJS: Parse async generator functions
13 files changed, 270 insertions, 40 deletions
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<JS::Value> 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<ObjectExpression> 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<ObjectExpression> 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<FunctionExpression>(parse_options); properties.append(create_ast_node<ObjectProperty>({ m_state.current_token.filename(), rule_start.position(), position() }, *property_name, function, property_type, true)); @@ -2278,12 +2283,13 @@ NonnullRefPtr<FunctionNodeType> 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<FunctionNodeType, FunctionExpression>; 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<FunctionNodeType> 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<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); + 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 <davidot@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <LibJS/Interpreter.h> +#include <LibJS/Runtime/AsyncGeneratorFunctionConstructor.h> +#include <LibJS/Runtime/ECMAScriptFunctionObject.h> +#include <LibJS/Runtime/FunctionConstructor.h> +#include <LibJS/Runtime/FunctionObject.h> +#include <LibJS/Runtime/GlobalObject.h> + +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<Value> AsyncGeneratorFunctionConstructor::call() +{ + return TRY(construct(*this)); +} + +// 27.4.1.1 AsyncGeneratorFunction ( p1, p2, … , pn, body ), https://tc39.es/ecma262/#sec-asyncgeneratorfunction +ThrowCompletionOr<Object*> AsyncGeneratorFunctionConstructor::construct(FunctionObject& new_target) +{ + auto& vm = this->vm(); + auto function = TRY(FunctionConstructor::create_dynamic_function_node(global_object(), new_target, FunctionKind::AsyncGenerator)); + + OwnPtr<Interpreter> 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<ECMAScriptFunctionObject>(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 <davidot@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <LibJS/Runtime/NativeFunction.h> + +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<Value> call() override; + virtual ThrowCompletionOr<Object*> 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 <davidot@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <LibJS/Runtime/AsyncGeneratorFunctionConstructor.h> +#include <LibJS/Runtime/AsyncGeneratorFunctionPrototype.h> +#include <LibJS/Runtime/GlobalObject.h> + +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 <davidot@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <LibJS/Runtime/PrototypeObject.h> + +namespace JS { + +class AsyncGeneratorFunctionPrototype final : public PrototypeObject<AsyncGeneratorFunctionPrototype, AsyncGeneratorFunction> { + 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<ECMAScriptFunctionObject>(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<InternalError>(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<RefPtr<FunctionExpression>> 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<FunctionExpression>(); 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 <LibJS/Runtime/ArrayPrototype.h> #include <LibJS/Runtime/AsyncFunctionConstructor.h> #include <LibJS/Runtime/AsyncFunctionPrototype.h> +#include <LibJS/Runtime/AsyncGeneratorFunctionConstructor.h> +#include <LibJS/Runtime/AsyncGeneratorFunctionPrototype.h> #include <LibJS/Runtime/AtomicsObject.h> #include <LibJS/Runtime/BigIntConstructor.h> #include <LibJS/Runtime/BigIntPrototype.h> @@ -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<AsyncGeneratorFunctionConstructor>(*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(); + }); +}); |