/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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 AddonManagerStartup_inlines_h #define AddonManagerStartup_inlines_h #include #include "js/Array.h" // JS::GetArrayLength, JS::IsArrayObject #include "js/Exception.h" #include "js/PropertyAndElement.h" // JS_Enumerate, JS_GetElement, JS_GetProperty, JS_GetPropertyById #include "jsapi.h" #include "mozilla/Maybe.h" #include "nsJSUtils.h" namespace mozilla { class ArrayIterElem; class PropertyIterElem; /***************************************************************************** * Object iterator base classes *****************************************************************************/ template class MOZ_STACK_CLASS BaseIter { public: typedef T SelfType; PropertyType begin() const { return PropertyType(Self()); } PropertyType end() const { PropertyType elem(Self()); return elem.End(); } void* Context() const { return mContext; } protected: BaseIter(JSContext* cx, JS::Handle object, void* context = nullptr) : mCx(cx), mObject(object), mContext(context) {} const SelfType& Self() const { return *static_cast(this); } SelfType& Self() { return *static_cast(this); } JSContext* mCx; JS::Handle mObject; void* mContext; }; template class MOZ_STACK_CLASS BaseIterElem { public: typedef T SelfType; explicit BaseIterElem(const IterType& iter, uint32_t index = 0) : mIter(iter), mIndex(index) {} uint32_t Length() const { return mIter.Length(); } JS::Value Value() { JS::Rooted value(mIter.mCx, JS::UndefinedValue()); auto& self = Self(); if (!self.GetValue(&value)) { JS_ClearPendingException(mIter.mCx); } return value; } SelfType& operator*() { return Self(); } SelfType& operator++() { MOZ_ASSERT(mIndex < Length()); mIndex++; return Self(); } bool operator!=(const SelfType& other) const { return &mIter != &other.mIter || mIndex != other.mIndex; } SelfType End() const { SelfType end(mIter); end.mIndex = Length(); return end; } void* Context() const { return mIter.Context(); } protected: const SelfType& Self() const { return *static_cast(this); } SelfType& Self() { return *static_cast(this); } const IterType& mIter; uint32_t mIndex; }; /***************************************************************************** * Property iteration *****************************************************************************/ class MOZ_STACK_CLASS PropertyIter : public BaseIter { friend class PropertyIterElem; friend class BaseIterElem; public: PropertyIter(JSContext* cx, JS::Handle object, void* context = nullptr) : BaseIter(cx, object, context), mIds(cx, JS::IdVector(cx)) { if (!JS_Enumerate(cx, object, &mIds)) { JS_ClearPendingException(cx); } } PropertyIter(const PropertyIter& other) : PropertyIter(other.mCx, other.mObject, other.mContext) {} PropertyIter& operator=(const PropertyIter& other) { MOZ_ASSERT(other.mObject == mObject); mCx = other.mCx; mContext = other.mContext; mIds.clear(); if (!JS_Enumerate(mCx, mObject, &mIds)) { JS_ClearPendingException(mCx); } return *this; } int32_t Length() const { return mIds.length(); } protected: JS::Rooted mIds; }; class MOZ_STACK_CLASS PropertyIterElem : public BaseIterElem { friend class BaseIterElem; public: using BaseIterElem::BaseIterElem; PropertyIterElem(const PropertyIterElem& other) : BaseIterElem(other.mIter, other.mIndex) {} jsid Id() { MOZ_ASSERT(mIndex < mIter.mIds.length()); return mIter.mIds[mIndex]; } const nsAString& Name() { if (mName.isNothing()) { mName.emplace(); mName.ref().init(mIter.mCx, Id()); } return mName.ref(); } JSContext* Cx() { return mIter.mCx; } protected: bool GetValue(JS::MutableHandle value) { MOZ_ASSERT(mIndex < Length()); JS::Rooted id(mIter.mCx, Id()); return JS_GetPropertyById(mIter.mCx, mIter.mObject, id, value); } private: Maybe mName; }; /***************************************************************************** * Array iteration *****************************************************************************/ class MOZ_STACK_CLASS ArrayIter : public BaseIter { friend class ArrayIterElem; friend class BaseIterElem; public: ArrayIter(JSContext* cx, JS::Handle object) : BaseIter(cx, object), mLength(0) { bool isArray; if (!JS::IsArrayObject(cx, object, &isArray) || !isArray) { JS_ClearPendingException(cx); return; } if (!JS::GetArrayLength(cx, object, &mLength)) { JS_ClearPendingException(cx); } } uint32_t Length() const { return mLength; } private: uint32_t mLength; }; class MOZ_STACK_CLASS ArrayIterElem : public BaseIterElem { friend class BaseIterElem; public: using BaseIterElem::BaseIterElem; ArrayIterElem(const ArrayIterElem& other) : BaseIterElem(other.mIter, other.mIndex) {} protected: bool GetValue(JS::MutableHandle value) { MOZ_ASSERT(mIndex < Length()); return JS_GetElement(mIter.mCx, mIter.mObject, mIndex, value); } }; } // namespace mozilla #endif // AddonManagerStartup_inlines_h