1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 12:38:12 +00:00

LibJS: Implement async functions as generator functions in BC mode

This applies a simple transformation, and adds a simple wrapper that
translates the generator interface to the async function interface.
This commit is contained in:
Ali Mohammad Pur 2021-11-11 00:46:07 +03:30 committed by Linus Groh
parent c604e95993
commit 3b0bf05fa5
14 changed files with 192 additions and 43 deletions

View file

@ -18,6 +18,7 @@
#include <AK/Vector.h> #include <AK/Vector.h>
#include <LibJS/Forward.h> #include <LibJS/Forward.h>
#include <LibJS/Runtime/EnvironmentCoordinate.h> #include <LibJS/Runtime/EnvironmentCoordinate.h>
#include <LibJS/Runtime/FunctionKind.h>
#include <LibJS/Runtime/PropertyKey.h> #include <LibJS/Runtime/PropertyKey.h>
#include <LibJS/Runtime/Reference.h> #include <LibJS/Runtime/Reference.h>
#include <LibJS/Runtime/Value.h> #include <LibJS/Runtime/Value.h>
@ -33,12 +34,6 @@ class Identifier;
class MemberExpression; class MemberExpression;
class VariableDeclaration; class VariableDeclaration;
enum class FunctionKind {
Generator,
Regular,
Async,
};
template<class T, class... Args> template<class T, class... Args>
static inline NonnullRefPtr<T> static inline NonnullRefPtr<T>
create_ast_node(SourceRange range, Args&&... args) create_ast_node(SourceRange range, Args&&... args)
@ -575,6 +570,7 @@ public:
virtual Value execute(Interpreter&, GlobalObject&) const override; virtual Value execute(Interpreter&, GlobalObject&) const override;
virtual void dump(int indent) const override; virtual void dump(int indent) const override;
virtual void generate_bytecode(Bytecode::Generator&) const override;
private: private:
NonnullRefPtr<Expression> m_argument; NonnullRefPtr<Expression> m_argument;

View file

@ -911,7 +911,7 @@ void ReturnStatement::generate_bytecode(Bytecode::Generator& generator) const
if (m_argument) if (m_argument)
m_argument->generate_bytecode(generator); m_argument->generate_bytecode(generator);
if (generator.is_in_generator_function()) if (generator.is_in_generator_or_async_function())
generator.emit<Bytecode::Op::Yield>(nullptr); generator.emit<Bytecode::Op::Yield>(nullptr);
else else
generator.emit<Bytecode::Op::Return>(); generator.emit<Bytecode::Op::Return>();
@ -1269,4 +1269,16 @@ void ThisExpression::generate_bytecode(Bytecode::Generator& generator) const
generator.emit<Bytecode::Op::ResolveThisBinding>(); generator.emit<Bytecode::Op::ResolveThisBinding>();
} }
void AwaitExpression::generate_bytecode(Bytecode::Generator& generator) const
{
VERIFY(generator.is_in_async_function());
// Transform `await expr` to `yield expr`
m_argument->generate_bytecode(generator);
auto& continuation_block = generator.make_block();
generator.emit<Bytecode::Op::Yield>(Bytecode::Label { continuation_block });
generator.switch_to_basic_block(continuation_block);
}
} }

View file

@ -23,19 +23,19 @@ Generator::~Generator()
{ {
} }
Executable Generator::generate(ASTNode const& node, bool is_in_generator_function) Executable Generator::generate(ASTNode const& node, FunctionKind enclosing_function_kind)
{ {
Generator generator; Generator generator;
generator.switch_to_basic_block(generator.make_block()); generator.switch_to_basic_block(generator.make_block());
if (is_in_generator_function) { generator.m_enclosing_function_kind = enclosing_function_kind;
generator.enter_generator_context(); if (generator.is_in_generator_or_async_function()) {
// Immediately yield with no value. // Immediately yield with no value.
auto& start_block = generator.make_block(); auto& start_block = generator.make_block();
generator.emit<Bytecode::Op::Yield>(Label { start_block }); generator.emit<Bytecode::Op::Yield>(Label { start_block });
generator.switch_to_basic_block(start_block); generator.switch_to_basic_block(start_block);
} }
node.generate_bytecode(generator); node.generate_bytecode(generator);
if (is_in_generator_function) { if (generator.is_in_generator_or_async_function()) {
// Terminate all unterminated blocks with yield return // Terminate all unterminated blocks with yield return
for (auto& block : generator.m_root_basic_blocks) { for (auto& block : generator.m_root_basic_blocks) {
if (block.is_terminated()) if (block.is_terminated())

View file

@ -17,12 +17,13 @@
#include <LibJS/Bytecode/Register.h> #include <LibJS/Bytecode/Register.h>
#include <LibJS/Bytecode/StringTable.h> #include <LibJS/Bytecode/StringTable.h>
#include <LibJS/Forward.h> #include <LibJS/Forward.h>
#include <LibJS/Runtime/FunctionKind.h>
namespace JS::Bytecode { namespace JS::Bytecode {
class Generator { class Generator {
public: public:
static Executable generate(ASTNode const&, bool is_in_generator_function = false); static Executable generate(ASTNode const&, FunctionKind = FunctionKind::Regular);
Register allocate_register(); Register allocate_register();
@ -111,9 +112,9 @@ public:
return m_identifier_table->insert(move(string)); return m_identifier_table->insert(move(string));
} }
bool is_in_generator_function() const { return m_is_in_generator_function; } bool is_in_generator_or_async_function() const { return m_enclosing_function_kind == FunctionKind::Async || m_enclosing_function_kind == FunctionKind::Generator; }
void enter_generator_context() { m_is_in_generator_function = true; } bool is_in_generator_function() const { return m_enclosing_function_kind == FunctionKind::Generator; }
void leave_generator_context() { m_is_in_generator_function = false; } bool is_in_async_function() const { return m_enclosing_function_kind == FunctionKind::Async; }
private: private:
Generator(); Generator();
@ -129,7 +130,7 @@ private:
u32 m_next_register { 2 }; u32 m_next_register { 2 };
u32 m_next_block { 1 }; u32 m_next_block { 1 };
bool m_is_in_generator_function { false }; FunctionKind m_enclosing_function_kind { FunctionKind::Regular };
Vector<Label> m_continuable_scopes; Vector<Label> m_continuable_scopes;
Vector<Label> m_breakable_scopes; Vector<Label> m_breakable_scopes;
}; };

View file

@ -39,6 +39,7 @@ set(SOURCES
Runtime/ArrayIteratorPrototype.cpp Runtime/ArrayIteratorPrototype.cpp
Runtime/ArrayPrototype.cpp Runtime/ArrayPrototype.cpp
Runtime/AsyncFunctionConstructor.cpp Runtime/AsyncFunctionConstructor.cpp
Runtime/AsyncFunctionDriverWrapper.cpp
Runtime/AsyncFunctionPrototype.cpp Runtime/AsyncFunctionPrototype.cpp
Runtime/AtomicsObject.cpp Runtime/AtomicsObject.cpp
Runtime/BigInt.cpp Runtime/BigInt.cpp

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/AsyncFunctionDriverWrapper.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/NativeFunction.h>
#include <LibJS/Runtime/PromiseReaction.h>
#include <LibJS/Runtime/VM.h>
namespace JS {
ThrowCompletionOr<Value> AsyncFunctionDriverWrapper::create(GlobalObject& global_object, GeneratorObject* generator_object)
{
auto wrapper = global_object.heap().allocate<AsyncFunctionDriverWrapper>(global_object, global_object, generator_object);
return wrapper->react_to_async_task_completion(global_object.vm(), global_object, js_undefined(), true);
}
AsyncFunctionDriverWrapper::AsyncFunctionDriverWrapper(GlobalObject& global_object, GeneratorObject* generator_object)
: Promise(global_object)
, m_generator_object(generator_object)
, m_on_fulfillment(NativeFunction::create(global_object, "async.on_fulfillment"sv, [this](VM& vm, GlobalObject& global_object) {
return react_to_async_task_completion(vm, global_object, vm.argument(0), true);
}))
, m_on_rejection(NativeFunction::create(global_object, "async.on_rejection"sv, [this](VM& vm, GlobalObject& global_object) {
return react_to_async_task_completion(vm, global_object, vm.argument(0), false);
}))
{
}
ThrowCompletionOr<Value> AsyncFunctionDriverWrapper::react_to_async_task_completion(VM& vm, GlobalObject& global_object, Value value, bool is_successful)
{
auto generator_result = is_successful
? m_generator_object->next_impl(vm, global_object, value, {})
: m_generator_object->next_impl(vm, global_object, {}, value);
if (generator_result.is_throw_completion()) {
VERIFY(generator_result.throw_completion().type() == Completion::Type::Throw);
vm.clear_exception();
vm.stop_unwind();
auto promise = Promise::create(global_object);
promise->reject(generator_result.throw_completion().value());
return promise;
}
auto result = generator_result.release_value();
VERIFY(result.is_object());
auto promise_value = TRY(result.get(global_object, vm.names.value));
if (!promise_value.is_object() || !is<Promise>(promise_value.as_object())) {
auto promise = Promise::create(global_object);
promise->fulfill(promise_value);
return promise;
}
auto* promise = static_cast<Promise*>(&promise_value.as_object());
if (TRY(result.get(global_object, vm.names.done)).to_boolean())
return promise;
return promise->perform_then(m_on_fulfillment, m_on_rejection, PromiseCapability { promise, m_on_fulfillment, m_on_rejection });
}
AsyncFunctionDriverWrapper::~AsyncFunctionDriverWrapper()
{
}
void AsyncFunctionDriverWrapper::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_generator_object);
visitor.visit(m_on_fulfillment);
visitor.visit(m_on_rejection);
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Bytecode/Interpreter.h>
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
#include <LibJS/Runtime/GeneratorObject.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Promise.h>
namespace JS {
class AsyncFunctionDriverWrapper final : public Promise {
JS_OBJECT(AsyncFunctionDriverWrapper, Promise);
public:
static ThrowCompletionOr<Value> create(GlobalObject&, GeneratorObject*);
explicit AsyncFunctionDriverWrapper(GlobalObject&, GeneratorObject*);
virtual ~AsyncFunctionDriverWrapper() override;
void visit_edges(Cell::Visitor&) override;
ThrowCompletionOr<Value> react_to_async_task_completion(VM&, GlobalObject&, Value, bool is_successful);
private:
GeneratorObject* m_generator_object { nullptr };
NativeFunction* m_on_fulfillment { nullptr };
NativeFunction* m_on_rejection { nullptr };
};
}

View file

@ -13,6 +13,7 @@
#include <LibJS/Interpreter.h> #include <LibJS/Interpreter.h>
#include <LibJS/Runtime/AbstractOperations.h> #include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Array.h> #include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/AsyncFunctionDriverWrapper.h>
#include <LibJS/Runtime/ECMAScriptFunctionObject.h> #include <LibJS/Runtime/ECMAScriptFunctionObject.h>
#include <LibJS/Runtime/Error.h> #include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/ExecutionContext.h> #include <LibJS/Runtime/ExecutionContext.h>
@ -100,11 +101,10 @@ void ECMAScriptFunctionObject::initialize(GlobalObject& global_object)
MUST(prototype->define_property_or_throw(vm.names.constructor, { .value = this, .writable = true, .enumerable = false, .configurable = true })); MUST(prototype->define_property_or_throw(vm.names.constructor, { .value = this, .writable = true, .enumerable = false, .configurable = true }));
break; break;
case FunctionKind::Generator: case FunctionKind::Generator:
case FunctionKind::Async:
// prototype is "g1.prototype" in figure-2 (https://tc39.es/ecma262/img/figure-2.png) // prototype is "g1.prototype" in figure-2 (https://tc39.es/ecma262/img/figure-2.png)
prototype = global_object.generator_object_prototype(); prototype = global_object.generator_object_prototype();
break; break;
case FunctionKind::Async:
break;
} }
define_direct_property(vm.names.prototype, prototype, Attribute::Writable); define_direct_property(vm.names.prototype, prototype, Attribute::Writable);
} }
@ -753,12 +753,10 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
auto* bytecode_interpreter = Bytecode::Interpreter::current(); auto* bytecode_interpreter = Bytecode::Interpreter::current();
if (bytecode_interpreter) { if (bytecode_interpreter) {
if (m_kind == FunctionKind::Async)
return vm.throw_completion<InternalError>(global_object(), ErrorType::NotImplemented, "Async function execution in Bytecode interpreter");
// FIXME: pass something to evaluate default arguments with // FIXME: pass something to evaluate default arguments with
TRY(function_declaration_instantiation(nullptr)); TRY(function_declaration_instantiation(nullptr));
if (!m_bytecode_executable.has_value()) { if (!m_bytecode_executable.has_value()) {
m_bytecode_executable = Bytecode::Generator::generate(m_ecmascript_code, m_kind == FunctionKind::Generator); m_bytecode_executable = Bytecode::Generator::generate(m_ecmascript_code, m_kind);
m_bytecode_executable->name = m_name; m_bytecode_executable->name = m_name;
auto& passes = JS::Bytecode::Interpreter::optimization_pipeline(); auto& passes = JS::Bytecode::Interpreter::optimization_pipeline();
passes.perform(*m_bytecode_executable); passes.perform(*m_bytecode_executable);
@ -778,10 +776,17 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
// NOTE: Running the bytecode should eventually return a completion. // NOTE: Running the bytecode should eventually return a completion.
// Until it does, we assume "return" and include the undefined fallback from the call site. // Until it does, we assume "return" and include the undefined fallback from the call site.
if (m_kind != FunctionKind::Generator) if (m_kind == FunctionKind::Regular)
return { Completion::Type::Return, result.value_or(js_undefined()), {} }; return { Completion::Type::Return, result.value_or(js_undefined()), {} };
auto generator_object = TRY(GeneratorObject::create(global_object(), result, this, vm.running_execution_context().lexical_environment, move(*result_and_frame.frame))); auto generator_object = TRY(GeneratorObject::create(global_object(), result, this, vm.running_execution_context().copy(), move(*result_and_frame.frame)));
// NOTE: Async functions are entirely transformed to generator functions, and wrapped in a custom driver that returns a promise
// See AwaitExpression::generate_bytecode() for the transformation.
if (m_kind == FunctionKind::Async)
return { Completion::Type::Return, TRY(AsyncFunctionDriverWrapper::create(global_object(), generator_object)), {} };
VERIFY(m_kind == FunctionKind::Generator);
return { Completion::Type::Return, generator_object, {} }; return { Completion::Type::Return, generator_object, {} };
} else { } else {
if (m_kind == FunctionKind::Generator) if (m_kind == FunctionKind::Generator)

View file

@ -0,0 +1,17 @@
/*
* Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
namespace JS {
enum class FunctionKind {
Generator,
Regular,
Async,
};
}

View file

@ -50,7 +50,7 @@ ThrowCompletionOr<Object*> GeneratorFunctionConstructor::construct(FunctionObjec
auto* bytecode_interpreter = Bytecode::Interpreter::current(); auto* bytecode_interpreter = Bytecode::Interpreter::current();
VERIFY(bytecode_interpreter); VERIFY(bytecode_interpreter);
auto executable = Bytecode::Generator::generate(function->body(), true); auto executable = Bytecode::Generator::generate(function->body(), FunctionKind::Generator);
auto& passes = JS::Bytecode::Interpreter::optimization_pipeline(); auto& passes = JS::Bytecode::Interpreter::optimization_pipeline();
passes.perform(executable); passes.perform(executable);
if constexpr (JS_BYTECODE_DEBUG) { if constexpr (JS_BYTECODE_DEBUG) {

View file

@ -13,21 +13,21 @@
namespace JS { namespace JS {
ThrowCompletionOr<GeneratorObject*> GeneratorObject::create(GlobalObject& global_object, Value initial_value, ECMAScriptFunctionObject* generating_function, Environment* generating_scope, Bytecode::RegisterWindow frame) ThrowCompletionOr<GeneratorObject*> GeneratorObject::create(GlobalObject& global_object, Value initial_value, ECMAScriptFunctionObject* generating_function, ExecutionContext execution_context, Bytecode::RegisterWindow frame)
{ {
// This is "g1.prototype" in figure-2 (https://tc39.es/ecma262/img/figure-2.png) // This is "g1.prototype" in figure-2 (https://tc39.es/ecma262/img/figure-2.png)
auto generating_function_prototype = TRY(generating_function->get(global_object.vm().names.prototype)); auto generating_function_prototype = TRY(generating_function->get(global_object.vm().names.prototype));
auto* generating_function_prototype_object = TRY(generating_function_prototype.to_object(global_object)); auto* generating_function_prototype_object = TRY(generating_function_prototype.to_object(global_object));
auto object = global_object.heap().allocate<GeneratorObject>(global_object, global_object, *generating_function_prototype_object); auto object = global_object.heap().allocate<GeneratorObject>(global_object, global_object, *generating_function_prototype_object, move(execution_context));
object->m_generating_function = generating_function; object->m_generating_function = generating_function;
object->m_environment = generating_scope;
object->m_frame = move(frame); object->m_frame = move(frame);
object->m_previous_value = initial_value; object->m_previous_value = initial_value;
return object; return object;
} }
GeneratorObject::GeneratorObject(GlobalObject&, Object& prototype) GeneratorObject::GeneratorObject(GlobalObject&, Object& prototype, ExecutionContext context)
: Object(prototype) : Object(prototype)
, m_execution_context(move(context))
{ {
} }
@ -42,12 +42,11 @@ GeneratorObject::~GeneratorObject()
void GeneratorObject::visit_edges(Cell::Visitor& visitor) void GeneratorObject::visit_edges(Cell::Visitor& visitor)
{ {
Base::visit_edges(visitor); Base::visit_edges(visitor);
visitor.visit(m_environment);
visitor.visit(m_generating_function); visitor.visit(m_generating_function);
visitor.visit(m_previous_value); visitor.visit(m_previous_value);
} }
ThrowCompletionOr<Value> GeneratorObject::next_impl(VM& vm, GlobalObject& global_object, Optional<Value> value_to_throw) ThrowCompletionOr<Value> GeneratorObject::next_impl(VM& vm, GlobalObject& global_object, Optional<Value> next_argument, Optional<Value> value_to_throw)
{ {
auto bytecode_interpreter = Bytecode::Interpreter::current(); auto bytecode_interpreter = Bytecode::Interpreter::current();
VERIFY(bytecode_interpreter); VERIFY(bytecode_interpreter);
@ -92,20 +91,25 @@ ThrowCompletionOr<Value> GeneratorObject::next_impl(VM& vm, GlobalObject& global
// Restore the snapshot registers // Restore the snapshot registers
bytecode_interpreter->enter_frame(m_frame); bytecode_interpreter->enter_frame(m_frame);
// Temporarily switch to the captured execution context
vm.push_execution_context(m_execution_context, global_object);
// Pretend that 'yield' returned the passed value, or threw // Pretend that 'yield' returned the passed value, or threw
if (value_to_throw.has_value()) { if (value_to_throw.has_value()) {
vm.throw_exception(global_object, value_to_throw.release_value()); vm.throw_exception(global_object, value_to_throw.release_value());
bytecode_interpreter->accumulator() = js_undefined(); bytecode_interpreter->accumulator() = js_undefined();
} else { } else {
bytecode_interpreter->accumulator() = vm.argument(0); bytecode_interpreter->accumulator() = next_argument.value_or(js_undefined());
} }
// Temporarily switch to the captured environment record // Temporarily switch to the captured execution context
TemporaryChange change { vm.running_execution_context().lexical_environment, m_environment }; vm.push_execution_context(m_execution_context, global_object);
m_previous_value = bytecode_interpreter->run(*m_generating_function->bytecode_executable(), next_block); m_previous_value = bytecode_interpreter->run(*m_generating_function->bytecode_executable(), next_block);
bytecode_interpreter->leave_frame(); m_frame = move(*bytecode_interpreter->pop_frame());
vm.pop_execution_context();
m_done = TRY(generated_continuation(m_previous_value)) == nullptr; m_done = TRY(generated_continuation(m_previous_value)) == nullptr;

View file

@ -16,17 +16,17 @@ class GeneratorObject final : public Object {
JS_OBJECT(GeneratorObject, Object); JS_OBJECT(GeneratorObject, Object);
public: public:
static ThrowCompletionOr<GeneratorObject*> create(GlobalObject&, Value, ECMAScriptFunctionObject*, Environment*, Bytecode::RegisterWindow); static ThrowCompletionOr<GeneratorObject*> create(GlobalObject&, Value, ECMAScriptFunctionObject*, ExecutionContext, Bytecode::RegisterWindow);
GeneratorObject(GlobalObject&, Object& prototype); GeneratorObject(GlobalObject&, Object& prototype, ExecutionContext);
virtual void initialize(GlobalObject&) override; virtual void initialize(GlobalObject&) override;
virtual ~GeneratorObject() override; virtual ~GeneratorObject() override;
void visit_edges(Cell::Visitor&) override; void visit_edges(Cell::Visitor&) override;
ThrowCompletionOr<Value> next_impl(VM&, GlobalObject&, Optional<Value> value_to_throw); ThrowCompletionOr<Value> next_impl(VM&, GlobalObject&, Optional<Value> next_argument, Optional<Value> value_to_throw);
void set_done() { m_done = true; } void set_done() { m_done = true; }
private: private:
Environment* m_environment { nullptr }; ExecutionContext m_execution_context;
ECMAScriptFunctionObject* m_generating_function { nullptr }; ECMAScriptFunctionObject* m_generating_function { nullptr };
Value m_previous_value; Value m_previous_value;
Bytecode::RegisterWindow m_frame; Bytecode::RegisterWindow m_frame;

View file

@ -35,7 +35,7 @@ GeneratorObjectPrototype::~GeneratorObjectPrototype()
JS_DEFINE_NATIVE_FUNCTION(GeneratorObjectPrototype::next) JS_DEFINE_NATIVE_FUNCTION(GeneratorObjectPrototype::next)
{ {
auto* generator_object = TRY(typed_this_object(global_object)); auto* generator_object = TRY(typed_this_object(global_object));
return generator_object->next_impl(vm, global_object, {}); return generator_object->next_impl(vm, global_object, vm.argument(0), {});
} }
// 27.5.1.3 Generator.prototype.next ( value ), https://tc39.es/ecma262/#sec-generator.prototype.return // 27.5.1.3 Generator.prototype.next ( value ), https://tc39.es/ecma262/#sec-generator.prototype.return
@ -43,14 +43,14 @@ JS_DEFINE_NATIVE_FUNCTION(GeneratorObjectPrototype::return_)
{ {
auto* generator_object = TRY(typed_this_object(global_object)); auto* generator_object = TRY(typed_this_object(global_object));
generator_object->set_done(); generator_object->set_done();
return generator_object->next_impl(vm, global_object, {}); return generator_object->next_impl(vm, global_object, {}, {});
} }
// 27.5.1.4 Generator.prototype.next ( value ), https://tc39.es/ecma262/#sec-generator.prototype.throw // 27.5.1.4 Generator.prototype.next ( value ), https://tc39.es/ecma262/#sec-generator.prototype.throw
JS_DEFINE_NATIVE_FUNCTION(GeneratorObjectPrototype::throw_) JS_DEFINE_NATIVE_FUNCTION(GeneratorObjectPrototype::throw_)
{ {
auto* generator_object = TRY(typed_this_object(global_object)); auto* generator_object = TRY(typed_this_object(global_object));
return generator_object->next_impl(vm, global_object, vm.argument(0)); return generator_object->next_impl(vm, global_object, {}, vm.argument(0));
} }
} }

View file

@ -13,7 +13,7 @@ namespace JS {
ThrowCompletionOr<Object*> promise_resolve(GlobalObject&, Object& constructor, Value); ThrowCompletionOr<Object*> promise_resolve(GlobalObject&, Object& constructor, Value);
class Promise final : public Object { class Promise : public Object {
JS_OBJECT(Promise, Object); JS_OBJECT(Promise, Object);
public: public:
@ -45,9 +45,10 @@ public:
Value reject(Value reason); Value reject(Value reason);
Value perform_then(Value on_fulfilled, Value on_rejected, Optional<PromiseCapability> result_capability); Value perform_then(Value on_fulfilled, Value on_rejected, Optional<PromiseCapability> result_capability);
private: protected:
virtual void visit_edges(Visitor&) override; virtual void visit_edges(Visitor&) override;
private:
bool is_settled() const { return m_state == State::Fulfilled || m_state == State::Rejected; } bool is_settled() const { return m_state == State::Fulfilled || m_state == State::Rejected; }
void trigger_reactions() const; void trigger_reactions() const;