Qore Programming Language  0.9.16
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 - 2021 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  DLLLOCAL void getLocation(QoreExceptionLocation& loc) const {
143  if (isBuiltin() && callStack) {
144  ConstListIterator i(callStack);
145  while (i.next()) {
146  QoreValue v = i.getValue();
147  assert(v.getType() == NT_HASH);
148  QoreValue kv = v.get<const QoreHashNode>()->getKeyValue("file");
149  if (kv.getType() == NT_STRING) {
150  const QoreStringNode* str = kv.get<const QoreStringNode>();
151  if (*str != "<builtin>") {
152  loc.file = str->c_str();
153  kv = v.get<const QoreHashNode>()->getKeyValue("source");
154  if (kv.getType() == NT_STRING) {
155  loc.source = kv.get<const QoreStringNode>()->c_str();
156  }
157  kv = v.get<const QoreHashNode>()->getKeyValue("lang");
158  if (kv.getType() == NT_STRING) {
159  loc.lang = kv.get<const QoreStringNode>()->c_str();
160  }
161  kv = v.get<const QoreHashNode>()->getKeyValue("line");
162  loc.start_line = kv.getAsBigInt();
163  kv = v.get<const QoreHashNode>()->getKeyValue("endline");
164  loc.end_line = kv.getAsBigInt();
165  kv = v.get<const QoreHashNode>()->getKeyValue("offset");
166  loc.offset = kv.getAsBigInt();
167  return;
168  }
169  }
170  }
171  }
172  loc = *this;
173  }
174 
175  DLLLOCAL void del(ExceptionSink *xsink);
176 
177  DLLLOCAL QoreException* rethrow();
178 
179 protected:
180  DLLLOCAL ~QoreException() {
181  assert(!callStack);
182  assert(!err.hasNode());
183  assert(!desc.hasNode());
184  assert(!arg.hasNode());
185  }
186 
187  DLLLOCAL void addStackInfo(QoreHashNode* n);
188 
189  DLLLOCAL static const char* getType(qore_call_t type);
190 
191  DLLLOCAL static QoreHashNode* getStackHash(const QoreCallStackElement& cse);
192 
193 private:
194  DLLLOCAL QoreException& operator=(const QoreException&) = delete;
195 };
196 
197 class ParseException : public QoreException {
198 public:
199  // called for parse exceptions
200  DLLLOCAL ParseException(const QoreProgramLocation& loc, const char* err, QoreStringNode* desc) : QoreException(loc, err, desc) {
201  }
202 };
203 
204 hashdecl qore_es_private {
205  bool thread_exit = false;
206  QoreException* head = nullptr, * tail = nullptr;
207 
208  DLLLOCAL qore_es_private() {
209  }
210 
211  DLLLOCAL ~qore_es_private() {
212  }
213 
214  DLLLOCAL void clearIntern() {
215  // delete all exceptions
216  ExceptionSink xs;
217  if (head) {
218  head->del(&xs);
219  head = tail = nullptr;
220  }
221  }
222 
223  DLLLOCAL void insert(QoreException *e) {
224  // append exception to the list
225  if (!head)
226  head = e;
227  else
228  tail->next = e;
229  tail = e;
230  }
231 
232  DLLLOCAL void appendListIntern(QoreString& str) const {
233  QoreException* w = head;
234  while (w) {
235  QoreStringNodeValueHelper err(w->err);
236  QoreStringNodeValueHelper desc(w->desc);
237 
238  str.concat(" * ");
239  if (!w->file.empty())
240  str.sprintf("%s:", w->file.c_str());
241  if (w->start_line) {
242  str.sprintf("%d", w->start_line);
243  if (w->end_line && w->end_line != w->start_line)
244  str.sprintf("-%d", w->end_line);
245  str.concat(": ");
246  }
247  str.sprintf("%s: %s", err->getBuffer(), desc->getBuffer());
248  if (w != tail)
249  str.concat('\n');
250 
251  w = w->next;
252  }
253  }
254 
255  // creates a stack trace node and adds it to all exceptions in this sink
256  DLLLOCAL void addStackInfo(qore_call_t type, const char *class_name, const char *code,
257  const QoreProgramLocation& loc);
258 
259  DLLLOCAL void addStackInfo(const QoreCallStackElement& cse) {
260  assert(head);
261  QoreHashNode* n = QoreException::getStackHash(cse);
262 
263  assert(head);
264  QoreException* w = head;
265  while (w) {
266  w->addStackInfo(n);
267  w = w->next;
268  if (w)
269  n->ref();
270  }
271  }
272 
273  DLLLOCAL void addStackInfo(const QoreCallStack& stack) {
274  for (auto& i : stack)
275  addStackInfo(i);
276  }
277 
278  DLLLOCAL void assimilate(qore_es_private& es);
279 
280  DLLLOCAL static void addStackInfo(ExceptionSink& xsink, qore_call_t type, const char* class_name,
281  const char* code, const QoreProgramLocation& loc) {
282  xsink.priv->addStackInfo(type, class_name, code, loc);
283  }
284 
285  DLLLOCAL static void appendList(ExceptionSink& xsink, QoreString& str) {
286  xsink.priv->appendListIntern(str);
287  }
288 };
289 
290 class ParseExceptionSink {
291 protected:
292  ExceptionSink xsink;
293 
294 public:
295  DLLLOCAL ~ParseExceptionSink();
296 
297  DLLLOCAL ExceptionSink *operator*() {
298  return &xsink;
299  }
300 };
301 
302 #endif
QoreStringNodeValueHelper
this class is used to safely manage calls to AbstractQoreNode::getStringRepresentation() when a QoreS...
Definition: QoreStringNode.h:401
ConstListIterator
For use on the stack only: iterates through elements of a const QoreListNode.
Definition: QoreListNode.h:564
QoreListNode::size
DLLEXPORT size_t size() const
returns the number of elements in the list
QoreValue
The main value class in Qore, designed to be passed by value.
Definition: QoreValue.h:262
QoreHashNode
This is the hash or associative list container type in Qore, dynamically allocated only,...
Definition: QoreHashNode.h:50
QoreListNode::copyListFrom
DLLEXPORT QoreListNode * copyListFrom(size_t index) const
performs a deep copy of the list starting from element "offset" and returns the new list
QoreListNode
This is the list container type in Qore, dynamically allocated only, reference counted.
Definition: QoreListNode.h:52
QoreString::sprintf
DLLEXPORT int sprintf(const char *fmt,...)
this will concatentate a formatted string to the existing string according to the format string and t...
QoreValue::hasNode
DLLEXPORT bool hasNode() const
returns true if the object contains a non-null AbstractQoreNode pointer (ie type == QV_Node && v....
QoreString
Qore's string type supported by the QoreEncoding class.
Definition: QoreString.h:81
QoreString::c_str
const DLLEXPORT char * c_str() const
returns the string's buffer; this data should not be changed
NT_STRING
const qore_type_t NT_STRING
type value for QoreStringNode
Definition: node_types.h:45
QoreListNode::getReferencedEntry
DLLEXPORT QoreValue getReferencedEntry(size_t index) const
returns the element at "index" (first element is index 0), the caller owns the reference
QoreString::concat
DLLEXPORT void concat(const QoreString *str, ExceptionSink *xsink)
concatenates a string and converts encodings if necessary
ExceptionSink
container for holding Qore-language exception information and also for registering a "thread_exit" ca...
Definition: ExceptionSink.h:48
AbstractQoreNode::ref
DLLEXPORT void ref() const
increments the reference count
QoreCallStack
Qore call stack.
Definition: ExceptionSink.h:326
QoreCallStackElement
call stack element; strings must be in the default encoding for the Qore process
Definition: ExceptionSink.h:307
NT_HASH
const qore_type_t NT_HASH
type value for QoreHashNode
Definition: node_types.h:51
QoreStringNode
Qore's string value type, reference counted, dynamically-allocated only.
Definition: QoreStringNode.h:50