Qore Programming Language  0.9.16
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 - 2020 Qore Technologies, s.r.o.
8 
9  Permission is hereby granted, free of charge, to any person obtaining a
10  copy of this software and associated documentation files (the "Software"),
11  to deal in the Software without restriction, including without limitation
12  the rights to use, copy, modify, merge, publish, distribute, sublicense,
13  and/or sell copies of the Software, and to permit persons to whom the
14  Software is furnished to do so, subject to the following conditions:
15 
16  The above copyright notice and this permission notice shall be included in
17  all copies or substantial portions of the Software.
18 
19  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25  DEALINGS IN THE SOFTWARE.
26 
27  Note that the Qore library is released under a choice of three open-source
28  licenses: MIT (as above), LGPL 2+, or GPL 2+; see README-LICENSE for more
29  information.
30 */
31 
32 #ifndef _QORE_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, int offset) {
65  thread_set_class_and_ns(nullptr, nullptr, cls, ns);
66  beginParsing(file, nullptr, src, offset);
67  }
68 
69  DLLLOCAL ~QoreParseLocationHelper() {
70  endParsing();
71  thread_set_class_and_ns(cls, ns);
72  }
73 
74 private:
75  // issue #3596: clear & restore class and ns ctx
76  const qore_class_private* cls;
77  qore_ns_private* ns;
78 };
79 
80 class CharPtrList : public safe_dslist<std::string> {
81 public:
82  // returns true for found, false for not found
83  // FIXME: use STL find algorithm
84  DLLLOCAL bool find(const char* str) const {
85  const_iterator i = begin();
86  while (i != end()) {
87  if (*i == str)
88  return true;
89  ++i;
90  }
91 
92  return false;
93  }
94 };
95 
96 // local variable container
97 typedef safe_dslist<LocalVar*> local_var_list_t;
98 
99 // expression type
100 typedef StatementBlock* q_exp_t;
101 
102 class LocalVariableList : public local_var_list_t {
103 public:
104  DLLLOCAL LocalVariableList() {
105  }
106 
107  DLLLOCAL ~LocalVariableList() {
108  for (local_var_list_t::iterator i = begin(), e = end(); i != e; ++i) {
109  delete *i;
110  }
111  }
112 };
113 
115 
116 #include "qore/intern/ThreadLocalVariableData.h"
117 #include "qore/intern/ThreadClosureVariableStack.h"
118 
119 hashdecl ThreadLocalProgramData {
120 private:
121  // not implemented
122  DLLLOCAL ThreadLocalProgramData(const ThreadLocalProgramData& old) = delete;
123 
124  // thread debug types, field is read/write only in thread being debugged, no locking is needed
125  DebugRunStateEnum runState = DBG_RS_DETACH;
126  // used to implement "to to statement" debugger command, reset it when program is interrupted
127  const AbstractStatement* runToStatement = nullptr;
128  // when stepover or until return we need calls function calls
129  int functionCallLevel = 0;
130 
131  DLLLOCAL inline void setRunState(DebugRunStateEnum rs, const AbstractStatement* rts) {
132  assert(rs < DBG_RS_STOPPED); // DBG_RS_STOPPED is wrong value when program is running
133  if (rs == DBG_RS_UNTIL_RETURN) {
134  functionCallLevel = 1; // function called only when runState is not DBG_RS_UNTIL_RETURN
135  }
136  printd(5, "ThreadLocalProgramData::setRunState(), this: %p, rs: %d->%d, rts: %p\n", this, runState, rs, rts);
137  runState = rs;
138  runToStatement = rts;
139  }
140  // set to true by any process do break running program asap
141  volatile bool breakFlag = false;
142  // called from running thread
143  DLLLOCAL inline void checkBreakFlag() {
144  if (breakFlag && runState != DBG_RS_DETACH) {
145  breakFlag = false;
146  if (runState != DBG_RS_STOPPED) {
147  runState = DBG_RS_STEP;
148  }
149  printd(5, "ThreadLocalProgramData::checkBreakFlag(), this: %p, rs: %d\n", this, runState);
150  }
151  }
152  // to call onAttach when debug is attached or detached, -1 .. detach, 1 .. attach
153  int attachFlag = 0;
154  DLLLOCAL inline void checkAttach(ExceptionSink* xsink) {
155  if (runState != DBG_RS_STOPPED) {
156  if (attachFlag > 0) {
157  dbgAttach(xsink);
158  //if (rs != DBG_RS_DETACH) { // TODO: why this exception ?
159  attachFlag = 0;
160  //}
161  } else if (attachFlag < 0) {
162  dbgDetach(xsink);
163  attachFlag = 0;
164  }
165  }
166  }
167 
168 public:
169  // local variable data slots
170  ThreadLocalVariableData lvstack;
171  // closure variable stack
172  ThreadClosureVariableStack cvstack;
173  // current thread's time zone locale (if any)
174  const AbstractQoreZoneInfo* tz = nullptr;
175  // the "time zone set" flag
176  bool tz_set : 1;
177 
178  // top-level vars instantiated
179  bool inst : 1;
180 
181  DLLLOCAL ThreadLocalProgramData() : tz_set(false), inst(false) {
182  printd(5, "ThreadLocalProgramData::ThreadLocalProgramData() this: %p\n", this);
183  }
184 
185  DLLLOCAL ~ThreadLocalProgramData() {
186  printd(5, "ThreadLocalProgramData::~ThreadLocalProgramData() this: %p, rs: %d\n", this, runState);
187  assert(lvstack.empty());
188  assert(cvstack.empty());
189  }
190 
191  DLLLOCAL void finalize(SafeDerefHelper& sdh) {
192  lvstack.finalize(sdh);
193  cvstack.finalize(sdh);
194  }
195 
196  DLLLOCAL void del(ExceptionSink* xsink) {
197  lvstack.del(xsink);
198  cvstack.del(xsink);
199  delete this;
200  }
201 
202  DLLLOCAL void setTZ(const AbstractQoreZoneInfo* n_tz) {
203  tz_set = true;
204  tz = n_tz;
205  }
206 
207  DLLLOCAL void clearTZ() {
208  tz_set = false;
209  tz = 0;
210  }
211 
212  /*
213  DLLLOCAL void setEnable(bool n_enabled) {
214  enabled = n_enabled;
215  if (!enabled) {
216  runState = DBG_RS_RUN;
217  functionCallLevel = 0;
218  }
219  }
220  */
221 
235  DLLLOCAL void dbgAttach(ExceptionSink* xsink);
239  DLLLOCAL void dbgDetach(ExceptionSink* xsink);
245  DLLLOCAL int dbgStep(const StatementBlock* blockStatement, const AbstractStatement* statement, ExceptionSink* xsink);
249  DLLLOCAL void dbgFunctionEnter(const StatementBlock* statement, ExceptionSink* xsink);
253  DLLLOCAL void dbgFunctionExit(const StatementBlock* statement, QoreValue& returnValue, ExceptionSink* xsink);
257  DLLLOCAL void dbgException(const AbstractStatement* statement, ExceptionSink* xsink);
261  DLLLOCAL void dbgExit(const StatementBlock* statement, QoreValue& returnValue, ExceptionSink* xsink);
262 
266  DLLLOCAL void dbgBreak() {
267  printd(5, "ThreadLocalProgramData::dbgBreak(), this: %p\n", this);
268  breakFlag = true;
269  }
273  DLLLOCAL void dbgPendingAttach() {
274  printd(5, "ThreadLocalProgramData::dbgPendingAttach(), this: %p\n", this);
275  attachFlag = 1;
276  }
280  DLLLOCAL void dbgPendingDetach() {
281  printd(5, "ThreadLocalProgramData::dbgPendingDetach(), this: %p\n", this);
282  attachFlag = -1;
283  }
284 
288  DLLLOCAL bool dbgIsAttached() {
289  return /*runState != DBG_RS_STOPPED &&*/ runState != DBG_RS_DETACH;
290  }
291 };
292 
293 // maps from thread handles to thread-local data
294 typedef vector_map_t<ThreadProgramData*, ThreadLocalProgramData*> pgm_data_map_t;
295 //typedef std::map<ThreadProgramData*, ThreadLocalProgramData*> pgm_data_map_t;
296 
297 // map for "defines" in programs
298 typedef vector_map_t<std::string, QoreValue> dmap_t;
299 //typedef std::map<std::string, QoreValue> dmap_t;
300 
301 // map for pushed parse options
302 typedef vector_map_t<const char*, int64> ppo_t;
303 //typedef std::map<const char*, int64, ltstr> ppo_t;
304 
305 class AbstractQoreZoneInfo;
306 
307 class ltpgm {
308 public:
309  DLLLOCAL bool operator()(const QoreProgramLocation* p1, const QoreProgramLocation* p2) const {
310  assert(p1);
311  assert(p2);
312 
313  return *p1 < *p2;
314  }
315 };
316 
317 hashdecl pgmloc_vec_t : public std::vector<QoreProgramLocation*> {
318  DLLLOCAL ~pgmloc_vec_t() {
319  clear();
320  }
321 
322  DLLLOCAL void clear() {
323  for (auto& i : *this) {
324  delete i;
325  }
326  //std::for_each(begin(), end(), simple_delete<QoreProgramLocation>());
327  std::vector<QoreProgramLocation*>::clear();
328  }
329 };
330 
331 class qore_program_private_base {
332  friend class QoreProgramAccessHelper;
333 
334 protected:
335  DLLLOCAL void setDefines();
336 
337  typedef vector_map_t<const char*, AbstractQoreProgramExternalData*> extmap_t;
338  //typedef std::map<const char*, AbstractQoreProgramExternalData*, ltstr> extmap_t;
339  extmap_t extmap;
340 
341 public:
342  LocalVariableList local_var_list;
343 
344  // for the thread counter, used only with plock
345  QoreCondition pcond;
346  ptid_map_t tidmap; // map of tids -> thread count in program object
347  unsigned thread_count; // number of threads currently running in this Program
348  unsigned thread_waiting; // number of threads waiting on all threads to terminate or parsing to complete
349  unsigned parse_count; // recursive parse count
350 
351  // file name and unique string storage
352  cstr_vector_t str_vec;
353 
354  // unique program location storage
355  pgmloc_vec_t pgmloc;
356 
357  // temporary while parsing: to ensure unique strings in parsing
358  typedef std::set<const char*, ltstr> str_set_t;
359  str_set_t str_set;
360 
361  // temporary while parsing: unique locations; must be a set for performance reasons
362  typedef std::set<const QoreProgramLocation*, ltpgm> loc_set_t;
363  loc_set_t loc_set;
364 
365  // features present in this Program object
366  CharPtrList featureList;
367  // user modules present in this Program object
368  CharPtrList userFeatureList;
369 
370  // parse lock, making parsing actions atomic and thread-safe, also for runtime thread attachment
371  mutable QoreThreadLock plock;
372 
373  // set of signals being handled by code in this Program (to be deleted on exit)
374  int_set_t sigset;
375 
376  // weak reference dependency counter, when this hits zero, the object is deleted
378  ExceptionSink* parseSink, *warnSink, *pendingParseSink;
379  RootQoreNamespace* RootNS;
380  QoreNamespace* QoreNS;
381 
382  // top level statements
383  TopLevelStatementBlock sb;
384 
385  // bit field flags
386  bool only_first_except : 1,
387  po_locked : 1,
388  po_allow_restrict : 1,
389  exec_class : 1,
390  base_object : 1,
391  requires_exception : 1,
392  parsing_done : 1,
393  parsing_in_progress : 1,
394  ns_const : 1,
395  ns_vars : 1,
396  expression_mode : 1
397  ;
398 
399  typedef std::set<q_exp_t> q_exp_set_t;
400  q_exp_set_t exp_set;
401  q_exp_t new_expression = nullptr;
402 
403  int tclear; // clearing thread-local variables in progress? if so, this is the TID
404 
405  int exceptions_raised,
406  ptid; // TID of thread destroying the program's private data
407 
408  ParseWarnOptions pwo;
409 
410  int64 dom, // a mask of functional domains used in this Program
411  pend_dom; // a mask of pending function domains used in this Program
412 
413  std::string exec_class_name, script_dir, script_path, script_name, include_path;
414 
415  // thread-local data (could be inherited from another program)
416  qpgm_thread_local_storage_t* thread_local_storage;
417 
418  mutable QoreThreadLock tlock; // thread variable data lock, for accessing the thread variable data map and the thr_init variable
419  mutable QoreCondition tcond; // cond variable for tclear to become false, used only with tlock
420  mutable unsigned twaiting; // threads waiting on tclear to become false
421 
422  // thread-local variable storage - map from thread ID to thread-local storage
423  pgm_data_map_t pgm_data_map;
424 
425  // time zone setting for the program
426  const AbstractQoreZoneInfo* TZ;
427 
428  // define map
429  dmap_t dmap;
430 
431  // pushed parse option map
432  ppo_t ppo;
433 
434  // thread initialization user code
435  ResolvedCallReferenceNode* thr_init;
436 
437  // return value for use with %exec-class
438  QoreValue exec_class_rv;
439 
440  // public object that owns this private implementation
441  QoreProgram* pgm;
442 
443  DLLLOCAL qore_program_private_base(QoreProgram* n_pgm, int64 n_parse_options, QoreProgram* p_pgm = nullptr)
444  : thread_count(0), thread_waiting(0), parse_count(0), plock(&ma_recursive), parseSink(nullptr), warnSink(nullptr), pendingParseSink(nullptr), RootNS(nullptr), QoreNS(nullptr),
445  sb(this),
446  only_first_except(false), po_locked(false), po_allow_restrict(true), exec_class(false), base_object(false),
447  requires_exception(false),
448  parsing_done(false),
449  parsing_in_progress(false),
450  ns_const(false),
451  ns_vars(false),
452  expression_mode(false),
453  tclear(0),
454  exceptions_raised(0), ptid(0), pwo(n_parse_options), dom(0), pend_dom(0), thread_local_storage(nullptr), twaiting(0),
455  thr_init(nullptr), pgm(n_pgm) {
456  printd(QPP_DBG_LVL, "qore_program_private_base::qore_program_private_base() this: %p pgm: %p po: " QLLD "\n", this, pgm, n_parse_options);
457 
458  // must set priv before calling setParent()
459  pgm->priv = (qore_program_private*)this;
460 
461  if (p_pgm)
462  setParent(p_pgm, n_parse_options);
463  else {
464  TZ = QTZM.getLocalZoneInfo();
465  newProgram();
466  }
467 
468  // initialize global vars
469  Var *var = qore_root_ns_private::runtimeCreateVar(*RootNS, *QoreNS, "ARGV", listTypeInfo, true);
470  if (var && ARGV)
471  var->setInitial(ARGV->copy());
472 
473  var = qore_root_ns_private::runtimeCreateVar(*RootNS, *QoreNS, "QORE_ARGV", listTypeInfo, true);
474  if (var && QORE_ARGV)
475  var->setInitial(QORE_ARGV->copy());
476 
477  var = qore_root_ns_private::runtimeCreateVar(*RootNS, *QoreNS, "ENV", hashTypeInfo, true);
478  if (var)
479  var->setInitial(ENV->copy());
480  setDefines();
481  }
482 
483 #ifdef DEBUG
484  DLLLOCAL ~qore_program_private_base() {
485  printd(QPP_DBG_LVL, "qore_program_private_base::~qore_program_private_base() this: %p pgm: %p\n", this, pgm);
486  }
487 #endif
488 
489  DLLLOCAL const QoreProgramLocation* getLocation(int sline, int eline);
490  DLLLOCAL const QoreProgramLocation* getLocation(const QoreProgramLocation&, int sline, int eline);
491 
492  DLLLOCAL void startThread(ExceptionSink& xsink);
493 
494 protected:
495  DLLLOCAL void setParent(QoreProgram* p_pgm, int64 n_parse_options);
496 
497  // for independent programs (not inherited from another QoreProgram object)
498  DLLLOCAL void newProgram();
499 };
500 
501 class PreParseHelper {
502 protected:
503  qore_program_private_base *p;
504  bool swapped;
505 
506 public:
507  DLLLOCAL PreParseHelper(qore_program_private_base *n_p) : p(n_p), swapped(false) {
508  if (!p->parseSink) {
509  if (!p->pendingParseSink)
510  p->pendingParseSink = new ExceptionSink;
511  p->parseSink = p->pendingParseSink;
512  swapped = true;
513  }
514  }
515 
516  DLLLOCAL ~PreParseHelper() {
517  if (swapped)
518  p->parseSink = nullptr;
519  }
520 };
521 
522 class qore_debug_program_private;
523 
524 class AutoQoreCounterDec {
525 private:
526  QoreCounter* cnt;
527  bool incFlag;
528  AutoQoreCounterDec() {}
529 public:
530  AutoQoreCounterDec(QoreCounter* n_cnt, bool incNow = true): cnt(n_cnt), incFlag(false) {
531  if (incNow) {
532  inc();
533  }
534  }
535  ~AutoQoreCounterDec() {
536  if (incFlag)
537  cnt->dec();
538  }
539  void inc() {
540  cnt->inc();
541  incFlag = true;
542  }
543 };
544 
545 class QoreBreakpoint;
546 
547 class qore_program_private : public qore_program_private_base {
548 private:
549  mutable QoreCounter debug_program_counter; // number of thread calls to debug program instance.
550  DLLLOCAL void init(QoreProgram* n_pgm, int64 n_parse_options, const AbstractQoreZoneInfo* n_TZ = QTZM.getLocalZoneInfo()) {
551  }
552 
553  // only called from parseSetTimeZone
554  DLLLOCAL void mergeParseException(ExceptionSink &xsink) {
555  if (parseSink)
556  parseSink->assimilate(xsink);
557  else {
558  if (!pendingParseSink)
559  pendingParseSink = new ExceptionSink;
560  pendingParseSink->assimilate(xsink);
561  }
562  }
563 
564  qore_debug_program_private* dpgm;
565  QoreRWLock lck_breakpoint; // to protect breakpoint manipulation
566  QoreBreakpointList_t breakpointList;
567 
568  // map for line to statement
569  typedef std::map<int, AbstractStatement*> sline_statement_map_t;
570 
571  typedef std::map<const char*, int, ltstr> section_offset_map_t;
572 
573  hashdecl section_sline_statement_map {
574  section_offset_map_t sectionMap;
575  sline_statement_map_t statementMap;
576  };
577 
578  typedef section_sline_statement_map section_sline_statement_map_t;
579 
580  // map for filenames
581  typedef vector_map_t<const char*, section_sline_statement_map_t*> name_section_sline_statement_map_t;
582  //typedef std::map<const char*, section_sline_statement_map_t*, ltstr> name_section_sline_statement_map_t;
583 
584  // index source filename/label -> line -> statement
585  name_section_sline_statement_map_t statementByFileIndex;
586  name_section_sline_statement_map_t statementByLabelIndex;
587 
588  // statementId to AbstractStatement resolving
589  typedef std::vector<AbstractStatement*> StatementVector_t;
590  StatementVector_t statementIds;
591 
592  // to get statementId
593  typedef std::map<AbstractStatement*, unsigned long> ReverseStatementIdMap_t;
594  ReverseStatementIdMap_t reverseStatementIds;
595 
601  DLLLOCAL qore_debug_program_private* getDebugProgram(AutoQoreCounterDec& ad) {
602  AutoLocker al(tlock);
603  //QoreAutoRWReadLocker al(&lck_debug_program);
604  qore_debug_program_private* ret = dpgm;
605  if (ret) {
606  // new debug call in progress
607  ad.inc();
608  }
609  return ret;
610  }
611 
612  // lck_breakpoint lock should be aquired
613  DLLLOCAL bool isBreakpointRegistered(const QoreBreakpoint *bkpt) const {
614  return std::find(breakpointList.begin(), breakpointList.end(), bkpt) != breakpointList.end();
615  }
616  friend class QoreBreakpoint;
617 
618  typedef std::map<QoreProgram*, QoreObject*> qore_program_to_object_map_t;
619  static qore_program_to_object_map_t qore_program_to_object_map;
620  static QoreRWLock lck_programMap; // to protect program list manipulation
621  static volatile unsigned programIdCounter; // to generate programId
622  unsigned programId;
623 
624 public:
625  DLLLOCAL qore_program_private(QoreProgram* n_pgm, int64 n_parse_options, QoreProgram* p_pgm=nullptr);
626 
627  DLLLOCAL ~qore_program_private();
628 
629  DLLLOCAL void depRef() {
630  printd(QPP_DBG_LVL, "qore_program_private::depRef() this: %p pgm: %p %d->%d\n", this, pgm, dc.reference_count(), dc.reference_count() + 1);
631  dc.ROreference();
632  }
633 
634  DLLLOCAL void depDeref() {
635  printd(QPP_DBG_LVL, "qore_program_private::depDeref() this: %p pgm: %p %d->%d\n", this, pgm, dc.reference_count(), dc.reference_count() - 1);
636  if (dc.ROdereference())
637  delete pgm;
638  }
639 
640  DLLLOCAL void clearLocalVars(ExceptionSink* xsink) {
641  // grab all thread-local data in a vector and finalize it outside the lock
642  SafeDerefHelper sdh(xsink);
643  {
644  AutoLocker al(tlock);
645  // twaiting must be 0 here, as it can only be incremented while clearProgramThreadData() is in progress, which can only be executed once
646  assert(!twaiting);
647  assert(!tclear);
648  // while tclear is set, no threads can attach to this program object - pgm_data_map cannot be modified
649  tclear = q_gettid();
650 
651  for (auto& i : pgm_data_map) {
652  i.second->finalize(sdh);
653  }
654  }
655  }
656 
657  DLLLOCAL void clearProgramThreadData(ExceptionSink* xsink) {
658  for (auto& i : pgm_data_map) {
659  i.second->del(xsink);
660  i.first->delProgram(pgm);
661  }
662  }
663 
664  DLLLOCAL void waitForTerminationAndClear(ExceptionSink* xsink);
665 
666  // called when the program's ref count = 0 (but the dc count may not go to 0 yet)
667  DLLLOCAL void clear(ExceptionSink* xsink);
668 
669  // called when starting a new thread before the new thread is started, to avoid race conditions
670  // once the new thread has been started, the TID is registered in startThread()
671  DLLLOCAL int preregisterNewThread(ExceptionSink* xsink) {
672  // grab program-level lock
673  AutoLocker al(plock);
674 
675  if (ptid) {
676  xsink->raiseException("PROGRAM-ERROR", "the Program accessed has already been deleted and therefore no new threads can be started in it");
677  return -1;
678  }
679 
680  ++thread_count;
681  return 0;
682  }
683 
684  // called when thread startup fails after preregistration
685  DLLLOCAL void cancelPreregistration() {
686  // grab program-level lock
687  AutoLocker al(plock);
688 
689  assert(thread_count > 0);
690  if (!--thread_count && thread_waiting)
691  pcond.broadcast();
692  }
693 
694  // called from the new thread once the thread has been started (after preregisterNewThread())
695  DLLLOCAL void registerNewThread(int tid) {
696  // grab program-level lock
697  AutoLocker al(plock);
698 
699  assert(thread_count);
700  ++tidmap[tid];
701  }
702 
703  /*
704  DLLLOCAL int checkValid(ExceptionSink* xsink) {
705  if (ptid && ptid != q_gettid()) {
706  xsink->raiseException("PROGRAM-ERROR", "the Program accessed has already been deleted and therefore cannot be accessed at runtime");
707  return -1;
708  }
709  return 0;
710  }
711  */
712 
713  // returns 0 for OK, -1 for error
714  DLLLOCAL int incThreadCount(ExceptionSink* xsink) {
715  int tid = q_gettid();
716 
717  // grab program-level lock
718  AutoLocker al(plock);
719 
720  if (ptid && ptid != tid) {
721  xsink->raiseException("PROGRAM-ERROR", "the Program accessed has already been deleted and therefore cannot be accessed at runtime");
722  return -1;
723  }
724  if (parsing_in_progress) {
725  xsink->raiseException("PROGRAM-ERROR", "the Program accessed is currently undergoing parsing and cannot be accessed at runtime");
726  }
727 
728  ++tidmap[tid];
729  ++thread_count;
730  return 0;
731  }
732 
733  // throws a QoreStandardException if there is an error
734  DLLLOCAL void incThreadCount() {
735  int tid = q_gettid();
736 
737  // grab program-level lock
738  AutoLocker al(plock);
739 
740  if (ptid && ptid != tid) {
741  throw QoreStandardException("PROGRAM-ERROR", "the Program accessed has already been deleted and therefore cannot be accessed at runtime");
742  }
743  if (parsing_in_progress) {
744  throw QoreStandardException("PROGRAM-ERROR", "the Program accessed is currently undergoing parsing and cannot be accessed at runtime");
745  }
746 
747  ++tidmap[tid];
748  ++thread_count;
749  }
750 
751  DLLLOCAL void decThreadCount(int tid) {
752  // grab program-level lock
753  AutoLocker al(plock);
754 
755  ptid_map_t::iterator i = tidmap.find(tid);
756  assert(i != tidmap.end());
757  if (!--i->second)
758  tidmap.erase(i);
759 
760  assert(thread_count > 0);
761  if (!--thread_count && thread_waiting)
762  pcond.broadcast();
763  }
764 
765  // gets a list of all thread IDs using this Program
766  DLLLOCAL void getThreadList(QoreListNode& l) {
767  // grab program-level lock
768  AutoLocker al(plock);
769 
770  for (auto& i : tidmap) {
771  l.push(i.first, nullptr);
772  }
773  }
774 
775  DLLLOCAL int lockParsing(ExceptionSink* xsink) {
776  // grab program-level lock
777  AutoLocker al(plock);
778 
779  bool curr = (pgm == getProgram());
780  if (!curr) {
781  while (parse_count) {
782  ++thread_waiting;
783  pcond.wait(plock);
784  --thread_waiting;
785  }
786  }
787 
788  if (ptid && ptid != q_gettid()) {
789  if (xsink)
790  xsink->raiseException("PROGRAM-ERROR", "the Program accessed has already been deleted and therefore cannot be accessed");
791  return -1;
792  }
793 
794  //printd(5, "qore_program_private::lockParsing() this: %p ptid: %d thread_count: %d parse_count: %d -> %d\n", this, ptid, thread_count, parse_count, parse_count + 1);
795  ++parse_count;
796  return 0;
797  }
798 
799  DLLLOCAL void unlockParsing() {
800  // grab program-level lock
801  AutoLocker al(plock);
802  if (!(--parse_count) && thread_waiting)
803  pcond.broadcast();
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 (CharPtrList::const_iterator i = featureList.begin(), e = featureList.end(); i != e; ++i)
893  l->push(new QoreStringNode(*i), nullptr);
894 
895  for (CharPtrList::const_iterator i = userFeatureList.begin(), e = userFeatureList.end(); i != e; ++i)
896  l->push(new QoreStringNode(*i), nullptr);
897 
898  return l;
899  }
900 
901  DLLLOCAL void internParseRollback(ExceptionSink* xsink);
902 
903  // call must push the current program on the stack and pop it afterwards
904  DLLLOCAL int internParsePending(ExceptionSink* xsink, const char* code, const char* label, const char* orig_src = nullptr, int offset = 0, bool standard_parse = true) {
905  //printd(5, "qore_program_private::internParsePending() code: %p %d bytes label: '%s' src: '%s' offset: %d\n", code, strlen(code), label, orig_src ? orig_src : "(null)", offset);
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  QoreParseLocationHelper qplh(sname, src, offset);
920 
921  /*
922  beginParsing() called in QoreParseLocationHelper constructor but unless we call here twice
923  then endParsing() coredump with "Assertion `td->plStack' failed"
924  Seems endParsing() should do rather "if (!td->plStack) return".
925  */
926  beginParsing(sname, nullptr, src, offset);
927 
928  if (!parsing_in_progress) {
929  parsing_in_progress = true;
930  }
931 
932  // no need to save buffer, because it's deleted automatically in lexer
933  //printd(5, "qore_program_private::internParsePending() parsing tag: %s (%p): '%s'\n", label, label, code);
934 
935  yyscan_t lexer;
936  yylex_init(&lexer);
937 
938  yy_scan_string(code, lexer);
939  yyset_lineno(1, lexer);
940  // yyparse() will call endParsing() and restore old pgm position
941  yyparse(lexer);
942 
943  printd(5, "qore_program_private::internParsePending() returned from yyparse()\n");
944  int rc = 0;
945  if (parseSink->isException()) {
946  rc = -1;
947  if (standard_parse) {
948  printd(5, "qore_program_private::internParsePending() parse exception: calling parseRollback()\n");
949  internParseRollback(xsink);
950  requires_exception = false;
951  }
952  }
953 
954  printd(5, "qore_program_private::internParsePending() about to call yylex_destroy()\n");
955  yylex_destroy(lexer);
956  printd(5, "qore_program_private::internParsePending() returned from yylex_destroy()\n");
957  return rc;
958  }
959 
960  DLLLOCAL void startParsing(ExceptionSink* xsink, ExceptionSink* wS, int wm) {
961  warnSink = wS;
962  pwo.warn_mask = wm;
963  parseSink = xsink;
964 
965  if (pendingParseSink) {
966  parseSink->assimilate(pendingParseSink);
967  pendingParseSink = nullptr;
968  }
969  }
970 
971  DLLLOCAL int checkParse(ExceptionSink* xsink) const {
972  if (parsing_done) {
973  xsink->raiseException("PARSE-ERROR", "parsing can only happen once for each Program container");
974  return -1;
975  }
976  return 0;
977  }
978 
979  DLLLOCAL int parsePending(const char* code, const char* label, ExceptionSink* xsink, ExceptionSink* wS, int wm, const char* orig_src = nullptr, int offset = 0) {
980  //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);
981 
982  ProgramRuntimeParseContextHelper pch(xsink, pgm);
983  assert(xsink);
984  if (*xsink) {
985  return -1;
986  }
987 
988  if (checkParse(xsink)) {
989  return -1;
990  }
991 
992  startParsing(xsink, wS, wm);
993 
994  int rc = internParsePending(xsink, code, label, orig_src, offset);
995  warnSink = nullptr;
996 #ifdef DEBUG
997  parseSink = nullptr;
998 #endif
999  return rc;
1000  }
1001 
1002  // caller must have grabbed the lock and put the current program on the program stack
1003  DLLLOCAL int internParseCommit(bool standard_parse = true);
1004 
1005  DLLLOCAL int parseCommit(ExceptionSink* xsink, ExceptionSink* wS, int wm) {
1006  ProgramRuntimeParseCommitContextHelper pch(xsink, pgm);
1007  assert(xsink);
1008  if (*xsink) {
1009  return -1;
1010  }
1011 
1012  if (checkParse(xsink)) {
1013  return -1;
1014  }
1015 
1016  startParsing(xsink, wS, wm);
1017 
1018  // finalize parsing, back out or commit all changes
1019  int rc = internParseCommit();
1020 
1021 #ifdef DEBUG
1022  parseSink = nullptr;
1023 #endif
1024  warnSink = nullptr;
1025  // release program-level parse lock
1026  return rc;
1027  }
1028 
1029  DLLLOCAL int parseRollback(ExceptionSink* xsink) {
1030  ProgramRuntimeParseContextHelper pch(xsink, pgm);
1031  assert(xsink);
1032  if (*xsink)
1033  return -1;
1034 
1035  // back out all pending changes
1036  internParseRollback(xsink);
1037  return 0;
1038  }
1039 
1040  DLLLOCAL void parse(FILE *fp, const char* name, ExceptionSink* xsink, ExceptionSink* wS, int wm) {
1041  assert(xsink);
1042  printd(5, "QoreProgram::parse(fp: %p, name: %s, xsink: %p, wS: %p, wm: %d)\n", fp, name, xsink, wS, wm);
1043 
1044  // if already at the end of file, then return
1045  // try to get one character from file
1046  int c = fgetc(fp);
1047  if (feof(fp)) {
1048  printd(5, "QoreProgram::parse(fp: %p, name: %s) EOF\n", fp, name);
1049  return;
1050  }
1051  // push back read character
1052  ungetc(c, fp);
1053 
1054  yyscan_t lexer;
1055 
1056  {
1057  ProgramRuntimeParseCommitContextHelper pch(xsink, pgm);
1058  if (*xsink) {
1059  return;
1060  }
1061 
1062  if (checkParse(xsink)) {
1063  return;
1064  }
1065 
1066  startParsing(xsink, wS, wm);
1067 
1068  // save this file name for storage in the parse tree and deletion
1069  // when the QoreProgram object is deleted
1070  const char* sname = name;
1071  addFile(sname);
1072 
1073  QoreParseLocationHelper qplh(sname, nullptr, 0);
1074 
1075  /*
1076  beginParsing() called in QoreParseLocationHelper constructor but unless we call here twice
1077  then endParsing() coredump with "Assertion `td->plStack' failed"
1078  Seems endParsing() should do rather "if (!td->plStack) return".
1079  */
1080  beginParsing(sname);
1081 
1082  if (!parsing_in_progress) {
1083  parsing_in_progress = true;
1084  }
1085 
1086  //printd(5, "QoreProgram::parse(): about to call yyparse()\n");
1087  yylex_init(&lexer);
1088  yyset_in(fp, lexer);
1089  // yyparse() will call endParsing() and restore old pgm position
1090  yyparse(lexer);
1091 
1092  // finalize parsing, back out or commit all changes
1093  internParseCommit();
1094 
1095 #ifdef DEBUG
1096  parseSink = nullptr;
1097 #endif
1098  warnSink = nullptr;
1099  // release program-level parse lock
1100  }
1101 
1102  yylex_destroy(lexer);
1103  if (only_first_except && exceptions_raised > 1)
1104  fprintf(stderr, "\n%d exception(s) skipped\n\n", exceptions_raised);
1105  }
1106 
1107  DLLLOCAL void parse(const QoreString *str, const QoreString *lstr, ExceptionSink* xsink, ExceptionSink* wS, int wm, const QoreString* source = nullptr, int offset = 0) {
1108  assert(xsink);
1109  if (!str->strlen())
1110  return;
1111 
1112  // ensure code string has correct character set encoding
1113  TempEncodingHelper tstr(str, QCS_DEFAULT, xsink);
1114  if (*xsink)
1115  return;
1116 
1117  // ensure label string has correct character set encoding
1118  TempEncodingHelper tlstr(lstr, QCS_DEFAULT, xsink);
1119  if (*xsink)
1120  return;
1121 
1122  TempEncodingHelper src;
1123  if (source && !source->empty() && !src.set(source, QCS_DEFAULT, xsink))
1124  return;
1125 
1126  parse(tstr->getBuffer(), tlstr->getBuffer(), xsink, wS, wm, source ? src->getBuffer() : 0, offset);
1127  }
1128 
1129  DLLLOCAL void parse(const char* code, const char* label, ExceptionSink* xsink, ExceptionSink* wS, int wm, const char* orig_src = 0, 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  assert(pgm_data_map.find(td) != pgm_data_map.end());
1363  assert(pgm_data_map[td]->inst);
1364  break;
1365  }
1366  ++twaiting;
1367  tcond.wait(tlock);
1368  --twaiting;
1369  }
1370 
1371  pgm_data_map_t::iterator i = pgm_data_map.find(td);
1372  if (i == pgm_data_map.end()) {
1373  assert(!tclear);
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 void addUserFeature(const char* f) {
1839  //printd(5, "qore_program_private::addUserFeature() this: %p pgm: %p '%s'\n", this, pgm, f);
1840  assert(!userFeatureList.find(f));
1841  userFeatureList.push_back(f);
1842  }
1843 
1844  DLLLOCAL void removeUserFeature(const char* f) {
1845  CharPtrList::iterator i = userFeatureList.safe_dslist<std::string>::find(f);
1846  assert(i != userFeatureList.end());
1847  userFeatureList.erase(i);
1848  }
1849 
1850  DLLLOCAL void addFeature(const char* f) {
1851  assert(!featureList.find(f));
1852  featureList.push_back(f);
1853  }
1854 
1855  DLLLOCAL void removeFeature(const char* f) {
1856  CharPtrList::iterator i = featureList.safe_dslist<std::string>::find(f);
1857  assert(i != featureList.end());
1858  featureList.erase(i);
1859  }
1860 
1861  DLLLOCAL void runtimeImportSystemClassesIntern(const qore_program_private& spgm, ExceptionSink* xsink);
1862  DLLLOCAL void runtimeImportSystemConstantsIntern(const qore_program_private& spgm, ExceptionSink* xsink);
1863  DLLLOCAL void runtimeImportSystemFunctionsIntern(const qore_program_private& spgm, ExceptionSink* xsink);
1864  DLLLOCAL void runtimeImportSystemHashDeclsIntern(const qore_program_private& spgm, ExceptionSink* xsink);
1865 
1866  DLLLOCAL void runtimeImportSystemClasses(ExceptionSink* xsink);
1867  DLLLOCAL void runtimeImportSystemConstants(ExceptionSink* xsink);
1868  DLLLOCAL void runtimeImportSystemFunctions(ExceptionSink* xsink);
1869  DLLLOCAL void runtimeImportSystemHashDecls(ExceptionSink* xsink);
1870  DLLLOCAL void runtimeImportSystemApi(ExceptionSink* xsink);
1871 
1872  DLLLOCAL void doThreadInit(ExceptionSink* xsink);
1873 
1874  DLLLOCAL const QoreClass* runtimeFindClass(const char* class_name, ExceptionSink* xsink) const;
1875 
1876  DLLLOCAL void setExternalData(const char* owner, AbstractQoreProgramExternalData* pud) {
1877  AutoLocker al(plock);
1878  assert(extmap.find(owner) == extmap.end());
1879  extmap.insert(extmap_t::value_type(owner, pud));
1880  }
1881 
1882  DLLLOCAL AbstractQoreProgramExternalData* getExternalData(const char* owner) const {
1883  AutoLocker al(plock);
1884  extmap_t::const_iterator i = extmap.find(owner);
1885  return i == extmap.end() ? nullptr : i->second;
1886  }
1887 
1888  DLLLOCAL AbstractQoreProgramExternalData* removeExternalData(const char* owner) {
1889  AutoLocker al(plock);
1890  extmap_t::iterator i = extmap.find(owner);
1891  if (i == extmap.end()) {
1892  return nullptr;
1893  }
1894  AbstractQoreProgramExternalData* rv = i->second;
1895  extmap.erase(i);
1896  return rv;
1897  }
1898 
1899  DLLLOCAL QoreHashNode* getGlobalVars() const {
1900  return qore_root_ns_private::getGlobalVars(*RootNS);
1901  }
1902 
1903  DLLLOCAL LocalVar* createLocalVar(const char* name, const QoreTypeInfo* typeInfo);
1904 
1905  DLLLOCAL const AbstractQoreFunctionVariant* runtimeFindCall(const char* name, const QoreListNode* params, ExceptionSink* xsink);
1906 
1907  DLLLOCAL QoreListNode* runtimeFindCallVariants(const char* name, ExceptionSink* xsink);
1908 
1909  DLLLOCAL static const QoreClass* runtimeFindClass(const QoreProgram& pgm, const char* class_name, ExceptionSink* xsink) {
1910  return pgm.priv->runtimeFindClass(class_name, xsink);
1911  }
1912 
1913  DLLLOCAL static void doThreadInit(QoreProgram& pgm, ExceptionSink* xsink) {
1914  pgm.priv->doThreadInit(xsink);
1915  }
1916 
1917  DLLLOCAL static int setReturnValue(QoreProgram& pgm, QoreValue val, ExceptionSink* xsink) {
1918  ValueHolder rv(val, xsink);
1919  if (!pgm.priv->exec_class) {
1920  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>))");
1921  return -1;
1922  }
1923  pgm.priv->exec_class_rv.discard(xsink);
1924  pgm.priv->exec_class_rv = rv.release();
1925  return 0;
1926  }
1927 
1928  // called when starting a new thread before the new thread is started, to avoid race conditions
1929  DLLLOCAL static int preregisterNewThread(QoreProgram& pgm, ExceptionSink* xsink) {
1930  return pgm.priv->preregisterNewThread(xsink);
1931  }
1932 
1933  // called when thread startup fails after preregistration
1934  DLLLOCAL static void cancelPreregistration(QoreProgram& pgm) {
1935  pgm.priv->cancelPreregistration();
1936  }
1937 
1938  // called from the new thread once the thread has been started (after preregisterNewThread())
1939  DLLLOCAL static void registerNewThread(QoreProgram& pgm, int tid) {
1940  pgm.priv->registerNewThread(tid);
1941  }
1942 
1943  DLLLOCAL static void runtimeImportSystemClasses(QoreProgram& pgm, ExceptionSink* xsink) {
1944  pgm.priv->runtimeImportSystemClasses(xsink);
1945  }
1946 
1947  DLLLOCAL static void runtimeImportSystemHashDecls(QoreProgram& pgm, ExceptionSink* xsink) {
1948  pgm.priv->runtimeImportSystemHashDecls(xsink);
1949  }
1950 
1951  DLLLOCAL static void runtimeImportSystemConstants(QoreProgram& pgm, ExceptionSink* xsink) {
1952  pgm.priv->runtimeImportSystemConstants(xsink);
1953  }
1954 
1955  DLLLOCAL static void runtimeImportSystemFunctions(QoreProgram& pgm, ExceptionSink* xsink) {
1956  pgm.priv->runtimeImportSystemFunctions(xsink);
1957  }
1958 
1959  DLLLOCAL static void runtimeImportSystemApi(QoreProgram& pgm, ExceptionSink* xsink) {
1960  pgm.priv->runtimeImportSystemApi(xsink);
1961  }
1962 
1963  DLLLOCAL static qore_program_private* get(QoreProgram& pgm) {
1964  return pgm.priv;
1965  }
1966 
1967  DLLLOCAL static void clearThreadData(QoreProgram& pgm, ExceptionSink* xsink) {
1968  pgm.priv->clearThreadData(xsink);
1969  }
1970 
1971  DLLLOCAL static void addUserFeature(QoreProgram& pgm, const char* f) {
1972  pgm.priv->addUserFeature(f);
1973  }
1974 
1975  DLLLOCAL static void addStatement(QoreProgram& pgm, AbstractStatement* s) {
1976  pgm.priv->addStatement(s);
1977  }
1978 
1979  DLLLOCAL static const AbstractQoreZoneInfo* currentTZIntern(QoreProgram& pgm) {
1980  return pgm.priv->TZ;
1981  }
1982 
1983  DLLLOCAL static int lockParsing(QoreProgram& pgm, ExceptionSink* xsink) {
1984  return pgm.priv->lockParsing(xsink);
1985  }
1986 
1987  DLLLOCAL static void unlockParsing(QoreProgram& pgm) {
1988  pgm.priv->unlockParsing();
1989  }
1990 
1991  DLLLOCAL static int incThreadCount(QoreProgram& pgm, ExceptionSink* xsink) {
1992  return pgm.priv->incThreadCount(xsink);
1993  }
1994 
1995  DLLLOCAL static void decThreadCount(QoreProgram& pgm, int tid) {
1996  pgm.priv->decThreadCount(tid);
1997  }
1998 
1999  // locking is done by the signal manager
2000  DLLLOCAL static void addSignal(QoreProgram& pgm, int sig) {
2001  assert(pgm.priv->sigset.find(sig) == pgm.priv->sigset.end());
2002  pgm.priv->sigset.insert(sig);
2003  }
2004 
2005  // locking is done by the signal manager
2006  DLLLOCAL static void delSignal(QoreProgram& pgm, int sig) {
2007  assert(pgm.priv->sigset.find(sig) != pgm.priv->sigset.end());
2008  pgm.priv->sigset.erase(sig);
2009  }
2010 
2011  DLLLOCAL static void startThread(QoreProgram& pgm, ExceptionSink& xsink) {
2012  pgm.priv->qore_program_private_base::startThread(xsink);
2013  }
2014 
2015  DLLLOCAL static bool setThreadInit(QoreProgram& pgm, const ResolvedCallReferenceNode* n_thr_init, ExceptionSink* xsink) {
2016  return pgm.priv->setThreadInit(n_thr_init, xsink);
2017  }
2018 
2019  DLLLOCAL static ResolvedCallReferenceNode* runtimeGetCallReference(QoreProgram* pgm, const char* name, ExceptionSink* xsink) {
2020  return pgm->priv->runtimeGetCallReference(name, xsink);
2021  }
2022 
2023  DLLLOCAL static const ParseWarnOptions& getParseWarnOptions(const QoreProgram* pgm) {
2024  return pgm->priv->pwo;
2025  }
2026 
2027  DLLLOCAL static bool setSaveParseWarnOptions(const QoreProgram* pgm, const ParseWarnOptions &new_opts, ParseWarnOptions &old_opts) {
2028  if (new_opts == pgm->priv->pwo)
2029  return false;
2030  old_opts = pgm->priv->pwo;
2031  pgm->priv->pwo = new_opts;
2032  return true;
2033  }
2034 
2035  DLLLOCAL static void setParseWarnOptions(const QoreProgram* pgm, const ParseWarnOptions &new_opts) {
2036  pgm->priv->pwo = new_opts;
2037  }
2038 
2039  DLLLOCAL static bool setThreadVarData(QoreProgram* pgm, ThreadProgramData* td, ThreadLocalProgramData* &tlpd, bool run) {
2040  return pgm->priv->setThreadVarData(td, tlpd, run);
2041  }
2042 
2043  DLLLOCAL static void makeParseException(QoreProgram* pgm, const QoreProgramLocation &loc, QoreStringNode* desc) {
2044  pgm->priv->makeParseException(loc, "PARSE-EXCEPTION", desc);
2045  }
2046 
2047  DLLLOCAL static void makeParseException(QoreProgram* pgm, const QoreProgramLocation &loc, const char* err, QoreStringNode* desc) {
2048  pgm->priv->makeParseException(loc, err, desc);
2049  }
2050 
2051  DLLLOCAL static const QoreValue parseGetDefine(QoreProgram* pgm, const char* name) {
2052  bool is_defined;
2053  return pgm->priv->getDefine(name, is_defined);
2054  }
2055 
2056  DLLLOCAL static const QoreValue parseGetDefine(QoreProgram* pgm, const char* name, bool& is_defined) {
2057  return pgm->priv->getDefine(name, is_defined);
2058  }
2059 
2060  DLLLOCAL static QoreValue runTimeGetDefine(QoreProgram* pgm, const char* name) {
2061  bool is_defined;
2062  return pgm->priv->runTimeGetDefine(name, is_defined);
2063  }
2064 
2065  DLLLOCAL static QoreValue runTimeGetDefine(QoreProgram* pgm, const char* name, bool& is_defined) {
2066  return pgm->priv->runTimeGetDefine(name, is_defined);
2067  }
2068 
2069  DLLLOCAL static QoreHashNode* runTimeGetAllDefines(QoreProgram* pgm) {
2070  return pgm->priv->runTimeGetAllDefines();
2071  }
2072 
2073  DLLLOCAL static bool parseUnDefine(QoreProgram* pgm, const char* name) {
2074  return pgm->priv->parseUnDefine(name);
2075  }
2076 
2077  DLLLOCAL static bool runTimeUnDefine(QoreProgram* pgm, const char* name, ExceptionSink* xsink) {
2078  return pgm->priv->runTimeUnDefine(name, xsink);
2079  }
2080 
2081  DLLLOCAL static bool parseIsDefined(QoreProgram* pgm, const char* name) {
2082  return pgm->priv->isDefined(name);
2083  }
2084 
2085  DLLLOCAL static bool runTimeIsDefined(QoreProgram* pgm, const char* name) {
2086  return pgm->priv->runTimeIsDefined(name);
2087  }
2088 
2089  DLLLOCAL static void parseDefine(QoreProgram* pgm, const QoreProgramLocation* loc, const char* str, QoreValue val) {
2090  pgm->priv->parseDefine(loc, str, val);
2091  }
2092 
2093  DLLLOCAL static void runTimeDefine(QoreProgram* pgm, const char* str, QoreValue val, ExceptionSink* xsink) {
2094  pgm->priv->runTimeDefine(str, val, xsink);
2095  }
2096 
2097  DLLLOCAL static void addParseException(QoreProgram* pgm, ExceptionSink* xsink, const QoreProgramLocation* loc = nullptr) {
2098  assert(xsink);
2099  pgm->priv->addParseException(*xsink, loc);
2100  delete xsink;
2101  }
2102 
2103  DLLLOCAL static void addParseException(QoreProgram* pgm, ExceptionSink& xsink, const QoreProgramLocation* loc = nullptr) {
2104  pgm->priv->addParseException(xsink, loc);
2105  }
2106 
2107  DLLLOCAL static void exportFunction(QoreProgram* srcpgm, ExceptionSink* xsink, QoreProgram* trgpgm, const char* name, const char* new_name = nullptr, bool inject = false) {
2108  srcpgm->priv->exportFunction(xsink, trgpgm->priv, name, new_name, inject);
2109  }
2110 
2111  DLLLOCAL static int64 parseAddDomain(QoreProgram* pgm, int64 n_dom) {
2112  return pgm->priv->parseAddDomain(n_dom);
2113  }
2114 
2115  DLLLOCAL static int64 getDomain(const QoreProgram& pgm) {
2116  return pgm.priv->dom;
2117  }
2118 
2119  DLLLOCAL static void runtimeAddDomain(QoreProgram& pgm, int64 n_dom) {
2120  pgm.priv->dom |= n_dom;
2121  }
2122 
2123  DLLLOCAL static int64 forceReplaceParseOptions(QoreProgram& pgm, int64 po) {
2124  int64 rv = pgm.priv->pwo.parse_options;
2125  pgm.priv->pwo.parse_options = po;
2126  return rv;
2127  }
2128 
2129  DLLLOCAL static void makeParseWarning(QoreProgram* pgm, const QoreProgramLocation &loc, int code, const char* warn, const char* fmt, ...) {
2130  //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");
2131  if (!pgm->priv->warnSink || !(code & pgm->priv->pwo.warn_mask))
2132  return;
2133 
2134  QoreStringNode* desc = new QoreStringNode;
2135  while (true) {
2136  va_list args;
2137  va_start(args, fmt);
2138  int rc = desc->vsprintf(fmt, args);
2139  va_end(args);
2140  if (!rc)
2141  break;
2142  }
2143  QoreException *ne = new ParseException(loc, warn, desc);
2144  pgm->priv->warnSink->raiseException(ne);
2145  }
2146 
2147  DLLLOCAL static void makeParseWarning(QoreProgram* pgm, const QoreProgramLocation &loc, int code, const char* warn, QoreStringNode* desc) {
2148  //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");
2149  if (!pgm->priv->warnSink || !(code & pgm->priv->pwo.warn_mask)) {
2150  desc->deref();
2151  return;
2152  }
2153 
2154  QoreException *ne = new ParseException(loc, warn, desc);
2155  pgm->priv->warnSink->raiseException(ne);
2156  }
2157 
2158  DLLLOCAL static void exportGlobalVariable(QoreProgram* pgm, const char* name, bool readonly, QoreProgram* tpgm, ExceptionSink* xsink) {
2159  pgm->priv->exportGlobalVariable(name, readonly, *(tpgm->priv), xsink);
2160  }
2161 
2162  DLLLOCAL void attachDebug(const qore_debug_program_private* n_dpgm) {
2163  printd(5, "qore_program_private::attachDebug(n_dpgm: %p), dpgm: %p\n", n_dpgm, dpgm);
2164  AutoLocker al(tlock);
2165  //QoreAutoRWWriteLocker arwl(&lck_debug_program);
2166 
2167  if (dpgm == n_dpgm)
2168  return;
2169  dpgm = const_cast<qore_debug_program_private*>(n_dpgm);
2170  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());
2171  for (auto& i : pgm_data_map) {
2172  i.second->dbgPendingAttach();
2173  i.second->dbgBreak();
2174  }
2175  }
2176 
2177  DLLLOCAL void detachDebug(const qore_debug_program_private* n_dpgm) {
2178  printd(5, "qore_program_private::detachDebug(n_dpgm: %p), dpgm: %p\n", n_dpgm, dpgm);
2179  AutoLocker al(tlock);
2180  //QoreAutoRWWriteLocker arwl(&lck_debug_program);
2181  assert(n_dpgm==dpgm);
2182  if (!n_dpgm)
2183  return;
2184  dpgm = nullptr;
2185  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());
2186  for (auto& i : pgm_data_map) {
2187  i.second->dbgPendingDetach();
2188  }
2189  // debug_program_counter may be non zero to finish pending calls. Just this instance cannot be deleted, it's satisfied in destructor
2190  }
2191 
2192  DLLLOCAL void onAttach(DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink);
2193  DLLLOCAL void onDetach(DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink);
2194  DLLLOCAL void onStep(const StatementBlock *blockStatement, const AbstractStatement *statement, unsigned bkptId, int &flow, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink);
2195  DLLLOCAL void onFunctionEnter(const StatementBlock *statement, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink);
2196  DLLLOCAL void onFunctionExit(const StatementBlock *statement, QoreValue& returnValue, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink);
2197  DLLLOCAL void onException(const AbstractStatement *statement, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink);
2198  DLLLOCAL void onExit(const StatementBlock *statement, QoreValue& returnValue, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink);
2199 
2200  DLLLOCAL int breakProgramThread(int tid) {
2201  printd(5, "qore_program_private::breakProgramThread(), this: %p, tid: %d\n", this, q_gettid());
2202  AutoLocker al(tlock);
2203  for (auto& i : pgm_data_map) {
2204  if (i.first->gettid() == tid) {
2205  i.second->dbgBreak();
2206  return 0;
2207  }
2208  }
2209  return -1;
2210  }
2211 
2212  DLLLOCAL void breakProgram() {
2213  printd(5, "qore_program_private::breakProgram(), this: %p\n", this);
2214  AutoLocker al(tlock);
2215  for (auto& i : pgm_data_map) {
2216  i.second->dbgBreak();
2217  }
2218  }
2219 
2220  DLLLOCAL void assignBreakpoint(QoreBreakpoint *bkpt, ExceptionSink *xsink) {
2221  if (!bkpt || this == bkpt->pgm) return;
2222  if (!checkAllowDebugging(xsink))
2223  return;
2224  if (bkpt->pgm) {
2225  bkpt->assignProgram(nullptr, nullptr);
2226  }
2227  QoreAutoRWWriteLocker al(&lck_breakpoint);
2228  breakpointList.push_back(bkpt);
2229  bkpt->pgm = this;
2230  bkpt->ref();
2231  }
2232 
2233  DLLLOCAL void deleteAllBreakpoints() {
2234  QoreAutoRWWriteLocker al(&lck_breakpoint);
2235  for (QoreBreakpointList_t::iterator it = breakpointList.begin(); it != breakpointList.end(); ++it) {
2236  (*it)->unassignAllStatements();
2237  (*it)->pgm = nullptr;
2238  (*it)->deref();
2239  }
2240  breakpointList.clear();
2241  }
2242 
2243  DLLLOCAL void getBreakpoints(QoreBreakpointList_t &bkptList) {
2244  QoreAutoRWReadLocker al(&lck_breakpoint);
2245  bkptList.clear();
2246  for (QoreBreakpointList_t::iterator it = breakpointList.begin(); it != breakpointList.end(); ++it) {
2247  bkptList.push_back(*it);
2248  (*it)->ref();
2249  }
2250  }
2251 
2252  DLLLOCAL void getStatementBreakpoints(const AbstractStatement* statement, QoreBreakpointList_t &bkptList) {
2253  QoreAutoRWReadLocker al(&lck_breakpoint);
2254  bkptList.clear();
2255  if (statement->breakpoints) {
2256  for (std::list<QoreBreakpoint*>::iterator it = statement->breakpoints->begin(); it != statement->breakpoints->end(); ++it) {
2257  bkptList.push_back(*it);
2258  (*it)->ref();
2259  }
2260  }
2261  }
2262 
2263  DLLLOCAL inline unsigned onCheckBreakpoint(const AbstractStatement *statement, ExceptionSink* xsink) {
2264  QoreAutoRWReadLocker al(&lck_breakpoint);
2265  const QoreBreakpoint *b = statement->getBreakpoint();
2266  if (b != nullptr) {
2267  return b->getBreakpointId();
2268  } else {
2269  return 0;
2270  }
2271  }
2272 
2273  DLLLOCAL unsigned long getStatementId(const AbstractStatement* statement) const {
2274  AutoLocker al(&plock);
2275  if (!statement)
2276  return 0;
2277  ReverseStatementIdMap_t::const_iterator i = reverseStatementIds.find((AbstractStatement*) statement);
2278  if (i == reverseStatementIds.end())
2279  return 0;
2280  return i->second;
2281  }
2282 
2283  DLLLOCAL AbstractStatement* resolveStatementId(unsigned long statementId) const {
2284  AutoLocker al(&plock);
2285  if (statementId == 0 || statementId > statementIds.size())
2286  return nullptr;
2287  return statementIds[statementId-1];
2288  }
2289 
2290  DLLLOCAL unsigned getProgramId() const {
2291  return programId;
2292  }
2293 
2294  // called locked
2295  DLLLOCAL void clearNamespaceData(ExceptionSink* xsink) {
2296  if (ns_vars) {
2297  return;
2298  }
2299  assert(RootNS);
2300  ns_vars = true;
2301  // delete all global variables, etc
2302  // this call can only be made once
2303  qore_root_ns_private::clearData(*RootNS, xsink);
2304  }
2305 
2306  DLLLOCAL static QoreProgram* resolveProgramId(unsigned programId) {
2307  printd(5, "qore_program_private::resolveProgramId(%x)\n", programId);
2308  QoreAutoRWReadLocker al(&lck_programMap);
2309  for (qore_program_to_object_map_t::iterator i = qore_program_to_object_map.begin(); i != qore_program_to_object_map.end(); i++) {
2310  if (i->first->priv->programId == programId)
2311  return i->first;
2312  }
2313  return nullptr;
2314  }
2315 
2316  DLLLOCAL bool checkAllowDebugging(ExceptionSink *xsink) {
2317  if (pwo.parse_options & PO_NO_DEBUGGING) {
2318  if (xsink) {
2319  xsink->raiseException("DEBUGGING", "program does not provide internal data for debugging");
2320  }
2321  return false;
2322  } else {
2323  return true;
2324  }
2325  }
2326 
2327  DLLLOCAL void addStatementToIndexIntern(name_section_sline_statement_map_t* statementIndex, const char* key, AbstractStatement *statement, int offs, const char* section, int sectionOffs);
2328  DLLLOCAL static void registerStatement(QoreProgram *pgm, AbstractStatement *statement, bool addToIndex);
2329  DLLLOCAL QoreHashNode* getSourceIndicesIntern(name_section_sline_statement_map_t* statementIndex, ExceptionSink* xsink) const;
2330  DLLLOCAL QoreHashNode* getSourceLabels(ExceptionSink* xsink) {
2331  return getSourceIndicesIntern(&statementByLabelIndex, xsink);
2332  }
2333  DLLLOCAL QoreHashNode* getSourceFileNames(ExceptionSink* xsink) {
2334  return getSourceIndicesIntern(&statementByFileIndex, xsink);
2335  }
2336 
2337  DLLLOCAL AbstractStatement* getStatementFromIndex(const char* name, int line) {
2338  printd(5, "qore_program_private::getStatementFromIndex('%s',%d), this: %p, file#: %d, label#: %d\n", name, line, this, statementByFileIndex.size(), statementByLabelIndex.size());
2339  AutoLocker al(&plock);
2340  name_section_sline_statement_map_t::iterator it;
2341  if (statementByFileIndex.empty()) {
2342  return nullptr;
2343  }
2344  bool addOffs = true;
2345  if (!name || *name == '\0') {
2346  if (statementByFileIndex.size() != 1)
2347  return nullptr;
2348  it = statementByFileIndex.begin();
2349  } else {
2350  size_t l = strlen(name);
2351  it = statementByFileIndex.find(name);
2352  if (it == statementByFileIndex.end()) {
2353  it = statementByLabelIndex.find(name); // label only full name match
2354  if (it == statementByLabelIndex.end()) {
2355 
2356  // did not find exact match so try a heuristic
2357  it = statementByFileIndex.begin();
2358  while (it != statementByFileIndex.end()) {
2359  size_t k = strlen(it->first);
2360  if (k >= l) {
2361  if (strcmp(name, it->first + k - l) == 0) {
2362  // match against suffix following '/'
2363  if (k == l /* should not happen as it is full match*/ || name[0] == '/' || it->first[k-l-1] == '/') {
2364  break;
2365  }
2366  }
2367  }
2368  ++it;
2369  }
2370  if (it == statementByFileIndex.end()) {
2371  printd(5, "qore_program_private::getStatementFromIndex('%s',%d) no suffix match, this: %p\n", name, line, this);
2372  return nullptr;
2373  }
2374  printd(5, "qore_program_private::getStatementFromIndex('%s',%d) found by file suffix match, this: %p\n", name, line, this);
2375  } else {
2376  addOffs = false;
2377  printd(5, "qore_program_private::getStatementFromIndex('%s',%d) found by label full match, this: %p\n", name, line, this);
2378  }
2379  } else {
2380  printd(5, "qore_program_private::getStatementFromIndex('%s',%d) found by file full match, this: %p\n", name, line, this);
2381  }
2382  }
2383  sline_statement_map_t *ssm = &it->second->statementMap;
2384  printd(5, "qore_program_private::getStatementFromIndex('%s',%d) found '%s', this: %p, ssm#: %d\n", name, line, it->first, this, ssm->size());
2385  if (ssm->size() == 0)
2386  return nullptr;
2387 
2388  sline_statement_map_t::iterator li = ssm->upper_bound(line);
2389  if (li == ssm->begin()) {
2390  printd(5, "qore_program_private::getStatementFromIndex('%s',%d) no statement found by line #1, this: %p\n", name, line, this);
2391  return nullptr;
2392  }
2393  --li;
2394  int ln = li->first;
2395  int minnl = -1;
2396  AbstractStatement* st = nullptr;
2397  while (true) {
2398  // find the nearest statement, i.e. statement with smallest num of lines
2399  if (ln != li->first) {
2400  break;
2401  // we do not try to find outer statement when looking for line behind inner statement
2402  }
2403  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) {
2404  int n = li->second->loc->end_line - li->second->loc->start_line;
2405  if (minnl >= 0) {
2406  if (n < minnl) {
2407  minnl = n;
2408  st = li->second;
2409  }
2410  } else {
2411  minnl = n;
2412  st = li->second;
2413  }
2414  if (minnl == 0)
2415  break;
2416  }
2417  if (li == ssm->begin())
2418  break;
2419  --li;
2420  }
2421  if (st) {
2422  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);
2423  } else {
2424  printd(5, "qore_program_private::getStatementFromIndex('%s',%d) no statement found by line #2, this: %p\n", name, line, this);
2425  }
2426  return st;
2427  }
2428 
2429  DLLLOCAL void registerQoreObject(QoreObject *o, ExceptionSink* xsink) {
2430  printd(5, "qore_program_private::registerQoreObject() pgm: %p, pgmid: %d\n", pgm, getProgramId());
2431  QoreAutoRWWriteLocker al(&qore_program_private::lck_programMap);
2432  qore_program_to_object_map_t::iterator i = qore_program_private::qore_program_to_object_map.find(pgm);
2433  assert(i != qore_program_private::qore_program_to_object_map.end());
2434  if (i->second && i->second != o) {
2435  xsink->raiseException("PROGRAM-ERROR", "The Program has already assigned QoreObject");
2436  } else {
2437  i->second = o;
2438  }
2439  }
2440 
2441  DLLLOCAL void unregisterQoreObject(QoreObject *o, ExceptionSink* xsink) {
2442  printd(5, "qore_program_private::unregisterQoreObject() pgm: %p, pgmid: %d\n", pgm, getProgramId());
2443  QoreAutoRWWriteLocker al(&qore_program_private::lck_programMap);
2444  qore_program_to_object_map_t::iterator i = qore_program_private::qore_program_to_object_map.find(pgm);
2445  assert(i != qore_program_private::qore_program_to_object_map.end());
2446  assert(i->second == o);
2447  i->second = nullptr;
2448  }
2449 
2450  DLLLOCAL QoreObject* findQoreObject() {
2451  QoreAutoRWReadLocker al(&lck_programMap);
2452  qore_program_to_object_map_t::iterator i = qore_program_to_object_map.find(pgm);
2453  if (i == qore_program_to_object_map.end()) {
2454  return nullptr;
2455  } else {
2456  return i->second;
2457  }
2458  }
2459 
2460  DLLLOCAL static QoreObject* getQoreObject(QoreProgram* pgm) {
2461  QoreAutoRWWriteLocker al(&lck_programMap);
2462  qore_program_to_object_map_t::iterator i = qore_program_to_object_map.find(pgm);
2463  assert(i != qore_program_to_object_map.end());
2464  if (i->second) {
2465  printd(5, "qore_program_private::getQoreObject() pgm: %p, pgmid: %d, second: %p\n", i->first, i->first->getProgramId(), i->second);
2466  i->second->ref();
2467  } else {
2468  i->second = new QoreObject(QC_PROGRAMCONTROL, getProgram(), pgm);
2469  printd(5, "qore_program_private::getQoreObject() pgm: %p, pgmid: %d, new second: %p\n", pgm, pgm->getProgramId(), i->second);
2470  pgm->ref();
2471  }
2472  return i->second;
2473  }
2474 
2475  DLLLOCAL static QoreListNode* getAllQoreObjects(ExceptionSink *xsink) {
2476  QoreAutoRWWriteLocker al(&lck_programMap);
2477  ReferenceHolder<QoreListNode>l(new QoreListNode(QC_PROGRAMCONTROL->getTypeInfo()), xsink);
2478 
2479  qore_program_to_object_map_t::iterator i = qore_program_to_object_map.begin();
2480  while (i != qore_program_to_object_map.end()) {
2481  if (i->second) {
2482  printd(5, "qore_program_private::getAllQoreObjects() pgm: %p, pgmid: %d, second: %p\n", i->first, i->first->getProgramId(), i->second);
2483  i->second->ref();
2484  } else {
2485  i->second = new QoreObject(QC_PROGRAMCONTROL, getProgram(), i->first);
2486  printd(5, "qore_program_private::getAllQoreObjects() pgm: %p, pgmid: %d, new second: %p\n", i->first, i->first->getProgramId(), i->second);
2487  i->first->ref();
2488  }
2489  (*l)->push(i->second, nullptr);
2490  ++i;
2491  }
2492  return l.release();
2493  }
2494 };
2495 
2496 class ParseWarnHelper : public ParseWarnOptions {
2497 protected:
2498  bool restore;
2499 public:
2500  DLLLOCAL ParseWarnHelper(const ParseWarnOptions &new_opts) {
2501  QoreProgram* pgm = getProgram();
2502  restore = pgm ? qore_program_private::setSaveParseWarnOptions(pgm, new_opts, *this) : false;
2503  }
2504  DLLLOCAL ~ParseWarnHelper() {
2505  if (restore)
2506  qore_program_private::setParseWarnOptions(getProgram(), *this);
2507  }
2508 };
2509 
2510 typedef std::map<QoreProgram*, qore_program_private*> qore_program_map_t;
2511 class QoreDebugProgram;
2512 
2513 class qore_debug_program_private {
2514 private:
2515  //mutable QoreThreadLock tlock; // thread variable data lock, for accessing the program list variable
2516  mutable QoreRWLock tlock;
2517  QoreDebugProgram *dpgm;
2518  qore_program_map_t qore_program_map;
2519  mutable QoreCounter debug_program_counter; // number of thread calls from program instance.
2520 public:
2521  DLLLOCAL qore_debug_program_private(QoreDebugProgram *n_dpgm): dpgm(n_dpgm) {};
2522  DLLLOCAL ~qore_debug_program_private() {
2523  assert(qore_program_map.empty());
2524  }
2525 
2526  DLLLOCAL void addProgram(QoreProgram *pgm, ExceptionSink *xsink) {
2527  if (!pgm->priv->checkAllowDebugging(xsink))
2528  return;
2529  QoreAutoRWWriteLocker al(&tlock);
2530  qore_program_map_t::iterator i = qore_program_map.find(pgm);
2531  printd(5, "qore_debug_program_private::addProgram(), this: %p, pgm: %p, i: %p, end: %p\n", this, pgm, i, qore_program_map.end());
2532  if (i != qore_program_map.end())
2533  return; // already exists
2534  qore_program_map.insert(qore_program_map_t::value_type(pgm, pgm->priv));
2535  pgm->ref();
2536  pgm->priv->attachDebug(this);
2537  }
2538 
2539  DLLLOCAL void removeProgram(QoreProgram *pgm) {
2540  QoreAutoRWWriteLocker al(&tlock);
2541  qore_program_map_t::iterator i = qore_program_map.find(pgm);
2542  printd(5, "qore_debug_program_private::removeProgram(), this: %p, pgm: %p, i: %p, end: %p\n", this, pgm, i, qore_program_map.end());
2543  if (i == qore_program_map.end())
2544  return;
2545  pgm->priv->detachDebug(this);
2546  qore_program_map.erase(i);
2547  pgm->deref();
2548  // onDetach will not be executed as program is removed
2549  }
2550 
2551  DLLLOCAL void removeAllPrograms() {
2552  QoreAutoRWWriteLocker al(&tlock);
2553  printd(5, "qore_debug_program_private::removeAllPrograms(), this: %p\n", this);
2554  qore_program_map_t::iterator i;
2555  while ((i = qore_program_map.begin()) != qore_program_map.end()) {
2556  qore_program_private* qpp = i->second;
2557  QoreProgram *p = i->first;
2558  qore_program_map.erase(i);
2559  qpp->detachDebug(this);
2560  p->deref();
2561  }
2562  }
2563 
2564  DLLLOCAL QoreListNode* getAllProgramObjects() {
2565  QoreAutoRWReadLocker al(&tlock);
2566  printd(5, "qore_debug_program_private::getAllProgramObjects(), this: %p\n", this);
2567  ReferenceHolder<QoreListNode> l(new QoreListNode(QC_PROGRAM->getTypeInfo()), nullptr);
2568  qore_program_map_t::iterator i = qore_program_map.begin();
2569  while (i != qore_program_map.end()) {
2570  QoreObject* o = QoreProgram::getQoreObject(i->first);
2571  if (o) {
2572  (*l)->push(o, nullptr);
2573  }
2574  i++;
2575  }
2576  return l.release();
2577  }
2578 
2579  DLLLOCAL void onAttach(QoreProgram *pgm, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink) {
2580  AutoQoreCounterDec ad(&debug_program_counter);
2581  dpgm->onAttach(pgm, rs, rts, xsink);
2582  }
2583  DLLLOCAL void onDetach(QoreProgram *pgm, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink) {
2584  AutoQoreCounterDec ad(&debug_program_counter);
2585  dpgm->onDetach(pgm, rs, rts, xsink);
2586  }
2593  DLLLOCAL void onStep(QoreProgram *pgm, const StatementBlock *blockStatement, const AbstractStatement *statement, unsigned bkptId, int &flow, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink) {
2594  AutoQoreCounterDec ad(&debug_program_counter);
2595  dpgm->onStep(pgm, blockStatement, statement, bkptId, flow, rs, rts, xsink);
2596 
2597  }
2598  DLLLOCAL void onFunctionEnter(QoreProgram *pgm, const StatementBlock *statement, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink) {
2599  AutoQoreCounterDec ad(&debug_program_counter);
2600  dpgm->onFunctionEnter(pgm, statement, rs, rts, xsink);
2601  }
2605  DLLLOCAL void onFunctionExit(QoreProgram *pgm, const StatementBlock *statement, QoreValue& returnValue, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink) {
2606  AutoQoreCounterDec ad(&debug_program_counter);
2607  dpgm->onFunctionExit(pgm, statement, returnValue, rs, rts, xsink);
2608  }
2612  DLLLOCAL void onException(QoreProgram* pgm, const AbstractStatement* statement, DebugRunStateEnum& rs, const AbstractStatement *&rts, ExceptionSink* xsink) {
2613  AutoQoreCounterDec ad(&debug_program_counter);
2614  dpgm->onException(pgm, statement, rs, rts, xsink);
2615  }
2619  DLLLOCAL void onExit(QoreProgram *pgm, const StatementBlock *statement, QoreValue& returnValue, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink) {
2620  AutoQoreCounterDec ad(&debug_program_counter);
2621  dpgm->onExit(pgm, statement, returnValue, rs, rts, xsink);
2622  }
2623 
2624  DLLLOCAL int breakProgramThread(QoreProgram* pgm, int tid) {
2625  //printd(5, "breakProgramThread pgm: %p tid: %d po: %lld\n", pgm, tid, pgm->priv->pwo.parse_options);
2626  // do not allow breaking if the Program does not support debugging
2627  if (pgm->priv->pwo.parse_options & PO_NO_DEBUGGING)
2628  return -1;
2629 
2630  QoreAutoRWReadLocker al(&tlock);
2631  qore_program_map_t::iterator i = qore_program_map.find(pgm);
2632  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);
2633  if (i == qore_program_map.end())
2634  return -2;
2635  if (i->second->breakProgramThread(tid))
2636  return -3;
2637  return 0;
2638  }
2639 
2640  DLLLOCAL int breakProgram(QoreProgram* pgm) {
2641  //printd(5, "breakProgram pgm: %p po: %lld\n", pgm, pgm->priv->pwo.parse_options);
2642  // do not allow breaking if the Program does not support debugging
2643  if (pgm->priv->pwo.parse_options & PO_NO_DEBUGGING)
2644  return -1;
2645 
2646  QoreAutoRWReadLocker al(&tlock);
2647  qore_program_map_t::iterator i = qore_program_map.find(pgm);
2648  printd(5, "qore_debug_program_private::breakProgram(), this: %p, pgm: %p, i: %p, end: %p\n", this, pgm, i, qore_program_map.end());
2649  if (i == qore_program_map.end())
2650  return -2;
2651  i->second->breakProgram();
2652  return 0;
2653  }
2654 
2655  DLLLOCAL void waitForTerminationAndClear(ExceptionSink* xsink) {
2656  removeAllPrograms();
2657  // wait till all debug calls finished, avoid deadlock as it might be handled in current thread
2658  debug_program_counter.waitForZero();
2659  }
2660 
2661  DLLLOCAL int getInterruptedCount() {
2662  return debug_program_counter.getCount();
2663  }
2664 };
2665 
2666 DLLLOCAL TypedHashDecl* init_hashdecl_SourceLocationInfo(QoreNamespace& ns);
2667 
2668 #endif
QoreReferenceCounter::reference_count
DLLEXPORT int reference_count() const
gets the reference count
q_basenameptr
DLLEXPORT char * q_basenameptr(const char *path)
returns a pointer within the same string
SimpleRefHolder< QoreStringNode >
QoreBreakpoint::assignProgram
DLLEXPORT void assignProgram(QoreProgram *new_pgm, ExceptionSink *xsink)
ExceptionSink::clear
DLLEXPORT void clear()
deletes the exception list immediately
int_set_t
std::set< int > int_set_t
set of integers
Definition: common.h:82
QCS_DEFAULT
const DLLEXPORT QoreEncoding * QCS_DEFAULT
the default encoding for the Qore library
QoreProgram
supports parsing and executing Qore-language code, reference counted, dynamically-allocated only
Definition: QoreProgram.h:126
QoreValue
The main value class in Qore, designed to be passed by value.
Definition: QoreValue.h:262
QoreProgram::deref
virtual DLLEXPORT void deref(ExceptionSink *xsink)
decrements the reference count of the object
SafeDerefHelper
Helps dereference values outside of locks.
Definition: QoreLibIntern.h:462
QoreDebugProgram::onStep
virtual DLLEXPORT void onStep(QoreProgram *pgm, const StatementBlock *blockStatement, const AbstractStatement *statement, unsigned bkptId, int &flow, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink *xsink)
QoreHashNode
This is the hash or associative list container type in Qore, dynamically allocated only,...
Definition: QoreHashNode.h:50
PO_NO_LOCALE_CONTROL
#define PO_NO_LOCALE_CONTROL
do not allow changes to program locale
Definition: Restrictions.h:64
QoreListNode::copy
DLLEXPORT QoreListNode * copy() const
performs a deep copy of the list and returns the new list
QoreClass::getTypeInfo
const DLLEXPORT QoreTypeInfo * getTypeInfo() const
returns the type information structure for this class
QoreThreadLocalStorage
provides access to thread-local storage
Definition: QoreThreadLocalStorage.h:44
ExceptionSink::raiseErrnoException
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...
ReferenceHolder< QoreListNode >
QoreCounter
a simple thread-safe counter object; objects can block on it until the counter reaches zero
Definition: QoreCounter.h:40
QoreClass
defines a Qore-language class
Definition: QoreClass.h:239
QoreHashNode::copy
DLLEXPORT QoreHashNode * copy() const
performs a copy of the hash and returns the new hash
AbstractPrivateData::ref
DLLLOCAL void ref()
increments the reference count of the object
Definition: AbstractPrivateData.h:53
QoreCounter::getCount
DLLEXPORT int getCount() const
returns the current count
q_gettid
DLLEXPORT int q_gettid() noexcept
returns the current TID number
PO_ALLOW_INJECTION
#define PO_ALLOW_INJECTION
allow code injection
Definition: Restrictions.h:76
cstr_vector_t
non-thread-safe vector for storing "char*" that you want to delete
Definition: common.h:238
QoreThreadLocalStorage::set
DLLLOCAL void set(T *ptr)
sets the key's value
Definition: QoreThreadLocalStorage.h:82
QoreListNode
This is the list container type in Qore, dynamically allocated only, reference counted.
Definition: QoreListNode.h:52
QoreCondition
a thread condition class implementing a wrapper for pthread_cond_t
Definition: QoreCondition.h:45
PO_NO_DEBUGGING
#define PO_NO_DEBUGGING
disallows debugging actions that could be insecure such as reading the thread local variable stack
Definition: Restrictions.h:86
TempEncodingHelper::set
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:1066
int64
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
PO_POSITIVE_OPTIONS
#define PO_POSITIVE_OPTIONS
mask of all options allowing for more freedom (instead of less)
Definition: Restrictions.h:131
QoreProgram::getProgramId
DLLEXPORT unsigned getProgramId() const
get the program id
QoreAutoRWReadLocker
provides a safe and exception-safe way to hold read locks in Qore, only to be used on the stack,...
Definition: QoreRWLock.h:105
QoreBreakpoint
Class implementing breakpoint for debugging.
Definition: QoreProgram.h:1030
QoreDebugProgram::onExit
virtual DLLEXPORT void onExit(QoreProgram *pgm, const StatementBlock *statement, QoreValue &returnValue, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink *xsink)
AutoLocker
provides a safe and exception-safe way to hold locks in Qore, only to be used on the stack,...
Definition: QoreThreadLock.h:136
QoreNamespace
contains constants, classes, and subnamespaces in QoreProgram objects
Definition: QoreNamespace.h:65
QoreReferenceCounter::ROdereference
DLLEXPORT bool ROdereference() const
atomically decrements the reference count
QoreString
Qore's string type supported by the QoreEncoding class.
Definition: QoreString.h:81
QoreBreakpoint::getBreakpointId
DLLEXPORT unsigned getBreakpointId() const
get the breakpoint id
QoreString::c_str
const DLLEXPORT char * c_str() const
returns the string's buffer; this data should not be changed
QoreCounter::dec
DLLEXPORT int dec(ExceptionSink *xsink)
decrements the counter and wakes up any threads if the counter reaches 0
SafeDerefHelper::add
DLLLOCAL void add(QoreValue v)
adds a value for dereferencing on exit
Definition: QoreLibIntern.h:487
QoreHashNode::size
DLLEXPORT qore_size_t size() const
returns the number of members in the hash, executes in constant time
ExceptionSink::raiseException
DLLEXPORT AbstractQoreNode * raiseException(const char *err, const char *fmt,...)
appends a Qore-language exception to the list
QoreListNode::push
DLLEXPORT int push(QoreValue val, ExceptionSink *xsink)
adds a value to the list
TypedHashDecl
typed hash declaration
Definition: TypedHashDecl.h:44
ExceptionSink::assimilate
DLLEXPORT void assimilate(ExceptionSink *xs)
assimilates all entries of the "xs" argument by appending them to the internal list and deletes the "...
QoreString::getBuffer
const DLLEXPORT char * getBuffer() const
returns the string's buffer; this data should not be changed
QoreCounter::inc
DLLEXPORT int inc()
increments the counter
currentTZ
const DLLEXPORT AbstractQoreZoneInfo * currentTZ()
returns the current local time zone, note that if 0 = UTC
QoreObject
the implementation of Qore's object data type, reference counted, dynamically-allocated only
Definition: QoreObject.h:61
QoreDebugProgram::onFunctionEnter
virtual DLLEXPORT void onFunctionEnter(QoreProgram *pgm, const StatementBlock *statement, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink *xsink)
ExceptionSink
container for holding Qore-language exception information and also for registering a "thread_exit" ca...
Definition: ExceptionSink.h:48
QoreReferenceCounter::ROreference
DLLEXPORT void ROreference() const
atomically increments the reference count
QoreReferenceCounter
provides atomic reference counting to Qore objects
Definition: QoreReferenceCounter.h:44
TempEncodingHelper
use this class to manage strings where the character encoding must be specified and may be different ...
Definition: QoreString.h:1025
QoreDebugProgram::onException
virtual DLLEXPORT void onException(QoreProgram *pgm, const AbstractStatement *statement, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink *xsink)
QoreDebugProgram
supports parsing and executing Qore-language code, reference counted, dynamically-allocated only
Definition: QoreDebugProgram.h:66
QoreDebugProgram::onFunctionExit
virtual DLLEXPORT void onFunctionExit(QoreProgram *pgm, const StatementBlock *statement, QoreValue &returnValue, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink *xsink)
QoreCondition::broadcast
DLLEXPORT int broadcast()
singles all threads blocked on this condition to wake up
PO_FREE_OPTIONS
#define PO_FREE_OPTIONS
mask of options that have no effect on code access or code safety
Definition: Restrictions.h:134
QoreString::strlen
DLLEXPORT qore_size_t strlen() const
returns number of bytes in the string (not including the null pointer)
QoreThreadLock
provides a mutually-exclusive thread lock
Definition: QoreThreadLock.h:49
QoreString::vsprintf
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...
ExceptionSink::isException
DLLEXPORT bool isException() const
returns true if at least one exception is present
SafeLocker
provides an exception-safe way to manage locks in Qore, only to be used on the stack,...
Definition: QoreThreadLock.h:228
QoreAutoRWWriteLocker
provides a safe and exception-safe way to hold write locks in Qore, only to be used on the stack,...
Definition: QoreRWLock.h:144
PO_NO_CHILD_PO_RESTRICTIONS
#define PO_NO_CHILD_PO_RESTRICTIONS
turn off parse option inheritance restrictions
Definition: Restrictions.h:51
AbstractQoreProgramExternalData
an abstract class for program-specific external data
Definition: QoreProgram.h:1006
ValueHolder
holds an object and dereferences it in the destructor
Definition: QoreValue.h:452
AbstractQoreNode::deref
DLLEXPORT void deref(ExceptionSink *xsink)
decrements the reference count and calls derefImpl() if there_can_be_only_one is false,...
QoreCondition::wait
DLLEXPORT int wait(pthread_mutex_t *m)
blocks a thread on a mutex until the condition is signaled
QoreString::empty
DLLEXPORT bool empty() const
returns true if the string is empty, false if not
getProgram
DLLEXPORT QoreProgram * getProgram()
returns the current QoreProgram
ResolvedCallReferenceNode
base class for resolved call references
Definition: CallReferenceNode.h:105
QoreBreakpoint::resolveStatementId
DLLEXPORT AbstractStatement * resolveStatementId(unsigned long statementId, ExceptionSink *xsink) const
RootQoreNamespace
the root namespace of a QoreProgram object
Definition: QoreNamespace.h:335
QoreStringNode
Qore's string value type, reference counted, dynamically-allocated only.
Definition: QoreStringNode.h:50
QoreProgram::getQoreObject
static DLLEXPORT QoreObject * getQoreObject(QoreProgram *pgm)
get QoreObject of QoreProgram
QoreRWLock
provides a simple POSIX-threads-based read-write lock
Definition: QoreRWLock.h:47
QoreCounter::waitForZero
DLLEXPORT int waitForZero(ExceptionSink *xsink, int timeout_ms=0)
blocks the calling thread until the counter reaches 0
QoreStandardException
abstract base class for c++ Exceptions in the Qore library
Definition: QoreStandardException.h:49
QoreThreadLocalStorage::get
DLLLOCAL T * get()
retrieves the key's value
Definition: QoreThreadLocalStorage.h:77