tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Exception.h (8119B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef js_Exception_h
      8 #define js_Exception_h
      9 
     10 #include "mozilla/Attributes.h"
     11 #include "mozilla/Maybe.h"
     12 
     13 #include "jstypes.h"
     14 
     15 #include "js/RootingAPI.h"  // JS::{Handle,Rooted}
     16 #include "js/TypeDecls.h"
     17 #include "js/Value.h"  // JS::Value, JS::Handle<JS::Value>
     18 
     19 class JSErrorReport;
     20 
     21 namespace JS {
     22 enum class ExceptionStackBehavior : bool {
     23  // Do not capture any stack.
     24  DoNotCapture,
     25 
     26  // Capture the current JS stack when setting the exception. It may be
     27  // retrieved by JS::GetPendingExceptionStack.
     28  Capture
     29 };
     30 }  // namespace JS
     31 
     32 extern JS_PUBLIC_API bool JS_IsExceptionPending(JSContext* cx);
     33 
     34 // [SMDOC] Out Of Memory (OOM) Handling
     35 //
     36 // Many functions in SpiderMonkey can throw exceptions, and sometimes
     37 // the exception thrown is out of memory. Unlike other exceptions,
     38 // this is not an object, but rather the literal string "out of memory".
     39 //
     40 // **Out of Memory handling in SpiderMonkey is best-effort!**
     41 //
     42 // While the developers of SpiderMonkey do attempt to convert various scenarios
     43 // into OutOfMemory calls, such that embedders can attempt to do some sort of
     44 // recovery, we do not guarantee this in all cases.
     45 //
     46 // There are some places where attempting to convey OOM is challenging
     47 // or where it would leave the engine in a state with invariants
     48 // no longer holding. In those cases **the process will crash**.
     49 //
     50 // An example, though not comprehensive, signal of this to a curious
     51 // reader would be AutoEnterOOMUnsafeRegion, which flags various
     52 // places developers have indicated that crashing is better than
     53 // throwing OOM.
     54 //
     55 // Currently we endeavour to always throw out-of-memory when we
     56 // encounter GC heap limits. We also will sometimes throw OOM
     57 // exceptions for things which are not really OOM: For example
     58 // our executable code limits.
     59 //
     60 // It is important to not rely on OOM generation in SpiderMonkey
     61 // as your only reliability measure, as it is not guaranteed.
     62 
     63 // Check for pending out of memory exception.
     64 extern JS_PUBLIC_API bool JS_IsThrowingOutOfMemory(JSContext* cx);
     65 
     66 // Try and get the pending exception. This can return false
     67 // if there is no pending exception -or- if there is a problem
     68 // attempting to produce the exception (for example if wrapping
     69 // the exception fails.)
     70 extern JS_PUBLIC_API bool JS_GetPendingException(JSContext* cx,
     71                                                 JS::MutableHandleValue vp);
     72 
     73 extern JS_PUBLIC_API void JS_SetPendingException(
     74    JSContext* cx, JS::HandleValue v,
     75    JS::ExceptionStackBehavior behavior = JS::ExceptionStackBehavior::Capture);
     76 
     77 extern JS_PUBLIC_API void JS_ClearPendingException(JSContext* cx);
     78 
     79 /**
     80 * If the given object is an exception object, the exception will have (or be
     81 * able to lazily create) an error report struct, and this function will return
     82 * the address of that struct.  Otherwise, it returns nullptr. The lifetime
     83 * of the error report struct that might be returned is the same as the
     84 * lifetime of the exception object.
     85 */
     86 extern JS_PUBLIC_API JSErrorReport* JS_ErrorFromException(JSContext* cx,
     87                                                          JS::HandleObject obj);
     88 
     89 namespace JS {
     90 
     91 // When propagating an exception up the call stack, we store the underlying
     92 // reason on the JSContext as one of the following enum values.
     93 //
     94 // TODO: Track uncatchable exceptions explicitly.
     95 enum class ExceptionStatus {
     96  // No exception status.
     97  None,
     98 
     99  // Used by debugger when forcing an early return from a frame. This uses
    100  // exception machinery, but at the right time is turned back into a normal
    101  // non-error completion.
    102  ForcedReturn,
    103 
    104  // Throwing a (catchable) exception. Certain well-known exceptions are
    105  // explicitly tracked for convenience.
    106  Throwing,
    107  OutOfMemory,
    108  OverRecursed,
    109 };
    110 
    111 // Returns true if the status is a catchable exception. Formerly this was
    112 // indicated by the `JSContext::throwing` flag.
    113 static MOZ_ALWAYS_INLINE bool IsCatchableExceptionStatus(
    114    ExceptionStatus status) {
    115  return status >= ExceptionStatus::Throwing;
    116 }
    117 
    118 // This class encapsulates a (pending) exception and the corresponding optional
    119 // SavedFrame stack object captured when the pending exception was set
    120 // on the JSContext. This fuzzily correlates with a `throw` statement in JS,
    121 // although arbitrary JSAPI consumers or VM code may also set pending exceptions
    122 // via `JS_SetPendingException`.
    123 //
    124 // This is not the same stack as `e.stack` when `e` is an `Error` object.
    125 // (That would be JS::ExceptionStackOrNull).
    126 class MOZ_STACK_CLASS ExceptionStack {
    127  Rooted<Value> exception_;
    128  Rooted<JSObject*> stack_;
    129 
    130  friend JS_PUBLIC_API bool GetPendingExceptionStack(
    131      JSContext* cx, JS::ExceptionStack* exceptionStack);
    132 
    133  void init(HandleValue exception, HandleObject stack) {
    134    exception_ = exception;
    135    stack_ = stack;
    136  }
    137 
    138 public:
    139  explicit ExceptionStack(JSContext* cx) : exception_(cx), stack_(cx) {}
    140 
    141  ExceptionStack(JSContext* cx, HandleValue exception, HandleObject stack)
    142      : exception_(cx, exception), stack_(cx, stack) {}
    143 
    144  HandleValue exception() const { return exception_; }
    145 
    146  // |stack| can be null.
    147  HandleObject stack() const { return stack_; }
    148 };
    149 
    150 /**
    151 * Save and later restore the current exception state of a given JSContext.
    152 * This is useful for implementing behavior in C++ that's like try/catch
    153 * or try/finally in JS.
    154 *
    155 * Typical usage:
    156 *
    157 *     bool ok = JS::Evaluate(cx, ...);
    158 *     AutoSaveExceptionState savedExc(cx);
    159 *     ... cleanup that might re-enter JS ...
    160 *     return ok;
    161 */
    162 class JS_PUBLIC_API AutoSaveExceptionState {
    163 private:
    164  JSContext* context;
    165  ExceptionStatus status;
    166  RootedValue exceptionValue;
    167  RootedObject exceptionStack;
    168 
    169 public:
    170  /*
    171   * Take a snapshot of cx's current exception state. Then clear any current
    172   * pending exception in cx.
    173   */
    174  explicit AutoSaveExceptionState(JSContext* cx);
    175 
    176  /*
    177   * If neither drop() nor restore() was called, restore the exception
    178   * state only if no exception is currently pending on cx.
    179   */
    180  ~AutoSaveExceptionState();
    181 
    182  /*
    183   * Discard any stored exception state.
    184   * If this is called, the destructor is a no-op.
    185   */
    186  void drop();
    187 
    188  /*
    189   * Replace cx's exception state with the stored exception state. Then
    190   * discard the stored exception state. If this is called, the
    191   * destructor is a no-op.
    192   */
    193  void restore();
    194 };
    195 
    196 // Get the current pending exception value and stack.
    197 // This function asserts that there is a pending exception.
    198 // If this function returns false, then retrieving the current pending exception
    199 // failed and might have been overwritten by a new exception.
    200 extern JS_PUBLIC_API bool GetPendingExceptionStack(
    201    JSContext* cx, JS::ExceptionStack* exceptionStack);
    202 
    203 // Similar to GetPendingExceptionStack, but also clears the current
    204 // pending exception.
    205 extern JS_PUBLIC_API bool StealPendingExceptionStack(
    206    JSContext* cx, JS::ExceptionStack* exceptionStack);
    207 
    208 // Set both the exception value and its associated stack on the context as
    209 // the current pending exception.
    210 extern JS_PUBLIC_API void SetPendingExceptionStack(
    211    JSContext* cx, const JS::ExceptionStack& exceptionStack);
    212 
    213 /**
    214 * If the given object is an exception object (or an unwrappable
    215 * cross-compartment wrapper for one), return the stack for that exception, if
    216 * any.  Will return null if the given object is not an exception object
    217 * (including if it's null or a security wrapper that can't be unwrapped) or if
    218 * the exception has no stack.
    219 */
    220 extern JS_PUBLIC_API JSObject* ExceptionStackOrNull(JS::HandleObject obj);
    221 
    222 /**
    223 * If the given object is an exception object, return the error cause for that
    224 * exception, if any, or mozilla::Nothing.
    225 */
    226 extern JS_PUBLIC_API mozilla::Maybe<JS::Value> GetExceptionCause(JSObject* exc);
    227 
    228 }  // namespace JS
    229 
    230 #endif  // js_Exception_h