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 /* 8 * [SMDOC] JS::Result 9 * 10 * `Result` is used as the return type of many SpiderMonkey functions that 11 * can either succeed or fail. See "/mfbt/Result.h". 12 * 13 * 14 * ## Which return type to use 15 * 16 * `Result` is for return values. Obviously, if you're writing a function that 17 * can't fail, don't use Result. Otherwise: 18 * 19 * JS::Result<> - function can fail, doesn't return anything on success 20 * (defaults to `JS::Result<JS::Ok, JS::Error>`) 21 * JS::Result<JS::Ok, JS::OOM> - like JS::Result<>, but fails only on OOM 22 * 23 * JS::Result<Data> - function can fail, returns Data on success 24 * JS::Result<Data, JS::OOM> - returns Data, fails only on OOM 25 * 26 * mozilla::GenericErrorResult<JS::Error> - always fails 27 * 28 * That last type is like a Result with no success type. It's used for 29 * functions like `js::ReportNotFunction` that always return an error 30 * result. `GenericErrorResult<E>` implicitly converts to `Result<V, E>`, 31 * regardless of V. 32 * 33 * 34 * ## Checking Results when your return type is Result 35 * 36 * When you call a function that returns a `Result`, use the `MOZ_TRY` macro to 37 * check for errors: 38 * 39 * MOZ_TRY(DefenestrateObject(cx, obj)); 40 * 41 * If `DefenestrateObject` returns a success result, `MOZ_TRY` is done, and 42 * control flows to the next statement. If `DefenestrateObject` returns an 43 * error result, `MOZ_TRY` will immediately return it, propagating the error to 44 * your caller. It's kind of like exceptions, but more explicit -- you can see 45 * in the code exactly where errors can happen. 46 * 47 * You can do a tail call instead of using `MOZ_TRY`: 48 * 49 * return DefenestrateObject(cx, obj); 50 * 51 * Indicate success with `return Ok();`. 52 * 53 * If the function returns a value on success, use `MOZ_TRY_VAR` to get it: 54 * 55 * RootedValue thrug(cx); 56 * MOZ_TRY_VAR(thrug, GetObjectThrug(cx, obj)); 57 * 58 * This behaves the same as `MOZ_TRY` on error. On success, the success 59 * value of `GetObjectThrug(cx, obj)` is assigned to the variable `thrug`. 60 * 61 * 62 * ## Checking Results when your return type is not Result 63 * 64 * This header defines alternatives to MOZ_TRY and MOZ_TRY_VAR for when you 65 * need to call a `Result` function from a function that uses false or nullptr 66 * to indicate errors: 67 * 68 * JS_TRY_OR_RETURN_FALSE(cx, DefenestrateObject(cx, obj)); 69 * JS_TRY_VAR_OR_RETURN_FALSE(cx, v, GetObjectThrug(cx, obj)); 70 * 71 * JS_TRY_OR_RETURN_NULL(cx, DefenestrateObject(cx, obj)); 72 * JS_TRY_VAR_OR_RETURN_NULL(cx, v, GetObjectThrug(cx, obj)); 73 * 74 * When TRY is not what you want, because you need to do some cleanup or 75 * recovery on error, use this idiom: 76 * 77 * if (!cx->resultToBool(expr_that_is_a_Result)) { 78 * ... your recovery code here ... 79 * } 80 * 81 * In place of a tail call, you can use one of these methods: 82 * 83 * return cx->resultToBool(expr); // false on error 84 * return cx->resultToPtr(expr); // null on error 85 * 86 * Once we are using `Result` everywhere, including in public APIs, all of 87 * these will go away. 88 * 89 * 90 * ## GC safety 91 * 92 * When a function returns a `JS::Result<JSObject*>`, it is the program's 93 * responsibility to check for errors and root the object before continuing: 94 * 95 * RootedObject wrapper(cx); 96 * MOZ_TRY_VAR(wrapper, Enwrapify(cx, thing)); 97 * 98 * This is ideal. On error, there is no object to root; on success, the 99 * assignment to wrapper roots it. GC safety is ensured. 100 * 101 * `Result` has methods .isOk(), .isErr(), .unwrap(), and .unwrapErr(), but if 102 * you're actually using them, it's possible to create a GC hazard. The static 103 * analysis will catch it if so, but that's hardly convenient. So try to stick 104 * to the idioms shown above. 105 * 106 * 107 * ## Future directions 108 * 109 * At present, JS::Error and JS::OOM are empty structs. The plan is to make them 110 * GC things that contain the actual error information (including the exception 111 * value and a saved stack). 112 * 113 * The long-term plan is to remove JS_IsExceptionPending and 114 * JS_GetPendingException in favor of JS::Error. Exception state will no longer 115 * exist. 116 */ 117 118 #ifndef js_Result_h 119 #define js_Result_h 120 121 #include "mozilla/Result.h" 122 123 /** 124 * Evaluate the boolean expression expr. If it's true, do nothing. 125 * If it's false, return an error result. 126 */ 127 #define JS_TRY_BOOL_TO_RESULT(cx, expr) \ 128 do { \ 129 bool ok_ = (expr); \ 130 if (!ok_) return (cx)->boolToResult(ok_); \ 131 } while (0) 132 133 /** 134 * JS_TRY_OR_RETURN_FALSE(cx, expr) runs expr to compute a Result value. 135 * On success, nothing happens; on error, it returns false immediately. 136 * 137 * Implementation note: this involves cx because this may eventually 138 * do the work of setting a pending exception or reporting OOM. 139 */ 140 #define JS_TRY_OR_RETURN_FALSE(cx, expr) \ 141 do { \ 142 auto tmpResult_ = (expr); \ 143 if (tmpResult_.isErr()) return (cx)->resultToBool(tmpResult_); \ 144 } while (0) 145 146 /** 147 * Like JS_TRY_OR_RETURN_FALSE, but returning nullptr on error, 148 * rather than false. 149 */ 150 #define JS_TRY_OR_RETURN_NULL(cx, expr) \ 151 do { \ 152 auto tmpResult_ = (expr); \ 153 if (tmpResult_.isErr()) { \ 154 MOZ_ALWAYS_FALSE((cx)->resultToBool(tmpResult_)); \ 155 return nullptr; \ 156 } \ 157 } while (0) 158 159 #define JS_TRY_VAR_OR_RETURN_FALSE(cx, target, expr) \ 160 do { \ 161 auto tmpResult_ = (expr); \ 162 if (tmpResult_.isErr()) return (cx)->resultToBool(tmpResult_); \ 163 (target) = tmpResult_.unwrap(); \ 164 } while (0) 165 166 #define JS_TRY_VAR_OR_RETURN_NULL(cx, target, expr) \ 167 do { \ 168 auto tmpResult_ = (expr); \ 169 if (tmpResult_.isErr()) { \ 170 MOZ_ALWAYS_FALSE((cx)->resultToBool(tmpResult_)); \ 171 return nullptr; \ 172 } \ 173 (target) = tmpResult_.unwrap(); \ 174 } while (0) 175 176 namespace JS { 177 178 using mozilla::Ok; 179 180 template <typename T> 181 struct UnusedZero; 182 183 /** 184 * Type representing a JS error or exception. At the moment this only 185 * "represents" an error in a rather abstract way. 186 */ 187 struct Error { 188 // Since we claim UnusedZero<Error>::value and HasFreeLSB<Error>::value == 189 // true below, we must only use positive even enum values. 190 enum class ErrorKind : uintptr_t { Unspecified = 2, OOM = 4 }; 191 192 const ErrorKind kind = ErrorKind::Unspecified; 193 194 Error() = default; 195 196 protected: 197 friend struct UnusedZero<Error>; 198 199 constexpr MOZ_IMPLICIT Error(ErrorKind aKind) : kind(aKind) {} 200 }; 201 202 struct OOM : Error { 203 constexpr OOM() : Error(ErrorKind::OOM) {} 204 205 protected: 206 friend struct UnusedZero<OOM>; 207 208 using Error::Error; 209 }; 210 211 template <typename T> 212 struct UnusedZero { 213 using StorageType = std::underlying_type_t<Error::ErrorKind>; 214 215 static constexpr bool value = true; 216 static constexpr StorageType nullValue = 0; 217 218 static constexpr void AssertValid(StorageType aValue) {} 219 static constexpr T Inspect(const StorageType& aValue) { 220 return static_cast<Error::ErrorKind>(aValue); 221 } 222 static constexpr T Unwrap(StorageType aValue) { 223 return static_cast<Error::ErrorKind>(aValue); 224 } 225 static constexpr StorageType Store(T aValue) { 226 return static_cast<StorageType>(aValue.kind); 227 } 228 }; 229 230 } // namespace JS 231 232 namespace mozilla::detail { 233 234 template <> 235 struct UnusedZero<JS::Error> : JS::UnusedZero<JS::Error> {}; 236 237 template <> 238 struct UnusedZero<JS::OOM> : JS::UnusedZero<JS::OOM> {}; 239 240 template <> 241 struct HasFreeLSB<JS::Error> { 242 static const bool value = true; 243 }; 244 245 template <> 246 struct HasFreeLSB<JS::OOM> { 247 static const bool value = true; 248 }; 249 } // namespace mozilla::detail 250 251 namespace JS { 252 253 /** 254 * `Result` is intended to be the return type of JSAPI calls and internal 255 * functions that can run JS code or allocate memory from the JS GC heap. Such 256 * functions can: 257 * 258 * - succeed, possibly returning a value; 259 * 260 * - fail with a JS exception (out-of-memory falls in this category); or 261 * 262 * - fail because JS execution was terminated, which occurs when e.g. a 263 * user kills a script from the "slow script" UI. This is also how we 264 * unwind the stack when the debugger forces the current function to 265 * return. JS `catch` blocks can't catch this kind of failure, 266 * and JS `finally` blocks don't execute. 267 */ 268 template <typename V = Ok, typename E = Error> 269 using Result = mozilla::Result<V, E>; 270 271 static_assert(sizeof(Result<>) == sizeof(uintptr_t), 272 "Result<> should be pointer-sized"); 273 274 static_assert(sizeof(Result<int*, Error>) == sizeof(uintptr_t), 275 "Result<V*, Error> should be pointer-sized"); 276 277 } // namespace JS 278 279 #endif // js_Result_h 280