1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #ifndef AddonManagerStartup_inlines_h
7 #define AddonManagerStartup_inlines_h
8 
9 #include <utility>
10 
11 #include "js/Array.h"  // JS::GetArrayLength, JS::IsArrayObject
12 #include "jsapi.h"
13 #include "mozilla/Maybe.h"
14 #include "nsJSUtils.h"
15 
16 namespace mozilla {
17 
18 class ArrayIterElem;
19 class PropertyIterElem;
20 
21 /*****************************************************************************
22  * Object iterator base classes
23  *****************************************************************************/
24 
25 template <class T, class PropertyType>
26 class MOZ_STACK_CLASS BaseIter {
27  public:
28   typedef T SelfType;
29 
begin()30   PropertyType begin() const { return PropertyType(Self()); }
31 
end()32   PropertyType end() const {
33     PropertyType elem(Self());
34     return elem.End();
35   }
36 
Context()37   void* Context() const { return mContext; }
38 
39  protected:
40   BaseIter(JSContext* cx, JS::HandleObject object, void* context = nullptr)
mCx(cx)41       : mCx(cx), mObject(object), mContext(context) {}
42 
Self()43   const SelfType& Self() const { return *static_cast<const SelfType*>(this); }
Self()44   SelfType& Self() { return *static_cast<SelfType*>(this); }
45 
46   JSContext* mCx;
47 
48   JS::HandleObject mObject;
49 
50   void* mContext;
51 };
52 
53 template <class T, class IterType>
54 class MOZ_STACK_CLASS BaseIterElem {
55  public:
56   typedef T SelfType;
57 
58   explicit BaseIterElem(const IterType& iter, uint32_t index = 0)
mIter(iter)59       : mIter(iter), mIndex(index) {}
60 
Length()61   uint32_t Length() const { return mIter.Length(); }
62 
Value()63   JS::Value Value() {
64     JS::RootedValue value(mIter.mCx, JS::UndefinedValue());
65 
66     auto& self = Self();
67     if (!self.GetValue(&value)) {
68       JS_ClearPendingException(mIter.mCx);
69     }
70 
71     return value;
72   }
73 
74   SelfType& operator*() { return Self(); }
75 
76   SelfType& operator++() {
77     MOZ_ASSERT(mIndex < Length());
78     mIndex++;
79     return Self();
80   }
81 
82   bool operator!=(const SelfType& other) const {
83     return &mIter != &other.mIter || mIndex != other.mIndex;
84   }
85 
End()86   SelfType End() const {
87     SelfType end(mIter);
88     end.mIndex = Length();
89     return end;
90   }
91 
Context()92   void* Context() const { return mIter.Context(); }
93 
94  protected:
Self()95   const SelfType& Self() const { return *static_cast<const SelfType*>(this); }
Self()96   SelfType& Self() { return *static_cast<SelfType*>(this); }
97 
98   const IterType& mIter;
99 
100   uint32_t mIndex;
101 };
102 
103 /*****************************************************************************
104  * Property iteration
105  *****************************************************************************/
106 
107 class MOZ_STACK_CLASS PropertyIter
108     : public BaseIter<PropertyIter, PropertyIterElem> {
109   friend class PropertyIterElem;
110   friend class BaseIterElem<PropertyIterElem, PropertyIter>;
111 
112  public:
113   PropertyIter(JSContext* cx, JS::HandleObject object, void* context = nullptr)
BaseIter(cx,object,context)114       : BaseIter(cx, object, context), mIds(cx, JS::IdVector(cx)) {
115     if (!JS_Enumerate(cx, object, &mIds)) {
116       JS_ClearPendingException(cx);
117     }
118   }
119 
PropertyIter(const PropertyIter & other)120   PropertyIter(const PropertyIter& other)
121       : PropertyIter(other.mCx, other.mObject, other.mContext) {}
122 
123   PropertyIter& operator=(const PropertyIter& other) {
124     MOZ_ASSERT(other.mObject == mObject);
125     mCx = other.mCx;
126     mContext = other.mContext;
127 
128     mIds.clear();
129     if (!JS_Enumerate(mCx, mObject, &mIds)) {
130       JS_ClearPendingException(mCx);
131     }
132     return *this;
133   }
134 
Length()135   int32_t Length() const { return mIds.length(); }
136 
137  protected:
138   JS::Rooted<JS::IdVector> mIds;
139 };
140 
141 class MOZ_STACK_CLASS PropertyIterElem
142     : public BaseIterElem<PropertyIterElem, PropertyIter> {
143   friend class BaseIterElem<PropertyIterElem, PropertyIter>;
144 
145  public:
146   using BaseIterElem::BaseIterElem;
147 
PropertyIterElem(const PropertyIterElem & other)148   PropertyIterElem(const PropertyIterElem& other)
149       : BaseIterElem(other.mIter, other.mIndex) {}
150 
Id()151   jsid Id() {
152     MOZ_ASSERT(mIndex < mIter.mIds.length());
153 
154     return mIter.mIds[mIndex];
155   }
156 
Name()157   const nsAString& Name() {
158     if (mName.isNothing()) {
159       mName.emplace();
160       mName.ref().init(mIter.mCx, Id());
161     }
162     return mName.ref();
163   }
164 
Cx()165   JSContext* Cx() { return mIter.mCx; }
166 
167  protected:
GetValue(JS::MutableHandleValue value)168   bool GetValue(JS::MutableHandleValue value) {
169     MOZ_ASSERT(mIndex < Length());
170     JS::Rooted<jsid> id(mIter.mCx, Id());
171 
172     return JS_GetPropertyById(mIter.mCx, mIter.mObject, id, value);
173   }
174 
175  private:
176   Maybe<nsAutoJSString> mName;
177 };
178 
179 /*****************************************************************************
180  * Array iteration
181  *****************************************************************************/
182 
183 class MOZ_STACK_CLASS ArrayIter : public BaseIter<ArrayIter, ArrayIterElem> {
184   friend class ArrayIterElem;
185   friend class BaseIterElem<ArrayIterElem, ArrayIter>;
186 
187  public:
ArrayIter(JSContext * cx,JS::HandleObject object)188   ArrayIter(JSContext* cx, JS::HandleObject object)
189       : BaseIter(cx, object), mLength(0) {
190     bool isArray;
191     if (!JS::IsArrayObject(cx, object, &isArray) || !isArray) {
192       JS_ClearPendingException(cx);
193       return;
194     }
195 
196     if (!JS::GetArrayLength(cx, object, &mLength)) {
197       JS_ClearPendingException(cx);
198     }
199   }
200 
Length()201   uint32_t Length() const { return mLength; }
202 
203  private:
204   uint32_t mLength;
205 };
206 
207 class MOZ_STACK_CLASS ArrayIterElem
208     : public BaseIterElem<ArrayIterElem, ArrayIter> {
209   friend class BaseIterElem<ArrayIterElem, ArrayIter>;
210 
211  public:
212   using BaseIterElem::BaseIterElem;
213 
ArrayIterElem(const ArrayIterElem & other)214   ArrayIterElem(const ArrayIterElem& other)
215       : BaseIterElem(other.mIter, other.mIndex) {}
216 
217  protected:
GetValue(JS::MutableHandleValue value)218   bool GetValue(JS::MutableHandleValue value) {
219     MOZ_ASSERT(mIndex < Length());
220     return JS_GetElement(mIter.mCx, mIter.mObject, mIndex, value);
221   }
222 };
223 
224 }  // namespace mozilla
225 
226 #endif  // AddonManagerStartup_inlines_h
227