Qore Programming Language  1.12.0
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 - 2022 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 
39 hashdecl 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 
58 hashdecl 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 
101 class QoreException : public QoreExceptionBase, public QoreExceptionLocation {
102  friend class ExceptionSink;
103  friend hashdecl qore_es_private;
104 
105 public:
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 
187 protected:
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 
201 private:
202  DLLLOCAL QoreException& operator=(const QoreException&) = delete;
203 };
204 
205 class ParseException : public QoreException {
206 public:
207  // called for parse exceptions
208  DLLLOCAL ParseException(const QoreProgramLocation& loc, const char* err, QoreStringNode* desc) : QoreException(loc, err, desc) {
209  }
210 };
211 
212 hashdecl 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 
312 class ParseExceptionSink {
313 protected:
314  ExceptionSink xsink;
315 
316 public:
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:48
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 QoreListNode * copyListFrom(size_t index) const
performs a deep copy of the list starting from element "offset" and returns the new list
DLLEXPORT QoreValue getReferencedEntry(size_t index) const
returns the element at "index" (first element is index 0), the caller owns the reference
Qore's string type supported by the QoreEncoding class.
Definition: QoreString.h:93
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...
DLLEXPORT const char * c_str() const
returns the string's buffer; this data should not be changed
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:306
Qore call stack.
Definition: ExceptionSink.h:325
The main value class in Qore, designed to be passed by value.
Definition: QoreValue.h:275
DLLEXPORT bool hasNode() const
returns true if the object contains a non-null AbstractQoreNode pointer (ie type == QV_Node && v....