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