michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef __NSAUTOJSVALHOLDER_H__ michael@0: #define __NSAUTOJSVALHOLDER_H__ michael@0: michael@0: #include "nsDebug.h" michael@0: #include "jsapi.h" michael@0: michael@0: /** michael@0: * Simple class that looks and acts like a JS::Value except that it unroots michael@0: * itself automatically if Root() is ever called. Designed to be rooted on the michael@0: * context or runtime (but not both!). michael@0: */ michael@0: class nsAutoJSValHolder michael@0: { michael@0: public: michael@0: nsAutoJSValHolder() michael@0: : mVal(JSVAL_NULL), mRt(nullptr) michael@0: { michael@0: // nothing to do michael@0: } michael@0: michael@0: /** michael@0: * Always release on destruction. michael@0: */ michael@0: virtual ~nsAutoJSValHolder() { michael@0: Release(); michael@0: } michael@0: michael@0: nsAutoJSValHolder(const nsAutoJSValHolder& aOther) michael@0: : mVal(JSVAL_NULL), mRt(nullptr) michael@0: { michael@0: *this = aOther; michael@0: } michael@0: michael@0: nsAutoJSValHolder& operator=(const nsAutoJSValHolder& aOther) { michael@0: if (this != &aOther) { michael@0: if (aOther.IsHeld()) { michael@0: // XXX No error handling here... michael@0: this->Hold(aOther.mRt); michael@0: } michael@0: else { michael@0: this->Release(); michael@0: } michael@0: *this = static_cast(aOther); michael@0: } michael@0: return *this; michael@0: } michael@0: michael@0: /** michael@0: * Hold by rooting on the context's runtime. michael@0: */ michael@0: bool Hold(JSContext* aCx) { michael@0: return Hold(JS_GetRuntime(aCx)); michael@0: } michael@0: michael@0: /** michael@0: * Hold by rooting on the runtime. michael@0: * Note that mVal may be JSVAL_NULL, which is not a problem. michael@0: */ michael@0: bool Hold(JSRuntime* aRt) { michael@0: MOZ_ASSERT_IF(mRt, mRt == aRt); michael@0: michael@0: if (!mRt && JS::AddNamedValueRootRT(aRt, &mVal, "nsAutoJSValHolder")) { michael@0: mRt = aRt; michael@0: } michael@0: michael@0: return !!mRt; michael@0: } michael@0: michael@0: /** michael@0: * Manually release, nullifying mVal, and mRt, but returning michael@0: * the original JS::Value. michael@0: */ michael@0: JS::Value Release() { michael@0: JS::Value oldval = mVal; michael@0: michael@0: if (mRt) { michael@0: JS::RemoveValueRootRT(mRt, &mVal); // infallible michael@0: mRt = nullptr; michael@0: } michael@0: michael@0: mVal = JSVAL_NULL; michael@0: michael@0: return oldval; michael@0: } michael@0: michael@0: /** michael@0: * Determine if Hold has been called. michael@0: */ michael@0: bool IsHeld() const { michael@0: return !!mRt; michael@0: } michael@0: michael@0: /** michael@0: * Explicit JSObject* conversion. michael@0: */ michael@0: JSObject* ToJSObject() const { michael@0: return mVal.isObject() michael@0: ? &mVal.toObject() michael@0: : nullptr; michael@0: } michael@0: michael@0: /** michael@0: * Pretend to be a JS::Value. michael@0: */ michael@0: operator JS::Value() const { return mVal; } michael@0: JS::Value get() const { return mVal; } michael@0: michael@0: nsAutoJSValHolder &operator=(JSObject* aOther) { michael@0: return *this = OBJECT_TO_JSVAL(aOther); michael@0: } michael@0: michael@0: nsAutoJSValHolder &operator=(JS::Value aOther) { michael@0: #ifdef DEBUG michael@0: if (JSVAL_IS_GCTHING(aOther) && !JSVAL_IS_NULL(aOther)) { michael@0: MOZ_ASSERT(IsHeld(), "Not rooted!"); michael@0: } michael@0: #endif michael@0: mVal = aOther; michael@0: return *this; michael@0: } michael@0: michael@0: private: michael@0: JS::Heap mVal; michael@0: JSRuntime* mRt; michael@0: }; michael@0: michael@0: #endif /* __NSAUTOJSVALHOLDER_H__ */