michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ michael@0: /* vim: set ts=2 sw=2 et tw=79: */ 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 file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /** michael@0: * A struct for tracking exceptions that need to be thrown to JS. michael@0: */ michael@0: michael@0: #ifndef mozilla_ErrorResult_h michael@0: #define mozilla_ErrorResult_h michael@0: michael@0: #include michael@0: michael@0: #include "js/Value.h" michael@0: #include "nscore.h" michael@0: #include "nsStringGlue.h" michael@0: #include "mozilla/Assertions.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: namespace dom { michael@0: michael@0: enum ErrNum { michael@0: #define MSG_DEF(_name, _argc, _str) \ michael@0: _name, michael@0: #include "mozilla/dom/Errors.msg" michael@0: #undef MSG_DEF michael@0: Err_Limit michael@0: }; michael@0: michael@0: bool michael@0: ThrowErrorMessage(JSContext* aCx, const ErrNum aErrorNumber, ...); michael@0: michael@0: } // namespace dom michael@0: michael@0: class ErrorResult { michael@0: public: michael@0: ErrorResult() { michael@0: mResult = NS_OK; michael@0: michael@0: #ifdef DEBUG michael@0: // ErrorResult is extremely performance-sensitive code, where literally michael@0: // every machine instruction matters. Initialize mMessage only to suppress michael@0: // a debug-only warning from gcc 4.6. michael@0: mMessage = nullptr; michael@0: mMightHaveUnreportedJSException = false; michael@0: #endif michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: ~ErrorResult() { michael@0: MOZ_ASSERT_IF(IsTypeError(), !mMessage); michael@0: MOZ_ASSERT(!mMightHaveUnreportedJSException); michael@0: } michael@0: #endif michael@0: michael@0: void Throw(nsresult rv) { michael@0: MOZ_ASSERT(NS_FAILED(rv), "Please don't try throwing success"); michael@0: MOZ_ASSERT(rv != NS_ERROR_TYPE_ERR, "Use ThrowTypeError()"); michael@0: MOZ_ASSERT(!IsTypeError(), "Don't overwite TypeError"); michael@0: MOZ_ASSERT(rv != NS_ERROR_DOM_JS_EXCEPTION, "Use ThrowJSException()"); michael@0: MOZ_ASSERT(!IsJSException(), "Don't overwrite JS exceptions"); michael@0: MOZ_ASSERT(rv != NS_ERROR_XPC_NOT_ENOUGH_ARGS, "Use ThrowNotEnoughArgsError()"); michael@0: MOZ_ASSERT(!IsNotEnoughArgsError(), "Don't overwrite not enough args error"); michael@0: mResult = rv; michael@0: } michael@0: michael@0: void ThrowTypeError(const dom::ErrNum errorNumber, ...); michael@0: void ReportTypeError(JSContext* cx); michael@0: void ClearMessage(); michael@0: bool IsTypeError() const { return ErrorCode() == NS_ERROR_TYPE_ERR; } michael@0: michael@0: // Facilities for throwing a preexisting JS exception value via this michael@0: // ErrorResult. The contract is that any code which might end up calling michael@0: // ThrowJSException() must call MightThrowJSException() even if no exception michael@0: // is being thrown. Code that would call ReportJSException* or michael@0: // StealJSException as needed must first call WouldReportJSException even if michael@0: // this ErrorResult has not failed. michael@0: // michael@0: // The exn argument to ThrowJSException can be in any compartment. It does michael@0: // not have to be in the compartment of cx. If someone later uses it, they michael@0: // will wrap it into whatever compartment they're working in, as needed. michael@0: void ThrowJSException(JSContext* cx, JS::Handle exn); michael@0: void ReportJSException(JSContext* cx); michael@0: // Used to implement throwing exceptions from the JS implementation of michael@0: // bindings to callers of the binding. michael@0: void ReportJSExceptionFromJSImplementation(JSContext* aCx); michael@0: bool IsJSException() const { return ErrorCode() == NS_ERROR_DOM_JS_EXCEPTION; } michael@0: michael@0: void ThrowNotEnoughArgsError() { mResult = NS_ERROR_XPC_NOT_ENOUGH_ARGS; } michael@0: void ReportNotEnoughArgsError(JSContext* cx, michael@0: const char* ifaceName, michael@0: const char* memberName); michael@0: bool IsNotEnoughArgsError() const { return ErrorCode() == NS_ERROR_XPC_NOT_ENOUGH_ARGS; } michael@0: michael@0: // StealJSException steals the JS Exception from the object. This method must michael@0: // be called only if IsJSException() returns true. This method also resets the michael@0: // ErrorCode() to NS_OK. michael@0: void StealJSException(JSContext* cx, JS::MutableHandle value); michael@0: michael@0: void MOZ_ALWAYS_INLINE MightThrowJSException() michael@0: { michael@0: #ifdef DEBUG michael@0: mMightHaveUnreportedJSException = true; michael@0: #endif michael@0: } michael@0: void MOZ_ALWAYS_INLINE WouldReportJSException() michael@0: { michael@0: #ifdef DEBUG michael@0: mMightHaveUnreportedJSException = false; michael@0: #endif michael@0: } michael@0: michael@0: // In the future, we can add overloads of Throw that take more michael@0: // interesting things, like strings or DOM exception types or michael@0: // something if desired. michael@0: michael@0: // Backwards-compat to make conversion simpler. We don't call michael@0: // Throw() here because people can easily pass success codes to michael@0: // this. michael@0: void operator=(nsresult rv) { michael@0: MOZ_ASSERT(rv != NS_ERROR_TYPE_ERR, "Use ThrowTypeError()"); michael@0: MOZ_ASSERT(!IsTypeError(), "Don't overwite TypeError"); michael@0: MOZ_ASSERT(rv != NS_ERROR_DOM_JS_EXCEPTION, "Use ThrowJSException()"); michael@0: MOZ_ASSERT(!IsJSException(), "Don't overwrite JS exceptions"); michael@0: MOZ_ASSERT(rv != NS_ERROR_XPC_NOT_ENOUGH_ARGS, "Use ThrowNotEnoughArgsError()"); michael@0: MOZ_ASSERT(!IsNotEnoughArgsError(), "Don't overwrite not enough args error"); michael@0: mResult = rv; michael@0: } michael@0: michael@0: bool Failed() const { michael@0: return NS_FAILED(mResult); michael@0: } michael@0: michael@0: nsresult ErrorCode() const { michael@0: return mResult; michael@0: } michael@0: michael@0: private: michael@0: nsresult mResult; michael@0: struct Message; michael@0: // mMessage is set by ThrowTypeError and cleared (and deallocatd) by michael@0: // ReportTypeError. michael@0: // mJSException is set (and rooted) by ThrowJSException and unrooted michael@0: // by ReportJSException. michael@0: union { michael@0: Message* mMessage; // valid when IsTypeError() michael@0: JS::Value mJSException; // valid when IsJSException() michael@0: }; michael@0: michael@0: #ifdef DEBUG michael@0: // Used to keep track of codepaths that might throw JS exceptions, michael@0: // for assertion purposes. michael@0: bool mMightHaveUnreportedJSException; michael@0: #endif michael@0: michael@0: // Not to be implemented, to make sure people always pass this by michael@0: // reference, not by value. michael@0: ErrorResult(const ErrorResult&) MOZ_DELETE; michael@0: }; michael@0: michael@0: /****************************************************************************** michael@0: ** Macros for checking results michael@0: ******************************************************************************/ michael@0: michael@0: #define ENSURE_SUCCESS(res, ret) \ michael@0: do { \ michael@0: if (res.Failed()) { \ michael@0: nsCString msg; \ michael@0: msg.AppendPrintf("ENSURE_SUCCESS(%s, %s) failed with " \ michael@0: "result 0x%X", #res, #ret, res.ErrorCode()); \ michael@0: NS_WARNING(msg.get()); \ michael@0: return ret; \ michael@0: } \ michael@0: } while(0) michael@0: michael@0: #define ENSURE_SUCCESS_VOID(res) \ michael@0: do { \ michael@0: if (res.Failed()) { \ michael@0: nsCString msg; \ michael@0: msg.AppendPrintf("ENSURE_SUCCESS_VOID(%s) failed with " \ michael@0: "result 0x%X", #res, res.ErrorCode()); \ michael@0: NS_WARNING(msg.get()); \ michael@0: return; \ michael@0: } \ michael@0: } while(0) michael@0: michael@0: } // namespace mozilla michael@0: michael@0: #endif /* mozilla_ErrorResult_h */