mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-20 01:37:39 +03:00
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:
parent
0535c1abbd
commit
064c8be627
Notes:
sideshowbarker
2024-07-18 22:57:59 +09:00
Author: https://github.com/davidot Commit: https://github.com/SerenityOS/serenity/commit/064c8be6274 Pull-request: https://github.com/SerenityOS/serenity/pull/11041 Reviewed-by: https://github.com/linusg
@ -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
|
||||
|
@ -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;
|
||||
|
36
Userland/Libraries/LibJS/Runtime/AsyncFromSyncIterator.cpp
Normal file
36
Userland/Libraries/LibJS/Runtime/AsyncFromSyncIterator.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
32
Userland/Libraries/LibJS/Runtime/AsyncFromSyncIterator.h
Normal file
32
Userland/Libraries/LibJS/Runtime/AsyncFromSyncIterator.h
Normal 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]]
|
||||
};
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
@ -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);
|
||||
|
@ -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 };
|
||||
|
Loading…
Reference in New Issue
Block a user