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