зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1772772 - Implement ReadableStream.from. r=saschanaz,spidermonkey-reviewers,webidl,mgaudet
Differential Revision: https://phabricator.services.mozilla.com/D148358
This commit is contained in:
Родитель
03f524f519
Коммит
918d6e00af
|
@ -16,6 +16,7 @@
|
|||
#include "js/PropertyAndElement.h"
|
||||
#include "js/TypeDecls.h"
|
||||
#include "js/Value.h"
|
||||
#include "js/Iterator.h"
|
||||
#include "mozilla/AlreadyAddRefed.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
@ -240,6 +241,218 @@ already_AddRefed<ReadableStream> ReadableStream::Constructor(
|
|||
return readableStream.forget();
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#readable-stream-from-iterable
|
||||
class ReadableStreamFromAlgorithms final
|
||||
: public UnderlyingSourceAlgorithmsWrapper {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(
|
||||
ReadableStreamFromAlgorithms, UnderlyingSourceAlgorithmsWrapper)
|
||||
|
||||
ReadableStreamFromAlgorithms(nsIGlobalObject* aGlobal,
|
||||
JS::Handle<JSObject*> aIteratorRecord)
|
||||
: mGlobal(aGlobal), mIteratorRecord(aIteratorRecord) {
|
||||
mozilla::HoldJSObjects(this);
|
||||
};
|
||||
|
||||
// Step 3. Let startAlgorithm be an algorithm that returns undefined.
|
||||
// Note: Provided by UnderlyingSourceAlgorithmsWrapper::StartCallback.
|
||||
|
||||
// Step 4. Let pullAlgorithm be the following steps:
|
||||
MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> PullCallbackImpl(
|
||||
JSContext* aCx, ReadableStreamController& aController,
|
||||
ErrorResult& aRv) override {
|
||||
aRv.MightThrowJSException();
|
||||
|
||||
// Step 1. Let nextResult be IteratorNext(iteratorRecord).
|
||||
JS::Rooted<JSObject*> iteratorRecord(aCx, mIteratorRecord);
|
||||
JS::Rooted<JS::Value> nextResult(aCx);
|
||||
if (!JS::IteratorNext(aCx, iteratorRecord, &nextResult)) {
|
||||
// Step 2. If nextResult is an abrupt completion, return a promise
|
||||
// rejected with nextResult.[[Value]].
|
||||
aRv.StealExceptionFromJSContext(aCx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Step 3. Let nextPromise be a promise resolved with nextResult.[[Value]].
|
||||
RefPtr<Promise> nextPromise = Promise::CreateInfallible(mGlobal);
|
||||
nextPromise->MaybeResolve(nextResult);
|
||||
|
||||
// Step 4. Return the result of reacting to nextPromise with the following
|
||||
// fulfillment steps, given iterResult:
|
||||
auto result = nextPromise->ThenWithCycleCollectedArgs(
|
||||
[](JSContext* aCx, JS::Handle<JS::Value> aIterResult, ErrorResult& aRv,
|
||||
const RefPtr<ReadableStreamDefaultController>& aController)
|
||||
MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION -> already_AddRefed<Promise> {
|
||||
aRv.MightThrowJSException();
|
||||
|
||||
// Step 4.1. If Type(iterResult) is not Object, throw a TypeError.
|
||||
if (!aIterResult.isObject()) {
|
||||
aRv.ThrowTypeError("next() returned a non-object value");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Step 4.2. Let done be ? IteratorComplete(iterResult).
|
||||
JS::Rooted<JSObject*> iterResult(aCx, &aIterResult.toObject());
|
||||
bool done = false;
|
||||
if (!JS::IteratorComplete(aCx, iterResult, &done)) {
|
||||
aRv.StealExceptionFromJSContext(aCx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Step 4.3. If done is true:
|
||||
if (done) {
|
||||
// Step 4.3.1. Perform !
|
||||
// ReadableStreamDefaultControllerClose(stream.[[controller]]).
|
||||
ReadableStreamDefaultControllerClose(aCx, aController, aRv);
|
||||
} else {
|
||||
// Step 4.4. Otherwise:
|
||||
// Step 4.4.1. Let value be ? IteratorValue(iterResult).
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
if (!JS::IteratorValue(aCx, iterResult, &value)) {
|
||||
aRv.StealExceptionFromJSContext(aCx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Step 4.4.2. Perform !
|
||||
// ReadableStreamDefaultControllerEnqueue(stream.[[controller]],
|
||||
// value).
|
||||
ReadableStreamDefaultControllerEnqueue(aCx, aController, value,
|
||||
aRv);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
},
|
||||
RefPtr(aController.AsDefault()));
|
||||
if (result.isErr()) {
|
||||
aRv.Throw(result.unwrapErr());
|
||||
return nullptr;
|
||||
}
|
||||
return result.unwrap().forget();
|
||||
};
|
||||
|
||||
// Step 5. Let cancelAlgorithm be the following steps, given reason:
|
||||
MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> CancelCallbackImpl(
|
||||
JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason,
|
||||
ErrorResult& aRv) override {
|
||||
aRv.MightThrowJSException();
|
||||
|
||||
// Step 1. Let iterator be iteratorRecord.[[Iterator]].
|
||||
JS::Rooted<JSObject*> iteratorRecord(aCx, mIteratorRecord);
|
||||
JS::Rooted<JS::Value> iterator(aCx);
|
||||
if (!JS::GetIteratorRecordIterator(aCx, iteratorRecord, &iterator)) {
|
||||
aRv.StealExceptionFromJSContext(aCx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Step 2. Let returnMethod be GetMethod(iterator, "return").
|
||||
JS::Rooted<JS::Value> returnMethod(aCx);
|
||||
if (!JS::GetReturnMethod(aCx, iterator, &returnMethod)) {
|
||||
// Step 3. If returnMethod is an abrupt completion, return a promise
|
||||
// rejected with returnMethod.[[Value]].
|
||||
aRv.StealExceptionFromJSContext(aCx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Step 4. If returnMethod.[[Value]] is undefined, return a promise resolved
|
||||
// with undefined.
|
||||
if (returnMethod.isUndefined()) {
|
||||
return Promise::CreateResolvedWithUndefined(mGlobal, aRv);
|
||||
}
|
||||
|
||||
// Step 5. Let returnResult be Call(returnMethod.[[Value]], iterator, «
|
||||
// reason »).
|
||||
JS::Rooted<JS::Value> returnResult(aCx);
|
||||
if (!JS::Call(aCx, iterator, returnMethod,
|
||||
JS::HandleValueArray(aReason.Value()), &returnResult)) {
|
||||
// Step 6. If returnResult is an abrupt completion, return a promise
|
||||
// rejected with returnResult.[[Value]].
|
||||
aRv.StealExceptionFromJSContext(aCx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Step 7. Let returnPromise be a promise resolved with
|
||||
// returnResult.[[Value]].
|
||||
RefPtr<Promise> returnPromise = Promise::CreateInfallible(mGlobal);
|
||||
returnPromise->MaybeResolve(returnResult);
|
||||
|
||||
// Step 8. Return the result of reacting to returnPromise with the following
|
||||
// fulfillment steps, given iterResult:
|
||||
auto result = returnPromise->ThenWithCycleCollectedArgs(
|
||||
[](JSContext* aCx, JS::Handle<JS::Value> aIterResult, ErrorResult& aRv)
|
||||
MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION -> already_AddRefed<Promise> {
|
||||
// Step 8.1. If Type(iterResult) is not Object, throw a TypeError.
|
||||
if (!aIterResult.isObject()) {
|
||||
aRv.ThrowTypeError("return() returned a non-object value");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Step 8.2. Return undefined.
|
||||
return nullptr;
|
||||
});
|
||||
if (result.isErr()) {
|
||||
aRv.Throw(result.unwrapErr());
|
||||
return nullptr;
|
||||
}
|
||||
return result.unwrap().forget();
|
||||
};
|
||||
|
||||
protected:
|
||||
~ReadableStreamFromAlgorithms() override { mozilla::DropJSObjects(this); };
|
||||
|
||||
private:
|
||||
// Virtually const, but are cycle collected
|
||||
nsCOMPtr<nsIGlobalObject> mGlobal;
|
||||
JS::Heap<JSObject*> mIteratorRecord;
|
||||
};
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED_WITH_JS_MEMBERS(
|
||||
ReadableStreamFromAlgorithms, UnderlyingSourceAlgorithmsWrapper, (mGlobal),
|
||||
(mIteratorRecord))
|
||||
NS_IMPL_ADDREF_INHERITED(ReadableStreamFromAlgorithms,
|
||||
UnderlyingSourceAlgorithmsWrapper)
|
||||
NS_IMPL_RELEASE_INHERITED(ReadableStreamFromAlgorithms,
|
||||
UnderlyingSourceAlgorithmsWrapper)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableStreamFromAlgorithms)
|
||||
NS_INTERFACE_MAP_END_INHERITING(UnderlyingSourceAlgorithmsWrapper)
|
||||
|
||||
// https://streams.spec.whatwg.org/#readable-stream-from-iterable
|
||||
static already_AddRefed<ReadableStream> MOZ_CAN_RUN_SCRIPT
|
||||
ReadableStreamFromIterable(JSContext* aCx, nsIGlobalObject* aGlobal,
|
||||
JS::Handle<JS::Value> aAsyncIterable,
|
||||
ErrorResult& aRv) {
|
||||
aRv.MightThrowJSException();
|
||||
|
||||
// Step 1. Let stream be undefined. (not required)
|
||||
// Step 2. Let iteratorRecord be ? GetIterator(asyncIterable, async).
|
||||
JS::Rooted<JSObject*> iteratorRecord(
|
||||
aCx, JS::GetIteratorObject(aCx, aAsyncIterable, true));
|
||||
if (!iteratorRecord) {
|
||||
aRv.StealExceptionFromJSContext(aCx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Steps 3-5. are in ReadableStreamFromAlgorithms.
|
||||
auto algorithms =
|
||||
MakeRefPtr<ReadableStreamFromAlgorithms>(aGlobal, iteratorRecord);
|
||||
|
||||
// Step 6. Set stream to ! CreateReadableStream(startAlgorithm, pullAlgorithm,
|
||||
// cancelAlgorithm, 0).
|
||||
// Step 7. Return stream.
|
||||
return ReadableStream::CreateAbstract(aCx, aGlobal, algorithms,
|
||||
mozilla::Some(0.0), nullptr, aRv);
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<ReadableStream> ReadableStream::From(
|
||||
const GlobalObject& aGlobal, JS::Handle<JS::Value> aAsyncIterable,
|
||||
ErrorResult& aRv) {
|
||||
// Step 1. Return ? ReadableStreamFromIterable(asyncIterable).
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
return ReadableStreamFromIterable(aGlobal.Context(), global, aAsyncIterable,
|
||||
aRv);
|
||||
}
|
||||
|
||||
// Dealing with const this ptr is a pain, so just re-implement.
|
||||
// https://streams.spec.whatwg.org/#is-readable-stream-locked
|
||||
bool ReadableStream::Locked() const {
|
||||
|
|
|
@ -199,6 +199,10 @@ class ReadableStream : public nsISupports, public nsWrapperCache {
|
|||
const Optional<JS::Handle<JSObject*>>& aUnderlyingSource,
|
||||
const QueuingStrategy& aStrategy, ErrorResult& aRv);
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT static already_AddRefed<ReadableStream> From(
|
||||
const GlobalObject& aGlobal, JS::Handle<JS::Value> asyncIterable,
|
||||
ErrorResult& aRv);
|
||||
|
||||
bool Locked() const;
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> Cancel(
|
||||
|
|
|
@ -151,7 +151,9 @@ already_AddRefed<Promise> UnderlyingSourceAlgorithmsWrapper::CancelCallback(
|
|||
nsCOMPtr<nsIGlobalObject> global = xpc::CurrentNativeGlobal(aCx);
|
||||
return PromisifyAlgorithm(
|
||||
global,
|
||||
[&](ErrorResult& aRv) { return CancelCallbackImpl(aCx, aReason, aRv); },
|
||||
[&](ErrorResult& aRv) MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
|
||||
return CancelCallbackImpl(aCx, aReason, aRv);
|
||||
},
|
||||
aRv);
|
||||
}
|
||||
|
||||
|
|
|
@ -153,7 +153,7 @@ class UnderlyingSourceAlgorithmsWrapper
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
virtual already_AddRefed<Promise> CancelCallbackImpl(
|
||||
MOZ_CAN_RUN_SCRIPT virtual already_AddRefed<Promise> CancelCallbackImpl(
|
||||
JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason,
|
||||
ErrorResult& aRv) {
|
||||
// cancelAlgorithm is optional, return null by default
|
||||
|
|
|
@ -12,6 +12,9 @@ interface ReadableStream {
|
|||
[Throws]
|
||||
constructor(optional object underlyingSource, optional QueuingStrategy strategy = {});
|
||||
|
||||
[Pref="dom.streams.from.enabled", Throws]
|
||||
static ReadableStream from(any asyncIterable);
|
||||
|
||||
readonly attribute boolean locked;
|
||||
|
||||
[NewObject]
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef js_Iterator_h
|
||||
#define js_Iterator_h
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
|
||||
namespace JS {
|
||||
|
||||
// https://tc39.es/ecma262/#sec-getiterator
|
||||
// GetIterator
|
||||
JSObject* GetIteratorObject(JSContext* cx, Handle<Value> obj, bool isAsync);
|
||||
|
||||
// https://tc39.es/ecma262/#sec-iteratornext
|
||||
bool IteratorNext(JSContext* cx, Handle<JSObject*> iteratorRecord,
|
||||
MutableHandle<Value> result);
|
||||
|
||||
// https://tc39.es/ecma262/#sec-iteratorcomplete
|
||||
bool IteratorComplete(JSContext* cx, Handle<JSObject*> iterResult, bool* done);
|
||||
|
||||
// https://tc39.es/ecma262/#sec-iteratorvalue
|
||||
bool IteratorValue(JSContext* cx, Handle<JSObject*> iterResult,
|
||||
MutableHandle<Value> value);
|
||||
|
||||
// Implements iteratorRecord.[[Iterator]]
|
||||
bool GetIteratorRecordIterator(JSContext* cx, Handle<JSObject*> iteratorRecord,
|
||||
MutableHandle<Value> iterator);
|
||||
|
||||
// Implements GetMethod(iterator, "return").
|
||||
bool GetReturnMethod(JSContext* cx, Handle<Value> iterator,
|
||||
MutableHandle<Value> result);
|
||||
|
||||
} // namespace JS
|
||||
|
||||
#endif /* js_Iterator_h */
|
|
@ -68,6 +68,57 @@ function GetIteratorSync(obj) {
|
|||
|
||||
// Step 6. Let iteratorRecord be the Record { [[Iterator]]: iterator, [[NextMethod]]: nextMethod, [[Done]]: false }.
|
||||
var iteratorRecord = {
|
||||
__proto__: null,
|
||||
iterator,
|
||||
nextMethod,
|
||||
done: false
|
||||
};
|
||||
|
||||
// Step 7. Return iteratorRecord.
|
||||
return iteratorRecord;
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-getiterator
|
||||
function GetIterator(obj, isAsync, method) {
|
||||
// Step 1. If hint is not present, set hint to sync.
|
||||
// Step 2. If method is not present, then
|
||||
if (!method) {
|
||||
// Step 2.a. If hint is async, then
|
||||
if (isAsync) {
|
||||
// Step 2.a.i. Set method to ? GetMethod(obj, @@asyncIterator).
|
||||
method = GetMethod(obj, GetBuiltinSymbol("asyncIterator"));
|
||||
|
||||
// Step 2.a.ii. If method is undefined, then
|
||||
if (!method) {
|
||||
// Step 2.a.ii.1. Let syncMethod be ? GetMethod(obj, @@iterator).
|
||||
var syncMethod = GetMethod(obj, GetBuiltinSymbol("iterator"));
|
||||
|
||||
// Step 2.a.ii.2. Let syncIteratorRecord be ? GetIterator(obj, sync, syncMethod).
|
||||
var syncIteratorRecord = GetIterator(obj, false, syncMethod);
|
||||
|
||||
// Step 2.a.ii.2. Return CreateAsyncFromSyncIterator(syncIteratorRecord).
|
||||
return CreateAsyncFromSyncIterator(syncIteratorRecord.iterator, syncIteratorRecord.nextMethod);
|
||||
}
|
||||
} else {
|
||||
// Step 2.b. Otherwise, set method to ? GetMethod(obj, @@iterator).
|
||||
method = GetMethod(obj, GetBuiltinSymbol("iterator"));
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3. Let iterator be ? Call(method, obj).
|
||||
var iterator = callContentFunction(method, obj);
|
||||
|
||||
// Step 4. If Type(iterator) is not Object, throw a TypeError exception.
|
||||
if (!IsObject(iterator)) {
|
||||
ThrowTypeError(JSMSG_NOT_ITERABLE, obj === null ? "null" : typeof obj);
|
||||
}
|
||||
|
||||
// Step 5. Let nextMethod be ? GetV(iterator, "next").
|
||||
var nextMethod = iterator.next;
|
||||
|
||||
// Step 6. Let iteratorRecord be the Record { [[Iterator]]: iterator, [[NextMethod]]: nextMethod, [[Done]]: false }.
|
||||
var iteratorRecord = {
|
||||
__proto__: null,
|
||||
iterator,
|
||||
nextMethod,
|
||||
done: false,
|
||||
|
|
|
@ -159,6 +159,7 @@ EXPORTS.js += [
|
|||
"../public/Id.h",
|
||||
"../public/Initialization.h",
|
||||
"../public/Interrupt.h",
|
||||
"../public/Iterator.h",
|
||||
"../public/JitCodeAPI.h",
|
||||
"../public/JSON.h",
|
||||
"../public/LocaleSensitive.h",
|
||||
|
@ -377,6 +378,7 @@ UNIFIED_SOURCES += [
|
|||
"vm/Initialization.cpp",
|
||||
"vm/InternalThreadPool.cpp",
|
||||
"vm/Iteration.cpp",
|
||||
"vm/Iterator.cpp",
|
||||
"vm/JitActivation.cpp",
|
||||
"vm/JSAtom.cpp",
|
||||
"vm/JSContext.cpp",
|
||||
|
|
|
@ -237,6 +237,7 @@
|
|||
MACRO_(GetBuiltinSymbol, GetBuiltinSymbol, "GetBuiltinSymbol") \
|
||||
MACRO_(GetInternalError, GetInternalError, "GetInternalError") \
|
||||
MACRO_(getInternals, getInternals, "getInternals") \
|
||||
MACRO_(GetIterator, GetIterator, "GetIterator") \
|
||||
MACRO_(GetModuleNamespace, GetModuleNamespace, "GetModuleNamespace") \
|
||||
MACRO_(getOffsetNanosecondsFor, getOffsetNanosecondsFor, \
|
||||
"getOffsetNanosecondsFor") \
|
||||
|
@ -331,6 +332,7 @@
|
|||
MACRO_(isSubsetOf, isSubsetOf, "isSubsetOf") \
|
||||
MACRO_(isSupersetOf, isSupersetOf, "isSupersetOf") \
|
||||
MACRO_(IterableToList, IterableToList, "IterableToList") \
|
||||
MACRO_(IteratorNext, IteratorNext, "IteratorNext") \
|
||||
MACRO_(iterate, iterate, "iterate") \
|
||||
MACRO_(join, join, "join") \
|
||||
MACRO2(js, js, "js") \
|
||||
|
@ -402,6 +404,7 @@
|
|||
MACRO_(NegativeInfinity, NegativeInfinity, "-Infinity") \
|
||||
MACRO_(new, new_, "new") \
|
||||
MACRO_(next, next, "next") \
|
||||
MACRO_(nextMethod, nextMethod, "nextMethod") \
|
||||
MACRO_(NFC, NFC, "NFC") \
|
||||
MACRO_(NFD, NFD, "NFD") \
|
||||
MACRO_(NFKC, NFKC, "NFKC") \
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "js/Iterator.h"
|
||||
|
||||
#include "js/Conversions.h"
|
||||
#include "vm/Interpreter.h"
|
||||
#include "vm/JSAtomState.h"
|
||||
#include "vm/JSContext.h"
|
||||
#include "vm/ObjectOperations.h"
|
||||
#include "vm/SelfHosting.h"
|
||||
#include "vm/StringType.h"
|
||||
#include "vm/JSObject-inl.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
namespace JS {
|
||||
|
||||
// https://tc39.es/ecma262/#sec-getiterator
|
||||
// GetIterator(obj [, hint [, method]])
|
||||
JSObject* GetIteratorObject(JSContext* cx, HandleValue obj, bool isAsync) {
|
||||
FixedInvokeArgs<3> args(cx);
|
||||
args[0].set(obj);
|
||||
args[1].setBoolean(isAsync);
|
||||
args[2].setUndefined(); // method
|
||||
|
||||
RootedValue rval(cx);
|
||||
if (!CallSelfHostedFunction(cx, cx->names().GetIterator, UndefinedHandleValue,
|
||||
args, &rval)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(rval.isObject());
|
||||
return &rval.toObject();
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-iteratornext
|
||||
bool IteratorNext(JSContext* cx, HandleObject iteratorRecord,
|
||||
MutableHandleValue result) {
|
||||
FixedInvokeArgs<1> args(cx);
|
||||
args[0].setObject(*iteratorRecord);
|
||||
return CallSelfHostedFunction(cx, cx->names().IteratorNext,
|
||||
UndefinedHandleValue, args, result);
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-iteratorcomplete
|
||||
bool IteratorComplete(JSContext* cx, HandleObject iterResult, bool* done) {
|
||||
RootedValue doneV(cx);
|
||||
if (!GetProperty(cx, iterResult, iterResult, cx->names().done, &doneV)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*done = ToBoolean(doneV);
|
||||
return true;
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-iteratorvalue
|
||||
bool IteratorValue(JSContext* cx, HandleObject iterResult,
|
||||
MutableHandleValue value) {
|
||||
return GetProperty(cx, iterResult, iterResult, cx->names().value, value);
|
||||
}
|
||||
|
||||
bool GetIteratorRecordIterator(JSContext* cx, HandleObject iteratorRecord,
|
||||
MutableHandleValue iterator) {
|
||||
return GetProperty(cx, iteratorRecord, iteratorRecord, cx->names().iterator,
|
||||
iterator);
|
||||
}
|
||||
|
||||
static bool GetMethod(JSContext* cx, HandleValue v, Handle<PropertyName*> name,
|
||||
MutableHandleValue result) {
|
||||
// Step 1. Let func be ? GetV(V, P).
|
||||
RootedValue func(cx);
|
||||
if (!GetProperty(cx, v, name, &func)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 2. If func is either undefined or null, return undefined.
|
||||
if (func.isNullOrUndefined()) {
|
||||
result.setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Step 3. If IsCallable(func) is false, throw a TypeError exception.
|
||||
if (!IsCallable(func)) {
|
||||
return ReportIsNotFunction(cx, func, -1);
|
||||
}
|
||||
|
||||
// Step 4. Return func.
|
||||
result.set(func);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetReturnMethod(JSContext* cx, HandleValue iterator,
|
||||
MutableHandleValue result) {
|
||||
// Step 2. Let returnMethod be GetMethod(iterator, "return").
|
||||
return GetMethod(cx, iterator, cx->names().return_, result);
|
||||
}
|
||||
|
||||
} // namespace JS
|
|
@ -1788,6 +1788,64 @@ static bool intrinsic_NewAsyncIteratorHelper(JSContext* cx, unsigned argc,
|
|||
return true;
|
||||
}
|
||||
|
||||
static JSObject* NewIteratorRecord(JSContext* cx, HandleObject iterator,
|
||||
HandleValue nextMethod) {
|
||||
gc::AllocKind allocKind = gc::GetGCObjectKind(3);
|
||||
Rooted<PlainObject*> obj(
|
||||
cx, NewPlainObjectWithProtoAndAllocKind(cx, nullptr, allocKind));
|
||||
if (!obj) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RootedId propid(cx, NameToId(cx->names().iterator));
|
||||
RootedValue value(cx, ObjectValue(*iterator));
|
||||
if (!NativeDefineDataProperty(cx, obj, propid, value, JSPROP_ENUMERATE)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
propid = NameToId(cx->names().nextMethod);
|
||||
value.set(nextMethod);
|
||||
if (!NativeDefineDataProperty(cx, obj, propid, value, JSPROP_ENUMERATE)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
propid = NameToId(cx->names().done);
|
||||
value.setBoolean(false);
|
||||
if (!NativeDefineDataProperty(cx, obj, propid, value, JSPROP_ENUMERATE)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static bool intrinsic_CreateAsyncFromSyncIterator(JSContext* cx, unsigned argc,
|
||||
Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 2);
|
||||
|
||||
RootedObject iterator(cx, &args[0].toObject());
|
||||
RootedObject asyncIterator(
|
||||
cx, CreateAsyncFromSyncIterator(cx, iterator, args[1]));
|
||||
if (!asyncIterator) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedValue nextMethod(cx);
|
||||
if (!GetProperty(cx, asyncIterator, asyncIterator, cx->names().next,
|
||||
&nextMethod)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject iteratorRecord(cx,
|
||||
NewIteratorRecord(cx, asyncIterator, nextMethod));
|
||||
if (!iteratorRecord) {
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setObject(*iteratorRecord);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool intrinsic_NoPrivateGetter(JSContext* cx, unsigned argc, Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 0);
|
||||
|
@ -1858,6 +1916,8 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
|||
JS_FN("ConstructorForTypedArray", intrinsic_ConstructorForTypedArray, 1, 0),
|
||||
JS_FN("CopyDataPropertiesOrGetOwnKeys",
|
||||
intrinsic_CopyDataPropertiesOrGetOwnKeys, 3, 0),
|
||||
JS_FN("CreateAsyncFromSyncIterator", intrinsic_CreateAsyncFromSyncIterator,
|
||||
2, 0),
|
||||
JS_FN("CreateMapIterationResultPair",
|
||||
intrinsic_CreateMapIterationResultPair, 0, 0),
|
||||
JS_FN("CreateSetIterationResult", intrinsic_CreateSetIterationResult, 0, 0),
|
||||
|
|
|
@ -3983,6 +3983,12 @@
|
|||
value: true
|
||||
mirror: always
|
||||
|
||||
# ReadableStream.from(asyncIterable)
|
||||
- name: dom.streams.from.enabled
|
||||
type: RelaxedAtomicBool
|
||||
value: true
|
||||
mirror: always
|
||||
|
||||
- name: dom.workers.pFetch.enabled
|
||||
type: RelaxedAtomicBool
|
||||
value: true
|
||||
|
|
|
@ -1,446 +0,0 @@
|
|||
[from.any.serviceworker.html]
|
||||
[ReadableStream.from accepts an array of values]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an array of promises]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an array iterator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a string]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a Set]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a Set iterator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a sync generator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an async generator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a sync iterable of values]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a sync iterable of promises]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an async iterable]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a ReadableStream]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a ReadableStream async iterator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from re-throws errors from calling the @@iterator method]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from re-throws errors from calling the @@asyncIterator method]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from ignores @@iterator if @@asyncIterator exists]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an empty iterable]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: stream errors when next() rejects]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: stream stalls when next() never settles]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: calls next() after first read()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: cancelling the returned stream calls and awaits return()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: return() is not called when iterator completes normally]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: cancel() rejects when return() fulfills with a non-object]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: reader.read() inside next()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: reader.cancel() inside next()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: reader.cancel() inside return()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from(array), push() to array while reading]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[from.any.worker.html]
|
||||
[ReadableStream.from accepts an array of values]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an array of promises]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an array iterator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a string]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a Set]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a Set iterator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a sync generator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an async generator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a sync iterable of values]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a sync iterable of promises]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an async iterable]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a ReadableStream]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a ReadableStream async iterator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from re-throws errors from calling the @@iterator method]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from re-throws errors from calling the @@asyncIterator method]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from ignores @@iterator if @@asyncIterator exists]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an empty iterable]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: stream errors when next() rejects]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: stream stalls when next() never settles]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: calls next() after first read()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: cancelling the returned stream calls and awaits return()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: return() is not called when iterator completes normally]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: cancel() rejects when return() fulfills with a non-object]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: reader.read() inside next()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: reader.cancel() inside next()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: reader.cancel() inside return()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from(array), push() to array while reading]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[from.any.html]
|
||||
[ReadableStream.from accepts an array of values]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an array of promises]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an array iterator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a string]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a Set]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a Set iterator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a sync generator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an async generator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a sync iterable of values]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a sync iterable of promises]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an async iterable]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a ReadableStream]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a ReadableStream async iterator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from re-throws errors from calling the @@iterator method]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from re-throws errors from calling the @@asyncIterator method]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from ignores @@iterator if @@asyncIterator exists]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an empty iterable]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: stream errors when next() rejects]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: stream stalls when next() never settles]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: calls next() after first read()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: cancelling the returned stream calls and awaits return()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: return() is not called when iterator completes normally]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: cancel() rejects when return() fulfills with a non-object]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: reader.read() inside next()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: reader.cancel() inside next()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: reader.cancel() inside return()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from(array), push() to array while reading]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[from.any.sharedworker.html]
|
||||
[ReadableStream.from accepts an array of values]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an array of promises]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an array iterator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a string]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a Set]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a Set iterator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a sync generator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an async generator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a sync iterable of values]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a sync iterable of promises]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an async iterable]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a ReadableStream]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a ReadableStream async iterator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from re-throws errors from calling the @@iterator method]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from re-throws errors from calling the @@asyncIterator method]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from ignores @@iterator if @@asyncIterator exists]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an empty iterable]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: stream errors when next() rejects]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: stream stalls when next() never settles]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: calls next() after first read()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: cancelling the returned stream calls and awaits return()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: return() is not called when iterator completes normally]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: cancel() rejects when return() fulfills with a non-object]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: reader.read() inside next()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: reader.cancel() inside next()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: reader.cancel() inside return()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from(array), push() to array while reading]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[from.any.js]
|
||||
[ReadableStream.from accepts an array of values]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an array of promises]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an array iterator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a string]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a Set]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a Set iterator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a sync generator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an async generator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a sync iterable of values]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a sync iterable of promises]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an async iterable]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a ReadableStream]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a ReadableStream async iterator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from re-throws errors from calling the @@iterator method]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from re-throws errors from calling the @@asyncIterator method]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from ignores @@iterator if @@asyncIterator exists]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an empty iterable]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: stream errors when next() rejects]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: stream stalls when next() never settles]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: calls next() after first read()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: cancelling the returned stream calls and awaits return()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: return() is not called when iterator completes normally]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: cancel() rejects when return() fulfills with a non-object]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: reader.read() inside next()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: reader.cancel() inside next()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: reader.cancel() inside return()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from(array), push() to array while reading]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from throws on invalid iterables; specifically null]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from throws on invalid iterables; specifically undefined]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from throws on invalid iterables; specifically 0]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from throws on invalid iterables; specifically NaN]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from throws on invalid iterables; specifically true]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from throws on invalid iterables; specifically {}]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from throws on invalid iterables; specifically Object.create(null)]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from throws on invalid iterables; specifically a function]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from throws on invalid iterables; specifically a symbol]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from throws on invalid iterables; specifically an object with a non-callable @@iterator method]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from throws on invalid iterables; specifically an object with a non-callable @@asyncIterator method]
|
||||
expected: FAIL
|
|
@ -1,4 +1,4 @@
|
|||
// META: global=window,worker,jsshell
|
||||
// META: global=window,worker
|
||||
// META: script=../resources/test-utils.js
|
||||
'use strict';
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче