1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 * The IterableIterator class is used for WebIDL interfaces that have a
9 * iterable<> member defined with two types (so a pair iterator). It handles
10 * the ES6 Iterator-like functions that are generated for the iterable
13 * For iterable interfaces with a pair iterator, the implementation class will
14 * need to implement these two functions:
16 * - size_t GetIterableLength()
17 * - Returns the number of elements available to iterate over
18 * - [type] GetValueAtIndex(size_t index)
19 * - Returns the value at the requested index.
20 * - [type] GetKeyAtIndex(size_t index)
21 * - Returns the key at the requested index
23 * Examples of iterable interface implementations can be found in the bindings
27 #ifndef mozilla_dom_IterableIterator_h
28 #define mozilla_dom_IterableIterator_h
30 #include "js/RootingAPI.h"
31 #include "js/TypeDecls.h"
33 #include "nsISupports.h"
34 #include "mozilla/AlreadyAddRefed.h"
35 #include "mozilla/dom/IterableIteratorBinding.h"
36 #include "mozilla/dom/Promise.h"
37 #include "mozilla/dom/RootedDictionary.h"
38 #include "mozilla/dom/ToJSValue.h"
39 #include "mozilla/WeakPtr.h"
41 namespace mozilla::dom
{
43 namespace binding_details
{
45 // JS::MagicValue(END_OF_ITERATION) is the value we use for
46 // https://webidl.spec.whatwg.org/#end-of-iteration. It shouldn't be returned to
47 // JS, because AsyncIterableIteratorBase::NextSteps will detect it and will
48 // return the result of CreateIterResultObject(undefined, true) instead
49 // (discarding the magic value).
50 static const JSWhyMagic END_OF_ITERATION
= JS_GENERIC_MAGIC
;
52 } // namespace binding_details
54 namespace iterator_utils
{
56 void DictReturn(JSContext
* aCx
, JS::MutableHandle
<JSObject
*> aResult
,
57 bool aDone
, JS::Handle
<JS::Value
> aValue
, ErrorResult
& aRv
);
59 void KeyAndValueReturn(JSContext
* aCx
, JS::Handle
<JS::Value
> aKey
,
60 JS::Handle
<JS::Value
> aValue
,
61 JS::MutableHandle
<JSObject
*> aResult
, ErrorResult
& aRv
);
63 inline void ResolvePromiseForFinished(Promise
* aPromise
) {
64 aPromise
->MaybeResolve(JS::MagicValue(binding_details::END_OF_ITERATION
));
67 template <typename Key
, typename Value
>
68 void ResolvePromiseWithKeyAndValue(Promise
* aPromise
, const Key
& aKey
,
69 const Value
& aValue
) {
70 aPromise
->MaybeResolve(std::make_tuple(aKey
, aValue
));
73 } // namespace iterator_utils
75 class IterableIteratorBase
{
77 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(IterableIteratorBase
)
78 NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(IterableIteratorBase
)
80 typedef enum { Keys
= 0, Values
, Entries
} IteratorType
;
82 IterableIteratorBase() = default;
85 virtual ~IterableIteratorBase() = default;
86 virtual void UnlinkHelper() = 0;
87 virtual void TraverseHelper(nsCycleCollectionTraversalCallback
& cb
) = 0;
90 // Helpers to call iterator getter methods with the correct arguments, depending
91 // on the types they return, and convert the result to JS::Values.
93 // Helper for Get[Key,Value]AtIndex(uint32_t) methods, which accept an index and
94 // return a type supported by ToJSValue.
95 template <typename T
, typename U
>
96 bool CallIterableGetter(JSContext
* aCx
, U (T::*aMethod
)(uint32_t), T
* aInst
,
97 uint32_t aIndex
, JS::MutableHandle
<JS::Value
> aResult
) {
98 return ToJSValue(aCx
, (aInst
->*aMethod
)(aIndex
), aResult
);
101 template <typename T
, typename U
>
102 bool CallIterableGetter(JSContext
* aCx
, U (T::*aMethod
)(uint32_t) const,
103 const T
* aInst
, uint32_t aIndex
,
104 JS::MutableHandle
<JS::Value
> aResult
) {
105 return ToJSValue(aCx
, (aInst
->*aMethod
)(aIndex
), aResult
);
108 // Helper for Get[Key,Value]AtIndex(JSContext*, uint32_t, MutableHandleValue)
109 // methods, which accept a JS context, index, and mutable result value handle,
110 // and return true on success or false on failure.
111 template <typename T
>
112 bool CallIterableGetter(JSContext
* aCx
,
113 bool (T::*aMethod
)(JSContext
*, uint32_t,
114 JS::MutableHandle
<JS::Value
>),
115 T
* aInst
, uint32_t aIndex
,
116 JS::MutableHandle
<JS::Value
> aResult
) {
117 return (aInst
->*aMethod
)(aCx
, aIndex
, aResult
);
120 template <typename T
>
121 bool CallIterableGetter(JSContext
* aCx
,
122 bool (T::*aMethod
)(JSContext
*, uint32_t,
123 JS::MutableHandle
<JS::Value
>) const,
124 const T
* aInst
, uint32_t aIndex
,
125 JS::MutableHandle
<JS::Value
> aResult
) {
126 return (aInst
->*aMethod
)(aCx
, aIndex
, aResult
);
129 template <typename T
>
130 class IterableIterator
: public IterableIteratorBase
{
132 IterableIterator(T
* aIterableObj
, IteratorType aIteratorType
)
133 : mIterableObj(aIterableObj
), mIteratorType(aIteratorType
), mIndex(0) {
134 MOZ_ASSERT(mIterableObj
);
137 bool GetKeyAtIndex(JSContext
* aCx
, uint32_t aIndex
,
138 JS::MutableHandle
<JS::Value
> aResult
) {
139 return CallIterableGetter(aCx
, &T::GetKeyAtIndex
, mIterableObj
.get(),
143 bool GetValueAtIndex(JSContext
* aCx
, uint32_t aIndex
,
144 JS::MutableHandle
<JS::Value
> aResult
) {
145 return CallIterableGetter(aCx
, &T::GetValueAtIndex
, mIterableObj
.get(),
149 void Next(JSContext
* aCx
, JS::MutableHandle
<JSObject
*> aResult
,
151 JS::Rooted
<JS::Value
> value(aCx
, JS::UndefinedValue());
152 if (mIndex
>= this->mIterableObj
->GetIterableLength()) {
153 iterator_utils::DictReturn(aCx
, aResult
, true, value
, aRv
);
156 switch (mIteratorType
) {
157 case IteratorType::Keys
: {
158 if (!GetKeyAtIndex(aCx
, mIndex
, &value
)) {
159 aRv
.Throw(NS_ERROR_FAILURE
);
162 iterator_utils::DictReturn(aCx
, aResult
, false, value
, aRv
);
165 case IteratorType::Values
: {
166 if (!GetValueAtIndex(aCx
, mIndex
, &value
)) {
167 aRv
.Throw(NS_ERROR_FAILURE
);
170 iterator_utils::DictReturn(aCx
, aResult
, false, value
, aRv
);
173 case IteratorType::Entries
: {
174 JS::Rooted
<JS::Value
> key(aCx
);
175 if (!GetKeyAtIndex(aCx
, mIndex
, &key
)) {
176 aRv
.Throw(NS_ERROR_FAILURE
);
179 if (!GetValueAtIndex(aCx
, mIndex
, &value
)) {
180 aRv
.Throw(NS_ERROR_FAILURE
);
183 iterator_utils::KeyAndValueReturn(aCx
, key
, value
, aResult
, aRv
);
187 MOZ_CRASH("Invalid iterator type!");
193 virtual ~IterableIterator() = default;
195 // Since we're templated on a binding, we need to possibly CC it, but can't do
196 // that through macros. So it happens here.
197 void UnlinkHelper() final
{ mIterableObj
= nullptr; }
199 virtual void TraverseHelper(nsCycleCollectionTraversalCallback
& cb
) override
{
200 IterableIterator
<T
>* tmp
= this;
201 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIterableObj
);
204 // Binding Implementation object that we're iterating over.
205 RefPtr
<T
> mIterableObj
;
206 // Tells whether this is a key, value, or entries iterator.
207 IteratorType mIteratorType
;
208 // Current index of iteration.
212 namespace binding_detail
{
214 class AsyncIterableNextImpl
;
215 class AsyncIterableReturnImpl
;
217 } // namespace binding_detail
219 class AsyncIterableIteratorBase
: public IterableIteratorBase
{
221 IteratorType
GetIteratorType() { return mIteratorType
; }
224 explicit AsyncIterableIteratorBase(IteratorType aIteratorType
)
225 : mIteratorType(aIteratorType
) {}
227 void UnlinkHelper() override
{
228 AsyncIterableIteratorBase
* tmp
= this;
229 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOngoingPromise
);
232 void TraverseHelper(nsCycleCollectionTraversalCallback
& cb
) override
{
233 AsyncIterableIteratorBase
* tmp
= this;
234 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOngoingPromise
);
238 friend class binding_detail::AsyncIterableNextImpl
;
239 friend class binding_detail::AsyncIterableReturnImpl
;
241 // 3.7.10.1. Default asynchronous iterator objects
242 // Target is in AsyncIterableIterator
244 IteratorType mIteratorType
;
246 RefPtr
<Promise
> mOngoingPromise
;
248 bool mIsFinished
= false;
251 template <typename T
>
252 class AsyncIterableIterator
: public AsyncIterableIteratorBase
{
254 using IteratorData
= typename
T::IteratorData
;
257 AsyncIterableIterator(T
* aIterableObj
, IteratorType aIteratorType
)
258 : AsyncIterableIteratorBase(aIteratorType
), mIterableObj(aIterableObj
) {
259 MOZ_ASSERT(mIterableObj
);
262 IteratorData
& Data() { return mData
; }
265 // We'd prefer to use ImplCycleCollectionTraverse/ImplCycleCollectionUnlink on
266 // the iterator data, but unfortunately that doesn't work because it's
267 // dependent on the template parameter. Instead we detect if the data
268 // structure has Traverse and Unlink functions and call those.
269 template <typename Data
>
270 auto TraverseData(Data
& aData
, nsCycleCollectionTraversalCallback
& aCallback
,
271 int) -> decltype(aData
.Traverse(aCallback
)) {
272 return aData
.Traverse(aCallback
);
274 template <typename Data
>
275 void TraverseData(Data
& aData
, nsCycleCollectionTraversalCallback
& aCallback
,
278 template <typename Data
>
279 auto UnlinkData(Data
& aData
, int) -> decltype(aData
.Unlink()) {
280 return aData
.Unlink();
282 template <typename Data
>
283 void UnlinkData(Data
& aData
, double) {}
285 // Since we're templated on a binding, we need to possibly CC it, but can't do
286 // that through macros. So it happens here.
287 void UnlinkHelper() final
{
288 AsyncIterableIteratorBase::UnlinkHelper();
290 AsyncIterableIterator
<T
>* tmp
= this;
291 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIterableObj
);
292 UnlinkData(tmp
->mData
, 0);
295 void TraverseHelper(nsCycleCollectionTraversalCallback
& cb
) final
{
296 AsyncIterableIteratorBase::TraverseHelper(cb
);
298 AsyncIterableIterator
<T
>* tmp
= this;
299 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIterableObj
);
300 TraverseData(tmp
->mData
, cb
, 0);
303 // 3.7.10.1. Default asynchronous iterator objects
305 RefPtr
<T
> mIterableObj
;
309 // See AsyncIterableIteratorBase
311 // Opaque data of the backing object.
315 namespace binding_detail
{
317 template <typename T
>
318 using IterableIteratorWrapFunc
=
319 bool (*)(JSContext
* aCx
, IterableIterator
<T
>* aObject
,
320 JS::MutableHandle
<JSObject
*> aReflector
);
322 template <typename T
, IterableIteratorWrapFunc
<T
> WrapFunc
>
323 class WrappableIterableIterator final
: public IterableIterator
<T
> {
325 using IterableIterator
<T
>::IterableIterator
;
327 bool WrapObject(JSContext
* aCx
, JS::Handle
<JSObject
*> aGivenProto
,
328 JS::MutableHandle
<JSObject
*> aObj
) {
329 MOZ_ASSERT(!aGivenProto
);
330 return (*WrapFunc
)(aCx
, this, aObj
);
334 class AsyncIterableNextImpl
{
336 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> Next(
337 JSContext
* aCx
, AsyncIterableIteratorBase
* aObject
,
338 nsISupports
* aGlobalObject
, ErrorResult
& aRv
);
339 MOZ_CAN_RUN_SCRIPT
virtual already_AddRefed
<Promise
> GetNextResult(
340 ErrorResult
& aRv
) = 0;
343 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> NextSteps(
344 JSContext
* aCx
, AsyncIterableIteratorBase
* aObject
,
345 nsIGlobalObject
* aGlobalObject
, ErrorResult
& aRv
);
348 class AsyncIterableReturnImpl
{
350 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> Return(
351 JSContext
* aCx
, AsyncIterableIteratorBase
* aObject
,
352 nsISupports
* aGlobalObject
, JS::Handle
<JS::Value
> aValue
,
354 MOZ_CAN_RUN_SCRIPT
virtual already_AddRefed
<Promise
> GetReturnPromise(
355 JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
, ErrorResult
& aRv
) = 0;
358 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> ReturnSteps(
359 JSContext
* aCx
, AsyncIterableIteratorBase
* aObject
,
360 nsIGlobalObject
* aGlobalObject
, JS::Handle
<JS::Value
> aValue
,
364 template <typename T
>
365 class AsyncIterableIteratorNoReturn
: public AsyncIterableIterator
<T
>,
366 public AsyncIterableNextImpl
{
368 using AsyncIterableIterator
<T
>::AsyncIterableIterator
;
370 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> Next(JSContext
* aCx
,
372 nsCOMPtr
<nsISupports
> parentObject
= this->mIterableObj
->GetParentObject();
373 return AsyncIterableNextImpl::Next(aCx
, this, parentObject
, aRv
);
377 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> GetNextResult(
378 ErrorResult
& aRv
) override
{
379 RefPtr
<T
> iterableObj(this->mIterableObj
);
380 return iterableObj
->GetNextIterationResult(
381 static_cast<AsyncIterableIterator
<T
>*>(this), aRv
);
385 template <typename T
>
386 class AsyncIterableIteratorWithReturn
: public AsyncIterableIteratorNoReturn
<T
>,
387 public AsyncIterableReturnImpl
{
389 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> Return(
390 JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
, ErrorResult
& aRv
) {
391 nsCOMPtr
<nsISupports
> parentObject
= this->mIterableObj
->GetParentObject();
392 return AsyncIterableReturnImpl::Return(aCx
, this, parentObject
, aValue
,
397 using AsyncIterableIteratorNoReturn
<T
>::AsyncIterableIteratorNoReturn
;
399 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> GetReturnPromise(
400 JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
, ErrorResult
& aRv
) override
{
401 RefPtr
<T
> iterableObj(this->mIterableObj
);
402 return iterableObj
->IteratorReturn(
403 aCx
, static_cast<AsyncIterableIterator
<T
>*>(this), aValue
, aRv
);
407 template <typename T
, bool NeedReturnMethod
>
408 using AsyncIterableIteratorNative
=
409 std::conditional_t
<NeedReturnMethod
, AsyncIterableIteratorWithReturn
<T
>,
410 AsyncIterableIteratorNoReturn
<T
>>;
412 template <typename T
, bool NeedReturnMethod
>
413 using AsyncIterableIteratorWrapFunc
= bool (*)(
414 JSContext
* aCx
, AsyncIterableIteratorNative
<T
, NeedReturnMethod
>* aObject
,
415 JS::MutableHandle
<JSObject
*> aReflector
);
417 template <typename T
, bool NeedReturnMethod
,
418 AsyncIterableIteratorWrapFunc
<T
, NeedReturnMethod
> WrapFunc
,
419 typename Base
= AsyncIterableIteratorNative
<T
, NeedReturnMethod
>>
420 class WrappableAsyncIterableIterator final
: public Base
{
424 bool WrapObject(JSContext
* aCx
, JS::Handle
<JSObject
*> aGivenProto
,
425 JS::MutableHandle
<JSObject
*> aObj
) {
426 MOZ_ASSERT(!aGivenProto
);
427 return (*WrapFunc
)(aCx
, this, aObj
);
431 } // namespace binding_detail
433 } // namespace mozilla::dom
435 #endif // mozilla_dom_IterableIterator_h