diff options
author | davidot <davidot@serenityos.org> | 2021-11-23 15:01:35 +0100 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2021-11-29 15:20:07 +0000 |
commit | 064c8be6274123630be67f8c91163eae2596381c (patch) | |
tree | edb20dd3dad80423c57efdc0ea229a1b733dc619 /Userland/Libraries | |
parent | 0535c1abbd6807b901482b5c0cfdd8f7ce20a364 (diff) | |
download | serenity-064c8be6274123630be67f8c91163eae2596381c.zip |
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.
Diffstat (limited to 'Userland/Libraries')
8 files changed, 311 insertions, 0 deletions
diff --git a/Userland/Libraries/LibJS/CMakeLists.txt b/Userland/Libraries/LibJS/CMakeLists.txt index f6ddf44313..8238db7e9f 100644 --- a/Userland/Libraries/LibJS/CMakeLists.txt +++ b/Userland/Libraries/LibJS/CMakeLists.txt @@ -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 diff --git a/Userland/Libraries/LibJS/Forward.h b/Userland/Libraries/LibJS/Forward.h index fd0db9cccd..2ae6ba6e84 100644 --- a/Userland/Libraries/LibJS/Forward.h +++ b/Userland/Libraries/LibJS/Forward.h @@ -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; diff --git a/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIterator.cpp b/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIterator.cpp new file mode 100644 index 0000000000..50e48647e7 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIterator.cpp @@ -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); +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIterator.h b/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIterator.h new file mode 100644 index 0000000000..ea6474403d --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIterator.h @@ -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]] +}; + +} diff --git a/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIteratorPrototype.cpp b/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIteratorPrototype.cpp new file mode 100644 index 0000000000..e8c8f50e2f --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIteratorPrototype.cpp @@ -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); +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIteratorPrototype.h b/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIteratorPrototype.h new file mode 100644 index 0000000000..098ed9ab4f --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIteratorPrototype.h @@ -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); + +} diff --git a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp index 338e16ee9f..11ed075409 100644 --- a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp @@ -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); diff --git a/Userland/Libraries/LibJS/Runtime/GlobalObject.h b/Userland/Libraries/LibJS/Runtime/GlobalObject.h index 712f584c0b..35d9daac14 100644 --- a/Userland/Libraries/LibJS/Runtime/GlobalObject.h +++ b/Userland/Libraries/LibJS/Runtime/GlobalObject.h @@ -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 }; |