Qore Programming Language  0.9.3.2
qore_program_private.h
1 /* -*- mode: c++; indent-tabs-mode: nil -*- */
2 /*
3  qore_program_private.h
4 
5  Qore Programming Language
6 
7  Copyright (C) 2003 - 2019 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_QORE_PROGRAM_PRIVATE_H
33 #define _QORE_QORE_PROGRAM_PRIVATE_H
34 
35 #define QPP_DBG_LVL 5
36 
37 extern QoreListNode* ARGV, * QORE_ARGV;
38 extern QoreHashNode* ENV;
39 
40 #include "qore/intern/ParserSupport.h"
41 #include "qore/intern/QoreNamespaceIntern.h"
42 #include "qore/intern/QC_AutoReadLock.h"
43 #include "qore/intern/QC_AutoWriteLock.h"
44 #include "qore/intern/QC_Program.h"
45 #include "qore/intern/QC_ProgramControl.h"
46 #include "qore/QoreDebugProgram.h"
47 #include "qore/QoreRWLock.h"
48 #include "qore/vector_map"
49 #include "qore/vector_set"
50 
51 #include <cerrno>
52 #include <cstdarg>
53 #include <map>
54 #include <vector>
55 
56 typedef vector_map_t<int, unsigned> ptid_map_t;
57 //typedef std::map<int, unsigned> ptid_map_t;
58 
59 typedef std::vector<AbstractStatement*> stmt_vec_t;
60 
61 class QoreParseLocationHelper {
62 public:
63  DLLLOCAL QoreParseLocationHelper(const char* file, const char* src, int offset) {
64  thread_set_class_and_ns(nullptr, nullptr, cls, ns);
65  beginParsing(file, nullptr, src, offset);
66  }
67 
68  DLLLOCAL ~QoreParseLocationHelper() {
69  endParsing();
70  thread_set_class_and_ns(cls, ns);
71  }
72 
73 private:
74  // issue #3596: clear & restore class and ns ctx
75  const qore_class_private* cls;
76  qore_ns_private* ns;
77 };
78 
79 class CharPtrList : public safe_dslist<std::string> {
80 public:
81  // returns true for found, false for not found
82  // FIXME: use STL find algorithm
83  DLLLOCAL bool find(const char* str) const {
84  const_iterator i = begin();
85  while (i != end()) {
86  if (*i == str)
87  return true;
88  ++i;
89  }
90 
91  return false;
92  }
93 };
94 
95 // local variable container
96 typedef safe_dslist<LocalVar*> local_var_list_t;
97 
98 class LocalVariableList : public local_var_list_t {
99 public:
100  DLLLOCAL LocalVariableList() {
101  }
102 
103  DLLLOCAL ~LocalVariableList() {
104  for (local_var_list_t::iterator i = begin(), e = end(); i != e; ++i) {
105  delete *i;
106  }
107  }
108 };
109 
111 
112 #include "qore/intern/ThreadLocalVariableData.h"
113 #include "qore/intern/ThreadClosureVariableStack.h"
114 
115 hashdecl ThreadLocalProgramData {
116 private:
117  // not implemented
118  DLLLOCAL ThreadLocalProgramData(const ThreadLocalProgramData& old) = delete;
119 
120  // thread debug types, field is read/write only in thread being debugged, no locking is needed
121  DebugRunStateEnum runState;
122  // used to implement "to to statement" debugger command, reset it when program is interrupted
123  const AbstractStatement* runToStatement;
124  // when stepover or until return we need calls function calls
125  int functionCallLevel;
126 
127  inline void setRunState(DebugRunStateEnum rs, const AbstractStatement* rts) {
128  assert(rs < DBG_RS_STOPPED); // DBG_RS_STOPPED is wrong value when program is running
129  if (rs == DBG_RS_UNTIL_RETURN) {
130  functionCallLevel = 1; // function called only when runState is not DBG_RS_UNTIL_RETURN
131  }
132  printd(5, "ThreadLocalProgramData::setRunState(), this: %p, rs: %d->%d, rts: %p\n", this, runState, rs, rts);
133  runState = rs;
134  runToStatement = rts;
135  }
136  // set to true by any process do break running program asap
137  volatile bool breakFlag;
138  // called from running thread
139  inline void checkBreakFlag() {
140  if (breakFlag && runState != DBG_RS_DETACH) {
141  breakFlag = false;
142  if (runState != DBG_RS_STOPPED) {
143  runState = DBG_RS_STEP;
144  }
145  printd(5, "ThreadLocalProgramData::checkBreakFlag(), this: %p, rs: %d\n", this, runState);
146  }
147  }
148  // to call onAttach when debug is attached or detached, -1 .. detach, 1 .. attach
149  int attachFlag = 0;
150  inline void checkAttach(ExceptionSink* xsink) {
151  if (runState != DBG_RS_STOPPED) {
152  if (attachFlag > 0) {
153  dbgAttach(xsink);
154  //if (rs != DBG_RS_DETACH) { // TODO: why this exception ?
155  attachFlag = 0;
156  //}
157  } else if (attachFlag < 0) {
158  dbgDetach(xsink);
159  attachFlag = 0;
160  }
161  }
162  }
163 
164 public:
165  // local variable data slots
166  ThreadLocalVariableData lvstack;
167  // closure variable stack
168  ThreadClosureVariableStack cvstack;
169  // current thread's time zone locale (if any)
170  const AbstractQoreZoneInfo* tz = nullptr;
171  // the "time zone set" flag
172  bool tz_set : 1;
173 
174  // top-level vars instantiated
175  bool inst : 1;
176 
177  DLLLOCAL ThreadLocalProgramData() : runState(DBG_RS_DETACH), functionCallLevel(0), breakFlag(false), tz(0), tz_set(false), inst(false) {
178  printd(5, "ThreadLocalProgramData::ThreadLocalProgramData() this: %p\n", this);
179  }
180 
181  DLLLOCAL ~ThreadLocalProgramData() {
182  printd(5, "ThreadLocalProgramData::~ThreadLocalProgramData() this: %p, rs: %d\n", this, runState);
183  assert(lvstack.empty());
184  assert(cvstack.empty());
185  }
186 
187  DLLLOCAL void finalize(arg_vec_t*& cl) {
188  lvstack.finalize(cl);
189  cvstack.finalize(cl);
190  }
191 
192  DLLLOCAL void del(ExceptionSink* xsink) {
193  lvstack.del(xsink);
194  cvstack.del(xsink);
195  delete this;
196  }
197 
198  DLLLOCAL void setTZ(const AbstractQoreZoneInfo* n_tz) {
199  tz_set = true;
200  tz = n_tz;
201  }
202 
203  DLLLOCAL void clearTZ() {
204  tz_set = false;
205  tz = 0;
206  }
207 
208  /*
209  DLLLOCAL void setEnable(bool n_enabled) {
210  enabled = n_enabled;
211  if (!enabled) {
212  runState = DBG_RS_RUN;
213  functionCallLevel = 0;
214  }
215  }
216  */
217 
231  DLLLOCAL void dbgAttach(ExceptionSink* xsink);
235  DLLLOCAL void dbgDetach(ExceptionSink* xsink);
241  DLLLOCAL int dbgStep(const StatementBlock* blockStatement, const AbstractStatement* statement, ExceptionSink* xsink);
245  DLLLOCAL void dbgFunctionEnter(const StatementBlock* statement, ExceptionSink* xsink);
249  DLLLOCAL void dbgFunctionExit(const StatementBlock* statement, QoreValue& returnValue, ExceptionSink* xsink);
253  DLLLOCAL void dbgException(const AbstractStatement* statement, ExceptionSink* xsink);
257  DLLLOCAL void dbgExit(const StatementBlock* statement, QoreValue& returnValue, ExceptionSink* xsink);
258 
262  DLLLOCAL void dbgBreak() {
263  printd(5, "ThreadLocalProgramData::dbgBreak(), this: %p\n", this);
264  breakFlag = true;
265  }
269  DLLLOCAL void dbgPendingAttach() {
270  printd(5, "ThreadLocalProgramData::dbgPendingAttach(), this: %p\n", this);
271  attachFlag = 1;
272  }
276  DLLLOCAL void dbgPendingDetach() {
277  printd(5, "ThreadLocalProgramData::dbgPendingDetach(), this: %p\n", this);
278  attachFlag = -1;
279  }
280 
284  DLLLOCAL bool dbgIsAttached() {
285  return /*runState != DBG_RS_STOPPED &&*/ runState != DBG_RS_DETACH;
286  }
287 };
288 
289 // maps from thread handles to thread-local data
290 typedef vector_map_t<ThreadProgramData*, ThreadLocalProgramData*> pgm_data_map_t;
291 //typedef std::map<ThreadProgramData*, ThreadLocalProgramData*> pgm_data_map_t;
292 
293 // map for "defines" in programs
294 typedef vector_map_t<std::string, QoreValue> dmap_t;
295 //typedef std::map<std::string, QoreValue> dmap_t;
296 
297 // map for pushed parse options
298 typedef vector_map_t<const char*, int64> ppo_t;
299 //typedef std::map<const char*, int64, ltstr> ppo_t;
300 
301 class AbstractQoreZoneInfo;
302 
303 class ltpgm {
304 public:
305  DLLLOCAL bool operator()(const QoreProgramLocation* p1, const QoreProgramLocation* p2) const {
306  assert(p1);
307  assert(p2);
308 
309  return *p1 < *p2;
310  }
311 };
312 
313 hashdecl pgmloc_vec_t : public std::vector<QoreProgramLocation*> {
314  DLLLOCAL ~pgmloc_vec_t() {
315  clear();
316  }
317 
318  DLLLOCAL void clear() {
319  for (auto& i : *this) {
320  delete i;
321  }
322  //std::for_each(begin(), end(), simple_delete<QoreProgramLocation>());
323  std::vector<QoreProgramLocation*>::clear();
324  }
325 };
326 
327 class qore_program_private_base {
328  friend class QoreProgramAccessHelper;
329 
330 protected:
331  DLLLOCAL void setDefines();
332 
333  typedef vector_map_t<const char*, AbstractQoreProgramExternalData*> extmap_t;
334  //typedef std::map<const char*, AbstractQoreProgramExternalData*, ltstr> extmap_t;
335  extmap_t extmap;
336 
337 public:
338  LocalVariableList local_var_list;
339 
340  // for the thread counter, used only with plock
341  QoreCondition pcond;
342  ptid_map_t tidmap; // map of tids -> thread count in program object
343  unsigned thread_count; // number of threads currently running in this Program
344  unsigned thread_waiting; // number of threads waiting on all threads to terminate or parsing to complete
345  unsigned parse_count; // recursive parse count
346 
347  // file name and unique string storage
348  cstr_vector_t str_vec;
349 
350  // unique program location storage
351  pgmloc_vec_t pgmloc;
352 
353  // temporary while parsing: to ensure unique strings in parsing
354  typedef std::set<const char*, ltstr> str_set_t;
355  str_set_t str_set;
356 
357  // temporary while parsing: unique locations; must be a set for performance reasons
358  typedef std::set<const QoreProgramLocation*, ltpgm> loc_set_t;
359  loc_set_t loc_set;
360 
361  // features present in this Program object
362  CharPtrList featureList;
363  // user modules present in this Program object
364  CharPtrList userFeatureList;
365 
366  // parse lock, making parsing actions atomic and thread-safe, also for runtime thread attachment
367  mutable QoreThreadLock plock;
368 
369  // set of signals being handled by code in this Program (to be deleted on exit)
370  int_set_t sigset;
371 
372  // weak reference dependency counter, when this hits zero, the object is deleted
374  ExceptionSink* parseSink, *warnSink, *pendingParseSink;
375  RootQoreNamespace* RootNS;
376  QoreNamespace* QoreNS;
377 
378  // top level statements
379  TopLevelStatementBlock sb;
380 
381  // bit field flags
382  bool only_first_except : 1,
383  po_locked : 1,
384  po_allow_restrict : 1,
385  exec_class : 1,
386  base_object : 1,
387  requires_exception : 1,
388  parsing_done : 1,
389  parsing_in_progress : 1,
390  ns_const : 1,
391  ns_vars : 1
392  ;
393 
394  int tclear; // clearing thread-local variables in progress? if so, this is the TID
395 
396  int exceptions_raised,
397  ptid; // TID of thread destroying the program's private data
398 
399  ParseWarnOptions pwo;
400 
401  int64 dom, // a mask of functional domains used in this Program
402  pend_dom; // a mask of pending function domains used in this Program
403 
404  std::string exec_class_name, script_dir, script_path, script_name, include_path;
405 
406  // thread-local data (could be inherited from another program)
407  qpgm_thread_local_storage_t* thread_local_storage;
408 
409  mutable QoreThreadLock tlock; // thread variable data lock, for accessing the thread variable data map and the thr_init variable
410  mutable QoreCondition tcond; // cond variable for tclear to become false, used only with tlock
411  mutable unsigned twaiting; // threads waiting on tclear to become false
412 
413  // thread-local variable storage - map from thread ID to thread-local storage
414  pgm_data_map_t pgm_data_map;
415 
416  // time zone setting for the program
417  const AbstractQoreZoneInfo* TZ;
418 
419  // define map
420  dmap_t dmap;
421 
422  // pushed parse option map
423  ppo_t ppo;
424 
425  // thread initialization user code
426  ResolvedCallReferenceNode* thr_init;
427 
428  // return value for use with %exec-class
429  QoreValue exec_class_rv;
430 
431  // public object that owns this private implementation
432  QoreProgram* pgm;
433 
434  DLLLOCAL qore_program_private_base(QoreProgram* n_pgm, int64 n_parse_options, QoreProgram* p_pgm = nullptr)
435  : thread_count(0), thread_waiting(0), parse_count(0), plock(&ma_recursive), parseSink(nullptr), warnSink(nullptr), pendingParseSink(nullptr), RootNS(nullptr), QoreNS(nullptr),
436  sb(this),
437  only_first_except(false), po_locked(false), po_allow_restrict(true), exec_class(false), base_object(false),
438  requires_exception(false),
439  parsing_done(false),
440  parsing_in_progress(false),
441  ns_const(false),
442  ns_vars(false),
443  tclear(0),
444  exceptions_raised(0), ptid(0), pwo(n_parse_options), dom(0), pend_dom(0), thread_local_storage(nullptr), twaiting(0),
445  thr_init(nullptr), pgm(n_pgm) {
446  printd(QPP_DBG_LVL, "qore_program_private_base::qore_program_private_base() this: %p pgm: %p po: " QLLD "\n", this, pgm, n_parse_options);
447 
448  // must set priv before calling setParent()
449  pgm->priv = (qore_program_private*)this;
450 
451  if (p_pgm)
452  setParent(p_pgm, n_parse_options);
453  else {
454  TZ = QTZM.getLocalZoneInfo();
455  newProgram();
456  }
457 
458  // initialize global vars
459  Var *var = qore_root_ns_private::runtimeCreateVar(*RootNS, *QoreNS, "ARGV", listTypeInfo, true);
460  if (var && ARGV)
461  var->setInitial(ARGV->copy());
462 
463  var = qore_root_ns_private::runtimeCreateVar(*RootNS, *QoreNS, "QORE_ARGV", listTypeInfo, true);
464  if (var && QORE_ARGV)
465  var->setInitial(QORE_ARGV->copy());
466 
467  var = qore_root_ns_private::runtimeCreateVar(*RootNS, *QoreNS, "ENV", hashTypeInfo, true);
468  if (var)
469  var->setInitial(ENV->copy());
470  setDefines();
471  }
472 
473 #ifdef DEBUG
474  DLLLOCAL ~qore_program_private_base() {
475  printd(QPP_DBG_LVL, "qore_program_private_base::~qore_program_private_base() this: %p pgm: %p\n", this, pgm);
476  }
477 #endif
478 
479  DLLLOCAL const QoreProgramLocation* getLocation(int sline, int eline);
480  DLLLOCAL const QoreProgramLocation* getLocation(const QoreProgramLocation&, int sline, int eline);
481 
482  DLLLOCAL void startThread(ExceptionSink& xsink);
483 
484 protected:
485  DLLLOCAL void setParent(QoreProgram* p_pgm, int64 n_parse_options);
486 
487  // for independent programs (not inherited from another QoreProgram object)
488  DLLLOCAL void newProgram();
489 };
490 
491 class PreParseHelper {
492 protected:
493  qore_program_private_base *p;
494  bool swapped;
495 
496 public:
497  DLLLOCAL PreParseHelper(qore_program_private_base *n_p) : p(n_p), swapped(false) {
498  if (!p->parseSink) {
499  if (!p->pendingParseSink)
500  p->pendingParseSink = new ExceptionSink;
501  p->parseSink = p->pendingParseSink;
502  swapped = true;
503  }
504  }
505 
506  DLLLOCAL ~PreParseHelper() {
507  if (swapped)
508  p->parseSink = nullptr;
509  }
510 };
511 
512 class qore_debug_program_private;
513 
514 class AutoQoreCounterDec {
515 private:
516  QoreCounter* cnt;
517  bool incFlag;
518  AutoQoreCounterDec() {}
519 public:
520  AutoQoreCounterDec(QoreCounter* n_cnt, bool incNow = true): cnt(n_cnt), incFlag(false) {
521  if (incNow) {
522  inc();
523  }
524  }
525  ~AutoQoreCounterDec() {
526  if (incFlag)
527  cnt->dec();
528  }
529  void inc() {
530  cnt->inc();
531  incFlag = true;
532  }
533 };
534 
535 class QoreBreakpoint;
536 
537 class qore_program_private : public qore_program_private_base {
538 private:
539  mutable QoreCounter debug_program_counter; // number of thread calls to debug program instance.
540  DLLLOCAL void init(QoreProgram* n_pgm, int64 n_parse_options, const AbstractQoreZoneInfo *n_TZ = QTZM.getLocalZoneInfo()) {
541  }
542 
543  // only called from parseSetTimeZone
544  DLLLOCAL void mergeParseException(ExceptionSink &xsink) {
545  if (parseSink)
546  parseSink->assimilate(xsink);
547  else {
548  if (!pendingParseSink)
549  pendingParseSink = new ExceptionSink;
550  pendingParseSink->assimilate(xsink);
551  }
552  }
553 
554  qore_debug_program_private* dpgm;
555  QoreRWLock lck_breakpoint; // to protect breakpoint manipulation
556  QoreBreakpointList_t breakpointList;
557 
558  // map for line to statement
559  typedef std::map<int, AbstractStatement*> sline_statement_map_t;
560 
561  typedef std::map<const char*, int, ltstr> section_offset_map_t;
562 
563  hashdecl section_sline_statement_map {
564  section_offset_map_t sectionMap;
565  sline_statement_map_t statementMap;
566  };
567 
568  typedef section_sline_statement_map section_sline_statement_map_t;
569 
570  // map for filenames
571  typedef vector_map_t<const char*, section_sline_statement_map_t*> name_section_sline_statement_map_t;
572  //typedef std::map<const char*, section_sline_statement_map_t*, ltstr> name_section_sline_statement_map_t;
573 
574  // index source filename/label -> line -> statement
575  name_section_sline_statement_map_t statementByFileIndex;
576  name_section_sline_statement_map_t statementByLabelIndex;
577 
578  // statementId to AbstractStatement resolving
579  typedef std::vector<AbstractStatement*> StatementVector_t;
580  StatementVector_t statementIds;
581 
582  // to get statementId
583  typedef std::map<AbstractStatement*, unsigned long> ReverseStatementIdMap_t;
584  ReverseStatementIdMap_t reverseStatementIds;
585 
591  DLLLOCAL qore_debug_program_private* getDebugProgram(AutoQoreCounterDec& ad) {
592  AutoLocker al(tlock);
593  //QoreAutoRWReadLocker al(&lck_debug_program);
594  qore_debug_program_private* ret = dpgm;
595  if (ret) {
596  // new debug call in progress
597  ad.inc();
598  }
599  return ret;
600  }
601 
602  // lck_breakpoint lock should be aquired
603  DLLLOCAL bool isBreakpointRegistered(const QoreBreakpoint *bkpt) const {
604  return std::find(breakpointList.begin(), breakpointList.end(), bkpt) != breakpointList.end();
605  }
606  friend class QoreBreakpoint;
607 
608  typedef std::map<QoreProgram*, QoreObject*> qore_program_to_object_map_t;
609  static qore_program_to_object_map_t qore_program_to_object_map;
610  static QoreRWLock lck_programMap; // to protect program list manipulation
611  static volatile unsigned programIdCounter; // to generate programId
612  unsigned programId;
613 
614 public:
615  DLLLOCAL qore_program_private(QoreProgram* n_pgm, int64 n_parse_options, QoreProgram* p_pgm=nullptr);
616 
617  DLLLOCAL ~qore_program_private();
618 
619  DLLLOCAL void depRef() {
620  printd(QPP_DBG_LVL, "qore_program_private::depRef() this: %p pgm: %p %d->%d\n", this, pgm, dc.reference_count(), dc.reference_count() + 1);
621  dc.ROreference();
622  }
623 
624  DLLLOCAL void depDeref() {
625  printd(QPP_DBG_LVL, "qore_program_private::depDeref() this: %p pgm: %p %d->%d\n", this, pgm, dc.reference_count(), dc.reference_count() - 1);
626  if (dc.ROdereference())
627  delete pgm;
628  }
629 
630  DLLLOCAL void clearLocalVars(ExceptionSink* xsink) {
631  // grab all thread-local data in a vector and finalize it outside the lock
632  arg_vec_t* cl = nullptr;
633  {
634  AutoLocker al(tlock);
635  // twaiting must be 0 here, as it can only be incremented while clearProgramThreadData() is in progress, which can only be executed once
636  assert(!twaiting);
637  assert(!tclear);
638  // while tclear is set, no threads can attach to this program object - pgm_data_map cannot be modified
639  tclear = gettid();
640 
641  for (pgm_data_map_t::iterator i = pgm_data_map.begin(), e = pgm_data_map.end(); i != e; ++i)
642  i->second->finalize(cl);
643  }
644 
645  // dereference finalized thread-local data outside the lock to avoid deadlocks
646  if (cl) {
647  for (arg_vec_t::iterator i = cl->begin(), e = cl->end(); i != e; ++i) {
648  (*i).discard(xsink);
649  }
650  delete cl;
651  }
652  }
653 
654  DLLLOCAL void clearProgramThreadData(ExceptionSink* xsink) {
655  for (pgm_data_map_t::iterator i = pgm_data_map.begin(), e = pgm_data_map.end(); i != e; ++i) {
656  i->second->del(xsink);
657  i->first->delProgram(pgm);
658  }
659  }
660 
661  DLLLOCAL void waitForTerminationAndClear(ExceptionSink* xsink);
662 
663  // called when the program's ref count = 0 (but the dc count may not go to 0 yet)
664  DLLLOCAL void clear(ExceptionSink* xsink);
665 
666  // called when starting a new thread before the new thread is started, to avoid race conditions
667  // once the new thread has been started, the TID is registered in startThread()
668  DLLLOCAL int preregisterNewThread(ExceptionSink* xsink) {
669  // grab program-level lock
670  AutoLocker al(plock);
671 
672  if (ptid) {
673  xsink->raiseException("PROGRAM-ERROR", "the Program accessed has already been deleted and therefore no new threads can be started in it");
674  return -1;
675  }
676 
677  ++thread_count;
678  return 0;
679  }
680 
681  // called when thread startup fails after preregistration
682  DLLLOCAL void cancelPreregistration() {
683  // grab program-level lock
684  AutoLocker al(plock);
685 
686  assert(thread_count > 0);
687  if (!--thread_count && thread_waiting)
688  pcond.broadcast();
689  }
690 
691  // called from the new thread once the thread has been started (after preregisterNewThread())
692  DLLLOCAL void registerNewThread(int tid) {
693  // grab program-level lock
694  AutoLocker al(plock);
695 
696  assert(thread_count);
697  ++tidmap[tid];
698  }
699 
700  /*
701  DLLLOCAL int checkValid(ExceptionSink* xsink) {
702  if (ptid && ptid != gettid()) {
703  xsink->raiseException("PROGRAM-ERROR", "the Program accessed has already been deleted and therefore cannot be accessed at runtime");
704  return -1;
705  }
706  return 0;
707  }
708  */
709 
710  // returns 0 for OK, -1 for error
711  DLLLOCAL int incThreadCount(ExceptionSink* xsink) {
712  int tid = gettid();
713 
714  // grab program-level lock
715  AutoLocker al(plock);
716 
717  if (ptid && ptid != tid) {
718  xsink->raiseException("PROGRAM-ERROR", "the Program accessed has already been deleted and therefore cannot be accessed at runtime");
719  return -1;
720  }
721  if (parsing_in_progress) {
722  xsink->raiseException("PROGRAM-ERROR", "the Program accessed is currently undergoing parsing and cannot be accessed at runtime");
723  }
724 
725  ++tidmap[tid];
726  ++thread_count;
727  return 0;
728  }
729 
730  // throws a QoreStandardException if there is an error
731  DLLLOCAL void incThreadCount() {
732  int tid = gettid();
733 
734  // grab program-level lock
735  AutoLocker al(plock);
736 
737  if (ptid && ptid != tid) {
738  throw QoreStandardException("PROGRAM-ERROR", "the Program accessed has already been deleted and therefore cannot be accessed at runtime");
739  }
740  if (parsing_in_progress) {
741  throw QoreStandardException("PROGRAM-ERROR", "the Program accessed is currently undergoing parsing and cannot be accessed at runtime");
742  }
743 
744  ++tidmap[tid];
745  ++thread_count;
746  }
747 
748  DLLLOCAL void decThreadCount(int tid) {
749  // grab program-level lock
750  AutoLocker al(plock);
751 
752  ptid_map_t::iterator i = tidmap.find(tid);
753  assert(i != tidmap.end());
754  if (!--i->second)
755  tidmap.erase(i);
756 
757  assert(thread_count > 0);
758  if (!--thread_count && thread_waiting)
759  pcond.broadcast();
760  }
761 
762  // gets a list of all thread IDs using this Program
763  DLLLOCAL void getThreadList(QoreListNode& l) {
764  // grab program-level lock
765  AutoLocker al(plock);
766 
767  for (auto& i : tidmap) {
768  l.push(i.first, nullptr);
769  }
770  }
771 
772  DLLLOCAL int lockParsing(ExceptionSink* xsink) {
773  // grab program-level lock
774  AutoLocker al(plock);
775 
776  bool curr = (pgm == getProgram());
777  if (!curr) {
778  while (parse_count) {
779  ++thread_waiting;
780  pcond.wait(plock);
781  --thread_waiting;
782  }
783  }
784 
785  if (ptid && ptid != gettid()) {
786  if (xsink)
787  xsink->raiseException("PROGRAM-ERROR", "the Program accessed has already been deleted and therefore cannot be accessed");
788  return -1;
789  }
790 
791  //printd(5, "qore_program_private::lockParsing() this: %p ptid: %d thread_count: %d parse_count: %d -> %d\n", this, ptid, thread_count, parse_count, parse_count + 1);
792  ++parse_count;
793  return 0;
794  }
795 
796  DLLLOCAL void unlockParsing() {
797  // grab program-level lock
798  AutoLocker al(plock);
799  if (!(--parse_count) && thread_waiting)
800  pcond.broadcast();
801  }
802 
803  // called only with plock held
804  DLLLOCAL void waitForAllThreadsToTerminateIntern() {
805  int tid = gettid();
806 
807  ptid_map_t::iterator i = tidmap.find(tid);
808  unsigned adj = (i != tidmap.end() ? 1 : 0);
809 
810  while ((thread_count - adj) || parse_count) {
811  ++thread_waiting;
812  pcond.wait(plock);
813  --thread_waiting;
814  }
815  }
816 
817  DLLLOCAL void waitForAllThreadsToTerminate() {
818  // grab program-level lock
819  AutoLocker al(&plock);
820  waitForAllThreadsToTerminateIntern();
821  }
822 
823  DLLLOCAL const char* parseGetScriptPath() const {
824  return script_path.empty() ? nullptr : script_path.c_str();
825  }
826 
827  DLLLOCAL const char* parseGetScriptDir() const {
828  return script_dir.empty() ? nullptr : script_dir.c_str();
829  }
830 
831  DLLLOCAL const char* parseGetScriptName() const {
832  return script_name.empty() ? nullptr : script_name.c_str();
833  }
834 
835  DLLLOCAL QoreStringNode* getScriptPath() const {
836  // grab program-level parse lock
837  AutoLocker al(&plock);
838  return script_path.empty() ? nullptr : new QoreStringNode(script_path);
839  }
840 
841  DLLLOCAL QoreStringNode* getScriptDir() const {
842  // grab program-level parse lock
843  AutoLocker al(&plock);
844  return script_dir.empty() ? nullptr : new QoreStringNode(script_dir);
845  }
846 
847  DLLLOCAL QoreStringNode* getScriptName() const {
848  // grab program-level parse lock
849  AutoLocker al(&plock);
850  return script_name.empty() ? nullptr : new QoreStringNode(script_name);
851  }
852 
853  DLLLOCAL void setScriptPathExtern(const char* path) {
854  // grab program-level parse lock
855  AutoLocker al(&plock);
856  setScriptPath(path);
857  }
858 
859  DLLLOCAL void setScriptPath(const char* path) {
860  if (!path) {
861  script_dir.clear();
862  script_path.clear();
863  script_name.clear();
864  } else {
865  // find file name
866  const char* p = q_basenameptr(path);
867  if (p == path) {
868  script_name = path;
869  script_dir = "." QORE_DIR_SEP_STR;
870  script_path = script_dir + script_name;
871  } else {
872  script_path = path;
873  script_name = p;
874  script_dir.assign(path, p - path);
875  }
876  }
877  }
878 
879  DLLLOCAL QoreListNode* getVarList() {
880  //AutoLocker al(&plock);
881  // FIXME: implement
882  return new QoreListNode(stringTypeInfo);
883  //return global_var_list.getVarList();
884  }
885 
886  DLLLOCAL QoreListNode* getFeatureList() const {
887  QoreListNode* l = new QoreListNode(stringTypeInfo);
888 
889  for (CharPtrList::const_iterator i = featureList.begin(), e = featureList.end(); i != e; ++i)
890  l->push(new QoreStringNode(*i), nullptr);
891 
892  for (CharPtrList::const_iterator i = userFeatureList.begin(), e = userFeatureList.end(); i != e; ++i)
893  l->push(new QoreStringNode(*i), nullptr);
894 
895  return l;
896  }
897 
898  DLLLOCAL void internParseRollback(ExceptionSink* xsink);
899 
900  // call must push the current program on the stack and pop it afterwards
901  DLLLOCAL int internParsePending(ExceptionSink* xsink, const char* code, const char* label, const char* orig_src = nullptr, int offset = 0) {
902  //printd(5, "qore_program_private::internParsePending() code: %p %d bytes label: '%s' src: '%s' offset: %d\n", code, strlen(code), label, orig_src ? orig_src : "(null)", offset);
903 
904  assert(code && code[0]);
905 
906  // save this file name for storage in the parse tree and deletion
907  // when the QoreProgram object is deleted
908  const char* sname = label;
909  const char* src = orig_src;
910  if (orig_src) {
911  addFile(src, sname, offset);
912  } else {
913  addFile(sname);
914  }
915 
916  QoreParseLocationHelper qplh(sname, src, offset);
917 
918  /*
919  beginParsing() called in QoreParseLocationHelper constructor but unless we call here twice
920  then endParsing() coredump with "Assertion `td->plStack' failed"
921  Seems endParsing() should do rather "if (!td->plStack) return".
922  */
923  beginParsing(sname, nullptr, src, offset);
924 
925  if (!parsing_in_progress) {
926  parsing_in_progress = true;
927  }
928 
929  // no need to save buffer, because it's deleted automatically in lexer
930  //printd(5, "qore_program_private::internParsePending() parsing tag: %s (%p): '%s'\n", label, label, code);
931 
932  yyscan_t lexer;
933  yylex_init(&lexer);
934 
935  yy_scan_string(code, lexer);
936  yyset_lineno(1, lexer);
937  // yyparse() will call endParsing() and restore old pgm position
938  yyparse(lexer);
939 
940  printd(5, "qore_program_private::internParsePending() returned from yyparse()\n");
941  int rc = 0;
942  if (parseSink->isException()) {
943  rc = -1;
944  printd(5, "qore_program_private::internParsePending() parse exception: calling parseRollback()\n");
945  internParseRollback(xsink);
946  requires_exception = false;
947  }
948 
949  printd(5, "qore_program_private::internParsePending() about to call yylex_destroy()\n");
950  yylex_destroy(lexer);
951  printd(5, "qore_program_private::internParsePending() returned from yylex_destroy()\n");
952  return rc;
953  }
954 
955  DLLLOCAL void startParsing(ExceptionSink* xsink, ExceptionSink* wS, int wm) {
956  warnSink = wS;
957  pwo.warn_mask = wm;
958  parseSink = xsink;
959 
960  if (pendingParseSink) {
961  parseSink->assimilate(pendingParseSink);
962  pendingParseSink = nullptr;
963  }
964  }
965 
966  DLLLOCAL int checkParse(ExceptionSink* xsink) const {
967  if (parsing_done) {
968  xsink->raiseException("PARSE-ERROR", "parsing can only happen once for each Program container");
969  return -1;
970  }
971  return 0;
972  }
973 
974  DLLLOCAL int parsePending(const char* code, const char* label, ExceptionSink* xsink, ExceptionSink* wS, int wm, const char* orig_src = nullptr, int offset = 0) {
975  //printd(5, "qore_program_private::parsePending() wm=0x%x UV=0x%x on: %d\n", wm, QP_WARN_UNREFERENCED_VARIABLE, wm & QP_WARN_UNREFERENCED_VARIABLE);
976 
977  ProgramRuntimeParseContextHelper pch(xsink, pgm);
978  assert(xsink);
979  if (*xsink) {
980  return -1;
981  }
982 
983  if (checkParse(xsink)) {
984  return -1;
985  }
986 
987  startParsing(xsink, wS, wm);
988 
989  int rc = internParsePending(xsink, code, label, orig_src, offset);
990  warnSink = nullptr;
991 #ifdef DEBUG
992  parseSink = nullptr;
993 #endif
994  return rc;
995  }
996 
997  // caller must have grabbed the lock and put the current program on the program stack
998  DLLLOCAL int internParseCommit();
999 
1000  DLLLOCAL int parseCommit(ExceptionSink* xsink, ExceptionSink* wS, int wm) {
1001  ProgramRuntimeParseCommitContextHelper pch(xsink, pgm);
1002  assert(xsink);
1003  if (*xsink) {
1004  return -1;
1005  }
1006 
1007  if (checkParse(xsink)) {
1008  return -1;
1009  }
1010 
1011  startParsing(xsink, wS, wm);
1012 
1013  // finalize parsing, back out or commit all changes
1014  int rc = internParseCommit();
1015 
1016 #ifdef DEBUG
1017  parseSink = nullptr;
1018 #endif
1019  warnSink = nullptr;
1020  // release program-level parse lock
1021  return rc;
1022  }
1023 
1024  DLLLOCAL int parseRollback(ExceptionSink* xsink) {
1025  ProgramRuntimeParseContextHelper pch(xsink, pgm);
1026  assert(xsink);
1027  if (*xsink)
1028  return -1;
1029 
1030  // back out all pending changes
1031  internParseRollback(xsink);
1032  return 0;
1033  }
1034 
1035  DLLLOCAL void parse(FILE *fp, const char* name, ExceptionSink* xsink, ExceptionSink* wS, int wm) {
1036  assert(xsink);
1037  printd(5, "QoreProgram::parse(fp: %p, name: %s, xsink: %p, wS: %p, wm: %d)\n", fp, name, xsink, wS, wm);
1038 
1039  // if already at the end of file, then return
1040  // try to get one character from file
1041  int c = fgetc(fp);
1042  if (feof(fp)) {
1043  printd(5, "QoreProgram::parse(fp: %p, name: %s) EOF\n", fp, name);
1044  return;
1045  }
1046  // push back read character
1047  ungetc(c, fp);
1048 
1049  yyscan_t lexer;
1050 
1051  {
1052  ProgramRuntimeParseCommitContextHelper pch(xsink, pgm);
1053  if (*xsink) {
1054  return;
1055  }
1056 
1057  if (checkParse(xsink)) {
1058  return;
1059  }
1060 
1061  startParsing(xsink, wS, wm);
1062 
1063  // save this file name for storage in the parse tree and deletion
1064  // when the QoreProgram object is deleted
1065  const char* sname = name;
1066  addFile(sname);
1067 
1068  QoreParseLocationHelper qplh(sname, nullptr, 0);
1069 
1070  /*
1071  beginParsing() called in QoreParseLocationHelper constructor but unless we call here twice
1072  then endParsing() coredump with "Assertion `td->plStack' failed"
1073  Seems endParsing() should do rather "if (!td->plStack) return".
1074  */
1075  beginParsing(sname);
1076 
1077  if (!parsing_in_progress) {
1078  parsing_in_progress = true;
1079  }
1080 
1081  //printd(5, "QoreProgram::parse(): about to call yyparse()\n");
1082  yylex_init(&lexer);
1083  yyset_in(fp, lexer);
1084  // yyparse() will call endParsing() and restore old pgm position
1085  yyparse(lexer);
1086 
1087  // finalize parsing, back out or commit all changes
1088  internParseCommit();
1089 
1090 #ifdef DEBUG
1091  parseSink = nullptr;
1092 #endif
1093  warnSink = nullptr;
1094  // release program-level parse lock
1095  }
1096 
1097  yylex_destroy(lexer);
1098  if (only_first_except && exceptions_raised > 1)
1099  fprintf(stderr, "\n%d exception(s) skipped\n\n", exceptions_raised);
1100  }
1101 
1102  DLLLOCAL void parse(const QoreString *str, const QoreString *lstr, ExceptionSink* xsink, ExceptionSink* wS, int wm, const QoreString* source = nullptr, int offset = 0) {
1103  assert(xsink);
1104  if (!str->strlen())
1105  return;
1106 
1107  // ensure code string has correct character set encoding
1108  TempEncodingHelper tstr(str, QCS_DEFAULT, xsink);
1109  if (*xsink)
1110  return;
1111 
1112  // ensure label string has correct character set encoding
1113  TempEncodingHelper tlstr(lstr, QCS_DEFAULT, xsink);
1114  if (*xsink)
1115  return;
1116 
1117  TempEncodingHelper src;
1118  if (source && !source->empty() && !src.set(source, QCS_DEFAULT, xsink))
1119  return;
1120 
1121  parse(tstr->getBuffer(), tlstr->getBuffer(), xsink, wS, wm, source ? src->getBuffer() : 0, offset);
1122  }
1123 
1124  DLLLOCAL void parse(const char* code, const char* label, ExceptionSink* xsink, ExceptionSink* wS, int wm, const char* orig_src = 0, int offset = 0) {
1125  //printd(5, "qore_program_private::parse(%s) pgm: %p po: %lld\n", label, pgm, pwo.parse_options);
1126 
1127  assert(code && code[0]);
1128  assert(xsink);
1129 
1130  ProgramRuntimeParseCommitContextHelper pch(xsink, pgm);
1131  if (*xsink) {
1132  return;
1133  }
1134 
1135  if (checkParse(xsink)) {
1136  return;
1137  }
1138 
1139  startParsing(xsink, wS, wm);
1140 
1141  // parse text given
1142  if (!internParsePending(xsink, code, label, orig_src, offset))
1143  internParseCommit(); // finalize parsing, back out or commit all changes
1144 
1145 #ifdef DEBUG
1146  parseSink = nullptr;
1147 #endif
1148  warnSink = nullptr;
1149  }
1150 
1151  DLLLOCAL void parseFile(const char* filename, ExceptionSink* xsink, ExceptionSink* wS, int wm) {
1152  QORE_TRACE("QoreProgram::parseFile()");
1153 
1154  printd(5, "QoreProgram::parseFile(%s)\n", filename);
1155 
1156  FILE *fp;
1157  if (!(fp = fopen(filename, "r"))) {
1158  if ((only_first_except && !exceptions_raised) || !only_first_except)
1159  xsink->raiseErrnoException("PARSE-EXCEPTION", errno, "cannot open qore script '%s'", filename);
1160  exceptions_raised++;
1161  return;
1162  }
1163  ON_BLOCK_EXIT(fclose, fp);
1164 
1165  setScriptPath(filename);
1166 
1167  ProgramRuntimeParseCommitContextHelper pch(xsink, pgm);
1168  if (*xsink)
1169  return;
1170 
1171  parse(fp, filename, xsink, wS, wm);
1172  }
1173 
1174  DLLLOCAL void parsePending(const QoreString *str, const QoreString *lstr, ExceptionSink* xsink, ExceptionSink* wS, int wm, const QoreString* source = 0, int offset = 0) {
1175  assert(!str->empty());
1176  assert(xsink);
1177 
1178  // ensure code string has correct character set encoding
1179  TempEncodingHelper tstr(str, QCS_DEFAULT, xsink);
1180  if (*xsink)
1181  return;
1182 
1183  // ensure label string has correct character set encoding
1184  TempEncodingHelper tlstr(lstr, QCS_DEFAULT, xsink);
1185  if (*xsink)
1186  return;
1187 
1188  TempEncodingHelper src;
1189  if (source && !source->empty() && !src.set(source, QCS_DEFAULT, xsink))
1190  return;
1191 
1192  parsePending(tstr->getBuffer(), tlstr->getBuffer(), xsink, wS, wm, source ? src->getBuffer() : 0, offset);
1193  }
1194 
1195  // called during run time (not during parsing)
1196  DLLLOCAL void importFunction(ExceptionSink* xsink, QoreFunction *u, const qore_ns_private& oldns, const char* new_name = nullptr, bool inject = false);
1197 
1198  DLLLOCAL void del(ExceptionSink* xsink);
1199 
1200  DLLLOCAL QoreHashNode* getThreadData() {
1201  QoreHashNode* h = thread_local_storage->get();
1202  if (!h) {
1203  h = new QoreHashNode(autoTypeInfo);
1204  thread_local_storage->set(h);
1205  }
1206 
1207  return h;
1208  }
1209 
1210  DLLLOCAL QoreHashNode* clearThreadData(ExceptionSink* xsink) {
1211  QoreHashNode* h = thread_local_storage->get();
1212  printd(5, "QoreProgram::clearThreadData() this: %p h: %p (size: %d)\n", this, h, h ? h->size() : 0);
1213  if (h)
1214  h->clear(xsink);
1215  return h;
1216  }
1217 
1218  DLLLOCAL void deleteThreadData(ExceptionSink* xsink) {
1219  QoreHashNode* h = clearThreadData(xsink);
1220  if (h) {
1221  h->deref(xsink);
1222  thread_local_storage->set(0);
1223  }
1224  }
1225 
1226  DLLLOCAL void finalizeThreadData(ThreadProgramData* td, arg_vec_t*& cl) {
1227  QoreHashNode* h = thread_local_storage->get();
1228  if (h) {
1229  if (!cl)
1230  cl = new arg_vec_t;
1231  cl->push_back(h);
1232  thread_local_storage->set(0);
1233  }
1234 
1235  // delete all local variables for this thread
1236  AutoLocker al(tlock);
1237  if (tclear)
1238  return;
1239 
1240  pgm_data_map_t::iterator i = pgm_data_map.find(td);
1241  if (i != pgm_data_map.end())
1242  i->second->finalize(cl);
1243  }
1244 
1245  // TODO: xsink should not be necessary; vars should be emptied and finalized in the finalizeThreadData() call
1246  DLLLOCAL int endThread(ThreadProgramData* td, ExceptionSink* xsink) {
1247  ThreadLocalProgramData* tlpd = 0;
1248 
1249  // delete all local variables for this thread
1250  {
1251  AutoLocker al(tlock);
1252  if (tclear)
1253  return -1;
1254 
1255  pgm_data_map_t::iterator i = pgm_data_map.find(td);
1256  if (i == pgm_data_map.end())
1257  return -1;
1258  tlpd = i->second;
1259  pgm_data_map.erase(i);
1260  }
1261 
1262  tlpd->del(xsink);
1263  return 0;
1264  }
1265 
1266  DLLLOCAL void doTopLevelInstantiation(ThreadLocalProgramData& tlpd) {
1267  // instantiate top-level vars for this thread
1268  const LVList* lvl = sb.getLVList();
1269  if (lvl)
1270  for (unsigned i = 0; i < lvl->size(); ++i)
1271  lvl->lv[i]->instantiate();
1272 
1273  //printd(5, "qore_program_private::doTopLevelInstantiation() lvl: %p setup %ld local vars pgm: %p\n", lvl, lvl ? lvl->size() : 0, getProgram());
1274 
1275  tlpd.inst = true;
1276  }
1277 
1278  // returns true if setting for the first time, false if not
1279  DLLLOCAL bool setThreadVarData(ThreadProgramData* td, ThreadLocalProgramData*& new_tlpd, bool run) {
1280  SafeLocker sl(tlock);
1281  // wait for data to finished being cleared if applicable
1282  while (tclear) {
1283  if (tclear == gettid()) {
1284  // can be called recursively when destructors are run in local variable finalization
1285  assert(pgm_data_map.find(td) != pgm_data_map.end());
1286  assert(pgm_data_map[td]->inst);
1287  break;
1288  }
1289  ++twaiting;
1290  tcond.wait(tlock);
1291  --twaiting;
1292  }
1293 
1294  pgm_data_map_t::iterator i = pgm_data_map.find(td);
1295  if (i == pgm_data_map.end()) {
1296  assert(!tclear);
1297  ThreadLocalProgramData* tlpd = new ThreadLocalProgramData;
1298 
1299  printd(5, "qore_program_private::setThreadVarData() (first) this: %p pgm: %p td: %p run: %s inst: %s\n", this, pgm, td, run ? "true" : "false", tlpd->inst ? "true" : "false");
1300 
1301  new_tlpd = tlpd;
1302 
1303  pgm_data_map.insert(pgm_data_map_t::value_type(td, tlpd));
1304 
1305  sl.unlock();
1306 
1307  if (run) {
1308  printd(5, "qore_program_private::setThreadVarData() (first) this: %p pgm: %p td: %p\n", this, pgm, td);
1309  doTopLevelInstantiation(*tlpd);
1310  }
1311 
1312  return true;
1313  }
1314 
1315  ThreadLocalProgramData* tlpd = pgm_data_map[td];
1316  new_tlpd = tlpd;
1317 
1318  sl.unlock();
1319 
1320  printd(5, "qore_program_private::setThreadVarData() (not first) this: %p pgm: %p td: %p run: %s inst: %s\n", this, pgm, td, run ? "true" : "false", tlpd->inst ? "true" : "false");
1321 
1322  if (run && !tlpd->inst) {
1323  doTopLevelInstantiation(*tlpd);
1324  }
1325 
1326  return false;
1327  }
1328 
1329  DLLLOCAL const AbstractQoreZoneInfo* currentTZ(ThreadProgramData* tpd = get_thread_program_data()) const {
1330  AutoLocker al(tlock);
1331  pgm_data_map_t::const_iterator i = pgm_data_map.find(tpd);
1332  if (i != pgm_data_map.end() && i->second->tz_set)
1333  return i->second->tz;
1334  return TZ;
1335  }
1336 
1337  DLLLOCAL void setTZ(const AbstractQoreZoneInfo* n_TZ) {
1338  TZ = n_TZ;
1339  }
1340 
1341  DLLLOCAL void exportFunction(ExceptionSink* xsink, qore_program_private* p, const char* name, const char* new_name = nullptr, bool inject = false) {
1342  if (this == p) {
1343  xsink->raiseException("FUNCTION-IMPORT-ERROR", "cannot import a function from the same Program object");
1344  return;
1345  }
1346 
1347  if (inject && !(p->pwo.parse_options & PO_ALLOW_INJECTION)) {
1348  xsink->raiseException("FUNCTION-IMPORT-ERROR", "cannot import function \"%s\" in a Program object without PO_ALLOW_INJECTION set", name);
1349  return;
1350  }
1351 
1352  const QoreFunction* u;
1353  const qore_ns_private* ns = nullptr;
1354 
1355  {
1356  ProgramRuntimeParseAccessHelper rah(xsink, pgm);
1357  if (*xsink)
1358  return;
1359  u = qore_root_ns_private::runtimeFindFunction(*RootNS, name, ns);
1360  }
1361 
1362  if (!u)
1363  xsink->raiseException("PROGRAM-IMPORTFUNCTION-NO-FUNCTION", "function '%s' does not exist in the current program scope", name);
1364  else {
1365  assert(ns);
1366  p->importFunction(xsink, const_cast<QoreFunction*>(u), *ns, new_name, inject);
1367  }
1368  }
1369 
1370  DLLLOCAL bool parseExceptionRaised() const {
1371  assert(parseSink);
1372  return *parseSink;
1373  }
1374 
1375  DLLLOCAL void setParseOptionsIntern(int64 po) {
1376  pwo.parse_options |= po;
1377  }
1378 
1379  DLLLOCAL void disableParseOptionsIntern(int64 po) {
1380  pwo.parse_options &= ~po;
1381  }
1382 
1383  DLLLOCAL void replaceParseOptionsIntern(int64 po) {
1384  pwo.parse_options = po;
1385  }
1386 
1387  DLLLOCAL int setParseOptions(int64 po, ExceptionSink* xsink) {
1388  assert(xsink);
1389  // only raise the exception if parse options are locked and the option is not a "free option"
1390  // also check if options may be made more restrictive and the option also does so
1391  if (((po & PO_FREE_OPTIONS) != po) && po_locked && (!po_allow_restrict || (po & PO_POSITIVE_OPTIONS))) {
1392  xsink->raiseException("OPTIONS-LOCKED", "parse options have been locked on this program object");
1393  return -1;
1394  }
1395 
1396  setParseOptionsIntern(po);
1397  return 0;
1398  }
1399 
1400  DLLLOCAL int disableParseOptions(int64 po, ExceptionSink* xsink) {
1401  assert(xsink);
1402  // only raise the exception if parse options are locked and the option is not a "free option"
1403  // note: disabling PO_POSITIVE_OPTION is more restrictive so let's allow to disable
1404  if (((po & PO_FREE_OPTIONS) != po) && po_locked && !po_allow_restrict) {
1405  xsink->raiseException("OPTIONS-LOCKED", "parse options have been locked on this program object");
1406  return -1;
1407  }
1408 
1409  disableParseOptionsIntern(po);
1410  return 0;
1411  }
1412 
1413  DLLLOCAL int replaceParseOptions(int64 po, ExceptionSink* xsink) {
1414  assert(xsink);
1415  if (!(getProgram()->priv->pwo.parse_options & PO_NO_CHILD_PO_RESTRICTIONS)) {
1416  xsink->raiseException("OPTION-ERROR", "the calling Program does not have the PO_NO_CHILD_PO_RESTRICTIONS option set, and therefore cannot call Program::replaceParseOptions()");
1417  return -1;
1418  }
1419 
1420  //printd(5, "qore_program_private::replaceParseOptions() this: %p pgm: %p replacing po: %lld with po: %lld\n", this, pgm, pwo.parse_options, po);
1421  replaceParseOptionsIntern(po);
1422  return 0;
1423  }
1424 
1425  DLLLOCAL int parseSetParseOptions(const QoreProgramLocation* loc, int64 po) {
1426  // only raise the exception if parse options are locked and the option is not a "free option"
1427  // also check if options may be made more restrictive and the option also does so
1428  if (((po & PO_FREE_OPTIONS) != po) && po_locked && (!po_allow_restrict || (po & PO_POSITIVE_OPTIONS))) {
1429  parse_error(*loc, "parse options have been locked on this program object");
1430  return -1;
1431  }
1432 
1433  setParseOptionsIntern(po);
1434  return 0;
1435  }
1436 
1437  DLLLOCAL int parseDisableParseOptions(const QoreProgramLocation* loc, int64 po) {
1438  // only raise the exception if parse options are locked and the option is not a "free option"
1439  // note: disabling PO_POSITIVE_OPTION is more restrictive so let's allow to disable
1440  if (((po & PO_FREE_OPTIONS) != po) && po_locked && !po_allow_restrict) {
1441  parse_error(*loc, "parse options have been locked on this program object");
1442  return -1;
1443  }
1444 
1445  disableParseOptionsIntern(po);
1446  return 0;
1447  }
1448 
1449  DLLLOCAL void parseSetTimeZone(const char* zone) {
1450  // check PO_NO_LOCALE_CONTROL
1451  ExceptionSink xsink;
1452  if (pwo.parse_options & PO_NO_LOCALE_CONTROL) {
1453  mergeParseException(xsink);
1454  return;
1455  }
1456 
1457  const AbstractQoreZoneInfo *tz = (*zone == '-' || *zone == '+') ? QTZM.findCreateOffsetZone(zone, &xsink) : QTZM.findLoadRegion(zone, &xsink);
1458  if (xsink) {
1459  assert(!tz);
1460  mergeParseException(xsink);
1461  return;
1462  }
1463  // note that tz may be NULL in case the offset is UTC (ie 0)
1464  assert(tz || ((*zone == '-' || *zone == '+')));
1465 
1466  TZ = tz;
1467  }
1468 
1469  DLLLOCAL void makeParseException(const QoreProgramLocation& loc, const char* err, QoreStringNode* desc) {
1470  QORE_TRACE("QoreProgram::makeParseException()");
1471 
1472  QoreStringNodeHolder d(desc);
1473  if (!requires_exception) {
1474  if ((only_first_except && !exceptions_raised) || !only_first_except) {
1475  QoreException *ne = new ParseException(loc, err, d.release());
1476  parseSink->raiseException(ne);
1477  }
1478  exceptions_raised++;
1479  }
1480  }
1481 
1482  DLLLOCAL void parseException(QoreProgramLocation& loc, const char* fmt, ...) {
1483  //printd(5, "qore_program_private::parseException(\"%s\", ...) called\n", fmt);
1484 
1485  // ignore if a "requires" exception has been raised
1486  if (requires_exception)
1487  return;
1488 
1489  QoreStringNode* desc = new QoreStringNode;
1490  while (true) {
1491  va_list args;
1492  va_start(args, fmt);
1493  int rc = desc->vsprintf(fmt, args);
1494  va_end(args);
1495  if (!rc)
1496  break;
1497  }
1498  makeParseException(loc, "PARSE-EXCEPTION", desc);
1499  }
1500 
1501  // internal method - does not bother with the parse lock
1502  // returns true if the define already existed
1503  DLLLOCAL bool setDefine(const char* name, QoreValue v, ExceptionSink* xsink) {
1504  dmap_t::iterator i = dmap.find(name);
1505  if (i != dmap.end()) {
1506  i->second.discard(xsink);
1507  i->second = v;
1508  return true;
1509  }
1510 
1511  dmap[name] = v;
1512  return false;
1513  }
1514 
1515  // internal method - does not bother with the parse lock
1516  DLLLOCAL const QoreValue getDefine(const char* name, bool& is_defined) {
1517  dmap_t::iterator i = dmap.find(name);
1518  if (i != dmap.end()) {
1519  is_defined = true;
1520  return i->second;
1521  }
1522  is_defined = false;
1523  return QoreValue();
1524  }
1525 
1526  DLLLOCAL QoreValue runTimeGetDefine(const char* name, bool& is_defined) {
1527  AutoLocker al(plock);
1528  return getDefine(name, is_defined).refSelf();
1529  }
1530 
1531  DLLLOCAL QoreHashNode* runTimeGetAllDefines();
1532 
1533  // internal method - does not bother with the parse lock
1534  // returns true if the define existed
1535  DLLLOCAL bool unDefine(const char* name, ExceptionSink* xsink) {
1536  dmap_t::iterator i = dmap.find(name);
1537  if (i != dmap.end()) {
1538  i->second.discard(xsink);
1539  dmap.erase(i);
1540  return true;
1541  }
1542  return false;
1543  }
1544 
1545  DLLLOCAL bool parseUnDefine(const char* name) {
1546  PreParseHelper pph(this);
1547  return unDefine(name, parseSink);
1548  }
1549 
1550  DLLLOCAL bool runTimeUnDefine(const char* name, ExceptionSink* xsink) {
1551  AutoLocker al(plock);
1552  return unDefine(name, xsink);
1553  }
1554 
1555  // internal method - does not bother with the parse lock
1556  DLLLOCAL bool isDefined(const char* name) {
1557  dmap_t::iterator i = dmap.find(name);
1558  return i == dmap.end() ? false : true;
1559  }
1560 
1561  DLLLOCAL bool runTimeIsDefined(const char* name) {
1562  AutoLocker al(plock);
1563  return isDefined(name);
1564  }
1565 
1566  DLLLOCAL int checkDefine(const QoreProgramLocation* loc, const char* str, ExceptionSink* xsink) {
1567  const char* p = str;
1568  if (!isalpha(*p)) {
1569  xsink->raiseException(*loc, "PARSE-EXCEPTION", 0, "illegal define variable '%s'; does not begin with an alphabetic character", p);
1570  return -1;
1571  }
1572 
1573  while (*(++p)) {
1574  if (!isalnum(*p) && *p != '_') {
1575 
1576  xsink->raiseException(*loc, "PARSE-EXCEPTION", 0, "illegal character '%c' in define variable '%s'", *p, str);
1577  return -1;
1578  }
1579  }
1580 
1581  return 0;
1582  }
1583 
1584  DLLLOCAL void parseDefine(const QoreProgramLocation* loc, const char* str, QoreValue val) {
1585  PreParseHelper pph(this);
1586 
1587  if (checkDefine(loc, str, parseSink))
1588  return;
1589 
1590  setDefine(str, val, parseSink);
1591  }
1592 
1593  DLLLOCAL void runTimeDefine(const char* str, QoreValue val, ExceptionSink* xsink) {
1594  const QoreProgramLocation* loc = get_runtime_location();
1595 
1596  if (checkDefine(loc, str, xsink))
1597  return;
1598 
1599  AutoLocker al(plock);
1600  setDefine(str, val, xsink);
1601  }
1602 
1603  DLLLOCAL ResolvedCallReferenceNode* runtimeGetCallReference(const char* name, ExceptionSink* xsink) {
1604  assert(xsink);
1605  ProgramRuntimeParseAccessHelper pah(xsink, pgm);
1606  if (*xsink)
1607  return nullptr;
1608  return qore_root_ns_private::runtimeGetCallReference(*RootNS, name, xsink);
1609  }
1610 
1611  DLLLOCAL void pushParseOptions(const char* pf) {
1612  // ignore %push-parse-options used multiple times in the same file
1613  ppo_t::iterator i = ppo.lower_bound(pf);
1614  if (i != ppo.end() && !strcmp(pf, i->first))
1615  return;
1616  ppo.insert(i, ppo_t::value_type(pf, pwo.parse_options));
1617  //printd(5, "qore_program_private::pushParseOptions() this: %p %p '%s' saving %lld\n", this, pf, pf, pwo.parse_options);
1618  }
1619 
1620  DLLLOCAL void restoreParseOptions(const char* pf) {
1621  ppo_t::iterator i = ppo.find(pf);
1622  if (i != ppo.end()) {
1623  //printd(5, "qore_program_private::restoreParseOptions() this: %p %p '%s' restoring %lld\n", this, pf, pf, pwo.parse_options);
1624  pwo.parse_options = i->second;
1625  ppo.erase(i);
1626  }
1627  }
1628 
1629  DLLLOCAL void addParseException(ExceptionSink& xsink, const QoreProgramLocation* loc) {
1630  if (requires_exception) {
1631  xsink.clear();
1632  return;
1633  }
1634 
1635  if (loc) {
1636  // ensure that all exceptions reflect the current parse location
1637  xsink.overrideLocation(*loc);
1638  }
1639 
1640  parseSink->assimilate(xsink);
1641  }
1642 
1643  // returns the mask of domain options not met in the current program
1644  DLLLOCAL int64 parseAddDomain(int64 n_dom) {
1645  assert(!(n_dom & PO_FREE_OPTIONS));
1646  int64 rv = 0;
1647 
1648  // handle negative and positive options separately / differently
1649  int64 pos = (n_dom & PO_POSITIVE_OPTIONS);
1650  if (pos) {
1651  int64 p_tmp = pwo.parse_options & PO_POSITIVE_OPTIONS;
1652  // make sure all positive arguments are set
1653  if ((pos & p_tmp) != pos) {
1654  rv = ((pos & p_tmp) ^ pos);
1655  pend_dom |= pos;
1656  }
1657  }
1658  int64 neg = (n_dom & ~PO_POSITIVE_OPTIONS);
1659  if (neg && (neg & pwo.parse_options)) {
1660  rv |= (neg & pwo.parse_options);
1661  pend_dom |= neg;
1662  }
1663 
1664  //printd(5, "qore_program_private::parseAddDomain() this: %p n_dom: " QLLD " po: " QLLD "\n", this, n_dom, pwo.parse_options);
1665  return rv;
1666  }
1667 
1668  DLLLOCAL void exportGlobalVariable(const char* name, bool readonly, qore_program_private& tpgm, ExceptionSink* xsink);
1669 
1670  DLLLOCAL int setGlobalVarValue(const char* name, QoreValue val, ExceptionSink* xsink);
1671 
1672  // returns true if there was already a thread init closure set, false if not
1673  DLLLOCAL bool setThreadInit(const ResolvedCallReferenceNode* n_thr_init, ExceptionSink* xsink);
1674 
1675  DLLLOCAL void setThreadTZ(ThreadProgramData* tpd, const AbstractQoreZoneInfo* tz) {
1676  AutoLocker al(tlock);
1677  pgm_data_map_t::iterator i = pgm_data_map.find(tpd);
1678  assert(i != pgm_data_map.end());
1679  i->second->setTZ(tz);
1680  }
1681 
1682  DLLLOCAL const AbstractQoreZoneInfo* getThreadTZ(ThreadProgramData* tpd, bool& set) const {
1683  AutoLocker al(tlock);
1684  pgm_data_map_t::const_iterator i = pgm_data_map.find(tpd);
1685  assert(i != pgm_data_map.end());
1686  set = i->second->tz_set;
1687  return i->second->tz;
1688  }
1689 
1690  DLLLOCAL void clearThreadTZ(ThreadProgramData* tpd) {
1691  AutoLocker al(tlock);
1692  pgm_data_map_t::iterator i = pgm_data_map.find(tpd);
1693  assert(i != pgm_data_map.end());
1694  i->second->clearTZ();
1695  }
1696 
1697  DLLLOCAL void addStatement(AbstractStatement* s) {
1698  sb.addStatement(s);
1699 
1700  // see if top level statements are allowed
1701  if (pwo.parse_options & PO_NO_TOP_LEVEL_STATEMENTS && !s->isDeclaration())
1702  parse_error(*s->loc, "illegal top-level statement (conflicts with parse option NO_TOP_LEVEL_STATEMENTS)");
1703  }
1704 
1705  DLLLOCAL void importClass(ExceptionSink* xsink, qore_program_private& from_pgm, const char* path, const char* new_name = nullptr, bool inject = false, q_setpub_t set_pub = CSP_UNCHANGED);
1706 
1707  DLLLOCAL void importHashDecl(ExceptionSink* xsink, qore_program_private& from_pgm, const char* path, const char* new_name = nullptr, q_setpub_t set_pub = CSP_UNCHANGED);
1708 
1709  DLLLOCAL const char* addString(const char* str) {
1710  str_set_t::iterator i = str_set.lower_bound(str);
1711  if (i == str_set.end() || strcmp(*i, str)) {
1712  str_vec.push_back(strdup(str));
1713  i = str_set.insert(i, str_vec.back());
1714  }
1715  return *i;
1716  }
1717 
1718  DLLLOCAL void addFile(const char*& file) {
1719  file = addString(file);
1720  printd(5, "qore_program_private::addFile('%s')\n", file);
1721  addStatementToIndexIntern(&statementByFileIndex, file, nullptr, -1, nullptr, -1);
1722  }
1723 
1724  DLLLOCAL void addFile(const char*& file, const char*& source, int offset) {
1725  file = addString(file);
1726  if (source) {
1727  source = addString(source);
1728  }
1729  printd(5, "qore_program_private::addFile('%s', '%s', %d)\n", file, source ? source : "(null)", offset);
1730  addStatementToIndexIntern(&statementByFileIndex, file, nullptr, -1, source, offset);
1731  if (source) {
1732  addStatementToIndexIntern(&statementByLabelIndex, source, nullptr, -1, file, offset);
1733  }
1734  }
1735 
1736  DLLLOCAL void addUserFeature(const char* f) {
1737  userFeatureList.push_back(f);
1738  }
1739 
1740  DLLLOCAL void runtimeImportSystemClassesIntern(const qore_program_private& spgm, ExceptionSink* xsink);
1741  DLLLOCAL void runtimeImportSystemConstantsIntern(const qore_program_private& spgm, ExceptionSink* xsink);
1742  DLLLOCAL void runtimeImportSystemFunctionsIntern(const qore_program_private& spgm, ExceptionSink* xsink);
1743  DLLLOCAL void runtimeImportSystemHashDeclsIntern(const qore_program_private& spgm, ExceptionSink* xsink);
1744 
1745  DLLLOCAL void runtimeImportSystemClasses(ExceptionSink* xsink);
1746  DLLLOCAL void runtimeImportSystemConstants(ExceptionSink* xsink);
1747  DLLLOCAL void runtimeImportSystemFunctions(ExceptionSink* xsink);
1748  DLLLOCAL void runtimeImportSystemHashDecls(ExceptionSink* xsink);
1749  DLLLOCAL void runtimeImportSystemApi(ExceptionSink* xsink);
1750 
1751  DLLLOCAL void doThreadInit(ExceptionSink* xsink);
1752 
1753  DLLLOCAL const QoreClass* runtimeFindClass(const char* class_name, ExceptionSink* xsink) const;
1754 
1755  DLLLOCAL void setExternalData(const char* owner, AbstractQoreProgramExternalData* pud) {
1756  AutoLocker al(plock);
1757  assert(extmap.find(owner) == extmap.end());
1758  extmap.insert(extmap_t::value_type(owner, pud));
1759  }
1760 
1761  DLLLOCAL AbstractQoreProgramExternalData* getExternalData(const char* owner) const {
1762  AutoLocker al(plock);
1763  extmap_t::const_iterator i = extmap.find(owner);
1764  return i == extmap.end() ? nullptr : i->second;
1765  }
1766 
1767  DLLLOCAL QoreHashNode* getGlobalVars() const {
1768  return qore_root_ns_private::getGlobalVars(*RootNS);
1769  }
1770 
1771  DLLLOCAL LocalVar* createLocalVar(const char* name, const QoreTypeInfo* typeInfo);
1772 
1773  DLLLOCAL const AbstractQoreFunctionVariant* runtimeFindCall(const char* name, const QoreListNode* params, ExceptionSink* xsink);
1774 
1775  DLLLOCAL QoreListNode* runtimeFindCallVariants(const char* name, ExceptionSink* xsink);
1776 
1777  DLLLOCAL static const QoreClass* runtimeFindClass(const QoreProgram& pgm, const char* class_name, ExceptionSink* xsink) {
1778  return pgm.priv->runtimeFindClass(class_name, xsink);
1779  }
1780 
1781  DLLLOCAL static void doThreadInit(QoreProgram& pgm, ExceptionSink* xsink) {
1782  pgm.priv->doThreadInit(xsink);
1783  }
1784 
1785  DLLLOCAL static int setReturnValue(QoreProgram& pgm, QoreValue val, ExceptionSink* xsink) {
1786  ValueHolder rv(val, xsink);
1787  if (!pgm.priv->exec_class) {
1788  xsink->raiseException("SETRETURNVALUE-ERROR", "cannot set return value when not running in %%exec-class mode; in this case simply return the value directly (or call exit(<val>))");
1789  return -1;
1790  }
1791  pgm.priv->exec_class_rv.discard(xsink);
1792  pgm.priv->exec_class_rv = rv.release();
1793  return 0;
1794  }
1795 
1796  // called when starting a new thread before the new thread is started, to avoid race conditions
1797  DLLLOCAL static int preregisterNewThread(QoreProgram& pgm, ExceptionSink* xsink) {
1798  return pgm.priv->preregisterNewThread(xsink);
1799  }
1800 
1801  // called when thread startup fails after preregistration
1802  DLLLOCAL static void cancelPreregistration(QoreProgram& pgm) {
1803  pgm.priv->cancelPreregistration();
1804  }
1805 
1806  // called from the new thread once the thread has been started (after preregisterNewThread())
1807  DLLLOCAL static void registerNewThread(QoreProgram& pgm, int tid) {
1808  pgm.priv->registerNewThread(tid);
1809  }
1810 
1811  DLLLOCAL static void runtimeImportSystemClasses(QoreProgram& pgm, ExceptionSink* xsink) {
1812  pgm.priv->runtimeImportSystemClasses(xsink);
1813  }
1814 
1815  DLLLOCAL static void runtimeImportSystemHashDecls(QoreProgram& pgm, ExceptionSink* xsink) {
1816  pgm.priv->runtimeImportSystemHashDecls(xsink);
1817  }
1818 
1819  DLLLOCAL static void runtimeImportSystemConstants(QoreProgram& pgm, ExceptionSink* xsink) {
1820  pgm.priv->runtimeImportSystemConstants(xsink);
1821  }
1822 
1823  DLLLOCAL static void runtimeImportSystemFunctions(QoreProgram& pgm, ExceptionSink* xsink) {
1824  pgm.priv->runtimeImportSystemFunctions(xsink);
1825  }
1826 
1827  DLLLOCAL static void runtimeImportSystemApi(QoreProgram& pgm, ExceptionSink* xsink) {
1828  pgm.priv->runtimeImportSystemApi(xsink);
1829  }
1830 
1831  DLLLOCAL static qore_program_private* get(QoreProgram& pgm) {
1832  return pgm.priv;
1833  }
1834 
1835  DLLLOCAL static void clearThreadData(QoreProgram& pgm, ExceptionSink* xsink) {
1836  pgm.priv->clearThreadData(xsink);
1837  }
1838 
1839  DLLLOCAL static void addUserFeature(QoreProgram& pgm, const char* f) {
1840  pgm.priv->addUserFeature(f);
1841  }
1842 
1843  DLLLOCAL static void addStatement(QoreProgram& pgm, AbstractStatement* s) {
1844  pgm.priv->addStatement(s);
1845  }
1846 
1847  DLLLOCAL static const AbstractQoreZoneInfo* currentTZIntern(QoreProgram& pgm) {
1848  return pgm.priv->TZ;
1849  }
1850 
1851  DLLLOCAL static int lockParsing(QoreProgram& pgm, ExceptionSink* xsink) {
1852  return pgm.priv->lockParsing(xsink);
1853  }
1854 
1855  DLLLOCAL static void unlockParsing(QoreProgram& pgm) {
1856  pgm.priv->unlockParsing();
1857  }
1858 
1859  DLLLOCAL static int incThreadCount(QoreProgram& pgm, ExceptionSink* xsink) {
1860  return pgm.priv->incThreadCount(xsink);
1861  }
1862 
1863  DLLLOCAL static void decThreadCount(QoreProgram& pgm, int tid) {
1864  pgm.priv->decThreadCount(tid);
1865  }
1866 
1867  // locking is done by the signal manager
1868  DLLLOCAL static void addSignal(QoreProgram& pgm, int sig) {
1869  assert(pgm.priv->sigset.find(sig) == pgm.priv->sigset.end());
1870  pgm.priv->sigset.insert(sig);
1871  }
1872 
1873  // locking is done by the signal manager
1874  DLLLOCAL static void delSignal(QoreProgram& pgm, int sig) {
1875  assert(pgm.priv->sigset.find(sig) != pgm.priv->sigset.end());
1876  pgm.priv->sigset.erase(sig);
1877  }
1878 
1879  DLLLOCAL static void startThread(QoreProgram& pgm, ExceptionSink& xsink) {
1880  pgm.priv->qore_program_private_base::startThread(xsink);
1881  }
1882 
1883  DLLLOCAL static bool setThreadInit(QoreProgram& pgm, const ResolvedCallReferenceNode* n_thr_init, ExceptionSink* xsink) {
1884  return pgm.priv->setThreadInit(n_thr_init, xsink);
1885  }
1886 
1887  DLLLOCAL static ResolvedCallReferenceNode* runtimeGetCallReference(QoreProgram* pgm, const char* name, ExceptionSink* xsink) {
1888  return pgm->priv->runtimeGetCallReference(name, xsink);
1889  }
1890 
1891  DLLLOCAL static const ParseWarnOptions& getParseWarnOptions(const QoreProgram* pgm) {
1892  return pgm->priv->pwo;
1893  }
1894 
1895  DLLLOCAL static bool setSaveParseWarnOptions(const QoreProgram* pgm, const ParseWarnOptions &new_opts, ParseWarnOptions &old_opts) {
1896  if (new_opts == pgm->priv->pwo)
1897  return false;
1898  old_opts = pgm->priv->pwo;
1899  pgm->priv->pwo = new_opts;
1900  return true;
1901  }
1902 
1903  DLLLOCAL static void setParseWarnOptions(const QoreProgram* pgm, const ParseWarnOptions &new_opts) {
1904  pgm->priv->pwo = new_opts;
1905  }
1906 
1907  DLLLOCAL static bool setThreadVarData(QoreProgram* pgm, ThreadProgramData* td, ThreadLocalProgramData* &tlpd, bool run) {
1908  return pgm->priv->setThreadVarData(td, tlpd, run);
1909  }
1910 
1911  DLLLOCAL static void finalizeThreadData(QoreProgram* pgm, ThreadProgramData* td, arg_vec_t*& cl) {
1912  pgm->priv->finalizeThreadData(td, cl);
1913  }
1914 
1915  DLLLOCAL static int endThread(QoreProgram* pgm, ThreadProgramData* td, ExceptionSink* xsink) {
1916  return pgm->priv->endThread(td, xsink);
1917  }
1918 
1919  DLLLOCAL static void makeParseException(QoreProgram* pgm, const QoreProgramLocation &loc, QoreStringNode* desc) {
1920  pgm->priv->makeParseException(loc, "PARSE-EXCEPTION", desc);
1921  }
1922 
1923  DLLLOCAL static void makeParseException(QoreProgram* pgm, const QoreProgramLocation &loc, const char* err, QoreStringNode* desc) {
1924  pgm->priv->makeParseException(loc, err, desc);
1925  }
1926 
1927  DLLLOCAL static const QoreValue parseGetDefine(QoreProgram* pgm, const char* name) {
1928  bool is_defined;
1929  return pgm->priv->getDefine(name, is_defined);
1930  }
1931 
1932  DLLLOCAL static const QoreValue parseGetDefine(QoreProgram* pgm, const char* name, bool& is_defined) {
1933  return pgm->priv->getDefine(name, is_defined);
1934  }
1935 
1936  DLLLOCAL static QoreValue runTimeGetDefine(QoreProgram* pgm, const char* name) {
1937  bool is_defined;
1938  return pgm->priv->runTimeGetDefine(name, is_defined);
1939  }
1940 
1941  DLLLOCAL static QoreValue runTimeGetDefine(QoreProgram* pgm, const char* name, bool& is_defined) {
1942  return pgm->priv->runTimeGetDefine(name, is_defined);
1943  }
1944 
1945  DLLLOCAL static QoreHashNode* runTimeGetAllDefines(QoreProgram* pgm) {
1946  return pgm->priv->runTimeGetAllDefines();
1947  }
1948 
1949  DLLLOCAL static bool parseUnDefine(QoreProgram* pgm, const char* name) {
1950  return pgm->priv->parseUnDefine(name);
1951  }
1952 
1953  DLLLOCAL static bool runTimeUnDefine(QoreProgram* pgm, const char* name, ExceptionSink* xsink) {
1954  return pgm->priv->runTimeUnDefine(name, xsink);
1955  }
1956 
1957  DLLLOCAL static bool parseIsDefined(QoreProgram* pgm, const char* name) {
1958  return pgm->priv->isDefined(name);
1959  }
1960 
1961  DLLLOCAL static bool runTimeIsDefined(QoreProgram* pgm, const char* name) {
1962  return pgm->priv->runTimeIsDefined(name);
1963  }
1964 
1965  DLLLOCAL static void parseDefine(QoreProgram* pgm, const QoreProgramLocation* loc, const char* str, QoreValue val) {
1966  pgm->priv->parseDefine(loc, str, val);
1967  }
1968 
1969  DLLLOCAL static void runTimeDefine(QoreProgram* pgm, const char* str, QoreValue val, ExceptionSink* xsink) {
1970  pgm->priv->runTimeDefine(str, val, xsink);
1971  }
1972 
1973  DLLLOCAL static void addParseException(QoreProgram* pgm, ExceptionSink* xsink, const QoreProgramLocation* loc = nullptr) {
1974  assert(xsink);
1975  pgm->priv->addParseException(*xsink, loc);
1976  delete xsink;
1977  }
1978 
1979  DLLLOCAL static void addParseException(QoreProgram* pgm, ExceptionSink& xsink, const QoreProgramLocation* loc = nullptr) {
1980  pgm->priv->addParseException(xsink, loc);
1981  }
1982 
1983  DLLLOCAL static void exportFunction(QoreProgram* srcpgm, ExceptionSink* xsink, QoreProgram* trgpgm, const char* name, const char* new_name = nullptr, bool inject = false) {
1984  srcpgm->priv->exportFunction(xsink, trgpgm->priv, name, new_name, inject);
1985  }
1986 
1987  DLLLOCAL static int64 parseAddDomain(QoreProgram* pgm, int64 n_dom) {
1988  return pgm->priv->parseAddDomain(n_dom);
1989  }
1990 
1991  DLLLOCAL static int64 getDomain(const QoreProgram& pgm) {
1992  return pgm.priv->dom;
1993  }
1994 
1995  DLLLOCAL static void runtimeAddDomain(QoreProgram& pgm, int64 n_dom) {
1996  pgm.priv->dom |= n_dom;
1997  }
1998 
1999  DLLLOCAL static int64 forceReplaceParseOptions(QoreProgram& pgm, int64 po) {
2000  int64 rv = pgm.priv->pwo.parse_options;
2001  pgm.priv->pwo.parse_options = po;
2002  return rv;
2003  }
2004 
2005  DLLLOCAL static void makeParseWarning(QoreProgram* pgm, const QoreProgramLocation &loc, int code, const char* warn, const char* fmt, ...) {
2006  //printd(5, "QP::mPW(code: %d, warn: '%s', fmt: '%s') priv->pwo.warn_mask: %d priv->warnSink: %p %s\n", code, warn, fmt, priv->pwo.warn_mask, priv->warnSink, priv->warnSink && (code & priv->pwo.warn_mask) ? "OK" : "SKIPPED");
2007  if (!pgm->priv->warnSink || !(code & pgm->priv->pwo.warn_mask))
2008  return;
2009 
2010  QoreStringNode* desc = new QoreStringNode;
2011  while (true) {
2012  va_list args;
2013  va_start(args, fmt);
2014  int rc = desc->vsprintf(fmt, args);
2015  va_end(args);
2016  if (!rc)
2017  break;
2018  }
2019  QoreException *ne = new ParseException(loc, warn, desc);
2020  pgm->priv->warnSink->raiseException(ne);
2021  }
2022 
2023  DLLLOCAL static void makeParseWarning(QoreProgram* pgm, const QoreProgramLocation &loc, int code, const char* warn, QoreStringNode* desc) {
2024  //printd(5, "QoreProgram::makeParseWarning(code: %d, warn: '%s', desc: '%s') priv->pwo.warn_mask: %d priv->warnSink: %p %s\n", code, warn, desc->getBuffer(), priv->pwo.warn_mask, priv->warnSink, priv->warnSink && (code & priv->pwo.warn_mask) ? "OK" : "SKIPPED");
2025  if (!pgm->priv->warnSink || !(code & pgm->priv->pwo.warn_mask)) {
2026  desc->deref();
2027  return;
2028  }
2029 
2030  QoreException *ne = new ParseException(loc, warn, desc);
2031  pgm->priv->warnSink->raiseException(ne);
2032  }
2033 
2034  DLLLOCAL static void exportGlobalVariable(QoreProgram* pgm, const char* name, bool readonly, QoreProgram* tpgm, ExceptionSink* xsink) {
2035  pgm->priv->exportGlobalVariable(name, readonly, *(tpgm->priv), xsink);
2036  }
2037 
2038  DLLLOCAL void attachDebug(const qore_debug_program_private* n_dpgm) {
2039  printd(5, "qore_program_private::attachDebug(n_dpgm: %p), dpgm: %p\n", n_dpgm, dpgm);
2040  AutoLocker al(tlock);
2041  //QoreAutoRWWriteLocker arwl(&lck_debug_program);
2042 
2043  if (dpgm == n_dpgm)
2044  return;
2045  dpgm = const_cast<qore_debug_program_private*>(n_dpgm);
2046  printd(5, "qore_program_private::attachDebug, dpgm: %p, pgm_data_map: size:%d, begin: %p, end: %p\n", dpgm, pgm_data_map.size(), pgm_data_map.begin(), pgm_data_map.end());
2047  for (auto& i : pgm_data_map) {
2048  i.second->dbgPendingAttach();
2049  i.second->dbgBreak();
2050  }
2051  }
2052 
2053  DLLLOCAL void detachDebug(const qore_debug_program_private* n_dpgm) {
2054  printd(5, "qore_program_private::detachDebug(n_dpgm: %p), dpgm: %p\n", n_dpgm, dpgm);
2055  AutoLocker al(tlock);
2056  //QoreAutoRWWriteLocker arwl(&lck_debug_program);
2057  assert(n_dpgm==dpgm);
2058  if (!n_dpgm)
2059  return;
2060  dpgm = nullptr;
2061  printd(5, "qore_program_private::detachDebug, dpgm: %p, pgm_data_map: size:%d, begin: %p, end: %p\n", dpgm, pgm_data_map.size(), pgm_data_map.begin(), pgm_data_map.end());
2062  for (auto& i : pgm_data_map) {
2063  i.second->dbgPendingDetach();
2064  }
2065  // debug_program_counter may be non zero to finish pending calls. Just this instance cannot be deleted, it's satisfied in destructor
2066  }
2067 
2068  DLLLOCAL void onAttach(DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink);
2069  DLLLOCAL void onDetach(DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink);
2070  DLLLOCAL void onStep(const StatementBlock *blockStatement, const AbstractStatement *statement, unsigned bkptId, int &flow, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink);
2071  DLLLOCAL void onFunctionEnter(const StatementBlock *statement, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink);
2072  DLLLOCAL void onFunctionExit(const StatementBlock *statement, QoreValue& returnValue, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink);
2073  DLLLOCAL void onException(const AbstractStatement *statement, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink);
2074  DLLLOCAL void onExit(const StatementBlock *statement, QoreValue& returnValue, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink);
2075 
2076  DLLLOCAL int breakProgramThread(int tid) {
2077  printd(5, "qore_program_private::breakProgramThread(), this: %p, tid: %d\n", this, gettid());
2078  AutoLocker al(tlock);
2079  for (auto& i : pgm_data_map) {
2080  if (i.first->gettid() == tid) {
2081  i.second->dbgBreak();
2082  return 0;
2083  }
2084  }
2085  return -1;
2086  }
2087 
2088  DLLLOCAL void breakProgram() {
2089  printd(5, "qore_program_private::breakProgram(), this: %p\n", this);
2090  AutoLocker al(tlock);
2091  for (auto& i : pgm_data_map) {
2092  i.second->dbgBreak();
2093  }
2094  }
2095 
2096  DLLLOCAL void assignBreakpoint(QoreBreakpoint *bkpt, ExceptionSink *xsink) {
2097  if (!bkpt || this == bkpt->pgm) return;
2098  if (!checkAllowDebugging(xsink))
2099  return;
2100  if (bkpt->pgm) {
2101  bkpt->assignProgram(nullptr, nullptr);
2102  }
2103  QoreAutoRWWriteLocker al(&lck_breakpoint);
2104  breakpointList.push_back(bkpt);
2105  bkpt->pgm = this;
2106  bkpt->ref();
2107  }
2108 
2109  DLLLOCAL void deleteAllBreakpoints() {
2110  QoreAutoRWWriteLocker al(&lck_breakpoint);
2111  for (QoreBreakpointList_t::iterator it = breakpointList.begin(); it != breakpointList.end(); ++it) {
2112  (*it)->unassignAllStatements();
2113  (*it)->pgm = nullptr;
2114  (*it)->deref();
2115  }
2116  breakpointList.clear();
2117  }
2118 
2119  DLLLOCAL void getBreakpoints(QoreBreakpointList_t &bkptList) {
2120  QoreAutoRWReadLocker al(&lck_breakpoint);
2121  bkptList.clear();
2122  for (QoreBreakpointList_t::iterator it = breakpointList.begin(); it != breakpointList.end(); ++it) {
2123  bkptList.push_back(*it);
2124  (*it)->ref();
2125  }
2126  }
2127 
2128  DLLLOCAL void getStatementBreakpoints(const AbstractStatement* statement, QoreBreakpointList_t &bkptList) {
2129  QoreAutoRWReadLocker al(&lck_breakpoint);
2130  bkptList.clear();
2131  if (statement->breakpoints) {
2132  for (std::list<QoreBreakpoint*>::iterator it = statement->breakpoints->begin(); it != statement->breakpoints->end(); ++it) {
2133  bkptList.push_back(*it);
2134  (*it)->ref();
2135  }
2136  }
2137  }
2138 
2139  DLLLOCAL inline unsigned onCheckBreakpoint(const AbstractStatement *statement, ExceptionSink* xsink) {
2140  QoreAutoRWReadLocker al(&lck_breakpoint);
2141  const QoreBreakpoint *b = statement->getBreakpoint();
2142  if (b != nullptr) {
2143  return b->getBreakpointId();
2144  } else {
2145  return 0;
2146  }
2147  }
2148 
2149  DLLLOCAL unsigned long getStatementId(const AbstractStatement* statement) const {
2150  AutoLocker al(&plock);
2151  if (!statement)
2152  return 0;
2153  ReverseStatementIdMap_t::const_iterator i = reverseStatementIds.find((AbstractStatement*) statement);
2154  if (i == reverseStatementIds.end())
2155  return 0;
2156  return i->second;
2157  }
2158 
2159  DLLLOCAL AbstractStatement* resolveStatementId(unsigned long statementId) const {
2160  AutoLocker al(&plock);
2161  if (statementId == 0 || statementId > statementIds.size())
2162  return nullptr;
2163  return statementIds[statementId-1];
2164  }
2165 
2166  DLLLOCAL unsigned getProgramId() const {
2167  return programId;
2168  }
2169 
2170  // called locked
2171  DLLLOCAL void clearNamespaceData(ExceptionSink* xsink) {
2172  if (ns_vars) {
2173  return;
2174  }
2175  assert(RootNS);
2176  ns_vars = true;
2177  // delete all global variables, etc
2178  // this call can only be made once
2179  qore_root_ns_private::clearData(*RootNS, xsink);
2180  }
2181 
2182  DLLLOCAL static QoreProgram* resolveProgramId(unsigned programId) {
2183  printd(5, "qore_program_private::resolveProgramId(%x)\n", programId);
2184  QoreAutoRWReadLocker al(&lck_programMap);
2185  for (qore_program_to_object_map_t::iterator i = qore_program_to_object_map.begin(); i != qore_program_to_object_map.end(); i++) {
2186  if (i->first->priv->programId == programId)
2187  return i->first;
2188  }
2189  return nullptr;
2190  }
2191 
2192  DLLLOCAL bool checkAllowDebugging(ExceptionSink *xsink) {
2193  if (pwo.parse_options & PO_NO_DEBUGGING) {
2194  if (xsink) {
2195  xsink->raiseException("DEBUGGING", "program does not provide internal data for debugging");
2196  }
2197  return false;
2198  } else {
2199  return true;
2200  }
2201  }
2202 
2203  DLLLOCAL void addStatementToIndexIntern(name_section_sline_statement_map_t* statementIndex, const char* key, AbstractStatement *statement, int offs, const char* section, int sectionOffs);
2204  DLLLOCAL static void registerStatement(QoreProgram *pgm, AbstractStatement *statement, bool addToIndex);
2205  DLLLOCAL QoreHashNode* getSourceIndicesIntern(name_section_sline_statement_map_t* statementIndex, ExceptionSink* xsink) const;
2206  DLLLOCAL QoreHashNode* getSourceLabels(ExceptionSink* xsink) {
2207  return getSourceIndicesIntern(&statementByLabelIndex, xsink);
2208  }
2209  DLLLOCAL QoreHashNode* getSourceFileNames(ExceptionSink* xsink) {
2210  return getSourceIndicesIntern(&statementByFileIndex, xsink);
2211  }
2212 
2213  DLLLOCAL AbstractStatement* getStatementFromIndex(const char* name, int line) {
2214  printd(5, "qore_program_private::getStatementFromIndex('%s',%d), this: %p, file#: %d, label#: %d\n", name, line, this, statementByFileIndex.size(), statementByLabelIndex.size());
2215  AutoLocker al(&plock);
2216  name_section_sline_statement_map_t::iterator it;
2217  if (statementByFileIndex.empty()) {
2218  return nullptr;
2219  }
2220  bool addOffs = true;
2221  if (!name || *name == '\0') {
2222  if (statementByFileIndex.size() != 1)
2223  return nullptr;
2224  it = statementByFileIndex.begin();
2225  } else {
2226  size_t l = strlen(name);
2227  it = statementByFileIndex.find(name);
2228  if (it == statementByFileIndex.end()) {
2229  it = statementByLabelIndex.find(name); // label only full name match
2230  if (it == statementByLabelIndex.end()) {
2231 
2232  // did not find exact match so try a heuristic
2233  it = statementByFileIndex.begin();
2234  while (it != statementByFileIndex.end()) {
2235  size_t k = strlen(it->first);
2236  if (k >= l) {
2237  if (strcmp(name, it->first + k - l) == 0) {
2238  // match against suffix following '/'
2239  if (k == l /* should not happen as it is full match*/ || name[0] == '/' || it->first[k-l-1] == '/') {
2240  break;
2241  }
2242  }
2243  }
2244  ++it;
2245  }
2246  if (it == statementByFileIndex.end()) {
2247  printd(5, "qore_program_private::getStatementFromIndex('%s',%d) no suffix match, this: %p\n", name, line, this);
2248  return nullptr;
2249  }
2250  printd(5, "qore_program_private::getStatementFromIndex('%s',%d) found by file suffix match, this: %p\n", name, line, this);
2251  } else {
2252  addOffs = false;
2253  printd(5, "qore_program_private::getStatementFromIndex('%s',%d) found by label full match, this: %p\n", name, line, this);
2254  }
2255  } else {
2256  printd(5, "qore_program_private::getStatementFromIndex('%s',%d) found by file full match, this: %p\n", name, line, this);
2257  }
2258  }
2259  sline_statement_map_t *ssm = &it->second->statementMap;
2260  printd(5, "qore_program_private::getStatementFromIndex('%s',%d) found '%s', this: %p, ssm#: %d\n", name, line, it->first, this, ssm->size());
2261  if (ssm->size() == 0)
2262  return nullptr;
2263 
2264  sline_statement_map_t::iterator li = ssm->upper_bound(line);
2265  if (li == ssm->begin()) {
2266  printd(5, "qore_program_private::getStatementFromIndex('%s',%d) no statement found by line #1, this: %p\n", name, line, this);
2267  return nullptr;
2268  }
2269  --li;
2270  int ln = li->first;
2271  int minnl = -1;
2272  AbstractStatement* st = nullptr;
2273  while (true) {
2274  // find the nearest statement, i.e. statement with smallest num of lines
2275  if (ln != li->first) {
2276  break;
2277  // we do not try to find outer statement when looking for line behind inner statement
2278  }
2279  if (li->second->loc->start_line + (addOffs ? li->second->loc->offset : 0) <= line && li->second->loc->end_line + (addOffs ? li->second->loc->offset : 0) >= line) {
2280  int n = li->second->loc->end_line - li->second->loc->start_line;
2281  if (minnl >= 0) {
2282  if (n < minnl) {
2283  minnl = n;
2284  st = li->second;
2285  }
2286  } else {
2287  minnl = n;
2288  st = li->second;
2289  }
2290  if (minnl == 0)
2291  break;
2292  }
2293  if (li == ssm->begin())
2294  break;
2295  --li;
2296  }
2297  if (st) {
2298  printd(5, "qore_program_private::getStatementFromIndex('%s',%d) statement:('file':%s,source:%s,offset:%d,line:%d-%d), this: %p\n", name, line, st->loc->getFile(), st->loc->getSource(), st->loc->offset, st->loc->start_line, st->loc->end_line, this);
2299  } else {
2300  printd(5, "qore_program_private::getStatementFromIndex('%s',%d) no statement found by line #2, this: %p\n", name, line, this);
2301  }
2302  return st;
2303  }
2304 
2305  DLLLOCAL void registerQoreObject(QoreObject *o, ExceptionSink* xsink) {
2306  printd(5, "qore_program_private::registerQoreObject() pgm: %p, pgmid: %d\n", pgm, getProgramId());
2307  QoreAutoRWWriteLocker al(&qore_program_private::lck_programMap);
2308  qore_program_to_object_map_t::iterator i = qore_program_private::qore_program_to_object_map.find(pgm);
2309  assert(i != qore_program_private::qore_program_to_object_map.end());
2310  if (i->second && i->second != o) {
2311  xsink->raiseException("PROGRAM-ERROR", "The Program has already assigned QoreObject");
2312  } else {
2313  i->second = o;
2314  }
2315  }
2316 
2317  DLLLOCAL void unregisterQoreObject(QoreObject *o, ExceptionSink* xsink) {
2318  printd(5, "qore_program_private::unregisterQoreObject() pgm: %p, pgmid: %d\n", pgm, getProgramId());
2319  QoreAutoRWWriteLocker al(&qore_program_private::lck_programMap);
2320  qore_program_to_object_map_t::iterator i = qore_program_private::qore_program_to_object_map.find(pgm);
2321  assert(i != qore_program_private::qore_program_to_object_map.end());
2322  assert(i->second == o);
2323  i->second = nullptr;
2324  }
2325 
2326  DLLLOCAL QoreObject* findQoreObject() {
2327  QoreAutoRWReadLocker al(&lck_programMap);
2328  qore_program_to_object_map_t::iterator i = qore_program_to_object_map.find(pgm);
2329  if (i == qore_program_to_object_map.end()) {
2330  return nullptr;
2331  } else {
2332  return i->second;
2333  }
2334  }
2335 
2336  DLLLOCAL static QoreObject* getQoreObject(QoreProgram* pgm) {
2337  QoreAutoRWWriteLocker al(&lck_programMap);
2338  qore_program_to_object_map_t::iterator i = qore_program_to_object_map.find(pgm);
2339  assert(i != qore_program_to_object_map.end());
2340  if (i->second) {
2341  printd(5, "qore_program_private::getQoreObject() pgm: %p, pgmid: %d, second: %p\n", i->first, i->first->getProgramId(), i->second);
2342  i->second->ref();
2343  } else {
2344  i->second = new QoreObject(QC_PROGRAMCONTROL, getProgram(), pgm);
2345  printd(5, "qore_program_private::getQoreObject() pgm: %p, pgmid: %d, new second: %p\n", pgm, pgm->getProgramId(), i->second);
2346  pgm->ref();
2347  }
2348  return i->second;
2349  }
2350 
2351  DLLLOCAL static QoreListNode* getAllQoreObjects(ExceptionSink *xsink) {
2352  QoreAutoRWWriteLocker al(&lck_programMap);
2353  ReferenceHolder<QoreListNode>l(new QoreListNode(QC_PROGRAMCONTROL->getTypeInfo()), xsink);
2354 
2355  qore_program_to_object_map_t::iterator i = qore_program_to_object_map.begin();
2356  while (i != qore_program_to_object_map.end()) {
2357  if (i->second) {
2358  printd(5, "qore_program_private::getAllQoreObjects() pgm: %p, pgmid: %d, second: %p\n", i->first, i->first->getProgramId(), i->second);
2359  i->second->ref();
2360  } else {
2361  i->second = new QoreObject(QC_PROGRAMCONTROL, getProgram(), i->first);
2362  printd(5, "qore_program_private::getAllQoreObjects() pgm: %p, pgmid: %d, new second: %p\n", i->first, i->first->getProgramId(), i->second);
2363  i->first->ref();
2364  }
2365  (*l)->push(i->second, nullptr);
2366  ++i;
2367  }
2368  return l.release();
2369  }
2370 };
2371 
2372 class ParseWarnHelper : public ParseWarnOptions {
2373 protected:
2374  bool restore;
2375 public:
2376  DLLLOCAL ParseWarnHelper(const ParseWarnOptions &new_opts) {
2377  QoreProgram* pgm = getProgram();
2378  restore = pgm ? qore_program_private::setSaveParseWarnOptions(pgm, new_opts, *this) : false;
2379  }
2380  DLLLOCAL ~ParseWarnHelper() {
2381  if (restore)
2382  qore_program_private::setParseWarnOptions(getProgram(), *this);
2383  }
2384 };
2385 
2386 typedef std::map<QoreProgram*, qore_program_private*> qore_program_map_t;
2387 class QoreDebugProgram;
2388 
2389 class qore_debug_program_private {
2390 private:
2391  //mutable QoreThreadLock tlock; // thread variable data lock, for accessing the program list variable
2392  mutable QoreRWLock tlock;
2393  QoreDebugProgram *dpgm;
2394  qore_program_map_t qore_program_map;
2395  mutable QoreCounter debug_program_counter; // number of thread calls from program instance.
2396 public:
2397  DLLLOCAL qore_debug_program_private(QoreDebugProgram *n_dpgm): dpgm(n_dpgm) {};
2398  DLLLOCAL ~qore_debug_program_private() {
2399  assert(qore_program_map.empty());
2400  }
2401 
2402  DLLLOCAL void addProgram(QoreProgram *pgm, ExceptionSink *xsink) {
2403  if (!pgm->priv->checkAllowDebugging(xsink))
2404  return;
2405  QoreAutoRWWriteLocker al(&tlock);
2406  qore_program_map_t::iterator i = qore_program_map.find(pgm);
2407  printd(5, "qore_debug_program_private::addProgram(), this: %p, pgm: %p, i: %p, end: %p\n", this, pgm, i, qore_program_map.end());
2408  if (i != qore_program_map.end())
2409  return; // already exists
2410  qore_program_map.insert(qore_program_map_t::value_type(pgm, pgm->priv));
2411  pgm->ref();
2412  pgm->priv->attachDebug(this);
2413  }
2414 
2415  DLLLOCAL void removeProgram(QoreProgram *pgm) {
2416  QoreAutoRWWriteLocker al(&tlock);
2417  qore_program_map_t::iterator i = qore_program_map.find(pgm);
2418  printd(5, "qore_debug_program_private::removeProgram(), this: %p, pgm: %p, i: %p, end: %p\n", this, pgm, i, qore_program_map.end());
2419  if (i == qore_program_map.end())
2420  return;
2421  pgm->priv->detachDebug(this);
2422  qore_program_map.erase(i);
2423  pgm->deref();
2424  // onDetach will not be executed as program is removed
2425  }
2426 
2427  DLLLOCAL void removeAllPrograms() {
2428  QoreAutoRWWriteLocker al(&tlock);
2429  printd(5, "qore_debug_program_private::removeAllPrograms(), this: %p\n", this);
2430  qore_program_map_t::iterator i;
2431  while ((i = qore_program_map.begin()) != qore_program_map.end()) {
2432  qore_program_private* qpp = i->second;
2433  QoreProgram *p = i->first;
2434  qore_program_map.erase(i);
2435  qpp->detachDebug(this);
2436  p->deref();
2437  }
2438  }
2439 
2440  DLLLOCAL QoreListNode* getAllProgramObjects() {
2441  QoreAutoRWReadLocker al(&tlock);
2442  printd(5, "qore_debug_program_private::getAllProgramObjects(), this: %p\n", this);
2443  ReferenceHolder<QoreListNode> l(new QoreListNode(QC_PROGRAM->getTypeInfo()), nullptr);
2444  qore_program_map_t::iterator i = qore_program_map.begin();
2445  while (i != qore_program_map.end()) {
2446  QoreObject* o = QoreProgram::getQoreObject(i->first);
2447  if (o) {
2448  (*l)->push(o, nullptr);
2449  }
2450  i++;
2451  }
2452  return l.release();
2453  }
2454 
2455  DLLLOCAL void onAttach(QoreProgram *pgm, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink) {
2456  AutoQoreCounterDec ad(&debug_program_counter);
2457  dpgm->onAttach(pgm, rs, rts, xsink);
2458  }
2459  DLLLOCAL void onDetach(QoreProgram *pgm, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink) {
2460  AutoQoreCounterDec ad(&debug_program_counter);
2461  dpgm->onDetach(pgm, rs, rts, xsink);
2462  }
2469  DLLLOCAL void onStep(QoreProgram *pgm, const StatementBlock *blockStatement, const AbstractStatement *statement, unsigned bkptId, int &flow, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink) {
2470  AutoQoreCounterDec ad(&debug_program_counter);
2471  dpgm->onStep(pgm, blockStatement, statement, bkptId, flow, rs, rts, xsink);
2472 
2473  }
2474  DLLLOCAL void onFunctionEnter(QoreProgram *pgm, const StatementBlock *statement, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink) {
2475  AutoQoreCounterDec ad(&debug_program_counter);
2476  dpgm->onFunctionEnter(pgm, statement, rs, rts, xsink);
2477  }
2481  DLLLOCAL void onFunctionExit(QoreProgram *pgm, const StatementBlock *statement, QoreValue& returnValue, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink) {
2482  AutoQoreCounterDec ad(&debug_program_counter);
2483  dpgm->onFunctionExit(pgm, statement, returnValue, rs, rts, xsink);
2484  }
2488  DLLLOCAL void onException(QoreProgram* pgm, const AbstractStatement* statement, DebugRunStateEnum& rs, const AbstractStatement *&rts, ExceptionSink* xsink) {
2489  AutoQoreCounterDec ad(&debug_program_counter);
2490  dpgm->onException(pgm, statement, rs, rts, xsink);
2491  }
2495  DLLLOCAL void onExit(QoreProgram *pgm, const StatementBlock *statement, QoreValue& returnValue, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink) {
2496  AutoQoreCounterDec ad(&debug_program_counter);
2497  dpgm->onExit(pgm, statement, returnValue, rs, rts, xsink);
2498  }
2499 
2500  DLLLOCAL int breakProgramThread(QoreProgram* pgm, int tid) {
2501  //printd(5, "breakProgramThread pgm: %p tid: %d po: %lld\n", pgm, tid, pgm->priv->pwo.parse_options);
2502  // do not allow breaking if the Program does not support debugging
2503  if (pgm->priv->pwo.parse_options & PO_NO_DEBUGGING)
2504  return -1;
2505 
2506  QoreAutoRWReadLocker al(&tlock);
2507  qore_program_map_t::iterator i = qore_program_map.find(pgm);
2508  printd(5, "qore_debug_program_private::breakProgramThread(), this: %p, pgm: %p, i: %p, end: %p, tid: %d\n", this, pgm, i, qore_program_map.end(), tid);
2509  if (i == qore_program_map.end())
2510  return -2;
2511  if (i->second->breakProgramThread(tid))
2512  return -3;
2513  return 0;
2514  }
2515 
2516  DLLLOCAL int breakProgram(QoreProgram* pgm) {
2517  //printd(5, "breakProgram pgm: %p po: %lld\n", pgm, pgm->priv->pwo.parse_options);
2518  // do not allow breaking if the Program does not support debugging
2519  if (pgm->priv->pwo.parse_options & PO_NO_DEBUGGING)
2520  return -1;
2521 
2522  QoreAutoRWReadLocker al(&tlock);
2523  qore_program_map_t::iterator i = qore_program_map.find(pgm);
2524  printd(5, "qore_debug_program_private::breakProgram(), this: %p, pgm: %p, i: %p, end: %p\n", this, pgm, i, qore_program_map.end());
2525  if (i == qore_program_map.end())
2526  return -2;
2527  i->second->breakProgram();
2528  return 0;
2529  }
2530 
2531  DLLLOCAL void waitForTerminationAndClear(ExceptionSink* xsink) {
2532  removeAllPrograms();
2533  // wait till all debug calls finished, avoid deadlock as it might be handled in current thread
2534  debug_program_counter.waitForZero();
2535  }
2536 
2537  DLLLOCAL int getInterruptedCount() {
2538  return debug_program_counter.getCount();
2539  }
2540 };
2541 
2542 DLLLOCAL TypedHashDecl* init_hashdecl_SourceLocationInfo(QoreNamespace& ns);
2543 
2544 #endif
provides a safe and exception-safe way to hold write locks in Qore, only to be used on the stack...
Definition: QoreRWLock.h:144
DLLEXPORT int reference_count() const
gets the reference count
provides access to thread-local storage
Definition: QoreThreadLocalStorage.h:44
DLLEXPORT void assignProgram(QoreProgram *new_pgm, ExceptionSink *xsink)
abstract base class for c++ Exceptions in the Qore library
Definition: QoreStandardException.h:47
DLLEXPORT bool empty() const
returns true if the string is empty, false if not
This is the hash or associative list container type in Qore, dynamically allocated only...
Definition: QoreHashNode.h:50
a simple thread-safe counter object; objects can block on it until the counter reaches zero ...
Definition: QoreCounter.h:40
DLLEXPORT const QoreEncoding * QCS_DEFAULT
the default encoding for the Qore library
DLLLOCAL T * get()
retrieves the key&#39;s value
Definition: QoreThreadLocalStorage.h:77
DLLEXPORT AbstractStatement * resolveStatementId(unsigned long statementId, ExceptionSink *xsink) const
#define PO_NO_DEBUGGING
disallows debugging actions that could be insecure such as reading the thread local variable stack ...
Definition: Restrictions.h:86
Class implementing breakpoint for debugging.
Definition: QoreProgram.h:1009
DLLEXPORT int gettid() noexcept
returns the current TID number
virtual DLLEXPORT void deref(ExceptionSink *xsink)
decrements the reference count of the object
non-thread-safe vector for storing "char*" that you want to delete
Definition: common.h:238
DLLEXPORT unsigned getProgramId() const
get the program id
contains constants, classes, and subnamespaces in QoreProgram objects
Definition: QoreNamespace.h:64
virtual DLLEXPORT void onStep(QoreProgram *pgm, const StatementBlock *blockStatement, const AbstractStatement *statement, unsigned bkptId, int &flow, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink *xsink)
#define PO_NO_LOCALE_CONTROL
do not allow changes to program locale
Definition: Restrictions.h:64
#define PO_ALLOW_INJECTION
allow code injection
Definition: Restrictions.h:76
DLLEXPORT AbstractQoreNode * raiseException(const char *err, const char *fmt,...)
appends a Qore-language exception to the list
DLLEXPORT AbstractQoreNode * raiseErrnoException(const char *err, int en, const char *fmt,...)
appends a Qore-language exception to the list and appends the result of strerror(errno) to the descri...
#define PO_NO_TOP_LEVEL_STATEMENTS
cannot define new top-level statements (outside of sub or class defs)
Definition: Restrictions.h:44
DLLEXPORT int push(QoreValue val, ExceptionSink *xsink)
adds a value to the list
a thread condition class implementing a wrapper for pthread_cond_t
Definition: QoreCondition.h:45
DLLEXPORT bool ROdereference() const
atomically decrements the reference count
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 int broadcast()
singles all threads blocked on this condition to wake up
#define PO_POSITIVE_OPTIONS
mask of all options allowing for more freedom (instead of less)
Definition: Restrictions.h:127
DLLEXPORT const char * getBuffer() const
returns the string&#39;s buffer; this data should not be changed
DLLEXPORT QoreProgram * getProgram()
returns the current QoreProgram
virtual DLLEXPORT void onFunctionEnter(QoreProgram *pgm, const StatementBlock *statement, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink *xsink)
the root namespace of a QoreProgram object
Definition: QoreNamespace.h:286
provides a safe and exception-safe way to hold locks in Qore, only to be used on the stack...
Definition: QoreThreadLock.h:128
This is the list container type in Qore, dynamically allocated only, reference counted.
Definition: QoreListNode.h:52
provides atomic reference counting to Qore objects
Definition: QoreReferenceCounter.h:44
defines a Qore-language class
Definition: QoreClass.h:237
DLLEXPORT qore_size_t strlen() const
returns number of bytes in the string (not including the null pointer)
an abstract class for program-specific external data
Definition: QoreProgram.h:985
DLLEXPORT int dec(ExceptionSink *xsink)
decrements the counter and wakes up any threads if the counter reaches 0
provides a safe and exception-safe way to hold read locks in Qore, only to be used on the stack...
Definition: QoreRWLock.h:105
supports parsing and executing Qore-language code, reference counted, dynamically-allocated only ...
Definition: QoreDebugProgram.h:66
DLLEXPORT int getCount() const
returns the current count
use this class to manage strings where the character encoding must be specified and may be different ...
Definition: QoreString.h:1015
The main value class in Qore, designed to be passed by value.
Definition: QoreValue.h:262
supports parsing and executing Qore-language code, reference counted, dynamically-allocated only ...
Definition: QoreProgram.h:126
DLLEXPORT void clear()
deletes the exception list immediately
static DLLEXPORT QoreObject * getQoreObject(QoreProgram *pgm)
get QoreObject of QoreProgram
the implementation of Qore&#39;s object data type, reference counted, dynamically-allocated only ...
Definition: QoreObject.h:61
container for holding Qore-language exception information and also for registering a "thread_exit" ca...
Definition: ExceptionSink.h:46
#define PO_FREE_OPTIONS
mask of options that have no effect on code access or code safety
Definition: Restrictions.h:130
virtual DLLEXPORT void onException(QoreProgram *pgm, const AbstractStatement *statement, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink *xsink)
DLLEXPORT unsigned getBreakpointId() const
get the breakpoint id
DLLEXPORT const QoreTypeInfo * getTypeInfo() const
returns the type information structure for this class
DLLEXPORT int inc()
increments the counter
virtual DLLEXPORT void onExit(QoreProgram *pgm, const StatementBlock *statement, QoreValue &returnValue, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink *xsink)
DLLEXPORT void deref(ExceptionSink *xsink)
decrements the reference count and calls derefImpl() if there_can_be_only_one is false, otherwise does nothing
long long int64
64bit integer type, cannot use int64_t here since it breaks the API on some 64-bit systems due to equ...
Definition: common.h:260
std::vector< QoreValue > arg_vec_t
vector of value information for default argument lists
Definition: common.h:254
DLLLOCAL void ref()
increments the reference count of the object
Definition: AbstractPrivateData.h:53
typed hash declaration
Definition: TypedHashDecl.h:44
DLLEXPORT void ROreference() const
atomically increments the reference count
base class for resolved call references
Definition: CallReferenceNode.h:105
DLLEXPORT QoreHashNode * copy() const
performs a copy of the hash and returns the new hash
DLLEXPORT qore_size_t size() const
returns the number of members in the hash, executes in constant time
provides a mutually-exclusive thread lock
Definition: QoreThreadLock.h:47
provides a simple POSIX-threads-based read-write lock
Definition: QoreRWLock.h:47
std::set< int > int_set_t
set of integers
Definition: common.h:82
DLLEXPORT int waitForZero(ExceptionSink *xsink, int timeout_ms=0)
blocks the calling thread until the counter reaches 0
DLLEXPORT char * q_basenameptr(const char *path)
returns a pointer within the same string
virtual DLLEXPORT void onFunctionExit(QoreProgram *pgm, const StatementBlock *statement, QoreValue &returnValue, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink *xsink)
DLLEXPORT bool isException() const
returns true if at least one exception is present
DLLLOCAL void set(T *ptr)
sets the key&#39;s value
Definition: QoreThreadLocalStorage.h:82
DLLLOCAL int set(const QoreString *s, const QoreEncoding *qe, ExceptionSink *xsink)
discards any current state and sets and converts (if necessary) a new string to the desired encoding ...
Definition: QoreString.h:1092
DLLEXPORT int vsprintf(const char *fmt, va_list args)
this will concatentate a formatted string to the existing string according to the format string and t...
holds an object and dereferences it in the destructor
Definition: QoreValue.h:452
provides an exception-safe way to manage locks in Qore, only to be used on the stack, cannot be dynamically allocated
Definition: QoreThreadLock.h:230
DLLEXPORT int wait(pthread_mutex_t *m)
blocks a thread on a mutex until the condition is signaled
#define PO_NO_CHILD_PO_RESTRICTIONS
turn off parse option inheritance restrictions
Definition: Restrictions.h:51
DLLEXPORT void assimilate(ExceptionSink *xs)
assimilates all entries of the "xs" argument by appending them to the internal list and deletes the "...
DLLEXPORT const AbstractQoreZoneInfo * currentTZ()
returns the current local time zone, note that if 0 = UTC
DLLEXPORT QoreListNode * copy() const
performs a deep copy of the list and returns the new list