LibJS: Parse async generator functions

This commit is contained in:
davidot 2021-11-15 01:53:24 +01:00 committed by Linus Groh
parent 5d0f666f22
commit 0982a73d1d
Notes: sideshowbarker 2024-07-18 00:53:17 +09:00
13 changed files with 270 additions and 40 deletions

View file

@ -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");

View file

@ -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

View file

@ -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 \

View file

@ -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;

View file

@ -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();
}
}

View file

@ -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; }
};
}

View file

@ -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);
}
}

View file

@ -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;
};
}

View file

@ -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));

View file

@ -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>();

View file

@ -12,6 +12,7 @@ enum class FunctionKind {
Generator,
Regular,
Async,
AsyncGenerator
};
}

View file

@ -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();
}

View file

@ -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();
});
});