LibJS: Add AsyncFromSyncIteratorPrototype and Async-From-Sync instances

Until we have actual iterator records we have to store the sync iterator
as the raw object.
This commit is contained in:
davidot 2021-11-23 15:01:35 +01:00 committed by Linus Groh
parent 0535c1abbd
commit 064c8be627
Notes: sideshowbarker 2024-07-18 22:57:59 +09:00
8 changed files with 311 additions and 0 deletions

View File

@ -38,6 +38,8 @@ set(SOURCES
Runtime/ArrayIterator.cpp
Runtime/ArrayIteratorPrototype.cpp
Runtime/ArrayPrototype.cpp
Runtime/AsyncFromSyncIterator.cpp
Runtime/AsyncFromSyncIteratorPrototype.cpp
Runtime/AsyncFunctionConstructor.cpp
Runtime/AsyncFunctionDriverWrapper.cpp
Runtime/AsyncFunctionPrototype.cpp

View File

@ -185,6 +185,7 @@ class ProxyConstructor;
// Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct constructor
class GeneratorObjectPrototype;
class AsyncFromSyncIteratorPrototype;
class TypedArrayConstructor;
class TypedArrayPrototype;

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2021, David Tuin <davidot@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/AsyncFromSyncIterator.h>
#include <LibJS/Runtime/AsyncFromSyncIteratorPrototype.h>
#include <LibJS/Runtime/GlobalObject.h>
namespace JS {
AsyncFromSyncIterator* AsyncFromSyncIterator::create(GlobalObject& global_object, Object* sync_iterator_record)
{
return global_object.heap().allocate<AsyncFromSyncIterator>(global_object, global_object, sync_iterator_record);
}
AsyncFromSyncIterator::AsyncFromSyncIterator(GlobalObject& global_object, Object* sync_iterator_record)
: Object(*global_object.async_from_sync_iterator_prototype())
, m_sync_iterator_record(sync_iterator_record)
{
VERIFY(m_sync_iterator_record);
}
void AsyncFromSyncIterator::initialize(GlobalObject& global_object)
{
Object::initialize(global_object);
}
void AsyncFromSyncIterator::visit_edges(Cell::Visitor& visitor)
{
Object::visit_edges(visitor);
visitor.visit(m_sync_iterator_record);
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2021, David Tuin <davidot@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/Object.h>
namespace JS {
// 27.1.4.3 Properties of Async-from-Sync Iterator Instances, https://tc39.es/ecma262/#sec-properties-of-async-from-sync-iterator-instances
class AsyncFromSyncIterator final : public Object {
JS_OBJECT(AsyncFromSyncIterator, Object);
public:
static AsyncFromSyncIterator* create(GlobalObject&, Object* sync_iterator_record);
explicit AsyncFromSyncIterator(GlobalObject&, Object* sync_iterator_record);
virtual void initialize(GlobalObject&) override;
virtual ~AsyncFromSyncIterator() override = default;
void visit_edges(Visitor& visitor) override;
Object& sync_iterator_record() const { return *m_sync_iterator_record; }
private:
Object* m_sync_iterator_record { nullptr }; // [[SyncIteratorRecord]]
};
}

View File

@ -0,0 +1,203 @@
/*
* Copyright (c) 2021, David Tuin <davidot@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/AsyncFromSyncIteratorPrototype.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/IteratorOperations.h>
#include <LibJS/Runtime/Promise.h>
#include <LibJS/Runtime/PromiseConstructor.h>
#include <LibJS/Runtime/PromiseReaction.h>
namespace JS {
AsyncFromSyncIteratorPrototype::AsyncFromSyncIteratorPrototype(GlobalObject& global_object)
: PrototypeObject(*global_object.async_iterator_prototype())
{
}
void AsyncFromSyncIteratorPrototype::initialize(GlobalObject& global_object)
{
auto& vm = global_object.vm();
Object::initialize(global_object);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(vm.names.next, next, 1, attr);
define_native_function(vm.names.return_, return_, 1, attr);
define_native_function(vm.names.throw_, throw_, 1, attr);
}
// 27.1.4.4 AsyncFromSyncIteratorContinuation ( result, promiseCapability ), https://tc39.es/ecma262/#sec-asyncfromsynciteratorcontinuation
static ThrowCompletionOr<Object*> async_from_sync_iterator_continuation(VM& vm, GlobalObject& global_object, Object& result, PromiseCapability& promise_capability)
{
// 1. Let done be IteratorComplete(result).
// 2. IfAbruptRejectPromise(done, promiseCapability).
auto done = TRY_OR_REJECT(vm, promise_capability, iterator_complete(global_object, result));
// 3. Let value be IteratorValue(result).
// 4. IfAbruptRejectPromise(value, promiseCapability).
auto value = TRY_OR_REJECT(vm, promise_capability, iterator_value(global_object, result));
// 5. Let valueWrapper be PromiseResolve(%Promise%, value).
// 6. IfAbruptRejectPromise(valueWrapper, promiseCapability).
auto value_wrapper = TRY_OR_REJECT(vm, promise_capability, promise_resolve(global_object, *global_object.promise_constructor(), value));
// 7. Let unwrap be a new Abstract Closure with parameters (value) that captures done and performs the following steps when called:
auto unwrap = [done](VM& vm, GlobalObject& global_object) -> ThrowCompletionOr<Value> {
// a. Return ! CreateIterResultObject(value, done).
return create_iterator_result_object(global_object, vm.argument(0), done);
};
// 8. Let onFulfilled be ! CreateBuiltinFunction(unwrap, 1, "", « »).
auto on_fulfilled = NativeFunction::create(global_object, "", move(unwrap));
// 9. NOTE: onFulfilled is used when processing the "value" property of an IteratorResult object in order to wait for its value if it is a promise and re-package the result in a new "unwrapped" IteratorResult object.
VERIFY(is<Promise>(value_wrapper));
auto* value_wrapper_promise = static_cast<Promise*>(value_wrapper);
// 10. Perform ! PerformPromiseThen(valueWrapper, onFulfilled, undefined, promiseCapability).
value_wrapper_promise->perform_then(move(on_fulfilled), js_undefined(), promise_capability);
// 11. Return promiseCapability.[[Promise]].
return promise_capability.promise;
}
// 27.1.4.2.1 %AsyncFromSyncIteratorPrototype%.next ( [ value ] ), https://tc39.es/ecma262/#sec-%asyncfromsynciteratorprototype%.next
JS_DEFINE_NATIVE_FUNCTION(AsyncFromSyncIteratorPrototype::next)
{
// 1. Let O be the this value.
// 2. Assert: O is an Object that has a [[SyncIteratorRecord]] internal slot.
auto* this_object = MUST(typed_this_object(global_object));
// 3. Let promiseCapability be ! NewPromiseCapability(%Promise%).
auto promise_capability = MUST(new_promise_capability(global_object, global_object.promise_constructor()));
// 4. Let syncIteratorRecord be O.[[SyncIteratorRecord]].
auto& sync_iterator_record = this_object->sync_iterator_record();
// 5. If value is present, then
// a. Let result be IteratorNext(syncIteratorRecord, value).
// 6. Else,
// a. Let result be IteratorNext(syncIteratorRecord).
// 7. IfAbruptRejectPromise(result, promiseCapability).
auto* result = TRY_OR_REJECT(vm, promise_capability,
(vm.argument_count() > 0 ? iterator_next(sync_iterator_record, vm.argument(0))
: iterator_next(sync_iterator_record)));
// 8. Return ! AsyncFromSyncIteratorContinuation(result, promiseCapability).
return MUST(async_from_sync_iterator_continuation(vm, global_object, *result, promise_capability));
}
// 27.1.4.2.2 %AsyncFromSyncIteratorPrototype%.return ( [ value ] ), https://tc39.es/ecma262/#sec-%asyncfromsynciteratorprototype%.return
JS_DEFINE_NATIVE_FUNCTION(AsyncFromSyncIteratorPrototype::return_)
{
// 1. Let O be the this value.
// 2. Assert: O is an Object that has a [[SyncIteratorRecord]] internal slot.
auto* this_object = MUST(typed_this_object(global_object));
// 3. Let promiseCapability be ! NewPromiseCapability(%Promise%).
auto promise_capability = MUST(new_promise_capability(global_object, global_object.promise_constructor()));
// 4. Let syncIterator be O.[[SyncIteratorRecord]].[[Iterator]].
auto& sync_iterator = this_object->sync_iterator_record();
// 5. Let return be GetMethod(syncIterator, "return").
// 6. IfAbruptRejectPromise(return, promiseCapability).
auto* return_method = TRY_OR_REJECT(vm, promise_capability, Value(&sync_iterator).get_method(global_object, vm.names.return_));
// 7. If return is undefined, then
if (return_method == nullptr) {
// a. Let iterResult be ! CreateIterResultObject(value, true).
auto* iter_result = create_iterator_result_object(global_object, vm.argument(0), true);
// b. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iterResult »).
MUST(vm.call(*promise_capability.reject, js_undefined(), iter_result));
// c. Return promiseCapability.[[Promise]].
return promise_capability.promise;
}
// 8. If value is present, then
// a. Let result be Call(return, syncIterator, « value »).
// 9. Else,
// a. Let result be Call(return, syncIterator).
// 10. IfAbruptRejectPromise(result, promiseCapability).
auto result = TRY_OR_REJECT(vm, promise_capability,
(vm.argument_count() > 0 ? call(global_object, return_method, &sync_iterator, vm.argument(0))
: call(global_object, return_method, &sync_iterator)));
// 11. If Type(result) is not Object, then
if (!result.is_object()) {
auto* error = TypeError::create(global_object, String::formatted(ErrorType::NotAnObject.message(), "SyncIteratorReturnResult"));
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
MUST(vm.call(*promise_capability.reject, js_undefined(), error));
// b. Return promiseCapability.[[Promise]].
return promise_capability.promise;
}
// 12. Return ! AsyncFromSyncIteratorContinuation(result, promiseCapability).
return MUST(async_from_sync_iterator_continuation(vm, global_object, result.as_object(), promise_capability));
}
// 27.1.4.2.3 %AsyncFromSyncIteratorPrototype%.throw ( [ value ] ), https://tc39.es/ecma262/#sec-%asyncfromsynciteratorprototype%.throw
JS_DEFINE_NATIVE_FUNCTION(AsyncFromSyncIteratorPrototype::throw_)
{
// 1. Let O be the this value.
// 2. Assert: O is an Object that has a [[SyncIteratorRecord]] internal slot.
auto* this_object = MUST(typed_this_object(global_object));
// 3. Let promiseCapability be ! NewPromiseCapability(%Promise%).
auto promise_capability = MUST(new_promise_capability(global_object, global_object.promise_constructor()));
// 4. Let syncIterator be O.[[SyncIteratorRecord]].[[Iterator]].
auto& sync_iterator = this_object->sync_iterator_record();
// 5. Let throw be GetMethod(syncIterator, "throw").
// 6. IfAbruptRejectPromise(throw, promiseCapability).
auto* throw_method = TRY_OR_REJECT(vm, promise_capability, Value(&sync_iterator).get_method(global_object, vm.names.throw_));
// 7. If throw is undefined, then
if (throw_method == nullptr) {
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « value »).
MUST(vm.call(*promise_capability.reject, js_undefined(), vm.argument(0)));
// b. Return promiseCapability.[[Promise]].
return promise_capability.promise;
}
// 8. If value is present, then
// a. Let result be Call(throw, syncIterator, « value »).
// 9. Else,
// a. Let result be Call(throw, syncIterator).
// 10. IfAbruptRejectPromise(result, promiseCapability).
auto result = TRY_OR_REJECT(vm, promise_capability,
(vm.argument_count() > 0 ? call(global_object, throw_method, &sync_iterator, vm.argument(0))
: call(global_object, throw_method, &sync_iterator)));
// 11. If Type(result) is not Object, then
if (!result.is_object()) {
auto* error = TypeError::create(global_object, String::formatted(ErrorType::NotAnObject.message(), "SyncIteratorThrowResult"));
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
MUST(vm.call(*promise_capability.reject, js_undefined(), error));
// b. Return promiseCapability.[[Promise]].
return promise_capability.promise;
}
// 12. Return ! AsyncFromSyncIteratorContinuation(result, promiseCapability).
return MUST(async_from_sync_iterator_continuation(vm, global_object, result.as_object(), promise_capability));
}
// 27.1.4.1 CreateAsyncFromSyncIterator ( syncIteratorRecord ), https://tc39.es/ecma262/#sec-createasyncfromsynciterator
ThrowCompletionOr<Object*> create_async_from_sync_iterator(GlobalObject& global_object, Object& sync_iterator_record)
{
// 1. Let asyncIterator be ! OrdinaryObjectCreate(%AsyncFromSyncIteratorPrototype%, « [[SyncIteratorRecord]] »).
// 2. Set asyncIterator.[[SyncIteratorRecord]] to syncIteratorRecord.
// 3. Let nextMethod be ! Get(asyncIterator, "next").
// 4. Let iteratorRecord be the Record { [[Iterator]]: asyncIterator, [[NextMethod]]: nextMethod, [[Done]]: false }.
// 5. Return iteratorRecord.
// FIXME: Use actual iterator records instead of objects.
// Note: AsyncFromSyncIterator is an object with the extra slot SyncIteratorRecord.
return AsyncFromSyncIterator::create(global_object, &sync_iterator_record);
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2021, David Tuin <davidot@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/AsyncFromSyncIterator.h>
#include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/PrototypeObject.h>
namespace JS {
// 27.1.4.2 The %AsyncFromSyncIteratorPrototype% Object, https://tc39.es/ecma262/#sec-%asyncfromsynciteratorprototype%-object
class AsyncFromSyncIteratorPrototype final : public PrototypeObject<AsyncFromSyncIteratorPrototype, AsyncFromSyncIterator> {
JS_PROTOTYPE_OBJECT(AsyncFromSyncIteratorPrototype, AsyncFromSyncIterator, AsyncFromSyncIterator);
public:
explicit AsyncFromSyncIteratorPrototype(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~AsyncFromSyncIteratorPrototype() override = default;
private:
JS_DECLARE_NATIVE_FUNCTION(next);
JS_DECLARE_NATIVE_FUNCTION(return_);
JS_DECLARE_NATIVE_FUNCTION(throw_);
};
ThrowCompletionOr<Object*> create_async_from_sync_iterator(GlobalObject&, Object& sync_iterator);
}

View File

@ -20,6 +20,7 @@
#include <LibJS/Runtime/ArrayConstructor.h>
#include <LibJS/Runtime/ArrayIteratorPrototype.h>
#include <LibJS/Runtime/ArrayPrototype.h>
#include <LibJS/Runtime/AsyncFromSyncIteratorPrototype.h>
#include <LibJS/Runtime/AsyncFunctionConstructor.h>
#include <LibJS/Runtime/AsyncFunctionPrototype.h>
#include <LibJS/Runtime/AsyncGeneratorFunctionConstructor.h>
@ -166,6 +167,8 @@ void GlobalObject::initialize_global_object()
m_generator_object_prototype = heap().allocate<GeneratorObjectPrototype>(*this, *this);
m_generator_object_prototype->define_direct_property(vm.names.constructor, m_generator_function_constructor, Attribute::Configurable);
m_async_from_sync_iterator_prototype = heap().allocate<AsyncFromSyncIteratorPrototype>(*this, *this);
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
if (!m_##snake_name##_prototype) \
m_##snake_name##_prototype = heap().allocate<PrototypeName>(*this, *this);

View File

@ -36,6 +36,7 @@ public:
// Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct constructor
GeneratorObjectPrototype* generator_object_prototype() { return m_generator_object_prototype; }
AsyncFromSyncIteratorPrototype* async_from_sync_iterator_prototype() { return m_async_from_sync_iterator_prototype; }
FunctionObject* array_prototype_values_function() const { return m_array_prototype_values_function; }
FunctionObject* eval_function() const { return m_eval_function; }
@ -101,6 +102,7 @@ private:
// Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct constructor
GeneratorObjectPrototype* m_generator_object_prototype { nullptr };
AsyncFromSyncIteratorPrototype* m_async_from_sync_iterator_prototype { nullptr };
FunctionObject* m_array_prototype_values_function { nullptr };
FunctionObject* m_eval_function { nullptr };