/src/hermes/include/hermes/VM/JSError.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) Meta Platforms, Inc. and affiliates. |
3 | | * |
4 | | * This source code is licensed under the MIT license found in the |
5 | | * LICENSE file in the root directory of this source tree. |
6 | | */ |
7 | | |
8 | | #ifndef HERMES_VM_JSERROR_H |
9 | | #define HERMES_VM_JSERROR_H |
10 | | |
11 | | #include "hermes/VM/JSObject.h" |
12 | | #include "hermes/VM/NativeArgs.h" |
13 | | #include "hermes/VM/SmallXString.h" |
14 | | |
15 | | namespace hermes { |
16 | | namespace vm { |
17 | | |
18 | | /// StackTraceInfo holds information of an entry in the stacktrace upon |
19 | | /// exceptions. We only need to store the CodeBlock and bytecode offset |
20 | | /// to obtain the full function name/file name/position later when we |
21 | | /// need to generate the stacktrace string. |
22 | | /// We store the domains for the CodeBlocks within the JSError to ensure that |
23 | | /// the CodeBlocks never get freed, and thus every StackTraceInfo is still |
24 | | /// valid. |
25 | | struct StackTraceInfo { |
26 | | /// The code block of the function. |
27 | | CodeBlock *codeBlock; |
28 | | |
29 | | /// The bytecode offset where exception was thrown. |
30 | | uint32_t bytecodeOffset; |
31 | | |
32 | | StackTraceInfo(CodeBlock *codeBlock, uint32_t bytecodeOffset) |
33 | 85 | : codeBlock(codeBlock), bytecodeOffset(bytecodeOffset) {} |
34 | | |
35 | | StackTraceInfo(const StackTraceInfo &) = default; |
36 | | |
37 | | StackTraceInfo(StackTraceInfo &&) = default; |
38 | | }; |
39 | | using StackTrace = std::vector<StackTraceInfo>; |
40 | | using StackTracePtr = std::unique_ptr<StackTrace>; |
41 | | |
42 | | /// Error Object. |
43 | | class JSError final : public JSObject { |
44 | | public: |
45 | | using Super = JSObject; |
46 | | static const ObjectVTable vt; |
47 | | |
48 | 82 | static constexpr CellKind getCellKind() { |
49 | 82 | return CellKind::JSErrorKind; |
50 | 82 | } |
51 | 284 | static bool classof(const GCCell *cell) { |
52 | 284 | return cell->getKind() == CellKind::JSErrorKind; |
53 | 284 | } |
54 | | |
55 | | /// Create an Error Object. |
56 | | static PseudoHandle<JSError> create( |
57 | | Runtime &runtime, |
58 | | Handle<JSObject> prototype); |
59 | | /// Create an uncatchable Error Object. If this object is thrown, no catch |
60 | | /// handlers or finally handlers are called. |
61 | | /// NOTE: This should be used only in very specific circumstances where it is |
62 | | /// impossible or undesirable to continue running the VM. The error should be |
63 | | /// considered a fatal abort, but one that cleans up internal VM resources. |
64 | | static PseudoHandle<JSError> createUncatchable( |
65 | | Runtime &runtime, |
66 | | Handle<JSObject> prototype); |
67 | | |
68 | | /// If the stack trace is not set, attempt to record it by walking the runtime |
69 | | /// stack. If the top call frame indicates a JS callee, but the codeBlock and |
70 | | /// ip are not supplied, return without doing anything. This handles the case |
71 | | /// when an exception is thrown from within the current code block. |
72 | | /// |
73 | | /// \param skipTopFrame don't record the topmost frame. This is used when |
74 | | /// we want to skip the Error() constructor itself. |
75 | | /// \param codeBlock optional current CodeBlock. |
76 | | /// \param ip if \c codeBlock is not \c nullptr, the instruction in the |
77 | | /// current CodeBlock. |
78 | | static ExecutionStatus recordStackTrace( |
79 | | Handle<JSError> selfHandle, |
80 | | Runtime &runtime, |
81 | | bool skipTopFrame = false, |
82 | | CodeBlock *codeBlock = nullptr, |
83 | | const Inst *ip = nullptr); |
84 | | |
85 | | /// Define the stack setter and getter, for later stack trace creation. |
86 | | /// May be used on JSError instances, or on any JSObject that has a |
87 | | /// \c CapturedError property. |
88 | | static ExecutionStatus setupStack( |
89 | | Handle<JSObject> selfHandle, |
90 | | Runtime &runtime); |
91 | | |
92 | | /// Implements steps 3 through 9 for ES2023 20.5.3.4 Error.prototype.toString. |
93 | | static CallResult<Handle<StringPrimitive>> toString( |
94 | | Handle<JSObject> O, |
95 | | Runtime &runtime); |
96 | | |
97 | | /// Set the message property. |
98 | | static ExecutionStatus |
99 | | setMessage(Handle<JSError> selfHandle, Runtime &runtime, Handle<> message); |
100 | | |
101 | | /// \return a pointer to the stack trace, or NULL if the stack trace has been |
102 | | /// cleared or not been set. |
103 | 41 | const StackTrace *getStackTrace() const { |
104 | 41 | return stacktrace_.get(); |
105 | 41 | } |
106 | | |
107 | 41 | bool catchable() const { |
108 | 41 | return catchable_; |
109 | 41 | } |
110 | | |
111 | | /// When called, construct the stacktrace string based on the value of |
112 | | /// stacktrace_, and reset the stack property to the stacktrace string. |
113 | | friend CallResult<HermesValue> |
114 | | errorStackGetter(void *, Runtime &runtime, NativeArgs args); |
115 | | |
116 | | /// This is called when someone manually set the stack property to |
117 | | /// an error object, which should happen rarely. It destroys the |
118 | | /// stack access and replace it with a regular property. |
119 | | friend CallResult<HermesValue> |
120 | | errorStackSetter(void *, Runtime &runtime, NativeArgs args); |
121 | | |
122 | | /// Pop frames from the stack trace until we encounter a frame attributed to |
123 | | /// \p callable, and pop that frame too. No frames are skipped if a matching |
124 | | /// frame isn't found. |
125 | | /// \pre \p selfHandle's stacktrace_ is non-null. |
126 | | static void popFramesUntilInclusive( |
127 | | Runtime &runtime, |
128 | | Handle<JSError> selfHandle, |
129 | | Handle<Callable> callableHandle); |
130 | | |
131 | | /// Given a codeblock and opcode offset, \returns the debug information. |
132 | | static OptValue<hbc::DebugSourceLocation> getDebugInfo( |
133 | | CodeBlock *codeBlock, |
134 | | uint32_t bytecodeOffset); |
135 | | |
136 | | /// \return the name of the function at \p index. |
137 | | static Handle<StringPrimitive> getFunctionNameAtIndex( |
138 | | Runtime &runtime, |
139 | | Handle<JSError> selfHandle, |
140 | | size_t index); |
141 | | |
142 | | JSError( |
143 | | Runtime &runtime, |
144 | | Handle<JSObject> parent, |
145 | | Handle<HiddenClass> clazz, |
146 | | bool catchable) |
147 | 41 | : JSObject(runtime, *parent, *clazz), catchable_{catchable} {} |
148 | | |
149 | | private: |
150 | | friend void JSErrorBuildMeta(const GCCell *cell, Metadata::Builder &mb); |
151 | | static void _finalizeImpl(GCCell *cell, GC &gc); |
152 | | static size_t _mallocSizeImpl(GCCell *cell); |
153 | | |
154 | | static PseudoHandle<JSError> |
155 | | create(Runtime &runtime, Handle<JSObject> prototype, bool catchable); |
156 | | |
157 | | /// A pointer to the stack trace, or nullptr if it has not been set. |
158 | | StackTracePtr stacktrace_; |
159 | | |
160 | | /// The index of the stack frame to start from when converting the stack |
161 | | /// trace to a string or otherwise exposing it to user code. |
162 | | /// This can only be changed while stacktrace_ is non-null. |
163 | | size_t firstExposedFrameIndex_{0}; |
164 | | |
165 | | /// A list of Domains which are referenced by the stacktrace_. |
166 | | GCPointer<ArrayStorageSmall> domains_; |
167 | | |
168 | | /// If not null, an array of function names as the 'name' property of the |
169 | | /// Callables. This is parallel to the stack trace array. |
170 | | GCPointer<PropStorage> funcNames_; |
171 | | |
172 | | /// If true, JS catch and finally blocks will be run after this error is |
173 | | /// thrown. Else, there will be no more JS executed after this error is |
174 | | /// thrown. This is most useful in scenarios where an abrupt abort is desired, |
175 | | /// or when the VM is not in a state where it can execute JS usefully, for |
176 | | /// example if an OOM has occurred. |
177 | | bool catchable_{true}; |
178 | | |
179 | | /// Construct the stacktrace string, append to \p stack. |
180 | | /// If the construction of the stack throws an uncatchable error, this |
181 | | /// function returns prematurely. |
182 | | /// |
183 | | /// \param selfHandle supplies the call stack for the trace. |
184 | | /// \param targetHandle supplies the error name and message for the trace. |
185 | | /// |
186 | | /// In the `(new Error).stack` case, selfHandle and targetHandle will both |
187 | | /// refer to the same object. |
188 | | /// In the `target = {}; Error.captureErrorStack(target); target.stack` case, |
189 | | /// selfHandle will be the value of `target`'s [[CapturedError]] slot. |
190 | | static ExecutionStatus constructStackTraceString_RJS( |
191 | | Runtime &runtime, |
192 | | Handle<JSError> selfHandle, |
193 | | Handle<JSObject> targetHandle, |
194 | | SmallU16String<32> &stack); |
195 | | |
196 | | /// Construct the callSites array for Error.prepareStackTrace. |
197 | | static CallResult<HermesValue> constructCallSitesArray( |
198 | | Runtime &runtime, |
199 | | Handle<JSError> selfHandle); |
200 | | |
201 | | /// Append the name of the function at \p index to the given \p str. |
202 | | /// \return true on success, false if the name was missing, invalid or empty. |
203 | | static bool appendFunctionNameAtIndex( |
204 | | Runtime &runtime, |
205 | | Handle<JSError> selfHandle, |
206 | | size_t index, |
207 | | llvh::SmallVectorImpl<char16_t> &str); |
208 | | }; |
209 | | |
210 | | } // namespace vm |
211 | | } // namespace hermes |
212 | | #endif |