Bug 1782400 - Implement "return" method for WebIDL async iterable iterator. r=edgar

Differential Revision: https://phabricator.services.mozilla.com/D156350
This commit is contained in:
Peter Van der Beken 2022-09-22 15:02:26 +00:00
Родитель 9ffbe8f65e
Коммит 2fecef1b02
18 изменённых файлов: 372 добавлений и 51 удалений

Просмотреть файл

@ -4501,9 +4501,17 @@ def CopyUnforgeablePropertiesToInstance(descriptor, failureCode):
def AssertInheritanceChain(descriptor):
asserts = ""
# We can skip the reinterpret_cast check for the descriptor's nativeType
# if aObject is a pointer of that type.
asserts = fill(
"""
static_assert(std::is_same_v<decltype(aObject), ${nativeType}*>);
""",
nativeType=descriptor.nativeType,
)
iface = descriptor.interface
while iface:
while iface.parent:
iface = iface.parent
desc = descriptor.getDescriptor(iface.identifier.name)
asserts += (
"MOZ_ASSERT(static_cast<%s*>(aObject) == \n"
@ -4511,7 +4519,6 @@ def AssertInheritanceChain(descriptor):
' "Multiple inheritance for %s is broken.");\n'
% (desc.nativeType, desc.nativeType, desc.nativeType)
)
iface = iface.parent
asserts += "MOZ_ASSERT(ToSupportsIsCorrect(aObject));\n"
return asserts
@ -9574,7 +9581,6 @@ class CGPerSignatureCall(CGThing):
cgThings.append(
CGIterableMethodGenerator(
descriptor,
idlNode.maplikeOrSetlikeOrIterable,
idlNode.identifier.name,
self.getArgumentNames(),
)
@ -22274,7 +22280,7 @@ class CGIterableMethodGenerator(CGGeneric):
using CGCallGenerator.
"""
def __init__(self, descriptor, iterable, methodName, args):
def __init__(self, descriptor, methodName, args):
if methodName == "forEach":
assert len(args) == 2
@ -22319,8 +22325,14 @@ class CGIterableMethodGenerator(CGGeneric):
wrap = f"{descriptor.interface.identifier.name}Iterator_Binding::Wrap"
iterClass = f"mozilla::dom::binding_detail::WrappableIterableIterator<{descriptor.nativeType}, &{wrap}>"
else:
needReturnMethod = toStringBool(
descriptor.interface.maplikeOrSetlikeOrIterable.getExtendedAttribute(
"GenerateReturnMethod"
)
is not None
)
wrap = f"{descriptor.interface.identifier.name}AsyncIterator_Binding::Wrap"
iterClass = f"mozilla::dom::binding_detail::WrappableAsyncIterableIterator<{descriptor.nativeType}, &{wrap}>"
iterClass = f"mozilla::dom::binding_detail::WrappableAsyncIterableIterator<{descriptor.nativeType}, {needReturnMethod}, &{wrap}>"
createIterator = fill(
"""

Просмотреть файл

@ -13,6 +13,13 @@ from collections import defaultdict
autogenerated_comment = "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n"
def toStringBool(arg):
"""
Converts IDL/Python Boolean (True/False) to C++ Boolean (true/false)
"""
return str(not not arg).lower()
class DescriptorProvider:
"""
A way of getting descriptors for interface names. Subclasses must
@ -1052,9 +1059,15 @@ def iteratorNativeType(descriptor):
assert iterableDecl.isPairIterator() or descriptor.interface.isAsyncIterable()
if descriptor.interface.isIterable():
return "mozilla::dom::IterableIterator<%s>" % descriptor.nativeType
return (
"mozilla::dom::binding_detail::AsyncIterableIteratorNoReturn<%s>"
% descriptor.nativeType
needReturnMethod = toStringBool(
descriptor.interface.maplikeOrSetlikeOrIterable.getExtendedAttribute(
"GenerateReturnMethod"
)
is not None
)
return "mozilla::dom::binding_detail::AsyncIterableIteratorNative<%s, %s>" % (
descriptor.nativeType,
needReturnMethod,
)

Просмотреть файл

@ -109,7 +109,7 @@ already_AddRefed<Promise> AsyncIterableNextImpl::NextSteps(
// 4. Let nextPromise be the result of getting the next iteration result with
// objects target and object.
RefPtr<Promise> nextPromise = GetNextPromise(aRv);
RefPtr<Promise> nextPromise = GetNextResult(aRv);
// 5. Let fulfillSteps be the following steps, given next:
auto fulfillSteps = [](JSContext* aCx, JS::Handle<JS::Value> aNext,
@ -222,6 +222,102 @@ already_AddRefed<Promise> AsyncIterableNextImpl::Next(
return do_AddRef(aObject->mOngoingPromise);
}
already_AddRefed<Promise> AsyncIterableReturnImpl::ReturnSteps(
JSContext* aCx, AsyncIterableIteratorBase* aObject,
nsIGlobalObject* aGlobalObject, JS::Handle<JS::Value> aValue,
ErrorResult& aRv) {
// 2. If objects is finished is true, then:
if (aObject->mIsFinished) {
// 1. Let result be CreateIterResultObject(value, true).
JS::Rooted<JS::Value> dict(aCx);
iterator_utils::DictReturn(aCx, &dict, true, aValue, aRv);
if (aRv.Failed()) {
return Promise::CreateRejectedWithErrorResult(aGlobalObject, aRv);
}
// 2. Perform ! Call(returnPromiseCapability.[[Resolve]], undefined,
// «result»).
// 3. Return returnPromiseCapability.[[Promise]].
return Promise::Resolve(aGlobalObject, aCx, dict, aRv);
}
// 3. Set objects is finished to true.
aObject->mIsFinished = true;
// 4. Return the result of running the asynchronous iterator return algorithm
// for interface, given objects target, object, and value.
return GetReturnPromise(aCx, aValue, aRv);
}
already_AddRefed<Promise> AsyncIterableReturnImpl::Return(
JSContext* aCx, AsyncIterableIteratorBase* aObject,
nsISupports* aGlobalObject, JS::Handle<JS::Value> aValue,
ErrorResult& aRv) {
nsCOMPtr<nsIGlobalObject> globalObject = do_QueryInterface(aGlobalObject);
// 3.7.10.2. Asynchronous iterator prototype object
// …
RefPtr<Promise> returnStepsPromise;
// 11. If ongoingPromise is not null, then:
if (aObject->mOngoingPromise) {
// 1. Let afterOngoingPromiseCapability be
// ! NewPromiseCapability(%Promise%).
// 2. Let onSettled be CreateBuiltinFunction(returnSteps, « »).
// aObject is the same object as 'this', so it's fine to capture 'this'
// without taking a strong reference, because we already take a strong
// reference to it through aObject.
auto onSettled = [this](JSContext* aCx, JS::Handle<JS::Value> aValue,
ErrorResult& aRv,
const RefPtr<AsyncIterableIteratorBase>& aObject,
const nsCOMPtr<nsIGlobalObject>& aGlobalObject,
JS::Handle<JS::Value> aVal) {
return ReturnSteps(aCx, aObject, aGlobalObject, aVal, aRv);
};
// 3. Perform PerformPromiseThen(ongoingPromise, onSettled, onSettled,
// afterOngoingPromiseCapability).
Result<RefPtr<Promise>, nsresult> afterOngoingPromise =
aObject->mOngoingPromise->ThenCatchWithCycleCollectedArgsJS(
onSettled, onSettled,
std::make_tuple(RefPtr{aObject}, nsCOMPtr{globalObject}),
std::make_tuple(aValue));
if (afterOngoingPromise.isErr()) {
aRv.Throw(afterOngoingPromise.unwrapErr());
return nullptr;
}
// 4. Set returnStepsPromise to afterOngoingPromiseCapability.[[Promise]].
returnStepsPromise = afterOngoingPromise.unwrap().forget();
} else {
// 12. Otherwise:
// 1. Set returnStepsPromise to the result of running returnSteps.
returnStepsPromise = ReturnSteps(aCx, aObject, globalObject, aValue, aRv);
}
// 13. Let fulfillSteps be the following steps:
auto onFullFilled = [](JSContext* aCx, JS::Handle<JS::Value>,
ErrorResult& aRv,
const nsCOMPtr<nsIGlobalObject>& aGlobalObject,
JS::Handle<JS::Value> aVal) {
// 1. Return CreateIterResultObject(value, true).
JS::Rooted<JS::Value> dict(aCx);
iterator_utils::DictReturn(aCx, &dict, true, aVal, aRv);
return Promise::Resolve(aGlobalObject, aCx, dict, aRv);
};
// 14. Let onFulfilled be CreateBuiltinFunction(fulfillSteps, « »).
// 15. Perform PerformPromiseThen(returnStepsPromise, onFulfilled, undefined,
// returnPromiseCapability).
Result<RefPtr<Promise>, nsresult> returnPromise =
returnStepsPromise->ThenWithCycleCollectedArgsJS(
onFullFilled, std::make_tuple(std::move(globalObject)),
std::make_tuple(aValue));
// 16. Return returnPromiseCapability.[[Promise]].
return PromiseOrErr(std::move(returnPromise), aRv);
}
} // namespace binding_detail
} // namespace mozilla::dom

Просмотреть файл

@ -212,6 +212,7 @@ class IterableIterator : public IterableIteratorBase {
namespace binding_detail {
class AsyncIterableNextImpl;
class AsyncIterableReturnImpl;
} // namespace binding_detail
@ -225,6 +226,7 @@ class AsyncIterableIteratorBase : public IterableIteratorBase {
private:
friend class binding_detail::AsyncIterableNextImpl;
friend class binding_detail::AsyncIterableReturnImpl;
// 3.7.10.1. Default asynchronous iterator objects
// Target is in AsyncIterableIterator
@ -320,7 +322,7 @@ class AsyncIterableNextImpl {
already_AddRefed<Promise> Next(JSContext* aCx,
AsyncIterableIteratorBase* aObject,
nsISupports* aGlobalObject, ErrorResult& aRv);
virtual already_AddRefed<Promise> GetNextPromise(ErrorResult& aRv) = 0;
virtual already_AddRefed<Promise> GetNextResult(ErrorResult& aRv) = 0;
private:
already_AddRefed<Promise> NextSteps(JSContext* aCx,
@ -329,6 +331,24 @@ class AsyncIterableNextImpl {
ErrorResult& aRv);
};
class AsyncIterableReturnImpl {
protected:
already_AddRefed<Promise> Return(JSContext* aCx,
AsyncIterableIteratorBase* aObject,
nsISupports* aGlobalObject,
JS::Handle<JS::Value> aValue,
ErrorResult& aRv);
virtual already_AddRefed<Promise> GetReturnPromise(
JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv) = 0;
private:
already_AddRefed<Promise> ReturnSteps(JSContext* aCx,
AsyncIterableIteratorBase* aObject,
nsIGlobalObject* aGlobalObject,
JS::Handle<JS::Value> aValue,
ErrorResult& aRv);
};
template <typename T>
class AsyncIterableIteratorNoReturn : public AsyncIterableIterator<T>,
public AsyncIterableNextImpl {
@ -341,23 +361,50 @@ class AsyncIterableIteratorNoReturn : public AsyncIterableIterator<T>,
}
protected:
already_AddRefed<Promise> GetNextPromise(ErrorResult& aRv) override {
return this->mIterableObj->GetNextPromise(
already_AddRefed<Promise> GetNextResult(ErrorResult& aRv) override {
return this->mIterableObj->GetNextIterationResult(
static_cast<AsyncIterableIterator<T>*>(this), aRv);
}
};
template <typename T>
using AsyncIterableIteratorWrapFunc =
bool (*)(JSContext* aCx, AsyncIterableIteratorNoReturn<T>* aObject,
JS::MutableHandle<JSObject*> aReflector);
template <typename T, AsyncIterableIteratorWrapFunc<T> WrapFunc>
class WrappableAsyncIterableIterator final
: public AsyncIterableIteratorNoReturn<T> {
class AsyncIterableIteratorWithReturn : public AsyncIterableIteratorNoReturn<T>,
public AsyncIterableReturnImpl {
public:
already_AddRefed<Promise> Return(JSContext* aCx, JS::Handle<JS::Value> aValue,
ErrorResult& aRv) {
return AsyncIterableReturnImpl::Return(
aCx, this, this->mIterableObj->GetParentObject(), aValue, aRv);
}
protected:
using AsyncIterableIteratorNoReturn<T>::AsyncIterableIteratorNoReturn;
already_AddRefed<Promise> GetReturnPromise(JSContext* aCx,
JS::Handle<JS::Value> aValue,
ErrorResult& aRv) override {
return this->mIterableObj->IteratorReturn(
aCx, static_cast<AsyncIterableIterator<T>*>(this), aValue, aRv);
}
};
template <typename T, bool NeedReturnMethod>
using AsyncIterableIteratorNative =
std::conditional_t<NeedReturnMethod, AsyncIterableIteratorWithReturn<T>,
AsyncIterableIteratorNoReturn<T>>;
template <typename T, bool NeedReturnMethod>
using AsyncIterableIteratorWrapFunc = bool (*)(
JSContext* aCx, AsyncIterableIteratorNative<T, NeedReturnMethod>* aObject,
JS::MutableHandle<JSObject*> aReflector);
template <typename T, bool NeedReturnMethod,
AsyncIterableIteratorWrapFunc<T, NeedReturnMethod> WrapFunc,
typename Base = AsyncIterableIteratorNative<T, NeedReturnMethod>>
class WrappableAsyncIterableIterator final : public Base {
public:
using Base::Base;
bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> aObj) {
MOZ_ASSERT(!aGivenProto);

Просмотреть файл

@ -8908,12 +8908,39 @@ class Parser(Tokenizer):
else:
nextReturnType = BuiltinTypes[IDLBuiltinType.Types.object]
nextMethod = IDLMethod(
iface.location,
IDLUnresolvedIdentifier(iface.location, "next"),
iterable.location,
IDLUnresolvedIdentifier(iterable.location, "next"),
nextReturnType,
[],
)
nextMethod.addExtendedAttributes([simpleExtendedAttr("Throws")])
methods = [nextMethod]
if iterable.getExtendedAttribute("GenerateReturnMethod"):
assert isinstance(iterable, IDLAsyncIterable)
returnMethod = IDLMethod(
iterable.location,
IDLUnresolvedIdentifier(iterable.location, "return"),
IDLPromiseType(
iterable.location, BuiltinTypes[IDLBuiltinType.Types.any]
),
[
IDLArgument(
iterable.location,
IDLUnresolvedIdentifier(
BuiltinLocation("<auto-generated-identifier>"),
"value",
),
BuiltinTypes[IDLBuiltinType.Types.any],
optional=True,
),
],
)
returnMethod.addExtendedAttributes([simpleExtendedAttr("Throws")])
methods.append(returnMethod)
if iterable.isIterable():
itr_suffix = "Iterator"
else:
@ -8930,7 +8957,7 @@ class Parser(Tokenizer):
self.globalScope(),
itr_ident,
None,
[nextMethod],
methods,
isKnownNonPartial=True,
classNameOverride=classNameOverride,
)

Просмотреть файл

@ -54,15 +54,16 @@ nsPIDOMWindowInner* TestInterfaceAsyncIterableDouble::GetParentObject() const {
return mParent;
}
already_AddRefed<Promise> TestInterfaceAsyncIterableDouble::GetNextPromise(
Iterator* aIterator, ErrorResult& aRv) {
already_AddRefed<Promise>
TestInterfaceAsyncIterableDouble::GetNextIterationResult(Iterator* aIterator,
ErrorResult& aRv) {
RefPtr<Promise> promise = Promise::Create(mParent->AsGlobal(), aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
NS_DispatchToMainThread(NewRunnableMethod<RefPtr<Iterator>, RefPtr<Promise>>(
"TestInterfaceAsyncIterableDouble::GetNextPromise", this,
"TestInterfaceAsyncIterableDouble::GetNextIterationResult", this,
&TestInterfaceAsyncIterableDouble::ResolvePromise, aIterator, promise));
return promise.forget();

Просмотреть файл

@ -47,8 +47,8 @@ class TestInterfaceAsyncIterableDouble final : public nsISupports,
void InitAsyncIteratorData(IteratorData& aData, Iterator::IteratorType aType,
ErrorResult& aError) {}
already_AddRefed<Promise> GetNextPromise(Iterator* aIterator,
ErrorResult& aRv);
already_AddRefed<Promise> GetNextIterationResult(Iterator* aIterator,
ErrorResult& aRv);
private:
virtual ~TestInterfaceAsyncIterableDouble() = default;

Просмотреть файл

@ -60,7 +60,8 @@ nsPIDOMWindowInner* TestInterfaceAsyncIterableDoubleUnion::GetParentObject()
return mParent;
}
already_AddRefed<Promise> TestInterfaceAsyncIterableDoubleUnion::GetNextPromise(
already_AddRefed<Promise>
TestInterfaceAsyncIterableDoubleUnion::GetNextIterationResult(
Iterator* aIterator, ErrorResult& aRv) {
RefPtr<Promise> promise = Promise::Create(mParent->AsGlobal(), aRv);
if (NS_WARN_IF(aRv.Failed())) {
@ -68,7 +69,7 @@ already_AddRefed<Promise> TestInterfaceAsyncIterableDoubleUnion::GetNextPromise(
}
NS_DispatchToMainThread(NewRunnableMethod<RefPtr<Iterator>, RefPtr<Promise>>(
"TestInterfaceAsyncIterableDoubleUnion::GetNextPromise", this,
"TestInterfaceAsyncIterableDoubleUnion::GetNextIterationResult", this,
&TestInterfaceAsyncIterableDoubleUnion::ResolvePromise, aIterator,
promise));

Просмотреть файл

@ -47,8 +47,8 @@ class TestInterfaceAsyncIterableDoubleUnion final : public nsISupports,
void InitAsyncIteratorData(IteratorData& aData, Iterator::IteratorType aType,
ErrorResult& aError) {}
already_AddRefed<Promise> GetNextPromise(Iterator* aIterator,
ErrorResult& aRv);
already_AddRefed<Promise> GetNextIterationResult(Iterator* aIterator,
ErrorResult& aRv);
private:
virtual ~TestInterfaceAsyncIterableDoubleUnion() = default;

Просмотреть файл

@ -66,12 +66,14 @@ void TestInterfaceAsyncIterableSingle::InitAsyncIteratorData(
MOZ_ASSERT(aData.mMultiplier == 1);
}
already_AddRefed<Promise> TestInterfaceAsyncIterableSingle::GetNextPromise(
Iterator* aIterator, ErrorResult& aRv) {
return GetNextPromise(aIterator, aIterator->Data(), aRv);
already_AddRefed<Promise>
TestInterfaceAsyncIterableSingle::GetNextIterationResult(Iterator* aIterator,
ErrorResult& aRv) {
return GetNextIterationResult(aIterator, aIterator->Data(), aRv);
}
already_AddRefed<Promise> TestInterfaceAsyncIterableSingle::GetNextPromise(
already_AddRefed<Promise>
TestInterfaceAsyncIterableSingle::GetNextIterationResult(
IterableIteratorBase* aIterator, IteratorData& aData, ErrorResult& aRv) {
RefPtr<Promise> promise = Promise::Create(mParent->AsGlobal(), aRv);
if (NS_WARN_IF(aRv.Failed())) {
@ -81,7 +83,7 @@ already_AddRefed<Promise> TestInterfaceAsyncIterableSingle::GetNextPromise(
nsCOMPtr<nsIRunnable> callResolvePromise =
NewRunnableMethod<RefPtr<IterableIteratorBase>, IteratorData&,
RefPtr<Promise>>(
"TestInterfaceAsyncIterableSingle::GetNextPromise", this,
"TestInterfaceAsyncIterableSingle::GetNextIterationResult", this,
&TestInterfaceAsyncIterableSingle::ResolvePromise, aIterator, aData,
promise);
if (aData.mBlockingPromisesIndex < aData.mBlockingPromises.Length()) {

Просмотреть файл

@ -41,8 +41,8 @@ class TestInterfaceAsyncIterableSingle : public nsISupports,
const TestInterfaceAsyncIterableSingleOptions& aOptions, ErrorResult& rv);
using Iterator = AsyncIterableIterator<TestInterfaceAsyncIterableSingle>;
already_AddRefed<Promise> GetNextPromise(Iterator* aIterator,
ErrorResult& aRv);
already_AddRefed<Promise> GetNextIterationResult(Iterator* aIterator,
ErrorResult& aRv);
struct IteratorData {
void Traverse(nsCycleCollectionTraversalCallback& cb);
@ -58,9 +58,8 @@ class TestInterfaceAsyncIterableSingle : public nsISupports,
ErrorResult& aError);
protected:
already_AddRefed<Promise> GetNextPromise(IterableIteratorBase* aIterator,
IteratorData& aData,
ErrorResult& aRv);
already_AddRefed<Promise> GetNextIterationResult(
IterableIteratorBase* aIterator, IteratorData& aData, ErrorResult& aRv);
virtual ~TestInterfaceAsyncIterableSingle() = default;

Просмотреть файл

@ -5,6 +5,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/TestInterfaceAsyncIterableSingleWithArgs.h"
#include "js/Value.h"
#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeIterableBinding.h"
#include "nsPIDOMWindow.h"
#include "mozilla/dom/BindingUtils.h"
@ -13,6 +14,30 @@
namespace mozilla::dom {
NS_IMPL_CYCLE_COLLECTION_CLASS(TestInterfaceAsyncIterableSingleWithArgs)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(
TestInterfaceAsyncIterableSingleWithArgs, TestInterfaceAsyncIterableSingle)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(
TestInterfaceAsyncIterableSingleWithArgs, TestInterfaceAsyncIterableSingle)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(
TestInterfaceAsyncIterableSingleWithArgs, TestInterfaceAsyncIterableSingle)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mReturnLastCalledWith)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_ADDREF_INHERITED(TestInterfaceAsyncIterableSingleWithArgs,
TestInterfaceAsyncIterableSingle)
NS_IMPL_RELEASE_INHERITED(TestInterfaceAsyncIterableSingleWithArgs,
TestInterfaceAsyncIterableSingle)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
TestInterfaceAsyncIterableSingleWithArgs)
NS_INTERFACE_MAP_END_INHERITING(TestInterfaceAsyncIterableSingle)
// static
already_AddRefed<TestInterfaceAsyncIterableSingleWithArgs>
TestInterfaceAsyncIterableSingleWithArgs::Constructor(
@ -43,10 +68,26 @@ void TestInterfaceAsyncIterableSingleWithArgs::InitAsyncIteratorData(
}
already_AddRefed<Promise>
TestInterfaceAsyncIterableSingleWithArgs::GetNextPromise(Iterator* aIterator,
ErrorResult& aRv) {
return TestInterfaceAsyncIterableSingle::GetNextPromise(
TestInterfaceAsyncIterableSingleWithArgs::GetNextIterationResult(
Iterator* aIterator, ErrorResult& aRv) {
return TestInterfaceAsyncIterableSingle::GetNextIterationResult(
aIterator, aIterator->Data(), aRv);
}
already_AddRefed<Promise>
TestInterfaceAsyncIterableSingleWithArgs::IteratorReturn(
JSContext* aCx, Iterator* aIterator, JS::Handle<JS::Value> aValue,
ErrorResult& aRv) {
++mReturnCallCount;
RefPtr<Promise> promise = Promise::Create(GetParentObject()->AsGlobal(), aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
mReturnLastCalledWith = aValue;
promise->MaybeResolve(JS::UndefinedHandleValue);
return promise.forget();
}
} // namespace mozilla::dom

Просмотреть файл

@ -18,6 +18,11 @@ struct TestInterfaceAsyncIteratorOptions;
class TestInterfaceAsyncIterableSingleWithArgs final
: public TestInterfaceAsyncIterableSingle {
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(
TestInterfaceAsyncIterableSingleWithArgs,
TestInterfaceAsyncIterableSingle)
using TestInterfaceAsyncIterableSingle::TestInterfaceAsyncIterableSingle;
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
@ -31,8 +36,23 @@ class TestInterfaceAsyncIterableSingleWithArgs final
const TestInterfaceAsyncIteratorOptions& aOptions,
ErrorResult& aError);
already_AddRefed<Promise> GetNextPromise(Iterator* aIterator,
already_AddRefed<Promise> GetNextIterationResult(Iterator* aIterator,
ErrorResult& aRv);
already_AddRefed<Promise> IteratorReturn(JSContext* aCx, Iterator* aIterator,
JS::Handle<JS::Value> aValue,
ErrorResult& aRv);
uint32_t ReturnCallCount() { return mReturnCallCount; }
void GetReturnLastCalledWith(JSContext* aCx,
JS::MutableHandle<JS::Value> aReturnCalledWith) {
aReturnCalledWith.set(mReturnLastCalledWith);
}
private:
~TestInterfaceAsyncIterableSingleWithArgs() = default;
JS::Heap<JS::Value> mReturnLastCalledWith;
uint32_t mReturnCallCount = 0;
};
} // namespace mozilla::dom

Просмотреть файл

@ -14,11 +14,13 @@ add_task(async function init() {
await SpecialPowers.pushPrefEnv({set: [["dom.expose_test_interfaces", true]]});
});
const singleValues = Array(10).fill(0).map((_, i) => i * 9 % 7);
async function check_single_result_values(values, multiplier = 1) {
dump(JSON.stringify(values));
is(values.length, 10, `AsyncIterableSingle: should return 10 elements`);
for (let i = 0; i < 10; i++) {
let expected = i * 9 % 7 * multiplier;
let expected = singleValues[i] * multiplier;
is(values[i], expected,
`AsyncIterableSingle: should be ${expected}, get ${values[i]}`);
}
@ -107,6 +109,60 @@ async function test_data_single() {
}
check_single_result_values(await Promise.all(values).then(v => v.map(w => w.value)));
// eslint-disable-next-line no-undef
itr = new TestInterfaceAsyncIterableSingleWithArgs();
let callCount = itr.returnCallCount;
let i = 0;
for await (let v of itr) {
if (++i > 1) {
break;
}
values.push(v);
}
is(itr.returnCallCount, callCount + 1,
`AsyncIterableSingle: breaking out of for-await-of loop should call "return"`);
is(itr.returnLastCalledWith, undefined,
`AsyncIterableSingleWithArgs: the asynchronous iterator return algorithm should be called with the argument that was passed in.`);
// eslint-disable-next-line no-undef
itr = new TestInterfaceAsyncIterableSingleWithArgs();
async function * yieldFromIterator () {
yield * itr
}
let yieldingIterator = yieldFromIterator();
let result = await yieldingIterator.next();
is(result.value, singleValues[0],
`AsyncIterableSingle: should be ${singleValues[0]}, get ${result.value}`);
result = await yieldingIterator.next();
is(result.value, singleValues[1],
`AsyncIterableSingle: should be ${singleValues[1]}, get ${result.value}`);
result = await yieldingIterator.return("abcd");
is(typeof result, "object",
`AsyncIterableSingleWithArgs: "return("abcd")" should return { done: true, value: "abcd" }`);
is(result.done, true,
`AsyncIterableSingleWithArgs: "return("abcd")" should return { done: true, value: "abcd" }`);
is(result.value, "abcd",
`AsyncIterableSingleWithArgs: "return("abcd")" should return { done: true, value: "abcd" }`);
is(itr.returnLastCalledWith, "abcd",
`AsyncIterableSingleWithArgs: the asynchronous iterator return algorithm should be called with the argument that was passed in.`);
result = await yieldingIterator.return("efgh");
is(typeof result, "object",
`AsyncIterableSingleWithArgs: "return("efgh")" should return { done: true, value: "efgh" }`);
is(result.done, true,
`AsyncIterableSingleWithArgs: "return("efgh")" should return { done: true, value: "efgh" }`);
is(result.value, "efgh",
`AsyncIterableSingleWithArgs: "return("efgh")" should return { done: true, value: "efgh" }`);
is(itr.returnLastCalledWith, "abcd",
`AsyncIterableSingleWithArgs: the asynchronous iterator return algorithm shouldn't be called if the iterator's 'is finished' flag is true already.`);
}
async function test_data_double() {

Просмотреть файл

@ -56,7 +56,7 @@ void FileSystemDirectoryHandle::InitAsyncIteratorData(
fs::FileSystemDirectoryIteratorFactory::Create(mMetadata, aType);
}
already_AddRefed<Promise> FileSystemDirectoryHandle::GetNextPromise(
already_AddRefed<Promise> FileSystemDirectoryHandle::GetNextIterationResult(
FileSystemDirectoryHandle::iterator_t* aIterator, ErrorResult& aError) {
return aIterator->Data().mImpl->Next(mGlobal, mManager, aError);
}

Просмотреть файл

@ -53,8 +53,8 @@ class FileSystemDirectoryHandle final : public FileSystemHandle {
iterator_t::IteratorType aType,
ErrorResult& aError);
[[nodiscard]] already_AddRefed<Promise> GetNextPromise(iterator_t* aIterator,
ErrorResult& aError);
[[nodiscard]] already_AddRefed<Promise> GetNextIterationResult(
iterator_t* aIterator, ErrorResult& aError);
already_AddRefed<Promise> GetFileHandle(
const nsAString& aName, const FileSystemGetFileOptions& aOptions,

Просмотреть файл

@ -86,7 +86,8 @@ TEST_F(TestFileSystemDirectoryHandle, isNextPromiseReturned) {
iterator->Data().mImpl = std::move(mockIter);
IgnoredErrorResult rv;
RefPtr<Promise> promise = dirHandle->GetNextPromise(iterator.get(), rv);
RefPtr<Promise> promise =
dirHandle->GetNextIterationResult(iterator.get(), rv);
ASSERT_TRUE(promise);
}

Просмотреть файл

@ -120,7 +120,12 @@ interface TestInterfaceAsyncIterableSingleWithArgs {
[Throws]
constructor();
[GenerateReturnMethod]
async iterable<long>(optional TestInterfaceAsyncIteratorOptions options = {});
readonly attribute long returnCallCount;
readonly attribute any returnLastCalledWith;
};
[Pref="dom.expose_test_interfaces",