Qore Programming Language 1.19.1
Loading...
Searching...
No Matches
QoreException.h
1/* -*- mode: c++; indent-tabs-mode: nil -*- */
2/*
3 QoreException.h
4
5 Qore programming language exception handling support
6
7 Copyright (C) 2003 - 2023 Qore Technologies, s.r.o.
8
9 Permission is hereby granted, free of charge, to any person obtaining a
10 copy of this software and associated documentation files (the "Software"),
11 to deal in the Software without restriction, including without limitation
12 the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 and/or sell copies of the Software, and to permit persons to whom the
14 Software is furnished to do so, subject to the following conditions:
15
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
18
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26
27 Note that the Qore library is released under a choice of three open-source
28 licenses: MIT (as above), LGPL 2+, or GPL 2+; see README-LICENSE for more
29 information.
30*/
31
32#ifndef _QORE_QOREEXCEPTION_H
33
34#define _QORE_QOREEXCEPTION_H
35
36#include <cstdarg>
37#include <string>
38
39hashdecl QoreExceptionBase {
40 qore_call_t type;
41 QoreListNode* callStack = new QoreListNode(autoTypeInfo);
42 QoreValue err, desc, arg;
43
44 DLLLOCAL QoreExceptionBase(QoreValue n_err, QoreValue n_desc, QoreValue n_arg = QoreValue(),
45 qore_call_t n_type = CT_BUILTIN);
46
47 DLLLOCAL QoreExceptionBase(const QoreExceptionBase& old) :
48 type(old.type), callStack(old.callStack->copy()),
49 err(old.err.refSelf()), desc(old.desc.refSelf()),
50 arg(old.arg.refSelf()) {
51 }
52
53 DLLLOCAL ~QoreExceptionBase() {
54 assert(!callStack);
55 }
56};
57
58hashdecl QoreExceptionLocation : QoreProgramLineLocation {
59 std::string file;
60 std::string source;
61 std::string lang;
62 int offset = 0;
63
64 DLLLOCAL QoreExceptionLocation() {
65 }
66
67 DLLLOCAL QoreExceptionLocation(const QoreProgramLocation& loc) : QoreProgramLineLocation(loc),
68 file(loc.getFileValue()), source(loc.getSourceValue()), lang(loc.getLanguageValue()), offset(loc.offset) {
69 }
70
71 DLLLOCAL QoreExceptionLocation(const QoreExceptionLocation& old) : QoreProgramLineLocation(old),
72 file(old.file), source(old.source), lang(old.lang), offset(old.offset) {
73 }
74
75 DLLLOCAL QoreExceptionLocation(QoreExceptionLocation&& old) = default;
76
77 DLLLOCAL QoreExceptionLocation& operator=(const QoreExceptionLocation& other) {
78 start_line = other.start_line;
79 end_line = other.end_line;
80 file = other.file;
81 source = other.source;
82 lang = other.lang;
83 offset = other.offset;
84 return *this;
85 }
86
87 DLLLOCAL void set(const QoreProgramLocation& loc) {
88 start_line = loc.start_line;
89 end_line = loc.end_line;
90 file = loc.getFileValue();
91 source = loc.getSourceValue();
92 lang = loc.getLanguageValue();
93 offset = loc.offset;
94 }
95
96 DLLLOCAL bool isBuiltin() const {
97 return file == "<builtin>" && (start_line == end_line) && (start_line == -1);
98 }
99};
100
101class QoreException : public QoreExceptionBase, public QoreExceptionLocation {
102 friend class ExceptionSink;
103 friend hashdecl qore_es_private;
104
105public:
106 QoreException* next = nullptr;
107
108 // called for generic exceptions
109 DLLLOCAL QoreHashNode* makeExceptionObjectAndDelete(ExceptionSink *xsink);
110 DLLLOCAL QoreHashNode* makeExceptionObject(int level = 0) const;
111
112 // called for runtime exceptions
113 DLLLOCAL QoreException(const char *n_err, QoreValue n_desc, QoreValue n_arg = QoreValue())
114 : QoreExceptionBase(new QoreStringNode(n_err), n_desc, n_arg),
115 QoreExceptionLocation(*get_runtime_location()) {
116 }
117
118 DLLLOCAL QoreException(QoreStringNode *n_err, QoreValue n_desc, QoreValue n_arg = QoreValue())
119 : QoreExceptionBase(n_err, n_desc, n_arg),
120 QoreExceptionLocation(*get_runtime_location()) {
121 }
122
123 DLLLOCAL QoreException(const QoreException& old) : QoreExceptionBase(old),
124 QoreExceptionLocation(old), next(old.next ? new QoreException(*old.next) : nullptr) {
125 }
126
127 // called for user exceptions
128 DLLLOCAL QoreException(const QoreListNode* n) : QoreExceptionBase(0, 0, 0, CT_USER),
129 QoreExceptionLocation(*get_runtime_location()) {
130 if (n) {
131 err = n->getReferencedEntry(0);
132 desc = n->getReferencedEntry(1);
133 arg = n->size() > 3 ? n->copyListFrom(2) : n->getReferencedEntry(2);
134 }
135 }
136
137 DLLLOCAL QoreException(const QoreProgramLocation& n_loc, const char *n_err, QoreValue n_desc,
138 QoreValue n_arg = QoreValue(), qore_call_t n_type = CT_BUILTIN) :
139 QoreExceptionBase(new QoreStringNode(n_err), n_desc, n_arg, n_type), QoreExceptionLocation(n_loc) {
140 }
141
142 // replaces the top exception
148 DLLLOCAL QoreException* replaceTop(const QoreListNode& new_ex, ExceptionSink& xsink);
149
150 DLLLOCAL void getLocation(QoreExceptionLocation& loc) const {
151 if (isBuiltin() && callStack) {
152 ConstListIterator i(callStack);
153 while (i.next()) {
154 QoreValue v = i.getValue();
155 assert(v.getType() == NT_HASH);
156 QoreValue kv = v.get<const QoreHashNode>()->getKeyValue("file");
157 if (kv.getType() == NT_STRING) {
158 const QoreStringNode* str = kv.get<const QoreStringNode>();
159 if (*str != "<builtin>") {
160 loc.file = str->c_str();
161 kv = v.get<const QoreHashNode>()->getKeyValue("source");
162 if (kv.getType() == NT_STRING) {
163 loc.source = kv.get<const QoreStringNode>()->c_str();
164 }
165 kv = v.get<const QoreHashNode>()->getKeyValue("lang");
166 if (kv.getType() == NT_STRING) {
167 loc.lang = kv.get<const QoreStringNode>()->c_str();
168 }
169 kv = v.get<const QoreHashNode>()->getKeyValue("line");
170 loc.start_line = kv.getAsBigInt();
171 kv = v.get<const QoreHashNode>()->getKeyValue("endline");
172 loc.end_line = kv.getAsBigInt();
173 kv = v.get<const QoreHashNode>()->getKeyValue("offset");
174 loc.offset = kv.getAsBigInt();
175 return;
176 }
177 }
178 }
179 }
180 loc = *this;
181 }
182
183 DLLLOCAL void del(ExceptionSink *xsink);
184
185 DLLLOCAL QoreException* rethrow();
186
187protected:
188 DLLLOCAL ~QoreException() {
189 assert(!callStack);
190 assert(!err.hasNode());
191 assert(!desc.hasNode());
192 assert(!arg.hasNode());
193 }
194
195 DLLLOCAL void addStackInfo(QoreHashNode* n);
196
197 DLLLOCAL static const char* getType(qore_call_t type);
198
199 DLLLOCAL static QoreHashNode* getStackHash(const QoreCallStackElement& cse);
200
201private:
202 DLLLOCAL QoreException& operator=(const QoreException&) = delete;
203};
204
205class ParseException : public QoreException {
206public:
207 // called for parse exceptions
208 DLLLOCAL ParseException(const QoreProgramLocation& loc, const char* err, QoreStringNode* desc) : QoreException(loc, err, desc) {
209 }
210};
211
212hashdecl qore_es_private {
213 QoreException* head = nullptr, * tail = nullptr;
214 bool thread_exit = false;
215 bool rethrown = false;
216
217 DLLLOCAL qore_es_private() {
218 }
219
220 DLLLOCAL ~qore_es_private() {
221 }
222
223 DLLLOCAL void clearIntern() {
224 // delete all exceptions
225 ExceptionSink xs;
226 if (head) {
227 head->del(&xs);
228 head = tail = nullptr;
229 }
230 }
231
232 DLLLOCAL void insert(QoreException *e) {
233 // append exception to the list
234 if (!head)
235 head = e;
236 else
237 tail->next = e;
238 tail = e;
239 }
240
241 DLLLOCAL void appendListIntern(QoreString& str) const {
242 QoreException* w = head;
243 while (w) {
244 QoreStringNodeValueHelper err(w->err);
245 QoreStringNodeValueHelper desc(w->desc);
246
247 str.concat(" * ");
248 if (!w->file.empty())
249 str.sprintf("%s:", w->file.c_str());
250 if (w->start_line) {
251 str.sprintf("%d", w->start_line);
252 if (w->end_line && w->end_line != w->start_line)
253 str.sprintf("-%d", w->end_line);
254 str.concat(": ");
255 }
256 str.sprintf("%s: %s", err->getBuffer(), desc->getBuffer());
257 if (w != tail)
258 str.concat('\n');
259
260 w = w->next;
261 }
262 }
263
264 // creates a stack trace node and adds it to all exceptions in this sink
265 DLLLOCAL void addStackInfo(qore_call_t type, const char *class_name, const char *code,
266 const QoreProgramLocation& loc);
267
268 DLLLOCAL void addStackInfo(const QoreCallStackElement& cse) {
269 assert(head);
270 QoreHashNode* n = QoreException::getStackHash(cse);
271
272 assert(head);
273 QoreException* w = head;
274 while (w) {
275 w->addStackInfo(n);
276 w = w->next;
277 if (w)
278 n->ref();
279 }
280 }
281
282 DLLLOCAL void addStackInfo(const QoreCallStack& stack) {
283 for (auto& i : stack)
284 addStackInfo(i);
285 }
286
287 DLLLOCAL void assimilate(qore_es_private& es);
288
289 DLLLOCAL void rethrow(QoreException* old) {
290 insert(old->rethrow());
291 rethrown = true;
292 }
293
294 DLLLOCAL static qore_es_private* get(ExceptionSink& xsink) {
295 return xsink.priv;
296 }
297
298 DLLLOCAL static const qore_es_private* get(const ExceptionSink& xsink) {
299 return xsink.priv;
300 }
301
302 DLLLOCAL static void addStackInfo(ExceptionSink& xsink, qore_call_t type, const char* class_name,
303 const char* code, const QoreProgramLocation& loc) {
304 xsink.priv->addStackInfo(type, class_name, code, loc);
305 }
306
307 DLLLOCAL static void appendList(ExceptionSink& xsink, QoreString& str) {
308 xsink.priv->appendListIntern(str);
309 }
310};
311
312class ParseExceptionSink {
313protected:
314 ExceptionSink xsink;
315
316public:
317 DLLLOCAL ~ParseExceptionSink();
318
319 DLLLOCAL ExceptionSink *operator*() {
320 return &xsink;
321 }
322};
323
324#endif
DLLEXPORT void ref() const
increments the reference count
For use on the stack only: iterates through elements of a const QoreListNode.
Definition: QoreListNode.h:563
container for holding Qore-language exception information and also for registering a "thread_exit" ca...
Definition: ExceptionSink.h:50
This is the hash or associative list container type in Qore, dynamically allocated only,...
Definition: QoreHashNode.h:50
This is the list container type in Qore, dynamically allocated only, reference counted.
Definition: QoreListNode.h:52
DLLEXPORT size_t size() const
returns the number of elements in the list
DLLEXPORT QoreValue getReferencedEntry(size_t index) const
returns the element at "index" (first element is index 0), the caller owns the reference
DLLEXPORT QoreListNode * copyListFrom(size_t index) const
performs a deep copy of the list starting from element "offset" and returns the new list
DLLLOCAL detail::QoreValueCastHelper< T >::Result get()
returns the value as the given type
Definition: QoreValue.h:214
DLLEXPORT qore_type_t getType() const
returns the type of value contained
DLLEXPORT int64 getAsBigInt() const
returns the value as an int
Qore's string type supported by the QoreEncoding class.
Definition: QoreString.h:93
DLLEXPORT const char * c_str() const
returns the string's buffer; this data should not be changed
DLLEXPORT void concat(const QoreString *str, ExceptionSink *xsink)
concatenates a string and converts encodings if necessary
DLLEXPORT int sprintf(const char *fmt,...)
this will concatentate a formatted string to the existing string according to the format string and t...
Qore's string value type, reference counted, dynamically-allocated only.
Definition: QoreStringNode.h:50
this class is used to safely manage calls to AbstractQoreNode::getStringRepresentation() when a QoreS...
Definition: QoreStringNode.h:378
const qore_type_t NT_STRING
type value for QoreStringNode
Definition: node_types.h:45
const qore_type_t NT_HASH
type value for QoreHashNode
Definition: node_types.h:51
call stack element; strings must be in the default encoding for the Qore process
Definition: ExceptionSink.h:315
Qore call stack.
Definition: ExceptionSink.h:334
The main value class in Qore, designed to be passed by value.
Definition: QoreValue.h:276
DLLEXPORT bool hasNode() const
returns true if the object contains a non-null AbstractQoreNode pointer (ie type == QV_Node && v....