Qore Programming Language  0.9.4.6
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 - 2020 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() 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 static void addStackInfo(ExceptionSink& xsink, qore_call_t type, const char* class_name,
279  const char* code, const QoreProgramLocation& loc) {
280  xsink.priv->addStackInfo(type, class_name, code, loc);
281  }
282 
283  DLLLOCAL static void appendList(ExceptionSink& xsink, QoreString& str) {
284  xsink.priv->appendListIntern(str);
285  }
286 };
287 
288 class ParseExceptionSink {
289 protected:
290  ExceptionSink xsink;
291 
292 public:
293  DLLLOCAL ~ParseExceptionSink();
294 
295  DLLLOCAL ExceptionSink *operator*() {
296  return &xsink;
297  }
298 };
299 
300 #endif
DLLEXPORT const char * c_str() const
returns the string&#39;s buffer; this data should not be changed
DLLEXPORT QoreValue getReferencedEntry(size_t index) const
returns the element at "index" (first element is index 0), the caller owns the reference ...
This is the hash or associative list container type in Qore, dynamically allocated only...
Definition: QoreHashNode.h:50
DLLEXPORT int sprintf(const char *fmt,...)
this will concatentate a formatted string to the existing string according to the format string and t...
Qore&#39;s string type supported by the QoreEncoding class.
Definition: QoreString.h:81
Qore&#39;s string value type, reference counted, dynamically-allocated only.
Definition: QoreStringNode.h:50
DLLEXPORT void concat(const QoreString *str, ExceptionSink *xsink)
concatenates a string and converts encodings if necessary
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
const qore_type_t NT_HASH
type value for QoreHashNode
Definition: node_types.h:51
The main value class in Qore, designed to be passed by value.
Definition: QoreValue.h:262
const qore_type_t NT_STRING
type value for QoreStringNode
Definition: node_types.h:45
container for holding Qore-language exception information and also for registering a "thread_exit" ca...
Definition: ExceptionSink.h:46
DLLEXPORT void ref() const
increments the reference count
this class is used to safely manage calls to AbstractQoreNode::getStringRepresentation() when a QoreS...
Definition: QoreStringNode.h:401
DLLEXPORT QoreListNode * copyListFrom(size_t index) const
performs a deep copy of the list starting from element "offset" and returns the new list ...
Qore call stack.
Definition: ExceptionSink.h:310
DLLEXPORT bool hasNode() const
returns true if the object contains a non-null AbstractQoreNode pointer (ie type == QV_Node && v...
call stack element; strings must be in the default encoding for the Qore process
Definition: ExceptionSink.h:291
For use on the stack only: iterates through elements of a const QoreListNode.
Definition: QoreListNode.h:566