Qore Programming Language  0.9.4.6
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;
126  // used to implement "to to statement" debugger command, reset it when program is interrupted
127  const AbstractStatement* runToStatement;
128  // when stepover or until return we need calls function calls
129  int functionCallLevel;
130 
131  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;
142  // called from running thread
143  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  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() : runState(DBG_RS_DETACH), functionCallLevel(0), breakFlag(false), tz(0), 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(arg_vec_t*& cl) {
192  lvstack.finalize(cl);
193  cvstack.finalize(cl);
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  arg_vec_t* cl = nullptr;
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 = gettid();
650 
651  for (pgm_data_map_t::iterator i = pgm_data_map.begin(), e = pgm_data_map.end(); i != e; ++i)
652  i->second->finalize(cl);
653  }
654 
655  // dereference finalized thread-local data outside the lock to avoid deadlocks
656  if (cl) {
657  for (arg_vec_t::iterator i = cl->begin(), e = cl->end(); i != e; ++i) {
658  (*i).discard(xsink);
659  }
660  delete cl;
661  }
662  }
663 
664  DLLLOCAL void clearProgramThreadData(ExceptionSink* xsink) {
665  for (pgm_data_map_t::iterator i = pgm_data_map.begin(), e = pgm_data_map.end(); i != e; ++i) {
666  i->second->del(xsink);
667  i->first->delProgram(pgm);
668  }
669  }
670 
671  DLLLOCAL void waitForTerminationAndClear(ExceptionSink* xsink);
672 
673  // called when the program's ref count = 0 (but the dc count may not go to 0 yet)
674  DLLLOCAL void clear(ExceptionSink* xsink);
675 
676  // called when starting a new thread before the new thread is started, to avoid race conditions
677  // once the new thread has been started, the TID is registered in startThread()
678  DLLLOCAL int preregisterNewThread(ExceptionSink* xsink) {
679  // grab program-level lock
680  AutoLocker al(plock);
681 
682  if (ptid) {
683  xsink->raiseException("PROGRAM-ERROR", "the Program accessed has already been deleted and therefore no new threads can be started in it");
684  return -1;
685  }
686 
687  ++thread_count;
688  return 0;
689  }
690 
691  // called when thread startup fails after preregistration
692  DLLLOCAL void cancelPreregistration() {
693  // grab program-level lock
694  AutoLocker al(plock);
695 
696  assert(thread_count > 0);
697  if (!--thread_count && thread_waiting)
698  pcond.broadcast();
699  }
700 
701  // called from the new thread once the thread has been started (after preregisterNewThread())
702  DLLLOCAL void registerNewThread(int tid) {
703  // grab program-level lock
704  AutoLocker al(plock);
705 
706  assert(thread_count);
707  ++tidmap[tid];
708  }
709 
710  /*
711  DLLLOCAL int checkValid(ExceptionSink* xsink) {
712  if (ptid && ptid != gettid()) {
713  xsink->raiseException("PROGRAM-ERROR", "the Program accessed has already been deleted and therefore cannot be accessed at runtime");
714  return -1;
715  }
716  return 0;
717  }
718  */
719 
720  // returns 0 for OK, -1 for error
721  DLLLOCAL int incThreadCount(ExceptionSink* xsink) {
722  int tid = gettid();
723 
724  // grab program-level lock
725  AutoLocker al(plock);
726 
727  if (ptid && ptid != tid) {
728  xsink->raiseException("PROGRAM-ERROR", "the Program accessed has already been deleted and therefore cannot be accessed at runtime");
729  return -1;
730  }
731  if (parsing_in_progress) {
732  xsink->raiseException("PROGRAM-ERROR", "the Program accessed is currently undergoing parsing and cannot be accessed at runtime");
733  }
734 
735  ++tidmap[tid];
736  ++thread_count;
737  return 0;
738  }
739 
740  // throws a QoreStandardException if there is an error
741  DLLLOCAL void incThreadCount() {
742  int tid = gettid();
743 
744  // grab program-level lock
745  AutoLocker al(plock);
746 
747  if (ptid && ptid != tid) {
748  throw QoreStandardException("PROGRAM-ERROR", "the Program accessed has already been deleted and therefore cannot be accessed at runtime");
749  }
750  if (parsing_in_progress) {
751  throw QoreStandardException("PROGRAM-ERROR", "the Program accessed is currently undergoing parsing and cannot be accessed at runtime");
752  }
753 
754  ++tidmap[tid];
755  ++thread_count;
756  }
757 
758  DLLLOCAL void decThreadCount(int tid) {
759  // grab program-level lock
760  AutoLocker al(plock);
761 
762  ptid_map_t::iterator i = tidmap.find(tid);
763  assert(i != tidmap.end());
764  if (!--i->second)
765  tidmap.erase(i);
766 
767  assert(thread_count > 0);
768  if (!--thread_count && thread_waiting)
769  pcond.broadcast();
770  }
771 
772  // gets a list of all thread IDs using this Program
773  DLLLOCAL void getThreadList(QoreListNode& l) {
774  // grab program-level lock
775  AutoLocker al(plock);
776 
777  for (auto& i : tidmap) {
778  l.push(i.first, nullptr);
779  }
780  }
781 
782  DLLLOCAL int lockParsing(ExceptionSink* xsink) {
783  // grab program-level lock
784  AutoLocker al(plock);
785 
786  bool curr = (pgm == getProgram());
787  if (!curr) {
788  while (parse_count) {
789  ++thread_waiting;
790  pcond.wait(plock);
791  --thread_waiting;
792  }
793  }
794 
795  if (ptid && ptid != gettid()) {
796  if (xsink)
797  xsink->raiseException("PROGRAM-ERROR", "the Program accessed has already been deleted and therefore cannot be accessed");
798  return -1;
799  }
800 
801  //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);
802  ++parse_count;
803  return 0;
804  }
805 
806  DLLLOCAL void unlockParsing() {
807  // grab program-level lock
808  AutoLocker al(plock);
809  if (!(--parse_count) && thread_waiting)
810  pcond.broadcast();
811  }
812 
813  // called only with plock held
814  DLLLOCAL void waitForAllThreadsToTerminateIntern() {
815  int tid = gettid();
816 
817  ptid_map_t::iterator i = tidmap.find(tid);
818  unsigned adj = (i != tidmap.end() ? 1 : 0);
819 
820  while ((thread_count - adj) || parse_count) {
821  ++thread_waiting;
822  pcond.wait(plock);
823  --thread_waiting;
824  }
825  }
826 
827  DLLLOCAL void waitForAllThreadsToTerminate() {
828  // grab program-level lock
829  AutoLocker al(&plock);
830  waitForAllThreadsToTerminateIntern();
831  }
832 
833  DLLLOCAL const char* parseGetScriptPath() const {
834  return script_path.empty() ? nullptr : script_path.c_str();
835  }
836 
837  DLLLOCAL const char* parseGetScriptDir() const {
838  return script_dir.empty() ? nullptr : script_dir.c_str();
839  }
840 
841  DLLLOCAL const char* parseGetScriptName() const {
842  return script_name.empty() ? nullptr : script_name.c_str();
843  }
844 
845  DLLLOCAL QoreStringNode* getScriptPath() const {
846  // grab program-level parse lock
847  AutoLocker al(&plock);
848  return script_path.empty() ? nullptr : new QoreStringNode(script_path);
849  }
850 
851  DLLLOCAL QoreStringNode* getScriptDir() const {
852  // grab program-level parse lock
853  AutoLocker al(&plock);
854  return script_dir.empty() ? nullptr : new QoreStringNode(script_dir);
855  }
856 
857  DLLLOCAL QoreStringNode* getScriptName() const {
858  // grab program-level parse lock
859  AutoLocker al(&plock);
860  return script_name.empty() ? nullptr : new QoreStringNode(script_name);
861  }
862 
863  DLLLOCAL void setScriptPathExtern(const char* path) {
864  // grab program-level parse lock
865  AutoLocker al(&plock);
866  setScriptPath(path);
867  }
868 
869  DLLLOCAL void setScriptPath(const char* path) {
870  if (!path) {
871  script_dir.clear();
872  script_path.clear();
873  script_name.clear();
874  } else {
875  // find file name
876  const char* p = q_basenameptr(path);
877  if (p == path) {
878  script_name = path;
879  script_dir = "." QORE_DIR_SEP_STR;
880  script_path = script_dir + script_name;
881  } else {
882  script_path = path;
883  script_name = p;
884  script_dir.assign(path, p - path);
885  }
886  }
887  }
888 
889  DLLLOCAL QoreListNode* getVarList() {
890  //AutoLocker al(&plock);
891  // FIXME: implement
892  return new QoreListNode(stringTypeInfo);
893  //return global_var_list.getVarList();
894  }
895 
896  DLLLOCAL QoreListNode* getFeatureList() const {
897  QoreListNode* l = new QoreListNode(stringTypeInfo);
898 
899  for (CharPtrList::const_iterator i = featureList.begin(), e = featureList.end(); i != e; ++i)
900  l->push(new QoreStringNode(*i), nullptr);
901 
902  for (CharPtrList::const_iterator i = userFeatureList.begin(), e = userFeatureList.end(); i != e; ++i)
903  l->push(new QoreStringNode(*i), nullptr);
904 
905  return l;
906  }
907 
908  DLLLOCAL void internParseRollback(ExceptionSink* xsink);
909 
910  // call must push the current program on the stack and pop it afterwards
911  DLLLOCAL int internParsePending(ExceptionSink* xsink, const char* code, const char* label, const char* orig_src = nullptr, int offset = 0, bool standard_parse = true) {
912  //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);
913 
914  assert(code && code[0]);
915 
916  // save this file name for storage in the parse tree and deletion
917  // when the QoreProgram object is deleted
918  const char* sname = label;
919  const char* src = orig_src;
920  if (orig_src) {
921  addFile(src, sname, offset);
922  } else {
923  addFile(sname);
924  }
925 
926  QoreParseLocationHelper qplh(sname, src, offset);
927 
928  /*
929  beginParsing() called in QoreParseLocationHelper constructor but unless we call here twice
930  then endParsing() coredump with "Assertion `td->plStack' failed"
931  Seems endParsing() should do rather "if (!td->plStack) return".
932  */
933  beginParsing(sname, nullptr, src, offset);
934 
935  if (!parsing_in_progress) {
936  parsing_in_progress = true;
937  }
938 
939  // no need to save buffer, because it's deleted automatically in lexer
940  //printd(5, "qore_program_private::internParsePending() parsing tag: %s (%p): '%s'\n", label, label, code);
941 
942  yyscan_t lexer;
943  yylex_init(&lexer);
944 
945  yy_scan_string(code, lexer);
946  yyset_lineno(1, lexer);
947  // yyparse() will call endParsing() and restore old pgm position
948  yyparse(lexer);
949 
950  printd(5, "qore_program_private::internParsePending() returned from yyparse()\n");
951  int rc = 0;
952  if (parseSink->isException()) {
953  rc = -1;
954  if (standard_parse) {
955  printd(5, "qore_program_private::internParsePending() parse exception: calling parseRollback()\n");
956  internParseRollback(xsink);
957  requires_exception = false;
958  }
959  }
960 
961  printd(5, "qore_program_private::internParsePending() about to call yylex_destroy()\n");
962  yylex_destroy(lexer);
963  printd(5, "qore_program_private::internParsePending() returned from yylex_destroy()\n");
964  return rc;
965  }
966 
967  DLLLOCAL void startParsing(ExceptionSink* xsink, ExceptionSink* wS, int wm) {
968  warnSink = wS;
969  pwo.warn_mask = wm;
970  parseSink = xsink;
971 
972  if (pendingParseSink) {
973  parseSink->assimilate(pendingParseSink);
974  pendingParseSink = nullptr;
975  }
976  }
977 
978  DLLLOCAL int checkParse(ExceptionSink* xsink) const {
979  if (parsing_done) {
980  xsink->raiseException("PARSE-ERROR", "parsing can only happen once for each Program container");
981  return -1;
982  }
983  return 0;
984  }
985 
986  DLLLOCAL int parsePending(const char* code, const char* label, ExceptionSink* xsink, ExceptionSink* wS, int wm, const char* orig_src = nullptr, int offset = 0) {
987  //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);
988 
989  ProgramRuntimeParseContextHelper pch(xsink, pgm);
990  assert(xsink);
991  if (*xsink) {
992  return -1;
993  }
994 
995  if (checkParse(xsink)) {
996  return -1;
997  }
998 
999  startParsing(xsink, wS, wm);
1000 
1001  int rc = internParsePending(xsink, code, label, orig_src, offset);
1002  warnSink = nullptr;
1003 #ifdef DEBUG
1004  parseSink = nullptr;
1005 #endif
1006  return rc;
1007  }
1008 
1009  // caller must have grabbed the lock and put the current program on the program stack
1010  DLLLOCAL int internParseCommit(bool standard_parse = true);
1011 
1012  DLLLOCAL int parseCommit(ExceptionSink* xsink, ExceptionSink* wS, int wm) {
1013  ProgramRuntimeParseCommitContextHelper pch(xsink, pgm);
1014  assert(xsink);
1015  if (*xsink) {
1016  return -1;
1017  }
1018 
1019  if (checkParse(xsink)) {
1020  return -1;
1021  }
1022 
1023  startParsing(xsink, wS, wm);
1024 
1025  // finalize parsing, back out or commit all changes
1026  int rc = internParseCommit();
1027 
1028 #ifdef DEBUG
1029  parseSink = nullptr;
1030 #endif
1031  warnSink = nullptr;
1032  // release program-level parse lock
1033  return rc;
1034  }
1035 
1036  DLLLOCAL int parseRollback(ExceptionSink* xsink) {
1037  ProgramRuntimeParseContextHelper pch(xsink, pgm);
1038  assert(xsink);
1039  if (*xsink)
1040  return -1;
1041 
1042  // back out all pending changes
1043  internParseRollback(xsink);
1044  return 0;
1045  }
1046 
1047  DLLLOCAL void parse(FILE *fp, const char* name, ExceptionSink* xsink, ExceptionSink* wS, int wm) {
1048  assert(xsink);
1049  printd(5, "QoreProgram::parse(fp: %p, name: %s, xsink: %p, wS: %p, wm: %d)\n", fp, name, xsink, wS, wm);
1050 
1051  // if already at the end of file, then return
1052  // try to get one character from file
1053  int c = fgetc(fp);
1054  if (feof(fp)) {
1055  printd(5, "QoreProgram::parse(fp: %p, name: %s) EOF\n", fp, name);
1056  return;
1057  }
1058  // push back read character
1059  ungetc(c, fp);
1060 
1061  yyscan_t lexer;
1062 
1063  {
1064  ProgramRuntimeParseCommitContextHelper pch(xsink, pgm);
1065  if (*xsink) {
1066  return;
1067  }
1068 
1069  if (checkParse(xsink)) {
1070  return;
1071  }
1072 
1073  startParsing(xsink, wS, wm);
1074 
1075  // save this file name for storage in the parse tree and deletion
1076  // when the QoreProgram object is deleted
1077  const char* sname = name;
1078  addFile(sname);
1079 
1080  QoreParseLocationHelper qplh(sname, nullptr, 0);
1081 
1082  /*
1083  beginParsing() called in QoreParseLocationHelper constructor but unless we call here twice
1084  then endParsing() coredump with "Assertion `td->plStack' failed"
1085  Seems endParsing() should do rather "if (!td->plStack) return".
1086  */
1087  beginParsing(sname);
1088 
1089  if (!parsing_in_progress) {
1090  parsing_in_progress = true;
1091  }
1092 
1093  //printd(5, "QoreProgram::parse(): about to call yyparse()\n");
1094  yylex_init(&lexer);
1095  yyset_in(fp, lexer);
1096  // yyparse() will call endParsing() and restore old pgm position
1097  yyparse(lexer);
1098 
1099  // finalize parsing, back out or commit all changes
1100  internParseCommit();
1101 
1102 #ifdef DEBUG
1103  parseSink = nullptr;
1104 #endif
1105  warnSink = nullptr;
1106  // release program-level parse lock
1107  }
1108 
1109  yylex_destroy(lexer);
1110  if (only_first_except && exceptions_raised > 1)
1111  fprintf(stderr, "\n%d exception(s) skipped\n\n", exceptions_raised);
1112  }
1113 
1114  DLLLOCAL void parse(const QoreString *str, const QoreString *lstr, ExceptionSink* xsink, ExceptionSink* wS, int wm, const QoreString* source = nullptr, int offset = 0) {
1115  assert(xsink);
1116  if (!str->strlen())
1117  return;
1118 
1119  // ensure code string has correct character set encoding
1120  TempEncodingHelper tstr(str, QCS_DEFAULT, xsink);
1121  if (*xsink)
1122  return;
1123 
1124  // ensure label string has correct character set encoding
1125  TempEncodingHelper tlstr(lstr, QCS_DEFAULT, xsink);
1126  if (*xsink)
1127  return;
1128 
1129  TempEncodingHelper src;
1130  if (source && !source->empty() && !src.set(source, QCS_DEFAULT, xsink))
1131  return;
1132 
1133  parse(tstr->getBuffer(), tlstr->getBuffer(), xsink, wS, wm, source ? src->getBuffer() : 0, offset);
1134  }
1135 
1136  DLLLOCAL void parse(const char* code, const char* label, ExceptionSink* xsink, ExceptionSink* wS, int wm, const char* orig_src = 0, int offset = 0) {
1137  //printd(5, "qore_program_private::parse(%s) pgm: %p po: %lld\n", label, pgm, pwo.parse_options);
1138 
1139  assert(code && code[0]);
1140  assert(xsink);
1141 
1142  ProgramRuntimeParseCommitContextHelper pch(xsink, pgm);
1143  if (*xsink) {
1144  return;
1145  }
1146 
1147  if (checkParse(xsink)) {
1148  return;
1149  }
1150 
1151  startParsing(xsink, wS, wm);
1152 
1153  // parse text given
1154  if (!internParsePending(xsink, code, label, orig_src, offset))
1155  internParseCommit(); // finalize parsing, back out or commit all changes
1156 
1157 #ifdef DEBUG
1158  parseSink = nullptr;
1159 #endif
1160  warnSink = nullptr;
1161  }
1162 
1163  DLLLOCAL q_exp_t parseExpression(const QoreString& str, const QoreString& lstr, ExceptionSink* xsink,
1164  ExceptionSink* wS = nullptr, int wm = 0, const QoreString* source = nullptr, int offset = 0) {
1165  assert(xsink);
1166  if (!str.strlen()) {
1167  xsink->raiseException("EXPRESSION-ERROR", "the expression cannot be empty");
1168  return nullptr;
1169  }
1170 
1171  // ensure code string has correct character set encoding
1172  TempEncodingHelper tstr(str, QCS_DEFAULT, xsink);
1173  if (*xsink)
1174  return nullptr;
1175 
1176  // ensure label string has correct character set encoding
1177  TempEncodingHelper tlstr(lstr, QCS_DEFAULT, xsink);
1178  if (*xsink)
1179  return nullptr;
1180 
1181  TempEncodingHelper src;
1182  if (source && !source->empty() && !src.set(source, QCS_DEFAULT, xsink))
1183  return nullptr;
1184 
1185  return parseExpression(tstr->c_str(), tlstr->c_str(), xsink, wS, wm, source ? src->c_str() : nullptr, offset);
1186  }
1187 
1188  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) {
1189  //printd(5, "qore_program_private::parse(%s) pgm: %p po: %lld\n", label, pgm, pwo.parse_options);
1190 
1191  assert(code && code[0]);
1192  assert(xsink);
1193 
1194  ProgramRuntimeParseCommitContextHelper pch(xsink, pgm);
1195  if (*xsink) {
1196  return nullptr;
1197  }
1198 
1199  assert(!expression_mode);
1200  assert(!new_expression);
1201  expression_mode = true;
1202 
1203  QoreStringMaker exp_code("return (%s);", code);
1204 
1205  startParsing(xsink, wS, wm);
1206 
1207  // parse text given
1208  if (!internParsePending(xsink, exp_code.c_str(), label, orig_src, offset, false)) {
1209  internParseCommit(false); // finalize parsing, back out or commit all changes
1210  } else {
1211  parsing_in_progress = false;
1212  }
1213 
1214 #ifdef DEBUG
1215  parseSink = nullptr;
1216 #endif
1217  warnSink = nullptr;
1218 
1219  expression_mode = false;
1220  q_exp_t rv = new_expression;
1221  if (new_expression) {
1222  if (*xsink) {
1223  exp_set.erase(new_expression);
1224  delete new_expression;
1225  rv = nullptr;
1226  }
1227  new_expression = nullptr;
1228  }
1229  return rv;
1230  }
1231 
1232  DLLLOCAL void parseFile(const char* filename, ExceptionSink* xsink, ExceptionSink* wS, int wm) {
1233  QORE_TRACE("QoreProgram::parseFile()");
1234 
1235  printd(5, "QoreProgram::parseFile(%s)\n", filename);
1236 
1237  FILE *fp;
1238  if (!(fp = fopen(filename, "r"))) {
1239  if ((only_first_except && !exceptions_raised) || !only_first_except)
1240  xsink->raiseErrnoException("PARSE-EXCEPTION", errno, "cannot open qore script '%s'", filename);
1241  exceptions_raised++;
1242  return;
1243  }
1244  ON_BLOCK_EXIT(fclose, fp);
1245 
1246  setScriptPath(filename);
1247 
1248  ProgramRuntimeParseCommitContextHelper pch(xsink, pgm);
1249  if (*xsink)
1250  return;
1251 
1252  parse(fp, filename, xsink, wS, wm);
1253  }
1254 
1255  DLLLOCAL void parsePending(const QoreString *str, const QoreString *lstr, ExceptionSink* xsink, ExceptionSink* wS, int wm, const QoreString* source = 0, int offset = 0) {
1256  assert(!str->empty());
1257  assert(xsink);
1258 
1259  // ensure code string has correct character set encoding
1260  TempEncodingHelper tstr(str, QCS_DEFAULT, xsink);
1261  if (*xsink)
1262  return;
1263 
1264  // ensure label string has correct character set encoding
1265  TempEncodingHelper tlstr(lstr, QCS_DEFAULT, xsink);
1266  if (*xsink)
1267  return;
1268 
1269  TempEncodingHelper src;
1270  if (source && !source->empty() && !src.set(source, QCS_DEFAULT, xsink))
1271  return;
1272 
1273  parsePending(tstr->getBuffer(), tlstr->getBuffer(), xsink, wS, wm, source ? src->getBuffer() : 0, offset);
1274  }
1275 
1276  // called during run time (not during parsing)
1277  DLLLOCAL void importFunction(ExceptionSink* xsink, QoreFunction *u, const qore_ns_private& oldns, const char* new_name = nullptr, bool inject = false);
1278 
1279  DLLLOCAL void del(ExceptionSink* xsink);
1280 
1281  DLLLOCAL QoreHashNode* getThreadData() {
1282  QoreHashNode* h = thread_local_storage->get();
1283  if (!h) {
1284  h = new QoreHashNode(autoTypeInfo);
1285  thread_local_storage->set(h);
1286  }
1287 
1288  return h;
1289  }
1290 
1291  DLLLOCAL QoreHashNode* clearThreadData(ExceptionSink* xsink) {
1292  QoreHashNode* h = thread_local_storage->get();
1293  printd(5, "QoreProgram::clearThreadData() this: %p h: %p (size: %d)\n", this, h, h ? h->size() : 0);
1294  if (h)
1295  h->clear(xsink);
1296  return h;
1297  }
1298 
1299  DLLLOCAL void deleteThreadData(ExceptionSink* xsink) {
1300  QoreHashNode* h = clearThreadData(xsink);
1301  if (h) {
1302  h->deref(xsink);
1303  thread_local_storage->set(0);
1304  }
1305  }
1306 
1307  DLLLOCAL void finalizeThreadData(ThreadProgramData* td, arg_vec_t*& cl) {
1308  QoreHashNode* h = thread_local_storage->get();
1309  if (h) {
1310  if (!cl)
1311  cl = new arg_vec_t;
1312  cl->push_back(h);
1313  thread_local_storage->set(0);
1314  }
1315 
1316  // delete all local variables for this thread
1317  AutoLocker al(tlock);
1318  if (tclear)
1319  return;
1320 
1321  pgm_data_map_t::iterator i = pgm_data_map.find(td);
1322  if (i != pgm_data_map.end())
1323  i->second->finalize(cl);
1324  }
1325 
1326  // TODO: xsink should not be necessary; vars should be emptied and finalized in the finalizeThreadData() call
1327  DLLLOCAL int endThread(ThreadProgramData* td, ExceptionSink* xsink) {
1328  ThreadLocalProgramData* tlpd = 0;
1329 
1330  // delete all local variables for this thread
1331  {
1332  AutoLocker al(tlock);
1333  if (tclear)
1334  return -1;
1335 
1336  pgm_data_map_t::iterator i = pgm_data_map.find(td);
1337  if (i == pgm_data_map.end())
1338  return -1;
1339  tlpd = i->second;
1340  pgm_data_map.erase(i);
1341  }
1342 
1343  tlpd->del(xsink);
1344  return 0;
1345  }
1346 
1347  DLLLOCAL void doTopLevelInstantiation(ThreadLocalProgramData& tlpd) {
1348  // instantiate top-level vars for this thread
1349  const LVList* lvl = sb.getLVList();
1350  if (lvl)
1351  for (unsigned i = 0; i < lvl->size(); ++i)
1352  lvl->lv[i]->instantiate();
1353 
1354  //printd(5, "qore_program_private::doTopLevelInstantiation() lvl: %p setup %ld local vars pgm: %p\n", lvl, lvl ? lvl->size() : 0, getProgram());
1355 
1356  tlpd.inst = true;
1357  }
1358 
1359  // returns true if setting for the first time, false if not
1360  DLLLOCAL bool setThreadVarData(ThreadProgramData* td, ThreadLocalProgramData*& new_tlpd, bool run) {
1361  SafeLocker sl(tlock);
1362  // wait for data to finished being cleared if applicable
1363  while (tclear) {
1364  if (tclear == gettid()) {
1365  // can be called recursively when destructors are run in local variable finalization
1366  assert(pgm_data_map.find(td) != pgm_data_map.end());
1367  assert(pgm_data_map[td]->inst);
1368  break;
1369  }
1370  ++twaiting;
1371  tcond.wait(tlock);
1372  --twaiting;
1373  }
1374 
1375  pgm_data_map_t::iterator i = pgm_data_map.find(td);
1376  if (i == pgm_data_map.end()) {
1377  assert(!tclear);
1378  ThreadLocalProgramData* tlpd = new ThreadLocalProgramData;
1379 
1380  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");
1381 
1382  new_tlpd = tlpd;
1383 
1384  pgm_data_map.insert(pgm_data_map_t::value_type(td, tlpd));
1385 
1386  sl.unlock();
1387 
1388  if (run) {
1389  printd(5, "qore_program_private::setThreadVarData() (first) this: %p pgm: %p td: %p\n", this, pgm, td);
1390  doTopLevelInstantiation(*tlpd);
1391  }
1392 
1393  return true;
1394  }
1395 
1396  ThreadLocalProgramData* tlpd = pgm_data_map[td];
1397  new_tlpd = tlpd;
1398 
1399  sl.unlock();
1400 
1401  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");
1402 
1403  if (run && !tlpd->inst) {
1404  doTopLevelInstantiation(*tlpd);
1405  }
1406 
1407  return false;
1408  }
1409 
1410  DLLLOCAL const AbstractQoreZoneInfo* currentTZ(ThreadProgramData* tpd = get_thread_program_data()) const {
1411  AutoLocker al(tlock);
1412  pgm_data_map_t::const_iterator i = pgm_data_map.find(tpd);
1413  if (i != pgm_data_map.end() && i->second->tz_set)
1414  return i->second->tz;
1415  return TZ;
1416  }
1417 
1418  DLLLOCAL void setTZ(const AbstractQoreZoneInfo* n_TZ) {
1419  TZ = n_TZ;
1420  }
1421 
1422  DLLLOCAL void exportFunction(ExceptionSink* xsink, qore_program_private* p, const char* name, const char* new_name = nullptr, bool inject = false) {
1423  if (this == p) {
1424  xsink->raiseException("FUNCTION-IMPORT-ERROR", "cannot import a function from the same Program object");
1425  return;
1426  }
1427 
1428  if (inject && !(p->pwo.parse_options & PO_ALLOW_INJECTION)) {
1429  xsink->raiseException("FUNCTION-IMPORT-ERROR", "cannot import function \"%s\" in a Program object without PO_ALLOW_INJECTION set", name);
1430  return;
1431  }
1432 
1433  const QoreFunction* u;
1434  const qore_ns_private* ns = nullptr;
1435 
1436  {
1437  ProgramRuntimeParseAccessHelper rah(xsink, pgm);
1438  if (*xsink)
1439  return;
1440  u = qore_root_ns_private::runtimeFindFunction(*RootNS, name, ns);
1441  }
1442 
1443  if (!u)
1444  xsink->raiseException("PROGRAM-IMPORTFUNCTION-NO-FUNCTION", "function '%s' does not exist in the current program scope", name);
1445  else {
1446  assert(ns);
1447  p->importFunction(xsink, const_cast<QoreFunction*>(u), *ns, new_name, inject);
1448  }
1449  }
1450 
1451  DLLLOCAL bool parseExceptionRaised() const {
1452  assert(parseSink);
1453  return *parseSink;
1454  }
1455 
1456  DLLLOCAL void setParseOptionsIntern(int64 po) {
1457  pwo.parse_options |= po;
1458  }
1459 
1460  DLLLOCAL void disableParseOptionsIntern(int64 po) {
1461  pwo.parse_options &= ~po;
1462  }
1463 
1464  DLLLOCAL void replaceParseOptionsIntern(int64 po) {
1465  pwo.parse_options = po;
1466  }
1467 
1468  DLLLOCAL int setParseOptions(int64 po, ExceptionSink* xsink) {
1469  assert(xsink);
1470  // only raise the exception if parse options are locked and the option is not a "free option"
1471  // also check if options may be made more restrictive and the option also does so
1472  if (((po & PO_FREE_OPTIONS) != po) && po_locked && (!po_allow_restrict || (po & PO_POSITIVE_OPTIONS))) {
1473  xsink->raiseException("OPTIONS-LOCKED", "parse options have been locked on this program object");
1474  return -1;
1475  }
1476 
1477  setParseOptionsIntern(po);
1478  return 0;
1479  }
1480 
1481  DLLLOCAL int disableParseOptions(int64 po, ExceptionSink* xsink) {
1482  assert(xsink);
1483  // only raise the exception if parse options are locked and the option is not a "free option"
1484  // note: disabling PO_POSITIVE_OPTION is more restrictive so let's allow to disable
1485  if (((po & PO_FREE_OPTIONS) != po) && po_locked && !po_allow_restrict) {
1486  xsink->raiseException("OPTIONS-LOCKED", "parse options have been locked on this program object");
1487  return -1;
1488  }
1489 
1490  disableParseOptionsIntern(po);
1491  return 0;
1492  }
1493 
1494  DLLLOCAL int replaceParseOptions(int64 po, ExceptionSink* xsink) {
1495  assert(xsink);
1496  if (!(getProgram()->priv->pwo.parse_options & PO_NO_CHILD_PO_RESTRICTIONS)) {
1497  xsink->raiseException("OPTION-ERROR", "the calling Program does not have the PO_NO_CHILD_PO_RESTRICTIONS option set, and therefore cannot call Program::replaceParseOptions()");
1498  return -1;
1499  }
1500 
1501  //printd(5, "qore_program_private::replaceParseOptions() this: %p pgm: %p replacing po: %lld with po: %lld\n", this, pgm, pwo.parse_options, po);
1502  replaceParseOptionsIntern(po);
1503  return 0;
1504  }
1505 
1506  DLLLOCAL int parseSetParseOptions(const QoreProgramLocation* loc, int64 po) {
1507  // only raise the exception if parse options are locked and the option is not a "free option"
1508  // also check if options may be made more restrictive and the option also does so
1509  if (((po & PO_FREE_OPTIONS) != po) && po_locked && (!po_allow_restrict || (po & PO_POSITIVE_OPTIONS))) {
1510  parse_error(*loc, "parse options have been locked on this program object");
1511  return -1;
1512  }
1513 
1514  setParseOptionsIntern(po);
1515  return 0;
1516  }
1517 
1518  DLLLOCAL int parseDisableParseOptions(const QoreProgramLocation* loc, int64 po) {
1519  // only raise the exception if parse options are locked and the option is not a "free option"
1520  // note: disabling PO_POSITIVE_OPTION is more restrictive so let's allow to disable
1521  if (((po & PO_FREE_OPTIONS) != po) && po_locked && !po_allow_restrict) {
1522  parse_error(*loc, "parse options have been locked on this program object");
1523  return -1;
1524  }
1525 
1526  disableParseOptionsIntern(po);
1527  return 0;
1528  }
1529 
1530  DLLLOCAL void parseSetTimeZone(const char* zone) {
1531  // check PO_NO_LOCALE_CONTROL
1532  ExceptionSink xsink;
1533  if (pwo.parse_options & PO_NO_LOCALE_CONTROL) {
1534  mergeParseException(xsink);
1535  return;
1536  }
1537 
1538  const AbstractQoreZoneInfo *tz = (*zone == '-' || *zone == '+') ? QTZM.findCreateOffsetZone(zone, &xsink) : QTZM.findLoadRegion(zone, &xsink);
1539  if (xsink) {
1540  assert(!tz);
1541  mergeParseException(xsink);
1542  return;
1543  }
1544  // note that tz may be NULL in case the offset is UTC (ie 0)
1545  assert(tz || ((*zone == '-' || *zone == '+')));
1546 
1547  TZ = tz;
1548  }
1549 
1550  DLLLOCAL void makeParseException(const QoreProgramLocation& loc, const char* err, QoreStringNode* desc) {
1551  QORE_TRACE("QoreProgram::makeParseException()");
1552 
1553  QoreStringNodeHolder d(desc);
1554  if (!requires_exception) {
1555  if ((only_first_except && !exceptions_raised) || !only_first_except) {
1556  QoreException *ne = new ParseException(loc, err, d.release());
1557  parseSink->raiseException(ne);
1558  }
1559  exceptions_raised++;
1560  }
1561  }
1562 
1563  DLLLOCAL void parseException(QoreProgramLocation& loc, const char* fmt, ...) {
1564  //printd(5, "qore_program_private::parseException(\"%s\", ...) called\n", fmt);
1565 
1566  // ignore if a "requires" exception has been raised
1567  if (requires_exception)
1568  return;
1569 
1570  QoreStringNode* desc = new QoreStringNode;
1571  while (true) {
1572  va_list args;
1573  va_start(args, fmt);
1574  int rc = desc->vsprintf(fmt, args);
1575  va_end(args);
1576  if (!rc)
1577  break;
1578  }
1579  makeParseException(loc, "PARSE-EXCEPTION", desc);
1580  }
1581 
1582  // internal method - does not bother with the parse lock
1583  // returns true if the define already existed
1584  DLLLOCAL bool setDefine(const char* name, QoreValue v, ExceptionSink* xsink) {
1585  dmap_t::iterator i = dmap.find(name);
1586  if (i != dmap.end()) {
1587  i->second.discard(xsink);
1588  i->second = v;
1589  return true;
1590  }
1591 
1592  dmap[name] = v;
1593  return false;
1594  }
1595 
1596  // internal method - does not bother with the parse lock
1597  DLLLOCAL const QoreValue getDefine(const char* name, bool& is_defined) {
1598  dmap_t::iterator i = dmap.find(name);
1599  if (i != dmap.end()) {
1600  is_defined = true;
1601  return i->second;
1602  }
1603  is_defined = false;
1604  return QoreValue();
1605  }
1606 
1607  DLLLOCAL QoreValue runTimeGetDefine(const char* name, bool& is_defined) {
1608  AutoLocker al(plock);
1609  return getDefine(name, is_defined).refSelf();
1610  }
1611 
1612  DLLLOCAL QoreHashNode* runTimeGetAllDefines();
1613 
1614  // internal method - does not bother with the parse lock
1615  // returns true if the define existed
1616  DLLLOCAL bool unDefine(const char* name, ExceptionSink* xsink) {
1617  dmap_t::iterator i = dmap.find(name);
1618  if (i != dmap.end()) {
1619  i->second.discard(xsink);
1620  dmap.erase(i);
1621  return true;
1622  }
1623  return false;
1624  }
1625 
1626  DLLLOCAL bool parseUnDefine(const char* name) {
1627  PreParseHelper pph(this);
1628  return unDefine(name, parseSink);
1629  }
1630 
1631  DLLLOCAL bool runTimeUnDefine(const char* name, ExceptionSink* xsink) {
1632  AutoLocker al(plock);
1633  return unDefine(name, xsink);
1634  }
1635 
1636  // internal method - does not bother with the parse lock
1637  DLLLOCAL bool isDefined(const char* name) {
1638  dmap_t::iterator i = dmap.find(name);
1639  return i == dmap.end() ? false : true;
1640  }
1641 
1642  DLLLOCAL bool runTimeIsDefined(const char* name) {
1643  AutoLocker al(plock);
1644  return isDefined(name);
1645  }
1646 
1647  DLLLOCAL int checkDefine(const QoreProgramLocation* loc, const char* str, ExceptionSink* xsink) {
1648  const char* p = str;
1649  if (!isalpha(*p)) {
1650  xsink->raiseException(*loc, "PARSE-EXCEPTION", 0, "illegal define variable '%s'; does not begin with an alphabetic character", p);
1651  return -1;
1652  }
1653 
1654  while (*(++p)) {
1655  if (!isalnum(*p) && *p != '_') {
1656 
1657  xsink->raiseException(*loc, "PARSE-EXCEPTION", 0, "illegal character '%c' in define variable '%s'", *p, str);
1658  return -1;
1659  }
1660  }
1661 
1662  return 0;
1663  }
1664 
1665  DLLLOCAL void parseDefine(const QoreProgramLocation* loc, const char* str, QoreValue val) {
1666  PreParseHelper pph(this);
1667 
1668  if (checkDefine(loc, str, parseSink))
1669  return;
1670 
1671  setDefine(str, val, parseSink);
1672  }
1673 
1674  DLLLOCAL void runTimeDefine(const char* str, QoreValue val, ExceptionSink* xsink) {
1675  const QoreProgramLocation* loc = get_runtime_location();
1676 
1677  if (checkDefine(loc, str, xsink))
1678  return;
1679 
1680  AutoLocker al(plock);
1681  setDefine(str, val, xsink);
1682  }
1683 
1684  DLLLOCAL ResolvedCallReferenceNode* runtimeGetCallReference(const char* name, ExceptionSink* xsink) {
1685  assert(xsink);
1686  ProgramRuntimeParseAccessHelper pah(xsink, pgm);
1687  if (*xsink)
1688  return nullptr;
1689  return qore_root_ns_private::runtimeGetCallReference(*RootNS, name, xsink);
1690  }
1691 
1692  DLLLOCAL void pushParseOptions(const char* pf) {
1693  // ignore %push-parse-options used multiple times in the same file
1694  ppo_t::iterator i = ppo.lower_bound(pf);
1695  if (i != ppo.end() && !strcmp(pf, i->first))
1696  return;
1697  ppo.insert(i, ppo_t::value_type(pf, pwo.parse_options));
1698  //printd(5, "qore_program_private::pushParseOptions() this: %p %p '%s' saving %lld\n", this, pf, pf, pwo.parse_options);
1699  }
1700 
1701  DLLLOCAL void restoreParseOptions(const char* pf) {
1702  ppo_t::iterator i = ppo.find(pf);
1703  if (i != ppo.end()) {
1704  //printd(5, "qore_program_private::restoreParseOptions() this: %p %p '%s' restoring %lld\n", this, pf, pf, pwo.parse_options);
1705  pwo.parse_options = i->second;
1706  ppo.erase(i);
1707  }
1708  }
1709 
1710  DLLLOCAL void addParseException(ExceptionSink& xsink, const QoreProgramLocation* loc) {
1711  if (requires_exception) {
1712  xsink.clear();
1713  return;
1714  }
1715 
1716  if (loc) {
1717  // ensure that all exceptions reflect the current parse location
1718  xsink.overrideLocation(*loc);
1719  }
1720 
1721  parseSink->assimilate(xsink);
1722  }
1723 
1724  // returns the mask of domain options not met in the current program
1725  DLLLOCAL int64 parseAddDomain(int64 n_dom) {
1726  assert(!(n_dom & PO_FREE_OPTIONS));
1727  int64 rv = 0;
1728 
1729  // handle negative and positive options separately / differently
1730  int64 pos = (n_dom & PO_POSITIVE_OPTIONS);
1731  if (pos) {
1732  int64 p_tmp = pwo.parse_options & PO_POSITIVE_OPTIONS;
1733  // make sure all positive arguments are set
1734  if ((pos & p_tmp) != pos) {
1735  rv = ((pos & p_tmp) ^ pos);
1736  pend_dom |= pos;
1737  }
1738  }
1739  int64 neg = (n_dom & ~PO_POSITIVE_OPTIONS);
1740  if (neg && (neg & pwo.parse_options)) {
1741  rv |= (neg & pwo.parse_options);
1742  pend_dom |= neg;
1743  }
1744 
1745  //printd(5, "qore_program_private::parseAddDomain() this: %p n_dom: " QLLD " po: " QLLD "\n", this, n_dom, pwo.parse_options);
1746  return rv;
1747  }
1748 
1749  DLLLOCAL void exportGlobalVariable(const char* name, bool readonly, qore_program_private& tpgm, ExceptionSink* xsink);
1750 
1751  DLLLOCAL int setGlobalVarValue(const char* name, QoreValue val, ExceptionSink* xsink);
1752 
1753  // returns true if there was already a thread init closure set, false if not
1754  DLLLOCAL bool setThreadInit(const ResolvedCallReferenceNode* n_thr_init, ExceptionSink* xsink);
1755 
1756  DLLLOCAL void setThreadTZ(ThreadProgramData* tpd, const AbstractQoreZoneInfo* tz) {
1757  AutoLocker al(tlock);
1758  pgm_data_map_t::iterator i = pgm_data_map.find(tpd);
1759  assert(i != pgm_data_map.end());
1760  i->second->setTZ(tz);
1761  }
1762 
1763  DLLLOCAL const AbstractQoreZoneInfo* getThreadTZ(ThreadProgramData* tpd, bool& set) const {
1764  AutoLocker al(tlock);
1765  pgm_data_map_t::const_iterator i = pgm_data_map.find(tpd);
1766  assert(i != pgm_data_map.end());
1767  set = i->second->tz_set;
1768  return i->second->tz;
1769  }
1770 
1771  DLLLOCAL void clearThreadTZ(ThreadProgramData* tpd) {
1772  AutoLocker al(tlock);
1773  pgm_data_map_t::iterator i = pgm_data_map.find(tpd);
1774  assert(i != pgm_data_map.end());
1775  i->second->clearTZ();
1776  }
1777 
1778  DLLLOCAL void addStatement(AbstractStatement* s);
1779 
1780  DLLLOCAL q_exp_t createExpression(const QoreStringNode& source, const QoreStringNode& label, ExceptionSink* xsink) {
1781  return parseExpression(source, label, xsink);
1782  }
1783 
1784  DLLLOCAL QoreValue evalExpression(q_exp_t exp, ExceptionSink* xsink) {
1785  ProgramThreadCountContextHelper pch(xsink, pgm, true);
1786  if (*xsink) {
1787  return QoreValue();
1788  }
1789  if (exp_set.find(exp) == exp_set.end()) {
1790  xsink->raiseException("INVALID-EXPRESSION", "expression not registered to this Program object");
1791  return QoreValue();
1792  }
1793  ThreadFrameBoundaryHelper tfbh(true);
1794 
1795  ValueHolder rv(exp->exec(xsink), xsink);
1796  if (*xsink) {
1797  return QoreValue();
1798  }
1799  return rv.release();
1800  }
1801 
1802  DLLLOCAL void deleteExpression(q_exp_t exp) {
1803  AutoLocker al(plock);
1804  q_exp_set_t::iterator i = exp_set.find(exp);
1805  if (i != exp_set.end()) {
1806  exp_set.erase(exp);
1807  delete exp;
1808  }
1809  }
1810 
1811  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);
1812 
1813  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);
1814 
1815  DLLLOCAL const char* addString(const char* str) {
1816  str_set_t::iterator i = str_set.lower_bound(str);
1817  if (i == str_set.end() || strcmp(*i, str)) {
1818  str_vec.push_back(strdup(str));
1819  i = str_set.insert(i, str_vec.back());
1820  }
1821  return *i;
1822  }
1823 
1824  DLLLOCAL void addFile(const char*& file) {
1825  file = addString(file);
1826  printd(5, "qore_program_private::addFile('%s')\n", file);
1827  addStatementToIndexIntern(&statementByFileIndex, file, nullptr, -1, nullptr, -1);
1828  }
1829 
1830  DLLLOCAL void addFile(const char*& file, const char*& source, int offset) {
1831  file = addString(file);
1832  if (source) {
1833  source = addString(source);
1834  }
1835  printd(5, "qore_program_private::addFile('%s', '%s', %d)\n", file, source ? source : "(null)", offset);
1836  addStatementToIndexIntern(&statementByFileIndex, file, nullptr, -1, source, offset);
1837  if (source) {
1838  addStatementToIndexIntern(&statementByLabelIndex, source, nullptr, -1, file, offset);
1839  }
1840  }
1841 
1842  DLLLOCAL void addUserFeature(const char* f) {
1843  //printd(5, "qore_program_private::addUserFeature() this: %p pgm: %p '%s'\n", this, pgm, f);
1844  assert(!userFeatureList.find(f));
1845  userFeatureList.push_back(f);
1846  }
1847 
1848  DLLLOCAL void removeUserFeature(const char* f) {
1849  CharPtrList::iterator i = userFeatureList.safe_dslist<std::string>::find(f);
1850  assert(i != userFeatureList.end());
1851  userFeatureList.erase(i);
1852  }
1853 
1854  DLLLOCAL void addFeature(const char* f) {
1855  assert(!featureList.find(f));
1856  featureList.push_back(f);
1857  }
1858 
1859  DLLLOCAL void removeFeature(const char* f) {
1860  CharPtrList::iterator i = featureList.safe_dslist<std::string>::find(f);
1861  assert(i != featureList.end());
1862  featureList.erase(i);
1863  }
1864 
1865  DLLLOCAL void runtimeImportSystemClassesIntern(const qore_program_private& spgm, ExceptionSink* xsink);
1866  DLLLOCAL void runtimeImportSystemConstantsIntern(const qore_program_private& spgm, ExceptionSink* xsink);
1867  DLLLOCAL void runtimeImportSystemFunctionsIntern(const qore_program_private& spgm, ExceptionSink* xsink);
1868  DLLLOCAL void runtimeImportSystemHashDeclsIntern(const qore_program_private& spgm, ExceptionSink* xsink);
1869 
1870  DLLLOCAL void runtimeImportSystemClasses(ExceptionSink* xsink);
1871  DLLLOCAL void runtimeImportSystemConstants(ExceptionSink* xsink);
1872  DLLLOCAL void runtimeImportSystemFunctions(ExceptionSink* xsink);
1873  DLLLOCAL void runtimeImportSystemHashDecls(ExceptionSink* xsink);
1874  DLLLOCAL void runtimeImportSystemApi(ExceptionSink* xsink);
1875 
1876  DLLLOCAL void doThreadInit(ExceptionSink* xsink);
1877 
1878  DLLLOCAL const QoreClass* runtimeFindClass(const char* class_name, ExceptionSink* xsink) const;
1879 
1880  DLLLOCAL void setExternalData(const char* owner, AbstractQoreProgramExternalData* pud) {
1881  AutoLocker al(plock);
1882  assert(extmap.find(owner) == extmap.end());
1883  extmap.insert(extmap_t::value_type(owner, pud));
1884  }
1885 
1886  DLLLOCAL AbstractQoreProgramExternalData* getExternalData(const char* owner) const {
1887  AutoLocker al(plock);
1888  extmap_t::const_iterator i = extmap.find(owner);
1889  return i == extmap.end() ? nullptr : i->second;
1890  }
1891 
1892  DLLLOCAL QoreHashNode* getGlobalVars() const {
1893  return qore_root_ns_private::getGlobalVars(*RootNS);
1894  }
1895 
1896  DLLLOCAL LocalVar* createLocalVar(const char* name, const QoreTypeInfo* typeInfo);
1897 
1898  DLLLOCAL const AbstractQoreFunctionVariant* runtimeFindCall(const char* name, const QoreListNode* params, ExceptionSink* xsink);
1899 
1900  DLLLOCAL QoreListNode* runtimeFindCallVariants(const char* name, ExceptionSink* xsink);
1901 
1902  DLLLOCAL static const QoreClass* runtimeFindClass(const QoreProgram& pgm, const char* class_name, ExceptionSink* xsink) {
1903  return pgm.priv->runtimeFindClass(class_name, xsink);
1904  }
1905 
1906  DLLLOCAL static void doThreadInit(QoreProgram& pgm, ExceptionSink* xsink) {
1907  pgm.priv->doThreadInit(xsink);
1908  }
1909 
1910  DLLLOCAL static int setReturnValue(QoreProgram& pgm, QoreValue val, ExceptionSink* xsink) {
1911  ValueHolder rv(val, xsink);
1912  if (!pgm.priv->exec_class) {
1913  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>))");
1914  return -1;
1915  }
1916  pgm.priv->exec_class_rv.discard(xsink);
1917  pgm.priv->exec_class_rv = rv.release();
1918  return 0;
1919  }
1920 
1921  // called when starting a new thread before the new thread is started, to avoid race conditions
1922  DLLLOCAL static int preregisterNewThread(QoreProgram& pgm, ExceptionSink* xsink) {
1923  return pgm.priv->preregisterNewThread(xsink);
1924  }
1925 
1926  // called when thread startup fails after preregistration
1927  DLLLOCAL static void cancelPreregistration(QoreProgram& pgm) {
1928  pgm.priv->cancelPreregistration();
1929  }
1930 
1931  // called from the new thread once the thread has been started (after preregisterNewThread())
1932  DLLLOCAL static void registerNewThread(QoreProgram& pgm, int tid) {
1933  pgm.priv->registerNewThread(tid);
1934  }
1935 
1936  DLLLOCAL static void runtimeImportSystemClasses(QoreProgram& pgm, ExceptionSink* xsink) {
1937  pgm.priv->runtimeImportSystemClasses(xsink);
1938  }
1939 
1940  DLLLOCAL static void runtimeImportSystemHashDecls(QoreProgram& pgm, ExceptionSink* xsink) {
1941  pgm.priv->runtimeImportSystemHashDecls(xsink);
1942  }
1943 
1944  DLLLOCAL static void runtimeImportSystemConstants(QoreProgram& pgm, ExceptionSink* xsink) {
1945  pgm.priv->runtimeImportSystemConstants(xsink);
1946  }
1947 
1948  DLLLOCAL static void runtimeImportSystemFunctions(QoreProgram& pgm, ExceptionSink* xsink) {
1949  pgm.priv->runtimeImportSystemFunctions(xsink);
1950  }
1951 
1952  DLLLOCAL static void runtimeImportSystemApi(QoreProgram& pgm, ExceptionSink* xsink) {
1953  pgm.priv->runtimeImportSystemApi(xsink);
1954  }
1955 
1956  DLLLOCAL static qore_program_private* get(QoreProgram& pgm) {
1957  return pgm.priv;
1958  }
1959 
1960  DLLLOCAL static void clearThreadData(QoreProgram& pgm, ExceptionSink* xsink) {
1961  pgm.priv->clearThreadData(xsink);
1962  }
1963 
1964  DLLLOCAL static void addUserFeature(QoreProgram& pgm, const char* f) {
1965  pgm.priv->addUserFeature(f);
1966  }
1967 
1968  DLLLOCAL static void addStatement(QoreProgram& pgm, AbstractStatement* s) {
1969  pgm.priv->addStatement(s);
1970  }
1971 
1972  DLLLOCAL static const AbstractQoreZoneInfo* currentTZIntern(QoreProgram& pgm) {
1973  return pgm.priv->TZ;
1974  }
1975 
1976  DLLLOCAL static int lockParsing(QoreProgram& pgm, ExceptionSink* xsink) {
1977  return pgm.priv->lockParsing(xsink);
1978  }
1979 
1980  DLLLOCAL static void unlockParsing(QoreProgram& pgm) {
1981  pgm.priv->unlockParsing();
1982  }
1983 
1984  DLLLOCAL static int incThreadCount(QoreProgram& pgm, ExceptionSink* xsink) {
1985  return pgm.priv->incThreadCount(xsink);
1986  }
1987 
1988  DLLLOCAL static void decThreadCount(QoreProgram& pgm, int tid) {
1989  pgm.priv->decThreadCount(tid);
1990  }
1991 
1992  // locking is done by the signal manager
1993  DLLLOCAL static void addSignal(QoreProgram& pgm, int sig) {
1994  assert(pgm.priv->sigset.find(sig) == pgm.priv->sigset.end());
1995  pgm.priv->sigset.insert(sig);
1996  }
1997 
1998  // locking is done by the signal manager
1999  DLLLOCAL static void delSignal(QoreProgram& pgm, int sig) {
2000  assert(pgm.priv->sigset.find(sig) != pgm.priv->sigset.end());
2001  pgm.priv->sigset.erase(sig);
2002  }
2003 
2004  DLLLOCAL static void startThread(QoreProgram& pgm, ExceptionSink& xsink) {
2005  pgm.priv->qore_program_private_base::startThread(xsink);
2006  }
2007 
2008  DLLLOCAL static bool setThreadInit(QoreProgram& pgm, const ResolvedCallReferenceNode* n_thr_init, ExceptionSink* xsink) {
2009  return pgm.priv->setThreadInit(n_thr_init, xsink);
2010  }
2011 
2012  DLLLOCAL static ResolvedCallReferenceNode* runtimeGetCallReference(QoreProgram* pgm, const char* name, ExceptionSink* xsink) {
2013  return pgm->priv->runtimeGetCallReference(name, xsink);
2014  }
2015 
2016  DLLLOCAL static const ParseWarnOptions& getParseWarnOptions(const QoreProgram* pgm) {
2017  return pgm->priv->pwo;
2018  }
2019 
2020  DLLLOCAL static bool setSaveParseWarnOptions(const QoreProgram* pgm, const ParseWarnOptions &new_opts, ParseWarnOptions &old_opts) {
2021  if (new_opts == pgm->priv->pwo)
2022  return false;
2023  old_opts = pgm->priv->pwo;
2024  pgm->priv->pwo = new_opts;
2025  return true;
2026  }
2027 
2028  DLLLOCAL static void setParseWarnOptions(const QoreProgram* pgm, const ParseWarnOptions &new_opts) {
2029  pgm->priv->pwo = new_opts;
2030  }
2031 
2032  DLLLOCAL static bool setThreadVarData(QoreProgram* pgm, ThreadProgramData* td, ThreadLocalProgramData* &tlpd, bool run) {
2033  return pgm->priv->setThreadVarData(td, tlpd, run);
2034  }
2035 
2036  DLLLOCAL static void finalizeThreadData(QoreProgram* pgm, ThreadProgramData* td, arg_vec_t*& cl) {
2037  pgm->priv->finalizeThreadData(td, cl);
2038  }
2039 
2040  DLLLOCAL static int endThread(QoreProgram* pgm, ThreadProgramData* td, ExceptionSink* xsink) {
2041  return pgm->priv->endThread(td, xsink);
2042  }
2043 
2044  DLLLOCAL static void makeParseException(QoreProgram* pgm, const QoreProgramLocation &loc, QoreStringNode* desc) {
2045  pgm->priv->makeParseException(loc, "PARSE-EXCEPTION", desc);
2046  }
2047 
2048  DLLLOCAL static void makeParseException(QoreProgram* pgm, const QoreProgramLocation &loc, const char* err, QoreStringNode* desc) {
2049  pgm->priv->makeParseException(loc, err, desc);
2050  }
2051 
2052  DLLLOCAL static const QoreValue parseGetDefine(QoreProgram* pgm, const char* name) {
2053  bool is_defined;
2054  return pgm->priv->getDefine(name, is_defined);
2055  }
2056 
2057  DLLLOCAL static const QoreValue parseGetDefine(QoreProgram* pgm, const char* name, bool& is_defined) {
2058  return pgm->priv->getDefine(name, is_defined);
2059  }
2060 
2061  DLLLOCAL static QoreValue runTimeGetDefine(QoreProgram* pgm, const char* name) {
2062  bool is_defined;
2063  return pgm->priv->runTimeGetDefine(name, is_defined);
2064  }
2065 
2066  DLLLOCAL static QoreValue runTimeGetDefine(QoreProgram* pgm, const char* name, bool& is_defined) {
2067  return pgm->priv->runTimeGetDefine(name, is_defined);
2068  }
2069 
2070  DLLLOCAL static QoreHashNode* runTimeGetAllDefines(QoreProgram* pgm) {
2071  return pgm->priv->runTimeGetAllDefines();
2072  }
2073 
2074  DLLLOCAL static bool parseUnDefine(QoreProgram* pgm, const char* name) {
2075  return pgm->priv->parseUnDefine(name);
2076  }
2077 
2078  DLLLOCAL static bool runTimeUnDefine(QoreProgram* pgm, const char* name, ExceptionSink* xsink) {
2079  return pgm->priv->runTimeUnDefine(name, xsink);
2080  }
2081 
2082  DLLLOCAL static bool parseIsDefined(QoreProgram* pgm, const char* name) {
2083  return pgm->priv->isDefined(name);
2084  }
2085 
2086  DLLLOCAL static bool runTimeIsDefined(QoreProgram* pgm, const char* name) {
2087  return pgm->priv->runTimeIsDefined(name);
2088  }
2089 
2090  DLLLOCAL static void parseDefine(QoreProgram* pgm, const QoreProgramLocation* loc, const char* str, QoreValue val) {
2091  pgm->priv->parseDefine(loc, str, val);
2092  }
2093 
2094  DLLLOCAL static void runTimeDefine(QoreProgram* pgm, const char* str, QoreValue val, ExceptionSink* xsink) {
2095  pgm->priv->runTimeDefine(str, val, xsink);
2096  }
2097 
2098  DLLLOCAL static void addParseException(QoreProgram* pgm, ExceptionSink* xsink, const QoreProgramLocation* loc = nullptr) {
2099  assert(xsink);
2100  pgm->priv->addParseException(*xsink, loc);
2101  delete xsink;
2102  }
2103 
2104  DLLLOCAL static void addParseException(QoreProgram* pgm, ExceptionSink& xsink, const QoreProgramLocation* loc = nullptr) {
2105  pgm->priv->addParseException(xsink, loc);
2106  }
2107 
2108  DLLLOCAL static void exportFunction(QoreProgram* srcpgm, ExceptionSink* xsink, QoreProgram* trgpgm, const char* name, const char* new_name = nullptr, bool inject = false) {
2109  srcpgm->priv->exportFunction(xsink, trgpgm->priv, name, new_name, inject);
2110  }
2111 
2112  DLLLOCAL static int64 parseAddDomain(QoreProgram* pgm, int64 n_dom) {
2113  return pgm->priv->parseAddDomain(n_dom);
2114  }
2115 
2116  DLLLOCAL static int64 getDomain(const QoreProgram& pgm) {
2117  return pgm.priv->dom;
2118  }
2119 
2120  DLLLOCAL static void runtimeAddDomain(QoreProgram& pgm, int64 n_dom) {
2121  pgm.priv->dom |= n_dom;
2122  }
2123 
2124  DLLLOCAL static int64 forceReplaceParseOptions(QoreProgram& pgm, int64 po) {
2125  int64 rv = pgm.priv->pwo.parse_options;
2126  pgm.priv->pwo.parse_options = po;
2127  return rv;
2128  }
2129 
2130  DLLLOCAL static void makeParseWarning(QoreProgram* pgm, const QoreProgramLocation &loc, int code, const char* warn, const char* fmt, ...) {
2131  //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");
2132  if (!pgm->priv->warnSink || !(code & pgm->priv->pwo.warn_mask))
2133  return;
2134 
2135  QoreStringNode* desc = new QoreStringNode;
2136  while (true) {
2137  va_list args;
2138  va_start(args, fmt);
2139  int rc = desc->vsprintf(fmt, args);
2140  va_end(args);
2141  if (!rc)
2142  break;
2143  }
2144  QoreException *ne = new ParseException(loc, warn, desc);
2145  pgm->priv->warnSink->raiseException(ne);
2146  }
2147 
2148  DLLLOCAL static void makeParseWarning(QoreProgram* pgm, const QoreProgramLocation &loc, int code, const char* warn, QoreStringNode* desc) {
2149  //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");
2150  if (!pgm->priv->warnSink || !(code & pgm->priv->pwo.warn_mask)) {
2151  desc->deref();
2152  return;
2153  }
2154 
2155  QoreException *ne = new ParseException(loc, warn, desc);
2156  pgm->priv->warnSink->raiseException(ne);
2157  }
2158 
2159  DLLLOCAL static void exportGlobalVariable(QoreProgram* pgm, const char* name, bool readonly, QoreProgram* tpgm, ExceptionSink* xsink) {
2160  pgm->priv->exportGlobalVariable(name, readonly, *(tpgm->priv), xsink);
2161  }
2162 
2163  DLLLOCAL void attachDebug(const qore_debug_program_private* n_dpgm) {
2164  printd(5, "qore_program_private::attachDebug(n_dpgm: %p), dpgm: %p\n", n_dpgm, dpgm);
2165  AutoLocker al(tlock);
2166  //QoreAutoRWWriteLocker arwl(&lck_debug_program);
2167 
2168  if (dpgm == n_dpgm)
2169  return;
2170  dpgm = const_cast<qore_debug_program_private*>(n_dpgm);
2171  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());
2172  for (auto& i : pgm_data_map) {
2173  i.second->dbgPendingAttach();
2174  i.second->dbgBreak();
2175  }
2176  }
2177 
2178  DLLLOCAL void detachDebug(const qore_debug_program_private* n_dpgm) {
2179  printd(5, "qore_program_private::detachDebug(n_dpgm: %p), dpgm: %p\n", n_dpgm, dpgm);
2180  AutoLocker al(tlock);
2181  //QoreAutoRWWriteLocker arwl(&lck_debug_program);
2182  assert(n_dpgm==dpgm);
2183  if (!n_dpgm)
2184  return;
2185  dpgm = nullptr;
2186  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());
2187  for (auto& i : pgm_data_map) {
2188  i.second->dbgPendingDetach();
2189  }
2190  // debug_program_counter may be non zero to finish pending calls. Just this instance cannot be deleted, it's satisfied in destructor
2191  }
2192 
2193  DLLLOCAL void onAttach(DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink);
2194  DLLLOCAL void onDetach(DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink);
2195  DLLLOCAL void onStep(const StatementBlock *blockStatement, const AbstractStatement *statement, unsigned bkptId, int &flow, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink);
2196  DLLLOCAL void onFunctionEnter(const StatementBlock *statement, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink);
2197  DLLLOCAL void onFunctionExit(const StatementBlock *statement, QoreValue& returnValue, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink);
2198  DLLLOCAL void onException(const AbstractStatement *statement, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink);
2199  DLLLOCAL void onExit(const StatementBlock *statement, QoreValue& returnValue, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink);
2200 
2201  DLLLOCAL int breakProgramThread(int tid) {
2202  printd(5, "qore_program_private::breakProgramThread(), this: %p, tid: %d\n", this, gettid());
2203  AutoLocker al(tlock);
2204  for (auto& i : pgm_data_map) {
2205  if (i.first->gettid() == tid) {
2206  i.second->dbgBreak();
2207  return 0;
2208  }
2209  }
2210  return -1;
2211  }
2212 
2213  DLLLOCAL void breakProgram() {
2214  printd(5, "qore_program_private::breakProgram(), this: %p\n", this);
2215  AutoLocker al(tlock);
2216  for (auto& i : pgm_data_map) {
2217  i.second->dbgBreak();
2218  }
2219  }
2220 
2221  DLLLOCAL void assignBreakpoint(QoreBreakpoint *bkpt, ExceptionSink *xsink) {
2222  if (!bkpt || this == bkpt->pgm) return;
2223  if (!checkAllowDebugging(xsink))
2224  return;
2225  if (bkpt->pgm) {
2226  bkpt->assignProgram(nullptr, nullptr);
2227  }
2228  QoreAutoRWWriteLocker al(&lck_breakpoint);
2229  breakpointList.push_back(bkpt);
2230  bkpt->pgm = this;
2231  bkpt->ref();
2232  }
2233 
2234  DLLLOCAL void deleteAllBreakpoints() {
2235  QoreAutoRWWriteLocker al(&lck_breakpoint);
2236  for (QoreBreakpointList_t::iterator it = breakpointList.begin(); it != breakpointList.end(); ++it) {
2237  (*it)->unassignAllStatements();
2238  (*it)->pgm = nullptr;
2239  (*it)->deref();
2240  }
2241  breakpointList.clear();
2242  }
2243 
2244  DLLLOCAL void getBreakpoints(QoreBreakpointList_t &bkptList) {
2245  QoreAutoRWReadLocker al(&lck_breakpoint);
2246  bkptList.clear();
2247  for (QoreBreakpointList_t::iterator it = breakpointList.begin(); it != breakpointList.end(); ++it) {
2248  bkptList.push_back(*it);
2249  (*it)->ref();
2250  }
2251  }
2252 
2253  DLLLOCAL void getStatementBreakpoints(const AbstractStatement* statement, QoreBreakpointList_t &bkptList) {
2254  QoreAutoRWReadLocker al(&lck_breakpoint);
2255  bkptList.clear();
2256  if (statement->breakpoints) {
2257  for (std::list<QoreBreakpoint*>::iterator it = statement->breakpoints->begin(); it != statement->breakpoints->end(); ++it) {
2258  bkptList.push_back(*it);
2259  (*it)->ref();
2260  }
2261  }
2262  }
2263 
2264  DLLLOCAL inline unsigned onCheckBreakpoint(const AbstractStatement *statement, ExceptionSink* xsink) {
2265  QoreAutoRWReadLocker al(&lck_breakpoint);
2266  const QoreBreakpoint *b = statement->getBreakpoint();
2267  if (b != nullptr) {
2268  return b->getBreakpointId();
2269  } else {
2270  return 0;
2271  }
2272  }
2273 
2274  DLLLOCAL unsigned long getStatementId(const AbstractStatement* statement) const {
2275  AutoLocker al(&plock);
2276  if (!statement)
2277  return 0;
2278  ReverseStatementIdMap_t::const_iterator i = reverseStatementIds.find((AbstractStatement*) statement);
2279  if (i == reverseStatementIds.end())
2280  return 0;
2281  return i->second;
2282  }
2283 
2284  DLLLOCAL AbstractStatement* resolveStatementId(unsigned long statementId) const {
2285  AutoLocker al(&plock);
2286  if (statementId == 0 || statementId > statementIds.size())
2287  return nullptr;
2288  return statementIds[statementId-1];
2289  }
2290 
2291  DLLLOCAL unsigned getProgramId() const {
2292  return programId;
2293  }
2294 
2295  // called locked
2296  DLLLOCAL void clearNamespaceData(ExceptionSink* xsink) {
2297  if (ns_vars) {
2298  return;
2299  }
2300  assert(RootNS);
2301  ns_vars = true;
2302  // delete all global variables, etc
2303  // this call can only be made once
2304  qore_root_ns_private::clearData(*RootNS, xsink);
2305  }
2306 
2307  DLLLOCAL static QoreProgram* resolveProgramId(unsigned programId) {
2308  printd(5, "qore_program_private::resolveProgramId(%x)\n", programId);
2309  QoreAutoRWReadLocker al(&lck_programMap);
2310  for (qore_program_to_object_map_t::iterator i = qore_program_to_object_map.begin(); i != qore_program_to_object_map.end(); i++) {
2311  if (i->first->priv->programId == programId)
2312  return i->first;
2313  }
2314  return nullptr;
2315  }
2316 
2317  DLLLOCAL bool checkAllowDebugging(ExceptionSink *xsink) {
2318  if (pwo.parse_options & PO_NO_DEBUGGING) {
2319  if (xsink) {
2320  xsink->raiseException("DEBUGGING", "program does not provide internal data for debugging");
2321  }
2322  return false;
2323  } else {
2324  return true;
2325  }
2326  }
2327 
2328  DLLLOCAL void addStatementToIndexIntern(name_section_sline_statement_map_t* statementIndex, const char* key, AbstractStatement *statement, int offs, const char* section, int sectionOffs);
2329  DLLLOCAL static void registerStatement(QoreProgram *pgm, AbstractStatement *statement, bool addToIndex);
2330  DLLLOCAL QoreHashNode* getSourceIndicesIntern(name_section_sline_statement_map_t* statementIndex, ExceptionSink* xsink) const;
2331  DLLLOCAL QoreHashNode* getSourceLabels(ExceptionSink* xsink) {
2332  return getSourceIndicesIntern(&statementByLabelIndex, xsink);
2333  }
2334  DLLLOCAL QoreHashNode* getSourceFileNames(ExceptionSink* xsink) {
2335  return getSourceIndicesIntern(&statementByFileIndex, xsink);
2336  }
2337 
2338  DLLLOCAL AbstractStatement* getStatementFromIndex(const char* name, int line) {
2339  printd(5, "qore_program_private::getStatementFromIndex('%s',%d), this: %p, file#: %d, label#: %d\n", name, line, this, statementByFileIndex.size(), statementByLabelIndex.size());
2340  AutoLocker al(&plock);
2341  name_section_sline_statement_map_t::iterator it;
2342  if (statementByFileIndex.empty()) {
2343  return nullptr;
2344  }
2345  bool addOffs = true;
2346  if (!name || *name == '\0') {
2347  if (statementByFileIndex.size() != 1)
2348  return nullptr;
2349  it = statementByFileIndex.begin();
2350  } else {
2351  size_t l = strlen(name);
2352  it = statementByFileIndex.find(name);
2353  if (it == statementByFileIndex.end()) {
2354  it = statementByLabelIndex.find(name); // label only full name match
2355  if (it == statementByLabelIndex.end()) {
2356 
2357  // did not find exact match so try a heuristic
2358  it = statementByFileIndex.begin();
2359  while (it != statementByFileIndex.end()) {
2360  size_t k = strlen(it->first);
2361  if (k >= l) {
2362  if (strcmp(name, it->first + k - l) == 0) {
2363  // match against suffix following '/'
2364  if (k == l /* should not happen as it is full match*/ || name[0] == '/' || it->first[k-l-1] == '/') {
2365  break;
2366  }
2367  }
2368  }
2369  ++it;
2370  }
2371  if (it == statementByFileIndex.end()) {
2372  printd(5, "qore_program_private::getStatementFromIndex('%s',%d) no suffix match, this: %p\n", name, line, this);
2373  return nullptr;
2374  }
2375  printd(5, "qore_program_private::getStatementFromIndex('%s',%d) found by file suffix match, this: %p\n", name, line, this);
2376  } else {
2377  addOffs = false;
2378  printd(5, "qore_program_private::getStatementFromIndex('%s',%d) found by label full match, this: %p\n", name, line, this);
2379  }
2380  } else {
2381  printd(5, "qore_program_private::getStatementFromIndex('%s',%d) found by file full match, this: %p\n", name, line, this);
2382  }
2383  }
2384  sline_statement_map_t *ssm = &it->second->statementMap;
2385  printd(5, "qore_program_private::getStatementFromIndex('%s',%d) found '%s', this: %p, ssm#: %d\n", name, line, it->first, this, ssm->size());
2386  if (ssm->size() == 0)
2387  return nullptr;
2388 
2389  sline_statement_map_t::iterator li = ssm->upper_bound(line);
2390  if (li == ssm->begin()) {
2391  printd(5, "qore_program_private::getStatementFromIndex('%s',%d) no statement found by line #1, this: %p\n", name, line, this);
2392  return nullptr;
2393  }
2394  --li;
2395  int ln = li->first;
2396  int minnl = -1;
2397  AbstractStatement* st = nullptr;
2398  while (true) {
2399  // find the nearest statement, i.e. statement with smallest num of lines
2400  if (ln != li->first) {
2401  break;
2402  // we do not try to find outer statement when looking for line behind inner statement
2403  }
2404  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) {
2405  int n = li->second->loc->end_line - li->second->loc->start_line;
2406  if (minnl >= 0) {
2407  if (n < minnl) {
2408  minnl = n;
2409  st = li->second;
2410  }
2411  } else {
2412  minnl = n;
2413  st = li->second;
2414  }
2415  if (minnl == 0)
2416  break;
2417  }
2418  if (li == ssm->begin())
2419  break;
2420  --li;
2421  }
2422  if (st) {
2423  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);
2424  } else {
2425  printd(5, "qore_program_private::getStatementFromIndex('%s',%d) no statement found by line #2, this: %p\n", name, line, this);
2426  }
2427  return st;
2428  }
2429 
2430  DLLLOCAL void registerQoreObject(QoreObject *o, ExceptionSink* xsink) {
2431  printd(5, "qore_program_private::registerQoreObject() pgm: %p, pgmid: %d\n", pgm, getProgramId());
2432  QoreAutoRWWriteLocker al(&qore_program_private::lck_programMap);
2433  qore_program_to_object_map_t::iterator i = qore_program_private::qore_program_to_object_map.find(pgm);
2434  assert(i != qore_program_private::qore_program_to_object_map.end());
2435  if (i->second && i->second != o) {
2436  xsink->raiseException("PROGRAM-ERROR", "The Program has already assigned QoreObject");
2437  } else {
2438  i->second = o;
2439  }
2440  }
2441 
2442  DLLLOCAL void unregisterQoreObject(QoreObject *o, ExceptionSink* xsink) {
2443  printd(5, "qore_program_private::unregisterQoreObject() pgm: %p, pgmid: %d\n", pgm, getProgramId());
2444  QoreAutoRWWriteLocker al(&qore_program_private::lck_programMap);
2445  qore_program_to_object_map_t::iterator i = qore_program_private::qore_program_to_object_map.find(pgm);
2446  assert(i != qore_program_private::qore_program_to_object_map.end());
2447  assert(i->second == o);
2448  i->second = nullptr;
2449  }
2450 
2451  DLLLOCAL QoreObject* findQoreObject() {
2452  QoreAutoRWReadLocker al(&lck_programMap);
2453  qore_program_to_object_map_t::iterator i = qore_program_to_object_map.find(pgm);
2454  if (i == qore_program_to_object_map.end()) {
2455  return nullptr;
2456  } else {
2457  return i->second;
2458  }
2459  }
2460 
2461  DLLLOCAL static QoreObject* getQoreObject(QoreProgram* pgm) {
2462  QoreAutoRWWriteLocker al(&lck_programMap);
2463  qore_program_to_object_map_t::iterator i = qore_program_to_object_map.find(pgm);
2464  assert(i != qore_program_to_object_map.end());
2465  if (i->second) {
2466  printd(5, "qore_program_private::getQoreObject() pgm: %p, pgmid: %d, second: %p\n", i->first, i->first->getProgramId(), i->second);
2467  i->second->ref();
2468  } else {
2469  i->second = new QoreObject(QC_PROGRAMCONTROL, getProgram(), pgm);
2470  printd(5, "qore_program_private::getQoreObject() pgm: %p, pgmid: %d, new second: %p\n", pgm, pgm->getProgramId(), i->second);
2471  pgm->ref();
2472  }
2473  return i->second;
2474  }
2475 
2476  DLLLOCAL static QoreListNode* getAllQoreObjects(ExceptionSink *xsink) {
2477  QoreAutoRWWriteLocker al(&lck_programMap);
2478  ReferenceHolder<QoreListNode>l(new QoreListNode(QC_PROGRAMCONTROL->getTypeInfo()), xsink);
2479 
2480  qore_program_to_object_map_t::iterator i = qore_program_to_object_map.begin();
2481  while (i != qore_program_to_object_map.end()) {
2482  if (i->second) {
2483  printd(5, "qore_program_private::getAllQoreObjects() pgm: %p, pgmid: %d, second: %p\n", i->first, i->first->getProgramId(), i->second);
2484  i->second->ref();
2485  } else {
2486  i->second = new QoreObject(QC_PROGRAMCONTROL, getProgram(), i->first);
2487  printd(5, "qore_program_private::getAllQoreObjects() pgm: %p, pgmid: %d, new second: %p\n", i->first, i->first->getProgramId(), i->second);
2488  i->first->ref();
2489  }
2490  (*l)->push(i->second, nullptr);
2491  ++i;
2492  }
2493  return l.release();
2494  }
2495 };
2496 
2497 class ParseWarnHelper : public ParseWarnOptions {
2498 protected:
2499  bool restore;
2500 public:
2501  DLLLOCAL ParseWarnHelper(const ParseWarnOptions &new_opts) {
2502  QoreProgram* pgm = getProgram();
2503  restore = pgm ? qore_program_private::setSaveParseWarnOptions(pgm, new_opts, *this) : false;
2504  }
2505  DLLLOCAL ~ParseWarnHelper() {
2506  if (restore)
2507  qore_program_private::setParseWarnOptions(getProgram(), *this);
2508  }
2509 };
2510 
2511 typedef std::map<QoreProgram*, qore_program_private*> qore_program_map_t;
2512 class QoreDebugProgram;
2513 
2514 class qore_debug_program_private {
2515 private:
2516  //mutable QoreThreadLock tlock; // thread variable data lock, for accessing the program list variable
2517  mutable QoreRWLock tlock;
2518  QoreDebugProgram *dpgm;
2519  qore_program_map_t qore_program_map;
2520  mutable QoreCounter debug_program_counter; // number of thread calls from program instance.
2521 public:
2522  DLLLOCAL qore_debug_program_private(QoreDebugProgram *n_dpgm): dpgm(n_dpgm) {};
2523  DLLLOCAL ~qore_debug_program_private() {
2524  assert(qore_program_map.empty());
2525  }
2526 
2527  DLLLOCAL void addProgram(QoreProgram *pgm, ExceptionSink *xsink) {
2528  if (!pgm->priv->checkAllowDebugging(xsink))
2529  return;
2530  QoreAutoRWWriteLocker al(&tlock);
2531  qore_program_map_t::iterator i = qore_program_map.find(pgm);
2532  printd(5, "qore_debug_program_private::addProgram(), this: %p, pgm: %p, i: %p, end: %p\n", this, pgm, i, qore_program_map.end());
2533  if (i != qore_program_map.end())
2534  return; // already exists
2535  qore_program_map.insert(qore_program_map_t::value_type(pgm, pgm->priv));
2536  pgm->ref();
2537  pgm->priv->attachDebug(this);
2538  }
2539 
2540  DLLLOCAL void removeProgram(QoreProgram *pgm) {
2541  QoreAutoRWWriteLocker al(&tlock);
2542  qore_program_map_t::iterator i = qore_program_map.find(pgm);
2543  printd(5, "qore_debug_program_private::removeProgram(), this: %p, pgm: %p, i: %p, end: %p\n", this, pgm, i, qore_program_map.end());
2544  if (i == qore_program_map.end())
2545  return;
2546  pgm->priv->detachDebug(this);
2547  qore_program_map.erase(i);
2548  pgm->deref();
2549  // onDetach will not be executed as program is removed
2550  }
2551 
2552  DLLLOCAL void removeAllPrograms() {
2553  QoreAutoRWWriteLocker al(&tlock);
2554  printd(5, "qore_debug_program_private::removeAllPrograms(), this: %p\n", this);
2555  qore_program_map_t::iterator i;
2556  while ((i = qore_program_map.begin()) != qore_program_map.end()) {
2557  qore_program_private* qpp = i->second;
2558  QoreProgram *p = i->first;
2559  qore_program_map.erase(i);
2560  qpp->detachDebug(this);
2561  p->deref();
2562  }
2563  }
2564 
2565  DLLLOCAL QoreListNode* getAllProgramObjects() {
2566  QoreAutoRWReadLocker al(&tlock);
2567  printd(5, "qore_debug_program_private::getAllProgramObjects(), this: %p\n", this);
2568  ReferenceHolder<QoreListNode> l(new QoreListNode(QC_PROGRAM->getTypeInfo()), nullptr);
2569  qore_program_map_t::iterator i = qore_program_map.begin();
2570  while (i != qore_program_map.end()) {
2571  QoreObject* o = QoreProgram::getQoreObject(i->first);
2572  if (o) {
2573  (*l)->push(o, nullptr);
2574  }
2575  i++;
2576  }
2577  return l.release();
2578  }
2579 
2580  DLLLOCAL void onAttach(QoreProgram *pgm, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink) {
2581  AutoQoreCounterDec ad(&debug_program_counter);
2582  dpgm->onAttach(pgm, rs, rts, xsink);
2583  }
2584  DLLLOCAL void onDetach(QoreProgram *pgm, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink) {
2585  AutoQoreCounterDec ad(&debug_program_counter);
2586  dpgm->onDetach(pgm, rs, rts, xsink);
2587  }
2594  DLLLOCAL void onStep(QoreProgram *pgm, const StatementBlock *blockStatement, const AbstractStatement *statement, unsigned bkptId, int &flow, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink) {
2595  AutoQoreCounterDec ad(&debug_program_counter);
2596  dpgm->onStep(pgm, blockStatement, statement, bkptId, flow, rs, rts, xsink);
2597 
2598  }
2599  DLLLOCAL void onFunctionEnter(QoreProgram *pgm, const StatementBlock *statement, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink) {
2600  AutoQoreCounterDec ad(&debug_program_counter);
2601  dpgm->onFunctionEnter(pgm, statement, rs, rts, xsink);
2602  }
2606  DLLLOCAL void onFunctionExit(QoreProgram *pgm, const StatementBlock *statement, QoreValue& returnValue, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink) {
2607  AutoQoreCounterDec ad(&debug_program_counter);
2608  dpgm->onFunctionExit(pgm, statement, returnValue, rs, rts, xsink);
2609  }
2613  DLLLOCAL void onException(QoreProgram* pgm, const AbstractStatement* statement, DebugRunStateEnum& rs, const AbstractStatement *&rts, ExceptionSink* xsink) {
2614  AutoQoreCounterDec ad(&debug_program_counter);
2615  dpgm->onException(pgm, statement, rs, rts, xsink);
2616  }
2620  DLLLOCAL void onExit(QoreProgram *pgm, const StatementBlock *statement, QoreValue& returnValue, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink* xsink) {
2621  AutoQoreCounterDec ad(&debug_program_counter);
2622  dpgm->onExit(pgm, statement, returnValue, rs, rts, xsink);
2623  }
2624 
2625  DLLLOCAL int breakProgramThread(QoreProgram* pgm, int tid) {
2626  //printd(5, "breakProgramThread pgm: %p tid: %d po: %lld\n", pgm, tid, pgm->priv->pwo.parse_options);
2627  // do not allow breaking if the Program does not support debugging
2628  if (pgm->priv->pwo.parse_options & PO_NO_DEBUGGING)
2629  return -1;
2630 
2631  QoreAutoRWReadLocker al(&tlock);
2632  qore_program_map_t::iterator i = qore_program_map.find(pgm);
2633  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);
2634  if (i == qore_program_map.end())
2635  return -2;
2636  if (i->second->breakProgramThread(tid))
2637  return -3;
2638  return 0;
2639  }
2640 
2641  DLLLOCAL int breakProgram(QoreProgram* pgm) {
2642  //printd(5, "breakProgram pgm: %p po: %lld\n", pgm, pgm->priv->pwo.parse_options);
2643  // do not allow breaking if the Program does not support debugging
2644  if (pgm->priv->pwo.parse_options & PO_NO_DEBUGGING)
2645  return -1;
2646 
2647  QoreAutoRWReadLocker al(&tlock);
2648  qore_program_map_t::iterator i = qore_program_map.find(pgm);
2649  printd(5, "qore_debug_program_private::breakProgram(), this: %p, pgm: %p, i: %p, end: %p\n", this, pgm, i, qore_program_map.end());
2650  if (i == qore_program_map.end())
2651  return -2;
2652  i->second->breakProgram();
2653  return 0;
2654  }
2655 
2656  DLLLOCAL void waitForTerminationAndClear(ExceptionSink* xsink) {
2657  removeAllPrograms();
2658  // wait till all debug calls finished, avoid deadlock as it might be handled in current thread
2659  debug_program_counter.waitForZero();
2660  }
2661 
2662  DLLLOCAL int getInterruptedCount() {
2663  return debug_program_counter.getCount();
2664  }
2665 };
2666 
2667 DLLLOCAL TypedHashDecl* init_hashdecl_SourceLocationInfo(QoreNamespace& ns);
2668 
2669 #endif
DLLEXPORT const char * c_str() const
returns the string&#39;s buffer; this data should not be changed
provides a safe and exception-safe way to hold write locks in Qore, only to be used on the stack...
Definition: QoreRWLock.h:144
DLLEXPORT int reference_count() const
gets the reference count
provides access to thread-local storage
Definition: QoreThreadLocalStorage.h:44
DLLEXPORT void assignProgram(QoreProgram *new_pgm, ExceptionSink *xsink)
abstract base class for c++ Exceptions in the Qore library
Definition: QoreStandardException.h:47
DLLEXPORT bool empty() const
returns true if the string is empty, false if not
This is the hash or associative list container type in Qore, dynamically allocated only...
Definition: QoreHashNode.h:50
a simple thread-safe counter object; objects can block on it until the counter reaches zero ...
Definition: QoreCounter.h:40
DLLEXPORT const QoreEncoding * QCS_DEFAULT
the default encoding for the Qore library
DLLLOCAL T * get()
retrieves the key&#39;s value
Definition: QoreThreadLocalStorage.h:77
DLLEXPORT AbstractStatement * resolveStatementId(unsigned long statementId, ExceptionSink *xsink) const
#define PO_NO_DEBUGGING
disallows debugging actions that could be insecure such as reading the thread local variable stack ...
Definition: Restrictions.h:86
Class implementing breakpoint for debugging.
Definition: QoreProgram.h:1009
DLLEXPORT int gettid() noexcept
returns the current TID number
virtual DLLEXPORT void deref(ExceptionSink *xsink)
decrements the reference count of the object
non-thread-safe vector for storing "char*" that you want to delete
Definition: common.h:238
DLLEXPORT unsigned getProgramId() const
get the program id
contains constants, classes, and subnamespaces in QoreProgram objects
Definition: QoreNamespace.h:64
virtual DLLEXPORT void onStep(QoreProgram *pgm, const StatementBlock *blockStatement, const AbstractStatement *statement, unsigned bkptId, int &flow, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink *xsink)
#define PO_NO_LOCALE_CONTROL
do not allow changes to program locale
Definition: Restrictions.h:64
#define PO_ALLOW_INJECTION
allow code injection
Definition: Restrictions.h:76
DLLEXPORT AbstractQoreNode * raiseException(const char *err, const char *fmt,...)
appends a Qore-language exception to the list
DLLEXPORT AbstractQoreNode * raiseErrnoException(const char *err, int en, const char *fmt,...)
appends a Qore-language exception to the list and appends the result of strerror(errno) to the descri...
DLLEXPORT int push(QoreValue val, ExceptionSink *xsink)
adds a value to the list
a thread condition class implementing a wrapper for pthread_cond_t
Definition: QoreCondition.h:45
DLLEXPORT bool ROdereference() const
atomically decrements the reference count
Qore&#39;s string type supported by the QoreEncoding class.
Definition: QoreString.h:81
Qore&#39;s string value type, reference counted, dynamically-allocated only.
Definition: QoreStringNode.h:50
DLLEXPORT int broadcast()
singles all threads blocked on this condition to wake up
#define PO_POSITIVE_OPTIONS
mask of all options allowing for more freedom (instead of less)
Definition: Restrictions.h:130
DLLEXPORT const char * getBuffer() const
returns the string&#39;s buffer; this data should not be changed
DLLEXPORT QoreProgram * getProgram()
returns the current QoreProgram
virtual DLLEXPORT void onFunctionEnter(QoreProgram *pgm, const StatementBlock *statement, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink *xsink)
the root namespace of a QoreProgram object
Definition: QoreNamespace.h:286
provides a safe and exception-safe way to hold locks in Qore, only to be used on the stack...
Definition: QoreThreadLock.h:128
This is the list container type in Qore, dynamically allocated only, reference counted.
Definition: QoreListNode.h:52
provides atomic reference counting to Qore objects
Definition: QoreReferenceCounter.h:44
defines a Qore-language class
Definition: QoreClass.h:239
DLLEXPORT qore_size_t strlen() const
returns number of bytes in the string (not including the null pointer)
an abstract class for program-specific external data
Definition: QoreProgram.h:985
DLLEXPORT int dec(ExceptionSink *xsink)
decrements the counter and wakes up any threads if the counter reaches 0
provides a safe and exception-safe way to hold read locks in Qore, only to be used on the stack...
Definition: QoreRWLock.h:105
supports parsing and executing Qore-language code, reference counted, dynamically-allocated only ...
Definition: QoreDebugProgram.h:66
DLLEXPORT int getCount() const
returns the current count
use this class to manage strings where the character encoding must be specified and may be different ...
Definition: QoreString.h:1027
The main value class in Qore, designed to be passed by value.
Definition: QoreValue.h:262
supports parsing and executing Qore-language code, reference counted, dynamically-allocated only ...
Definition: QoreProgram.h:126
DLLEXPORT void clear()
deletes the exception list immediately
static DLLEXPORT QoreObject * getQoreObject(QoreProgram *pgm)
get QoreObject of QoreProgram
the implementation of Qore&#39;s object data type, reference counted, dynamically-allocated only ...
Definition: QoreObject.h:61
container for holding Qore-language exception information and also for registering a "thread_exit" ca...
Definition: ExceptionSink.h:46
#define PO_FREE_OPTIONS
mask of options that have no effect on code access or code safety
Definition: Restrictions.h:133
virtual DLLEXPORT void onException(QoreProgram *pgm, const AbstractStatement *statement, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink *xsink)
DLLEXPORT unsigned getBreakpointId() const
get the breakpoint id
DLLEXPORT const QoreTypeInfo * getTypeInfo() const
returns the type information structure for this class
DLLEXPORT int inc()
increments the counter
virtual DLLEXPORT void onExit(QoreProgram *pgm, const StatementBlock *statement, QoreValue &returnValue, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink *xsink)
DLLEXPORT void deref(ExceptionSink *xsink)
decrements the reference count and calls derefImpl() if there_can_be_only_one is false, otherwise does nothing
long long int64
64bit integer type, cannot use int64_t here since it breaks the API on some 64-bit systems due to equ...
Definition: common.h:260
std::vector< QoreValue > arg_vec_t
vector of value information for default argument lists
Definition: common.h:254
DLLLOCAL void ref()
increments the reference count of the object
Definition: AbstractPrivateData.h:53
typed hash declaration
Definition: TypedHashDecl.h:44
DLLEXPORT void ROreference() const
atomically increments the reference count
base class for resolved call references
Definition: CallReferenceNode.h:105
DLLEXPORT QoreHashNode * copy() const
performs a copy of the hash and returns the new hash
DLLEXPORT qore_size_t size() const
returns the number of members in the hash, executes in constant time
provides a mutually-exclusive thread lock
Definition: QoreThreadLock.h:47
provides a simple POSIX-threads-based read-write lock
Definition: QoreRWLock.h:47
std::set< int > int_set_t
set of integers
Definition: common.h:82
DLLEXPORT int waitForZero(ExceptionSink *xsink, int timeout_ms=0)
blocks the calling thread until the counter reaches 0
DLLEXPORT char * q_basenameptr(const char *path)
returns a pointer within the same string
virtual DLLEXPORT void onFunctionExit(QoreProgram *pgm, const StatementBlock *statement, QoreValue &returnValue, DebugRunStateEnum &rs, const AbstractStatement *&rts, ExceptionSink *xsink)
DLLEXPORT bool isException() const
returns true if at least one exception is present
DLLLOCAL void set(T *ptr)
sets the key&#39;s value
Definition: QoreThreadLocalStorage.h:82
DLLLOCAL int set(const QoreString *s, const QoreEncoding *qe, ExceptionSink *xsink)
discards any current state and sets and converts (if necessary) a new string to the desired encoding ...
Definition: QoreString.h:1104
DLLEXPORT int vsprintf(const char *fmt, va_list args)
this will concatentate a formatted string to the existing string according to the format string and t...
holds an object and dereferences it in the destructor
Definition: QoreValue.h:452
provides an exception-safe way to manage locks in Qore, only to be used on the stack, cannot be dynamically allocated
Definition: QoreThreadLock.h:230
DLLEXPORT int wait(pthread_mutex_t *m)
blocks a thread on a mutex until the condition is signaled
#define PO_NO_CHILD_PO_RESTRICTIONS
turn off parse option inheritance restrictions
Definition: Restrictions.h:51
DLLEXPORT void assimilate(ExceptionSink *xs)
assimilates all entries of the "xs" argument by appending them to the internal list and deletes the "...
DLLEXPORT const AbstractQoreZoneInfo * currentTZ()
returns the current local time zone, note that if 0 = UTC
DLLEXPORT QoreListNode * copy() const
performs a deep copy of the list and returns the new list