summaryrefslogtreecommitdiff
path: root/Userland/Libraries
diff options
context:
space:
mode:
authordavidot <davidot@serenityos.org>2021-11-23 15:01:35 +0100
committerLinus Groh <mail@linusgroh.de>2021-11-29 15:20:07 +0000
commit064c8be6274123630be67f8c91163eae2596381c (patch)
treeedb20dd3dad80423c57efdc0ea229a1b733dc619 /Userland/Libraries
parent0535c1abbd6807b901482b5c0cfdd8f7ce20a364 (diff)
downloadserenity-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')
-rw-r--r--Userland/Libraries/LibJS/CMakeLists.txt2
-rw-r--r--Userland/Libraries/LibJS/Forward.h1
-rw-r--r--Userland/Libraries/LibJS/Runtime/AsyncFromSyncIterator.cpp36
-rw-r--r--Userland/Libraries/LibJS/Runtime/AsyncFromSyncIterator.h32
-rw-r--r--Userland/Libraries/LibJS/Runtime/AsyncFromSyncIteratorPrototype.cpp203
-rw-r--r--Userland/Libraries/LibJS/Runtime/AsyncFromSyncIteratorPrototype.h32
-rw-r--r--Userland/Libraries/LibJS/Runtime/GlobalObject.cpp3
-rw-r--r--Userland/Libraries/LibJS/Runtime/GlobalObject.h2
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 };