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