summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordavidot <davidot@serenityos.org>2021-11-15 01:53:24 +0100
committerLinus Groh <mail@linusgroh.de>2021-11-21 21:46:39 +0000
commit0982a73d1d8c03ed0b7f3686c2a801c543c4c1f7 (patch)
tree9b28f9fa1c1907583575eb4a759fdd4c49a0fc3e
parent5d0f666f22db855fb6ecede5e6a570ba66efbec5 (diff)
downloadserenity-0982a73d1d8c03ed0b7f3686c2a801c543c4c1f7.zip
LibJS: Parse async generator functions
-rw-r--r--Userland/Libraries/LibJS/AST.cpp4
-rw-r--r--Userland/Libraries/LibJS/CMakeLists.txt2
-rw-r--r--Userland/Libraries/LibJS/Forward.h49
-rw-r--r--Userland/Libraries/LibJS/Parser.cpp32
-rw-r--r--Userland/Libraries/LibJS/Runtime/AsyncGeneratorFunctionConstructor.cpp64
-rw-r--r--Userland/Libraries/LibJS/Runtime/AsyncGeneratorFunctionConstructor.h28
-rw-r--r--Userland/Libraries/LibJS/Runtime/AsyncGeneratorFunctionPrototype.cpp33
-rw-r--r--Userland/Libraries/LibJS/Runtime/AsyncGeneratorFunctionPrototype.h22
-rw-r--r--Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp9
-rw-r--r--Userland/Libraries/LibJS/Runtime/FunctionConstructor.cpp4
-rw-r--r--Userland/Libraries/LibJS/Runtime/FunctionKind.h1
-rw-r--r--Userland/Libraries/LibJS/Runtime/GlobalObject.cpp7
-rw-r--r--Userland/Libraries/LibJS/Tests/syntax/async-generators.js55
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();
+ });
+});