tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

IterableIterator.h (15189B)


      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/. */
      6 
      7 /**
      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
     11 * interface.
     12 *
     13 * For iterable interfaces with a pair iterator, the implementation class will
     14 * need to implement these two functions:
     15 *
     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
     22 *
     23 * Examples of iterable interface implementations can be found in the bindings
     24 * test directory.
     25 */
     26 
     27 #ifndef mozilla_dom_IterableIterator_h
     28 #define mozilla_dom_IterableIterator_h
     29 
     30 #include "js/RootingAPI.h"
     31 #include "js/TypeDecls.h"
     32 #include "js/Value.h"
     33 #include "mozilla/AlreadyAddRefed.h"
     34 #include "mozilla/dom/IterableIteratorBinding.h"
     35 #include "mozilla/dom/Promise.h"
     36 #include "mozilla/dom/RootedDictionary.h"
     37 #include "mozilla/dom/ToJSValue.h"
     38 #include "nsISupports.h"
     39 
     40 namespace mozilla::dom {
     41 
     42 namespace binding_details {
     43 
     44 // JS::MagicValue(END_OF_ITERATION) is the value we use for
     45 // https://webidl.spec.whatwg.org/#end-of-iteration. It shouldn't be returned to
     46 // JS, because AsyncIterableIteratorBase::NextSteps will detect it and will
     47 // return the result of CreateIterResultObject(undefined, true) instead
     48 // (discarding the magic value).
     49 static const JSWhyMagic END_OF_ITERATION = JS_GENERIC_MAGIC;
     50 
     51 }  // namespace binding_details
     52 
     53 namespace iterator_utils {
     54 
     55 void DictReturn(JSContext* aCx, JS::MutableHandle<JSObject*> aResult,
     56                bool aDone, JS::Handle<JS::Value> aValue, ErrorResult& aRv);
     57 
     58 void KeyAndValueReturn(JSContext* aCx, JS::Handle<JS::Value> aKey,
     59                       JS::Handle<JS::Value> aValue,
     60                       JS::MutableHandle<JSObject*> aResult, ErrorResult& aRv);
     61 
     62 inline void ResolvePromiseForFinished(Promise* aPromise) {
     63  aPromise->MaybeResolve(JS::MagicValue(binding_details::END_OF_ITERATION));
     64 }
     65 
     66 template <typename Key, typename Value>
     67 void ResolvePromiseWithKeyAndValue(Promise* aPromise, const Key& aKey,
     68                                   const Value& aValue) {
     69  aPromise->MaybeResolve(std::make_tuple(aKey, aValue));
     70 }
     71 
     72 }  // namespace iterator_utils
     73 
     74 class IterableIteratorBase {
     75 public:
     76  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(IterableIteratorBase)
     77  NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(IterableIteratorBase)
     78 
     79  typedef enum { Keys = 0, Values, Entries } IteratorType;
     80 
     81  IterableIteratorBase() = default;
     82 
     83 protected:
     84  virtual ~IterableIteratorBase() = default;
     85  virtual void UnlinkHelper() = 0;
     86  virtual void TraverseHelper(nsCycleCollectionTraversalCallback& cb) = 0;
     87 };
     88 
     89 // Helpers to call iterator getter methods with the correct arguments, depending
     90 // on the types they return, and convert the result to JS::Values.
     91 
     92 // Helper for Get[Key,Value]AtIndex(uint32_t) methods, which accept an index and
     93 // return a type supported by ToJSValue.
     94 template <typename T, typename U>
     95 bool CallIterableGetter(JSContext* aCx, U (T::*aMethod)(uint32_t), T* aInst,
     96                        uint32_t aIndex, JS::MutableHandle<JS::Value> aResult) {
     97  return ToJSValue(aCx, (aInst->*aMethod)(aIndex), aResult);
     98 }
     99 
    100 template <typename T, typename U>
    101 bool CallIterableGetter(JSContext* aCx, U (T::*aMethod)(uint32_t) const,
    102                        const T* aInst, uint32_t aIndex,
    103                        JS::MutableHandle<JS::Value> aResult) {
    104  return ToJSValue(aCx, (aInst->*aMethod)(aIndex), aResult);
    105 }
    106 
    107 // Helper for Get[Key,Value]AtIndex(JSContext*, uint32_t, MutableHandleValue)
    108 // methods, which accept a JS context, index, and mutable result value handle,
    109 // and return true on success or false on failure.
    110 template <typename T>
    111 bool CallIterableGetter(JSContext* aCx,
    112                        bool (T::*aMethod)(JSContext*, uint32_t,
    113                                           JS::MutableHandle<JS::Value>),
    114                        T* aInst, uint32_t aIndex,
    115                        JS::MutableHandle<JS::Value> aResult) {
    116  return (aInst->*aMethod)(aCx, aIndex, aResult);
    117 }
    118 
    119 template <typename T>
    120 bool CallIterableGetter(JSContext* aCx,
    121                        bool (T::*aMethod)(JSContext*, uint32_t,
    122                                           JS::MutableHandle<JS::Value>) const,
    123                        const T* aInst, uint32_t aIndex,
    124                        JS::MutableHandle<JS::Value> aResult) {
    125  return (aInst->*aMethod)(aCx, aIndex, aResult);
    126 }
    127 
    128 template <typename T>
    129 class IterableIterator : public IterableIteratorBase {
    130 public:
    131  IterableIterator(T* aIterableObj, IteratorType aIteratorType)
    132      : mIterableObj(aIterableObj), mIteratorType(aIteratorType), mIndex(0) {
    133    MOZ_ASSERT(mIterableObj);
    134  }
    135 
    136  bool GetKeyAtIndex(JSContext* aCx, uint32_t aIndex,
    137                     JS::MutableHandle<JS::Value> aResult) {
    138    return CallIterableGetter(aCx, &T::GetKeyAtIndex, mIterableObj.get(),
    139                              aIndex, aResult);
    140  }
    141 
    142  bool GetValueAtIndex(JSContext* aCx, uint32_t aIndex,
    143                       JS::MutableHandle<JS::Value> aResult) {
    144    return CallIterableGetter(aCx, &T::GetValueAtIndex, mIterableObj.get(),
    145                              aIndex, aResult);
    146  }
    147 
    148  void Next(JSContext* aCx, JS::MutableHandle<JSObject*> aResult,
    149            ErrorResult& aRv) {
    150    JS::Rooted<JS::Value> value(aCx, JS::UndefinedValue());
    151    if (mIndex >= this->mIterableObj->GetIterableLength()) {
    152      iterator_utils::DictReturn(aCx, aResult, true, value, aRv);
    153      return;
    154    }
    155    switch (mIteratorType) {
    156      case IteratorType::Keys: {
    157        if (!GetKeyAtIndex(aCx, mIndex, &value)) {
    158          aRv.Throw(NS_ERROR_FAILURE);
    159          return;
    160        }
    161        iterator_utils::DictReturn(aCx, aResult, false, value, aRv);
    162        break;
    163      }
    164      case IteratorType::Values: {
    165        if (!GetValueAtIndex(aCx, mIndex, &value)) {
    166          aRv.Throw(NS_ERROR_FAILURE);
    167          return;
    168        }
    169        iterator_utils::DictReturn(aCx, aResult, false, value, aRv);
    170        break;
    171      }
    172      case IteratorType::Entries: {
    173        JS::Rooted<JS::Value> key(aCx);
    174        if (!GetKeyAtIndex(aCx, mIndex, &key)) {
    175          aRv.Throw(NS_ERROR_FAILURE);
    176          return;
    177        }
    178        if (!GetValueAtIndex(aCx, mIndex, &value)) {
    179          aRv.Throw(NS_ERROR_FAILURE);
    180          return;
    181        }
    182        iterator_utils::KeyAndValueReturn(aCx, key, value, aResult, aRv);
    183        break;
    184      }
    185      default:
    186        MOZ_CRASH("Invalid iterator type!");
    187    }
    188    ++mIndex;
    189  }
    190 
    191 protected:
    192  virtual ~IterableIterator() = default;
    193 
    194  // Since we're templated on a binding, we need to possibly CC it, but can't do
    195  // that through macros. So it happens here.
    196  void UnlinkHelper() final { mIterableObj = nullptr; }
    197 
    198  virtual void TraverseHelper(nsCycleCollectionTraversalCallback& cb) override {
    199    IterableIterator<T>* tmp = this;
    200    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIterableObj);
    201  }
    202 
    203  // Binding Implementation object that we're iterating over.
    204  RefPtr<T> mIterableObj;
    205  // Tells whether this is a key, value, or entries iterator.
    206  IteratorType mIteratorType;
    207  // Current index of iteration.
    208  uint32_t mIndex;
    209 };
    210 
    211 namespace binding_detail {
    212 
    213 class AsyncIterableNextImpl;
    214 class AsyncIterableReturnImpl;
    215 
    216 }  // namespace binding_detail
    217 
    218 class AsyncIterableIteratorBase : public IterableIteratorBase {
    219 public:
    220  IteratorType GetIteratorType() { return mIteratorType; }
    221 
    222 protected:
    223  explicit AsyncIterableIteratorBase(IteratorType aIteratorType)
    224      : mIteratorType(aIteratorType) {}
    225 
    226  void UnlinkHelper() override {
    227    AsyncIterableIteratorBase* tmp = this;
    228    NS_IMPL_CYCLE_COLLECTION_UNLINK(mOngoingPromise);
    229  }
    230 
    231  void TraverseHelper(nsCycleCollectionTraversalCallback& cb) override {
    232    AsyncIterableIteratorBase* tmp = this;
    233    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOngoingPromise);
    234  }
    235 
    236 private:
    237  friend class binding_detail::AsyncIterableNextImpl;
    238  friend class binding_detail::AsyncIterableReturnImpl;
    239 
    240  // 3.7.10.1. Default asynchronous iterator objects
    241  // Target is in AsyncIterableIterator
    242  // Kind
    243  IteratorType mIteratorType;
    244  // Ongoing promise
    245  RefPtr<Promise> mOngoingPromise;
    246  // Is finished
    247  bool mIsFinished = false;
    248 };
    249 
    250 template <typename T>
    251 class AsyncIterableIterator : public AsyncIterableIteratorBase {
    252 private:
    253  using IteratorData = typename T::IteratorData;
    254 
    255 public:
    256  AsyncIterableIterator(T* aIterableObj, IteratorType aIteratorType)
    257      : AsyncIterableIteratorBase(aIteratorType), mIterableObj(aIterableObj) {
    258    MOZ_ASSERT(mIterableObj);
    259  }
    260 
    261  IteratorData& Data() { return mData; }
    262 
    263 protected:
    264  // We'd prefer to use ImplCycleCollectionTraverse/ImplCycleCollectionUnlink on
    265  // the iterator data, but unfortunately that doesn't work because it's
    266  // dependent on the template parameter. Instead we detect if the data
    267  // structure has Traverse and Unlink functions and call those.
    268  template <typename Data>
    269  auto TraverseData(Data& aData, nsCycleCollectionTraversalCallback& aCallback,
    270                    int) -> decltype(aData.Traverse(aCallback)) {
    271    return aData.Traverse(aCallback);
    272  }
    273  template <typename Data>
    274  void TraverseData(Data& aData, nsCycleCollectionTraversalCallback& aCallback,
    275                    double) {}
    276 
    277  template <typename Data>
    278  auto UnlinkData(Data& aData, int) -> decltype(aData.Unlink()) {
    279    return aData.Unlink();
    280  }
    281  template <typename Data>
    282  void UnlinkData(Data& aData, double) {}
    283 
    284  // Since we're templated on a binding, we need to possibly CC it, but can't do
    285  // that through macros. So it happens here.
    286  void UnlinkHelper() final {
    287    AsyncIterableIteratorBase::UnlinkHelper();
    288 
    289    AsyncIterableIterator<T>* tmp = this;
    290    NS_IMPL_CYCLE_COLLECTION_UNLINK(mIterableObj);
    291    UnlinkData(tmp->mData, 0);
    292  }
    293 
    294  void TraverseHelper(nsCycleCollectionTraversalCallback& cb) final {
    295    AsyncIterableIteratorBase::TraverseHelper(cb);
    296 
    297    AsyncIterableIterator<T>* tmp = this;
    298    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIterableObj);
    299    TraverseData(tmp->mData, cb, 0);
    300  }
    301 
    302  // 3.7.10.1. Default asynchronous iterator objects
    303  // Target
    304  RefPtr<T> mIterableObj;
    305  // Kind
    306  // Ongoing promise
    307  // Is finished
    308  // See AsyncIterableIteratorBase
    309 
    310  // Opaque data of the backing object.
    311  IteratorData mData;
    312 };
    313 
    314 namespace binding_detail {
    315 
    316 template <typename T>
    317 using IterableIteratorWrapFunc =
    318    bool (*)(JSContext* aCx, IterableIterator<T>* aObject,
    319             JS::MutableHandle<JSObject*> aReflector);
    320 
    321 template <typename T, IterableIteratorWrapFunc<T> WrapFunc>
    322 class WrappableIterableIterator final : public IterableIterator<T> {
    323 public:
    324  using IterableIterator<T>::IterableIterator;
    325 
    326  bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
    327                  JS::MutableHandle<JSObject*> aObj) {
    328    MOZ_ASSERT(!aGivenProto);
    329    return (*WrapFunc)(aCx, this, aObj);
    330  }
    331 };
    332 
    333 class AsyncIterableNextImpl {
    334 protected:
    335  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> Next(
    336      JSContext* aCx, AsyncIterableIteratorBase* aObject,
    337      nsISupports* aGlobalObject, ErrorResult& aRv);
    338  MOZ_CAN_RUN_SCRIPT virtual already_AddRefed<Promise> GetNextResult(
    339      ErrorResult& aRv) = 0;
    340 
    341 private:
    342  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> NextSteps(
    343      JSContext* aCx, AsyncIterableIteratorBase* aObject,
    344      nsIGlobalObject* aGlobalObject, ErrorResult& aRv);
    345 };
    346 
    347 class AsyncIterableReturnImpl {
    348 protected:
    349  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> Return(
    350      JSContext* aCx, AsyncIterableIteratorBase* aObject,
    351      nsISupports* aGlobalObject, JS::Handle<JS::Value> aValue,
    352      ErrorResult& aRv);
    353  MOZ_CAN_RUN_SCRIPT virtual already_AddRefed<Promise> GetReturnPromise(
    354      JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv) = 0;
    355 
    356 private:
    357  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> ReturnSteps(
    358      JSContext* aCx, AsyncIterableIteratorBase* aObject,
    359      nsIGlobalObject* aGlobalObject, JS::Handle<JS::Value> aValue,
    360      ErrorResult& aRv);
    361 };
    362 
    363 template <typename T>
    364 class AsyncIterableIteratorNoReturn : public AsyncIterableIterator<T>,
    365                                      public AsyncIterableNextImpl {
    366 public:
    367  using AsyncIterableIterator<T>::AsyncIterableIterator;
    368 
    369  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> Next(JSContext* aCx,
    370                                                    ErrorResult& aRv) {
    371    nsCOMPtr<nsISupports> parentObject = this->mIterableObj->GetParentObject();
    372    return AsyncIterableNextImpl::Next(aCx, this, parentObject, aRv);
    373  }
    374 
    375 protected:
    376  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> GetNextResult(
    377      ErrorResult& aRv) override {
    378    RefPtr<T> iterableObj(this->mIterableObj);
    379    return iterableObj->GetNextIterationResult(
    380        static_cast<AsyncIterableIterator<T>*>(this), aRv);
    381  }
    382 };
    383 
    384 template <typename T>
    385 class AsyncIterableIteratorWithReturn : public AsyncIterableIteratorNoReturn<T>,
    386                                        public AsyncIterableReturnImpl {
    387 public:
    388  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> Return(
    389      JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv) {
    390    nsCOMPtr<nsISupports> parentObject = this->mIterableObj->GetParentObject();
    391    return AsyncIterableReturnImpl::Return(aCx, this, parentObject, aValue,
    392                                           aRv);
    393  }
    394 
    395 protected:
    396  using AsyncIterableIteratorNoReturn<T>::AsyncIterableIteratorNoReturn;
    397 
    398  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> GetReturnPromise(
    399      JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv) override {
    400    RefPtr<T> iterableObj(this->mIterableObj);
    401    return iterableObj->IteratorReturn(
    402        aCx, static_cast<AsyncIterableIterator<T>*>(this), aValue, aRv);
    403  }
    404 };
    405 
    406 template <typename T, bool NeedReturnMethod>
    407 using AsyncIterableIteratorNative =
    408    std::conditional_t<NeedReturnMethod, AsyncIterableIteratorWithReturn<T>,
    409                       AsyncIterableIteratorNoReturn<T>>;
    410 
    411 template <typename T, bool NeedReturnMethod>
    412 using AsyncIterableIteratorWrapFunc = bool (*)(
    413    JSContext* aCx, AsyncIterableIteratorNative<T, NeedReturnMethod>* aObject,
    414    JS::MutableHandle<JSObject*> aReflector);
    415 
    416 template <typename T, bool NeedReturnMethod,
    417          AsyncIterableIteratorWrapFunc<T, NeedReturnMethod> WrapFunc,
    418          typename Base = AsyncIterableIteratorNative<T, NeedReturnMethod>>
    419 class WrappableAsyncIterableIterator final : public Base {
    420 public:
    421  using Base::Base;
    422 
    423  bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
    424                  JS::MutableHandle<JSObject*> aObj) {
    425    MOZ_ASSERT(!aGivenProto);
    426    return (*WrapFunc)(aCx, this, aObj);
    427  }
    428 };
    429 
    430 }  // namespace binding_detail
    431 
    432 }  // namespace mozilla::dom
    433 
    434 #endif  // mozilla_dom_IterableIterator_h