/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * 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 js_Exception_h #define js_Exception_h #include "mozilla/Attributes.h" #include "mozilla/Maybe.h" #include "jstypes.h" #include "js/RootingAPI.h" // JS::{Handle,Rooted} #include "js/TypeDecls.h" #include "js/Value.h" // JS::Value, JS::Handle class JSErrorReport; namespace JS { enum class ExceptionStackBehavior : bool { // Do not capture any stack. DoNotCapture, // Capture the current JS stack when setting the exception. It may be // retrieved by JS::GetPendingExceptionStack. Capture }; } // namespace JS extern JS_PUBLIC_API bool JS_IsExceptionPending(JSContext* cx); extern JS_PUBLIC_API bool JS_IsThrowingOutOfMemory(JSContext* cx); extern JS_PUBLIC_API bool JS_GetPendingException(JSContext* cx, JS::MutableHandleValue vp); extern JS_PUBLIC_API void JS_SetPendingException( JSContext* cx, JS::HandleValue v, JS::ExceptionStackBehavior behavior = JS::ExceptionStackBehavior::Capture); extern JS_PUBLIC_API void JS_ClearPendingException(JSContext* cx); /** * If the given object is an exception object, the exception will have (or be * able to lazily create) an error report struct, and this function will return * the address of that struct. Otherwise, it returns nullptr. The lifetime * of the error report struct that might be returned is the same as the * lifetime of the exception object. */ extern JS_PUBLIC_API JSErrorReport* JS_ErrorFromException(JSContext* cx, JS::HandleObject obj); namespace JS { // When propagating an exception up the call stack, we store the underlying // reason on the JSContext as one of the following enum values. // // TODO: Track uncatchable exceptions explicitly. enum class ExceptionStatus { // No exception status. None, // Used by debugger when forcing an early return from a frame. This uses // exception machinery, but at the right time is turned back into a normal // non-error completion. ForcedReturn, // Throwing a (catchable) exception. Certain well-known exceptions are // explicitly tracked for convenience. Throwing, OutOfMemory, OverRecursed, }; // Returns true if the status is a catchable exception. Formerly this was // indicated by the `JSContext::throwing` flag. static MOZ_ALWAYS_INLINE bool IsCatchableExceptionStatus( ExceptionStatus status) { return status >= ExceptionStatus::Throwing; } // This class encapsulates a (pending) exception and the corresponding optional // SavedFrame stack object captured when the pending exception was set // on the JSContext. This fuzzily correlates with a `throw` statement in JS, // although arbitrary JSAPI consumers or VM code may also set pending exceptions // via `JS_SetPendingException`. // // This is not the same stack as `e.stack` when `e` is an `Error` object. // (That would be JS::ExceptionStackOrNull). class MOZ_STACK_CLASS ExceptionStack { Rooted exception_; Rooted stack_; friend JS_PUBLIC_API bool GetPendingExceptionStack( JSContext* cx, JS::ExceptionStack* exceptionStack); void init(HandleValue exception, HandleObject stack) { exception_ = exception; stack_ = stack; } public: explicit ExceptionStack(JSContext* cx) : exception_(cx), stack_(cx) {} ExceptionStack(JSContext* cx, HandleValue exception, HandleObject stack) : exception_(cx, exception), stack_(cx, stack) {} HandleValue exception() const { return exception_; } // |stack| can be null. HandleObject stack() const { return stack_; } }; /** * Save and later restore the current exception state of a given JSContext. * This is useful for implementing behavior in C++ that's like try/catch * or try/finally in JS. * * Typical usage: * * bool ok = JS::Evaluate(cx, ...); * AutoSaveExceptionState savedExc(cx); * ... cleanup that might re-enter JS ... * return ok; */ class JS_PUBLIC_API AutoSaveExceptionState { private: JSContext* context; ExceptionStatus status; RootedValue exceptionValue; RootedObject exceptionStack; public: /* * Take a snapshot of cx's current exception state. Then clear any current * pending exception in cx. */ explicit AutoSaveExceptionState(JSContext* cx); /* * If neither drop() nor restore() was called, restore the exception * state only if no exception is currently pending on cx. */ ~AutoSaveExceptionState(); /* * Discard any stored exception state. * If this is called, the destructor is a no-op. */ void drop(); /* * Replace cx's exception state with the stored exception state. Then * discard the stored exception state. If this is called, the * destructor is a no-op. */ void restore(); }; // Get the current pending exception value and stack. // This function asserts that there is a pending exception. // If this function returns false, then retrieving the current pending exception // failed and might have been overwritten by a new exception. extern JS_PUBLIC_API bool GetPendingExceptionStack( JSContext* cx, JS::ExceptionStack* exceptionStack); // Similar to GetPendingExceptionStack, but also clears the current // pending exception. extern JS_PUBLIC_API bool StealPendingExceptionStack( JSContext* cx, JS::ExceptionStack* exceptionStack); // Set both the exception value and its associated stack on the context as // the current pending exception. extern JS_PUBLIC_API void SetPendingExceptionStack( JSContext* cx, const JS::ExceptionStack& exceptionStack); /** * If the given object is an exception object (or an unwrappable * cross-compartment wrapper for one), return the stack for that exception, if * any. Will return null if the given object is not an exception object * (including if it's null or a security wrapper that can't be unwrapped) or if * the exception has no stack. */ extern JS_PUBLIC_API JSObject* ExceptionStackOrNull(JS::HandleObject obj); /** * If the given object is an exception object, return the error cause for that * exception, if any, or mozilla::Nothing. */ extern JS_PUBLIC_API mozilla::Maybe GetExceptionCause(JSObject* exc); } // namespace JS #endif // js_Exception_h