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