Qore Programming Language 2.2.0
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 - 2025 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(ExceptionSink* xsink, const char* name, bool readonly,
1796 qore_program_private& tpgm, const char* import_as = nullptr);
1797
1798 DLLLOCAL int setGlobalVarValue(const char* name, QoreValue val, ExceptionSink* xsink);
1799
1800 // returns true if there was already a thread init closure set, false if not
1801 DLLLOCAL bool setThreadInit(const ResolvedCallReferenceNode* n_thr_init, ExceptionSink* xsink);
1802
1803 DLLLOCAL void setThreadTZ(ThreadProgramData* tpd, const AbstractQoreZoneInfo* tz) {
1804 AutoLocker al(tlock);
1805 pgm_data_map_t::iterator i = pgm_data_map.find(tpd);
1806 assert(i != pgm_data_map.end());
1807 i->second->setTZ(tz);
1808 }
1809
1810 DLLLOCAL const AbstractQoreZoneInfo* getThreadTZ(ThreadProgramData* tpd, bool& set) const {
1811 AutoLocker al(tlock);
1812 pgm_data_map_t::const_iterator i = pgm_data_map.find(tpd);
1813 assert(i != pgm_data_map.end());
1814 set = i->second->tz_set;
1815 return i->second->tz;
1816 }
1817
1818 DLLLOCAL void clearThreadTZ(ThreadProgramData* tpd) {
1819 AutoLocker al(tlock);
1820 pgm_data_map_t::iterator i = pgm_data_map.find(tpd);
1821 assert(i != pgm_data_map.end());
1822 i->second->clearTZ();
1823 }
1824
1825 DLLLOCAL void addStatement(AbstractStatement* s);
1826
1827 DLLLOCAL q_exp_t createExpression(const QoreStringNode& source, const QoreStringNode& label,
1828 ExceptionSink* xsink) {
1829 return parseExpression(source, label, xsink);
1830 }
1831
1832 DLLLOCAL QoreValue evalExpression(q_exp_t exp, ExceptionSink* xsink) {
1833 ProgramThreadCountContextHelper pch(xsink, pgm, true);
1834 if (*xsink) {
1835 return QoreValue();
1836 }
1837 if (exp_set.find(exp) == exp_set.end()) {
1838 xsink->raiseException("INVALID-EXPRESSION", "expression not registered to this Program object");
1839 return QoreValue();
1840 }
1841 ThreadFrameBoundaryHelper tfbh(true);
1842
1843 ValueHolder rv(exp->exec(xsink), xsink);
1844 if (*xsink) {
1845 return QoreValue();
1846 }
1847 return rv.release();
1848 }
1849
1850 DLLLOCAL void deleteExpression(q_exp_t exp) {
1851 AutoLocker al(plock);
1852 q_exp_set_t::iterator i = exp_set.find(exp);
1853 if (i != exp_set.end()) {
1854 exp_set.erase(exp);
1855 delete exp;
1856 }
1857 }
1858
1859 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);
1860
1861 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);
1862
1863 DLLLOCAL const char* addString(const char* str) {
1864 str_set_t::iterator i = str_set.lower_bound(str);
1865 if (i == str_set.end() || strcmp(*i, str)) {
1866 str_vec.push_back(strdup(str));
1867 i = str_set.insert(i, str_vec.back());
1868 }
1869 return *i;
1870 }
1871
1872 DLLLOCAL void addFile(const char*& file) {
1873 file = addString(file);
1874 printd(5, "qore_program_private::addFile('%s')\n", file);
1875 addStatementToIndexIntern(&statementByFileIndex, file, nullptr, -1, nullptr, -1);
1876 }
1877
1878 DLLLOCAL void addFile(const char*& file, const char*& source, int offset) {
1879 file = addString(file);
1880 if (source) {
1881 source = addString(source);
1882 }
1883 printd(5, "qore_program_private::addFile('%s', '%s', %d)\n", file, source ? source : "(null)", offset);
1884 addStatementToIndexIntern(&statementByFileIndex, file, nullptr, -1, source, offset);
1885 if (source) {
1886 addStatementToIndexIntern(&statementByLabelIndex, source, nullptr, -1, file, offset);
1887 }
1888 }
1889
1890 DLLLOCAL int addFeature(const char* f) {
1891 //printd(5, "qore_program_private::addFeature() this: %p pgm: %p '%s'\n", this, pgm, f);
1892 strset_t::iterator i = featureList.lower_bound(f);
1893 if (i != featureList.end() && (*i == f)) {
1894 return -1;
1895 }
1896
1897 featureList.insert(i, f);
1898 return 0;
1899 }
1900
1901 DLLLOCAL void removeFeature(const char* f) {
1902 strset_t::iterator i = featureList.find(f);
1903 assert(i != featureList.end());
1904 featureList.erase(i);
1905 }
1906
1907 DLLLOCAL int addUserFeature(const char* f) {
1908 //printd(5, "qore_program_private::addFeature() this: %p pgm: %p '%s'\n", this, pgm, f);
1909 strset_t::iterator i = userFeatureList.lower_bound(f);
1910 if (i != userFeatureList.end() && (*i == f)) {
1911 return -1;
1912 }
1913
1914 userFeatureList.insert(i, f);
1915 return 0;
1916 }
1917
1918 DLLLOCAL bool hasUserFeature(const std::string feature) const {
1919 return userFeatureList.find(feature) != userFeatureList.end();
1920 }
1921
1922 DLLLOCAL void removeUserFeature(const char* f) {
1923 strset_t::iterator i = userFeatureList.find(f);
1924 assert(i != userFeatureList.end());
1925 userFeatureList.erase(i);
1926 }
1927
1928 DLLLOCAL bool hasFeature(const char* f) const {
1929 return (featureList.find(f) != featureList.end())
1930 || (userFeatureList.find(f) != userFeatureList.end());
1931 }
1932
1933 DLLLOCAL void runtimeImportSystemClassesIntern(const qore_program_private& spgm, ExceptionSink* xsink);
1934 DLLLOCAL void runtimeImportSystemConstantsIntern(const qore_program_private& spgm, ExceptionSink* xsink);
1935 DLLLOCAL void runtimeImportSystemFunctionsIntern(const qore_program_private& spgm, ExceptionSink* xsink);
1936 DLLLOCAL void runtimeImportSystemHashDeclsIntern(const qore_program_private& spgm, ExceptionSink* xsink);
1937
1938 DLLLOCAL void runtimeImportSystemClasses(ExceptionSink* xsink);
1939 DLLLOCAL void runtimeImportSystemConstants(ExceptionSink* xsink);
1940 DLLLOCAL void runtimeImportSystemFunctions(ExceptionSink* xsink);
1941 DLLLOCAL void runtimeImportSystemHashDecls(ExceptionSink* xsink);
1942 DLLLOCAL void runtimeImportSystemApi(ExceptionSink* xsink);
1943
1944 DLLLOCAL void doThreadInit(ExceptionSink* xsink);
1945
1946 DLLLOCAL const QoreClass* runtimeFindClass(const char* class_name, ExceptionSink* xsink) const;
1947
1948 DLLLOCAL void setExternalData(const char* owner, AbstractQoreProgramExternalData* pud) {
1949 AutoLocker al(plock);
1950 assert(extmap.find(owner) == extmap.end());
1951 extmap.insert(extmap_t::value_type(owner, pud));
1952 }
1953
1954 DLLLOCAL AbstractQoreProgramExternalData* getExternalData(const char* owner) const {
1955 AutoLocker al(plock);
1956 extmap_t::const_iterator i = extmap.find(owner);
1957 return i == extmap.end() ? nullptr : i->second;
1958 }
1959
1960 DLLLOCAL AbstractQoreProgramExternalData* removeExternalData(const char* owner) {
1961 AutoLocker al(plock);
1962 extmap_t::iterator i = extmap.find(owner);
1963 if (i == extmap.end()) {
1964 return nullptr;
1965 }
1966 AbstractQoreProgramExternalData* rv = i->second;
1967 extmap.erase(i);
1968 return rv;
1969 }
1970
1971 DLLLOCAL QoreHashNode* getGlobalVars() const {
1972 return qore_root_ns_private::getGlobalVars(*RootNS);
1973 }
1974
1975 DLLLOCAL LocalVar* createLocalVar(const char* name, const QoreTypeInfo* typeInfo);
1976
1977 DLLLOCAL const AbstractQoreFunctionVariant* runtimeFindCall(const char* name, const QoreListNode* params, ExceptionSink* xsink);
1978
1979 DLLLOCAL QoreListNode* runtimeFindCallVariants(const char* name, ExceptionSink* xsink);
1980
1981 DLLLOCAL static const QoreClass* runtimeFindClass(const QoreProgram& pgm, const char* class_name, ExceptionSink* xsink) {
1982 return pgm.priv->runtimeFindClass(class_name, xsink);
1983 }
1984
1985 DLLLOCAL static void doThreadInit(QoreProgram& pgm, ExceptionSink* xsink) {
1986 pgm.priv->doThreadInit(xsink);
1987 }
1988
1989 DLLLOCAL static int setReturnValue(QoreProgram& pgm, QoreValue val, ExceptionSink* xsink) {
1990 ValueHolder rv(val, xsink);
1991 if (!pgm.priv->exec_class) {
1992 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>))");
1993 return -1;
1994 }
1995 pgm.priv->exec_class_rv.discard(xsink);
1996 pgm.priv->exec_class_rv = rv.release();
1997 return 0;
1998 }
1999
2000 // called when starting a new thread before the new thread is started, to avoid race conditions
2001 DLLLOCAL static int preregisterNewThread(QoreProgram& pgm, ExceptionSink* xsink) {
2002 return pgm.priv->preregisterNewThread(xsink);
2003 }
2004
2005 // called when thread startup fails after preregistration
2006 DLLLOCAL static void cancelPreregistration(QoreProgram& pgm) {
2007 pgm.priv->cancelPreregistration();
2008 }
2009
2010 // called from the new thread once the thread has been started (after preregisterNewThread())
2011 DLLLOCAL static void registerNewThread(QoreProgram& pgm, int tid) {
2012 pgm.priv->registerNewThread(tid);
2013 }
2014
2015 DLLLOCAL static void runtimeImportSystemClasses(QoreProgram& pgm, ExceptionSink* xsink) {
2016 pgm.priv->runtimeImportSystemClasses(xsink);
2017 }
2018
2019 DLLLOCAL static void runtimeImportSystemHashDecls(QoreProgram& pgm, ExceptionSink* xsink) {
2020 pgm.priv->runtimeImportSystemHashDecls(xsink);
2021 }
2022
2023 DLLLOCAL static void runtimeImportSystemConstants(QoreProgram& pgm, ExceptionSink* xsink) {
2024 pgm.priv->runtimeImportSystemConstants(xsink);
2025 }
2026
2027 DLLLOCAL static void runtimeImportSystemFunctions(QoreProgram& pgm, ExceptionSink* xsink) {
2028 pgm.priv->runtimeImportSystemFunctions(xsink);
2029 }
2030
2031 DLLLOCAL static void runtimeImportSystemApi(QoreProgram& pgm, ExceptionSink* xsink) {
2032 pgm.priv->runtimeImportSystemApi(xsink);
2033 }
2034
2035 DLLLOCAL static qore_program_private* get(QoreProgram& pgm) {
2036 return pgm.priv;
2037 }
2038
2039 DLLLOCAL static void clearThreadData(QoreProgram& pgm, ExceptionSink* xsink) {
2040 pgm.priv->clearThreadData(xsink);
2041 }
2042
2043 DLLLOCAL static void addStatement(QoreProgram& pgm, AbstractStatement* s) {
2044 pgm.priv->addStatement(s);
2045 }
2046
2047 DLLLOCAL static const AbstractQoreZoneInfo* currentTZIntern(QoreProgram& pgm) {
2048 return pgm.priv->TZ;
2049 }
2050
2051 DLLLOCAL static int lockParsing(QoreProgram& pgm, ExceptionSink* xsink) {
2052 return pgm.priv->lockParsing(xsink);
2053 }
2054
2055 DLLLOCAL static void unlockParsing(QoreProgram& pgm) {
2056 pgm.priv->unlockParsing();
2057 }
2058
2059 DLLLOCAL static int incThreadCount(QoreProgram& pgm, ExceptionSink* xsink) {
2060 return pgm.priv->incThreadCount(xsink);
2061 }
2062
2063 DLLLOCAL static void decThreadCount(QoreProgram& pgm, int tid) {
2064 pgm.priv->decThreadCount(tid);
2065 }
2066
2067 // locking is done by the signal manager
2068 DLLLOCAL static void addSignal(QoreProgram& pgm, int sig) {
2069 assert(pgm.priv->sigset.find(sig) == pgm.priv->sigset.end());
2070 pgm.priv->sigset.insert(sig);
2071 }
2072
2073 // locking is done by the signal manager
2074 DLLLOCAL static void delSignal(QoreProgram& pgm, int sig) {
2075 assert(pgm.priv->sigset.find(sig) != pgm.priv->sigset.end());
2076 pgm.priv->sigset.erase(sig);
2077 }
2078
2079 DLLLOCAL static void startThread(QoreProgram& pgm, ExceptionSink& xsink) {
2080 pgm.priv->qore_program_private_base::startThread(xsink);
2081 }
2082
2083 DLLLOCAL static bool setThreadInit(QoreProgram& pgm, const ResolvedCallReferenceNode* n_thr_init,
2084 ExceptionSink* xsink) {
2085 return pgm.priv->setThreadInit(n_thr_init, xsink);
2086 }
2087
2088 DLLLOCAL static ResolvedCallReferenceNode* runtimeGetCallReference(QoreProgram* pgm, const char* name,
2089 ExceptionSink* xsink) {
2090 return pgm->priv->runtimeGetCallReference(name, xsink);
2091 }
2092
2093 DLLLOCAL static const ParseWarnOptions& getParseWarnOptions(const QoreProgram* pgm) {
2094 return pgm->priv->pwo;
2095 }
2096
2097 DLLLOCAL static bool setSaveParseWarnOptions(const QoreProgram* pgm, const ParseWarnOptions &new_opts,
2098 ParseWarnOptions &old_opts) {
2099 if (new_opts == pgm->priv->pwo)
2100 return false;
2101 old_opts = pgm->priv->pwo;
2102 pgm->priv->pwo = new_opts;
2103 return true;
2104 }
2105
2106 DLLLOCAL static void setParseWarnOptions(const QoreProgram* pgm, const ParseWarnOptions &new_opts) {
2107 pgm->priv->pwo = new_opts;
2108 }
2109
2110 DLLLOCAL static bool setThreadVarData(QoreProgram* pgm, ThreadProgramData* td, ThreadLocalProgramData* &tlpd,
2111 bool run) {
2112 return pgm->priv->setThreadVarData(td, tlpd, run);
2113 }
2114
2115 DLLLOCAL static void makeParseException(QoreProgram* pgm, const QoreProgramLocation &loc, QoreStringNode* desc) {
2116 pgm->priv->makeParseException(loc, "PARSE-EXCEPTION", desc);
2117 }
2118
2119 DLLLOCAL static void makeParseException(QoreProgram* pgm, const QoreProgramLocation &loc, const char* err,
2120 QoreStringNode* desc) {
2121 pgm->priv->makeParseException(loc, err, desc);
2122 }
2123
2124 DLLLOCAL static const QoreValue parseGetDefine(QoreProgram* pgm, const char* name) {
2125 bool is_defined;
2126 return pgm->priv->getDefine(name, is_defined);
2127 }
2128
2129 DLLLOCAL static const QoreValue parseGetDefine(QoreProgram* pgm, const char* name, bool& is_defined) {
2130 return pgm->priv->getDefine(name, is_defined);
2131 }
2132
2133 DLLLOCAL static QoreValue runTimeGetDefine(QoreProgram* pgm, const char* name) {
2134 bool is_defined;
2135 return pgm->priv->runTimeGetDefine(name, is_defined);
2136 }
2137
2138 DLLLOCAL static QoreValue runTimeGetDefine(QoreProgram* pgm, const char* name, bool& is_defined) {
2139 return pgm->priv->runTimeGetDefine(name, is_defined);
2140 }
2141
2142 DLLLOCAL static QoreHashNode* runTimeGetAllDefines(QoreProgram* pgm) {
2143 return pgm->priv->runTimeGetAllDefines();
2144 }
2145
2146 DLLLOCAL static bool parseUnDefine(QoreProgram* pgm, const char* name) {
2147 return pgm->priv->parseUnDefine(name);
2148 }
2149
2150 DLLLOCAL static bool runTimeUnDefine(QoreProgram* pgm, const char* name, ExceptionSink* xsink) {
2151 return pgm->priv->runTimeUnDefine(name, xsink);
2152 }
2153
2154 DLLLOCAL static bool parseIsDefined(QoreProgram* pgm, const char* name) {
2155 return pgm->priv->isDefined(name);
2156 }
2157
2158 DLLLOCAL static bool runTimeIsDefined(QoreProgram* pgm, const char* name) {
2159 return pgm->priv->runTimeIsDefined(name);
2160 }
2161
2162 DLLLOCAL static void parseDefine(QoreProgram* pgm, const QoreProgramLocation* loc, const char* str,
2163 QoreValue val) {
2164 pgm->priv->parseDefine(loc, str, val);
2165 }
2166
2167 DLLLOCAL static void runTimeDefine(QoreProgram* pgm, const char* str, QoreValue val, ExceptionSink* xsink) {
2168 pgm->priv->runTimeDefine(str, val, xsink);
2169 }
2170
2171 DLLLOCAL static void addParseException(QoreProgram* pgm, ExceptionSink* xsink,
2172 const QoreProgramLocation* loc = nullptr) {
2173 assert(xsink);
2174 pgm->priv->addParseException(*xsink, loc);
2175 delete xsink;
2176 }
2177
2178 DLLLOCAL static void addParseException(QoreProgram* pgm, ExceptionSink& xsink,
2179 const QoreProgramLocation* loc = nullptr) {
2180 pgm->priv->addParseException(xsink, loc);
2181 }
2182
2183 DLLLOCAL static void exportFunction(QoreProgram* srcpgm, ExceptionSink* xsink, QoreProgram* trgpgm,
2184 const char* name, const char* new_name = nullptr, bool inject = false) {
2185 srcpgm->priv->exportFunction(xsink, trgpgm->priv, name, new_name, inject);
2186 }
2187
2188 DLLLOCAL static int64 parseAddDomain(QoreProgram* pgm, int64 n_dom) {
2189 return pgm->priv->parseAddDomain(n_dom);
2190 }
2191
2192 DLLLOCAL static int64 getDomain(const QoreProgram& pgm) {
2193 return pgm.priv->dom;
2194 }
2195
2196 DLLLOCAL static void runtimeAddDomain(QoreProgram& pgm, int64 n_dom) {
2197 pgm.priv->dom |= n_dom;
2198 }
2199
2200 DLLLOCAL static int64 forceReplaceParseOptions(QoreProgram& pgm, int64 po) {
2201 int64 rv = pgm.priv->pwo.parse_options;
2202 pgm.priv->pwo.parse_options = po;
2203 return rv;
2204 }
2205
2206 DLLLOCAL static void makeParseWarning(QoreProgram* pgm, const QoreProgramLocation &loc, int code,
2207 const char* warn, const char* fmt, ...) {
2208 //printd(5, "QP::mPW(code: %d, warn: '%s', fmt: '%s') priv->pwo.warn_mask: %d priv->warnSink: %p %s\n", code,
2209 // warn, fmt, priv->pwo.warn_mask, priv->warnSink,
2210 // priv->warnSink && (code & priv->pwo.warn_mask) ? "OK" : "SKIPPED");
2211 if (!pgm->priv->warnSink || !(code & pgm->priv->pwo.warn_mask)) {
2212 return;
2213 }
2214
2215 QoreStringNode* desc = new QoreStringNode;
2216 while (true) {
2217 va_list args;
2218 va_start(args, fmt);
2219 int rc = desc->vsprintf(fmt, args);
2220 va_end(args);
2221 if (!rc)
2222 break;
2223 }
2224 QoreException *ne = new ParseException(loc, warn, desc);
2225 pgm->priv->warnSink->raiseException(ne);
2226 }
2227
2228 DLLLOCAL static void makeParseWarning(QoreProgram* pgm, const QoreProgramLocation& loc, int code,
2229 const char* warn, QoreStringNode* desc) {
2230 //printd(5, "QoreProgram::makeParseWarning(code: %d, warn: '%s', desc: '%s') priv->pwo.warn_mask: %d "
2231 // "priv->warnSink: %p %s\n", code, warn, desc->c_str(), priv->pwo.warn_mask, priv->warnSink,
2232 // priv->warnSink && (code & priv->pwo.warn_mask) ? "OK" : "SKIPPED");
2233 if (!pgm->priv->warnSink || !(code & pgm->priv->pwo.warn_mask)) {
2234 desc->deref();
2235 return;
2236 }
2237
2238 QoreException *ne = new ParseException(loc, warn, desc);
2239 pgm->priv->warnSink->raiseException(ne);
2240 }
2241
2242 DLLLOCAL void attachDebug(const qore_debug_program_private* n_dpgm) {
2243 printd(5, "qore_program_private::attachDebug(n_dpgm: %p), dpgm: %p\n", n_dpgm, dpgm);
2244 AutoLocker al(tlock);
2245 //QoreAutoRWWriteLocker arwl(&lck_debug_program);
2246
2247 if (dpgm == n_dpgm)
2248 return;
2249 dpgm = const_cast<qore_debug_program_private*>(n_dpgm);
2250 printd(5, "qore_program_private::attachDebug, dpgm: %p, pgm_data_map: size:%d, begin: %p, end: %p\n", dpgm,
2251 pgm_data_map.size(), pgm_data_map.begin(), pgm_data_map.end());
2252 for (auto& i : pgm_data_map) {
2253 i.second->dbgPendingAttach();
2254 i.second->dbgBreak();
2255 }
2256 }
2257
2258 DLLLOCAL void detachDebug(const qore_debug_program_private* n_dpgm) {
2259 printd(5, "qore_program_private::detachDebug(n_dpgm: %p), dpgm: %p\n", n_dpgm, dpgm);
2260 AutoLocker al(tlock);
2261 //QoreAutoRWWriteLocker arwl(&lck_debug_program);
2262 assert(n_dpgm==dpgm);
2263 if (!n_dpgm)
2264 return;
2265 dpgm = nullptr;
2266 printd(5, "qore_program_private::detachDebug, dpgm: %p, pgm_data_map: size:%d, begin: %p, end: %p\n", dpgm,
2267 pgm_data_map.size(), pgm_data_map.begin(), pgm_data_map.end());
2268 for (auto& i : pgm_data_map) {
2269 i.second->dbgPendingDetach();
2270 }
2271 // debug_program_counter may be non zero to finish pending calls. Just this instance cannot be deleted, it's
2272 // satisfied in the destructor
2273 }
2274
2275 DLLLOCAL void onAttach(DebugRunStateEnum& rs, const AbstractStatement*& rts, ExceptionSink* xsink);
2276 DLLLOCAL void onDetach(DebugRunStateEnum& rs, const AbstractStatement*& rts, ExceptionSink* xsink);
2277 DLLLOCAL void onStep(const StatementBlock* blockStatement, const AbstractStatement* statement, unsigned bkptId, int& flow, DebugRunStateEnum& rs, const AbstractStatement*& rts, ExceptionSink* xsink);
2278 DLLLOCAL void onFunctionEnter(const StatementBlock* statement, DebugRunStateEnum& rs, const AbstractStatement*& rts, ExceptionSink* xsink);
2279 DLLLOCAL void onFunctionExit(const StatementBlock* statement, QoreValue& returnValue, DebugRunStateEnum& rs, const AbstractStatement*& rts, ExceptionSink* xsink);
2280 DLLLOCAL void onException(const AbstractStatement* statement, DebugRunStateEnum& rs, const AbstractStatement*& rts, ExceptionSink* xsink);
2281 DLLLOCAL void onExit(const StatementBlock* statement, QoreValue& returnValue, DebugRunStateEnum& rs, const AbstractStatement*& rts, ExceptionSink* xsink);
2282
2283 DLLLOCAL int breakProgramThread(int tid) {
2284 printd(5, "qore_program_private::breakProgramThread(), this: %p, tid: %d\n", this, q_gettid());
2285 AutoLocker al(tlock);
2286 for (auto& i : pgm_data_map) {
2287 if (i.first->gettid() == tid) {
2288 i.second->dbgBreak();
2289 return 0;
2290 }
2291 }
2292 return -1;
2293 }
2294
2295 DLLLOCAL void breakProgram() {
2296 printd(5, "qore_program_private::breakProgram(), this: %p\n", this);
2297 AutoLocker al(tlock);
2298 for (auto& i : pgm_data_map) {
2299 i.second->dbgBreak();
2300 }
2301 }
2302
2303 DLLLOCAL void assignBreakpoint(QoreBreakpoint* bkpt, ExceptionSink *xsink) {
2304 if (!bkpt || this == bkpt->pgm) return;
2305 if (!checkAllowDebugging(xsink))
2306 return;
2307 if (bkpt->pgm) {
2308 bkpt->assignProgram(nullptr, nullptr);
2309 }
2310 QoreAutoRWWriteLocker al(&lck_breakpoint);
2311 breakpointList.push_back(bkpt);
2312 bkpt->pgm = this;
2313 bkpt->ref();
2314 }
2315
2316 DLLLOCAL void deleteAllBreakpoints() {
2317 QoreAutoRWWriteLocker al(&lck_breakpoint);
2318 for (QoreBreakpointList_t::iterator it = breakpointList.begin(); it != breakpointList.end(); ++it) {
2319 (*it)->unassignAllStatements();
2320 (*it)->pgm = nullptr;
2321 (*it)->deref();
2322 }
2323 breakpointList.clear();
2324 }
2325
2326 DLLLOCAL void getBreakpoints(QoreBreakpointList_t &bkptList) {
2327 QoreAutoRWReadLocker al(&lck_breakpoint);
2328 bkptList.clear();
2329 for (QoreBreakpointList_t::iterator it = breakpointList.begin(); it != breakpointList.end(); ++it) {
2330 bkptList.push_back(*it);
2331 (*it)->ref();
2332 }
2333 }
2334
2335 DLLLOCAL void getStatementBreakpoints(const AbstractStatement* statement, QoreBreakpointList_t &bkptList) {
2336 QoreAutoRWReadLocker al(&lck_breakpoint);
2337 bkptList.clear();
2338 if (statement->breakpoints) {
2339 for (std::list<QoreBreakpoint*>::iterator it = statement->breakpoints->begin(); it != statement->breakpoints->end(); ++it) {
2340 bkptList.push_back(*it);
2341 (*it)->ref();
2342 }
2343 }
2344 }
2345
2346 DLLLOCAL inline unsigned onCheckBreakpoint(const AbstractStatement* statement, ExceptionSink* xsink) {
2347 QoreAutoRWReadLocker al(&lck_breakpoint);
2348 const QoreBreakpoint* b = statement->getBreakpoint();
2349 if (b != nullptr) {
2350 return b->getBreakpointId();
2351 } else {
2352 return 0;
2353 }
2354 }
2355
2356 DLLLOCAL unsigned long getStatementId(const AbstractStatement* statement) const {
2357 AutoLocker al(&plock);
2358 if (!statement)
2359 return 0;
2360 ReverseStatementIdMap_t::const_iterator i = reverseStatementIds.find((AbstractStatement*) statement);
2361 if (i == reverseStatementIds.end())
2362 return 0;
2363 return i->second;
2364 }
2365
2366 DLLLOCAL AbstractStatement* resolveStatementId(unsigned long statementId) const {
2367 AutoLocker al(&plock);
2368 if (statementId == 0 || statementId > statementIds.size())
2369 return nullptr;
2370 return statementIds[statementId-1];
2371 }
2372
2373 DLLLOCAL unsigned getProgramId() const {
2374 return programId;
2375 }
2376
2377 // called locked
2378 DLLLOCAL void clearNamespaceData(ExceptionSink* xsink) {
2379 if (ns_vars) {
2380 return;
2381 }
2382 assert(RootNS);
2383 ns_vars = true;
2384 // delete all global variables, etc
2385 // this call can only be made once
2386 qore_root_ns_private::clearData(*RootNS, xsink);
2387 }
2388
2389 DLLLOCAL static QoreProgram* resolveProgramId(unsigned programId) {
2390 printd(5, "qore_program_private::resolveProgramId(%x)\n", programId);
2391 QoreAutoRWReadLocker al(&lck_programMap);
2392 for (qore_program_to_object_map_t::iterator i = qore_program_to_object_map.begin(); i != qore_program_to_object_map.end(); i++) {
2393 if (i->first->priv->programId == programId)
2394 return i->first;
2395 }
2396 return nullptr;
2397 }
2398
2399 DLLLOCAL bool checkAllowDebugging(ExceptionSink *xsink) {
2400 if (pwo.parse_options & PO_NO_DEBUGGING) {
2401 if (xsink) {
2402 xsink->raiseException("DEBUGGING", "program does not provide internal data for debugging");
2403 }
2404 return false;
2405 } else {
2406 return true;
2407 }
2408 }
2409
2410 DLLLOCAL void addStatementToIndexIntern(name_section_sline_statement_map_t* statementIndex, const char* key, AbstractStatement* statement, int offs, const char* section, int sectionOffs);
2411 DLLLOCAL static void registerStatement(QoreProgram* pgm, AbstractStatement* statement, bool addToIndex);
2412 DLLLOCAL QoreHashNode* getSourceIndicesIntern(name_section_sline_statement_map_t* statementIndex, ExceptionSink* xsink) const;
2413 DLLLOCAL QoreHashNode* getSourceLabels(ExceptionSink* xsink) {
2414 return getSourceIndicesIntern(&statementByLabelIndex, xsink);
2415 }
2416 DLLLOCAL QoreHashNode* getSourceFileNames(ExceptionSink* xsink) {
2417 return getSourceIndicesIntern(&statementByFileIndex, xsink);
2418 }
2419
2420 DLLLOCAL AbstractStatement* getStatementFromIndex(const char* name, int line) {
2421 printd(5, "qore_program_private::getStatementFromIndex('%s',%d), this: %p, file#: %d, label#: %d\n", name, line, this, statementByFileIndex.size(), statementByLabelIndex.size());
2422 AutoLocker al(&plock);
2423 name_section_sline_statement_map_t::iterator it;
2424 if (statementByFileIndex.empty()) {
2425 return nullptr;
2426 }
2427 bool addOffs = true;
2428 if (!name || *name == '\0') {
2429 if (statementByFileIndex.size() != 1)
2430 return nullptr;
2431 it = statementByFileIndex.begin();
2432 } else {
2433 size_t l = strlen(name);
2434 it = statementByFileIndex.find(name);
2435 if (it == statementByFileIndex.end()) {
2436 it = statementByLabelIndex.find(name); // label only full name match
2437 if (it == statementByLabelIndex.end()) {
2438
2439 // did not find exact match so try a heuristic
2440 it = statementByFileIndex.begin();
2441 while (it != statementByFileIndex.end()) {
2442 size_t k = strlen(it->first);
2443 if (k >= l) {
2444 if (strcmp(name, it->first + k - l) == 0) {
2445 // match against suffix following '/'
2446 if (k == l /* should not happen as it is full match*/ || name[0] == '/' || it->first[k-l-1] == '/') {
2447 break;
2448 }
2449 }
2450 }
2451 ++it;
2452 }
2453 if (it == statementByFileIndex.end()) {
2454 printd(5, "qore_program_private::getStatementFromIndex('%s',%d) no suffix match, this: %p\n", name, line, this);
2455 return nullptr;
2456 }
2457 printd(5, "qore_program_private::getStatementFromIndex('%s',%d) found by file suffix match, this: %p\n", name, line, this);
2458 } else {
2459 addOffs = false;
2460 printd(5, "qore_program_private::getStatementFromIndex('%s',%d) found by label full match, this: %p\n", name, line, this);
2461 }
2462 } else {
2463 printd(5, "qore_program_private::getStatementFromIndex('%s',%d) found by file full match, this: %p\n", name, line, this);
2464 }
2465 }
2466 sline_statement_map_t *ssm = &it->second->statementMap;
2467 printd(5, "qore_program_private::getStatementFromIndex('%s',%d) found '%s', this: %p, ssm#: %d\n", name, line, it->first, this, ssm->size());
2468 if (ssm->size() == 0)
2469 return nullptr;
2470
2471 sline_statement_map_t::iterator li = ssm->upper_bound(line);
2472 if (li == ssm->begin()) {
2473 printd(5, "qore_program_private::getStatementFromIndex('%s',%d) no statement found by line #1, this: %p\n", name, line, this);
2474 return nullptr;
2475 }
2476 --li;
2477 int ln = li->first;
2478 int minnl = -1;
2479 AbstractStatement* st = nullptr;
2480 while (true) {
2481 // find the nearest statement, i.e. statement with smallest num of lines
2482 if (ln != li->first) {
2483 break;
2484 // we do not try to find outer statement when looking for line behind inner statement
2485 }
2486 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) {
2487 int n = li->second->loc->end_line - li->second->loc->start_line;
2488 if (minnl >= 0) {
2489 if (n < minnl) {
2490 minnl = n;
2491 st = li->second;
2492 }
2493 } else {
2494 minnl = n;
2495 st = li->second;
2496 }
2497 if (minnl == 0)
2498 break;
2499 }
2500 if (li == ssm->begin())
2501 break;
2502 --li;
2503 }
2504 if (st) {
2505 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);
2506 } else {
2507 printd(5, "qore_program_private::getStatementFromIndex('%s',%d) no statement found by line #2, this: %p\n", name, line, this);
2508 }
2509 return st;
2510 }
2511
2512 DLLLOCAL void registerQoreObject(QoreObject *o, ExceptionSink* xsink) {
2513 printd(5, "qore_program_private::registerQoreObject() pgm: %p, pgmid: %d\n", pgm, getProgramId());
2514 QoreAutoRWWriteLocker al(&qore_program_private::lck_programMap);
2515 qore_program_to_object_map_t::iterator i = qore_program_private::qore_program_to_object_map.find(pgm);
2516 assert(i != qore_program_private::qore_program_to_object_map.end());
2517 if (i->second && i->second != o) {
2518 xsink->raiseException("PROGRAM-ERROR", "The Program has already assigned QoreObject");
2519 } else {
2520 i->second = o;
2521 }
2522 }
2523
2524 DLLLOCAL void unregisterQoreObject(QoreObject *o, ExceptionSink* xsink) {
2525 printd(5, "qore_program_private::unregisterQoreObject() pgm: %p, pgmid: %d\n", pgm, getProgramId());
2526 QoreAutoRWWriteLocker al(&qore_program_private::lck_programMap);
2527 qore_program_to_object_map_t::iterator i = qore_program_private::qore_program_to_object_map.find(pgm);
2528 assert(i != qore_program_private::qore_program_to_object_map.end());
2529 assert(i->second == o);
2530 i->second = nullptr;
2531 }
2532
2533 DLLLOCAL QoreObject* findQoreObject() {
2534 QoreAutoRWReadLocker al(&lck_programMap);
2535 qore_program_to_object_map_t::iterator i = qore_program_to_object_map.find(pgm);
2536 if (i == qore_program_to_object_map.end()) {
2537 return nullptr;
2538 } else {
2539 return i->second;
2540 }
2541 }
2542
2543 DLLLOCAL int serialize(ExceptionSink* xsink, StreamWriter& sw);
2544 DLLLOCAL int deserialize(ExceptionSink* xsink, StreamReader& sr);
2545
2546 DLLLOCAL void deferCodeInitialization(DeferredCodeObject* o) {
2547 dset.insert(o);
2548 }
2549
2550 DLLLOCAL void removeDeferredCode(DeferredCodeObject* o) {
2551 dset_t::iterator i = dset.find(o);
2552 if (i != dset.end()) {
2553 dset.erase(i);
2554 }
2555 }
2556
2557 DLLLOCAL int initDeferredCode();
2558
2559 DLLLOCAL static QoreObject* getQoreObject(QoreProgram* pgm) {
2560 QoreAutoRWWriteLocker al(&lck_programMap);
2561 qore_program_to_object_map_t::iterator i = qore_program_to_object_map.find(pgm);
2562 assert(i != qore_program_to_object_map.end());
2563 if (i->second) {
2564 printd(5, "qore_program_private::getQoreObject() pgm: %p, pgmid: %d, second: %p\n", i->first, i->first->getProgramId(), i->second);
2565 i->second->ref();
2566 } else {
2567 i->second = new QoreObject(QC_PROGRAMCONTROL, getProgram(), pgm);
2568 printd(5, "qore_program_private::getQoreObject() pgm: %p, pgmid: %d, new second: %p\n", pgm, pgm->getProgramId(), i->second);
2569 pgm->ref();
2570 }
2571 return i->second;
2572 }
2573
2574 DLLLOCAL static QoreListNode* getAllQoreObjects(ExceptionSink *xsink) {
2575 QoreAutoRWWriteLocker al(&lck_programMap);
2576 ReferenceHolder<QoreListNode>l(new QoreListNode(QC_PROGRAMCONTROL->getTypeInfo()), xsink);
2577
2578 qore_program_to_object_map_t::iterator i = qore_program_to_object_map.begin();
2579 while (i != qore_program_to_object_map.end()) {
2580 if (i->second) {
2581 printd(5, "qore_program_private::getAllQoreObjects() pgm: %p, pgmid: %d, second: %p\n", i->first, i->first->getProgramId(), i->second);
2582 i->second->ref();
2583 } else {
2584 i->second = new QoreObject(QC_PROGRAMCONTROL, getProgram(), i->first);
2585 printd(5, "qore_program_private::getAllQoreObjects() pgm: %p, pgmid: %d, new second: %p\n", i->first, i->first->getProgramId(), i->second);
2586 i->first->ref();
2587 }
2588 (*l)->push(i->second, nullptr);
2589 ++i;
2590 }
2591 return l.release();
2592 }
2593
2594private:
2595 mutable QoreCounter debug_program_counter; // number of thread calls to debug program instance.
2596 DLLLOCAL void init(QoreProgram* n_pgm, int64 n_parse_options,
2597 const AbstractQoreZoneInfo* n_TZ = QTZM.getLocalZoneInfo()) {
2598 }
2599
2600 // only called from parseSetTimeZone
2601 DLLLOCAL void mergeParseException(ExceptionSink &xsink) {
2602 if (parseSink)
2603 parseSink->assimilate(xsink);
2604 else {
2605 if (!pendingParseSink)
2606 pendingParseSink = new ExceptionSink;
2607 pendingParseSink->assimilate(xsink);
2608 }
2609 }
2610
2611 qore_debug_program_private* dpgm = nullptr;
2612 QoreRWLock lck_breakpoint; // to protect breakpoint manipulation
2613 QoreBreakpointList_t breakpointList;
2614
2615 // index source filename/label -> line -> statement
2616 name_section_sline_statement_map_t statementByFileIndex;
2617 name_section_sline_statement_map_t statementByLabelIndex;
2618
2619 // statementId to AbstractStatement resolving
2620 typedef std::vector<AbstractStatement*> StatementVector_t;
2621 StatementVector_t statementIds;
2622
2623 // to get statementId
2624 typedef std::map<AbstractStatement*, unsigned long> ReverseStatementIdMap_t;
2625 ReverseStatementIdMap_t reverseStatementIds;
2626
2627 // for deferred code initialization while parsing constants
2628 typedef std::set<DeferredCodeObject*> dset_t;
2629 dset_t dset;
2630
2637 DLLLOCAL qore_debug_program_private* getDebugProgram(AutoQoreCounterDec& ad) {
2638 AutoLocker al(tlock);
2639 //QoreAutoRWReadLocker al(&lck_debug_program);
2640 qore_debug_program_private* ret = dpgm;
2641 if (ret) {
2642 // new debug call in progress
2643 ad.inc();
2644 }
2645 return ret;
2646 }
2647
2648 // lck_breakpoint lock should be aquired
2649 DLLLOCAL bool isBreakpointRegistered(const QoreBreakpoint* bkpt) const {
2650 return std::find(breakpointList.begin(), breakpointList.end(), bkpt) != breakpointList.end();
2651 }
2652 friend class QoreBreakpoint;
2653
2654 typedef std::map<QoreProgram*, QoreObject*> qore_program_to_object_map_t;
2655 static qore_program_to_object_map_t qore_program_to_object_map;
2656 static QoreRWLock lck_programMap; // to protect program list manipulation
2657 static volatile unsigned programIdCounter; // to generate programId
2658 unsigned programId;
2659};
2660
2661class ParseWarnHelper : public ParseWarnOptions {
2662protected:
2663 bool restore;
2664public:
2665 DLLLOCAL ParseWarnHelper(const ParseWarnOptions &new_opts) {
2666 QoreProgram* pgm = getProgram();
2667 restore = pgm ? qore_program_private::setSaveParseWarnOptions(pgm, new_opts, *this) : false;
2668 }
2669 DLLLOCAL ~ParseWarnHelper() {
2670 if (restore)
2671 qore_program_private::setParseWarnOptions(getProgram(), *this);
2672 }
2673};
2674
2675typedef std::map<QoreProgram*, qore_program_private*> qore_program_map_t;
2676class QoreDebugProgram;
2677
2678class qore_debug_program_private {
2679public:
2680 DLLLOCAL qore_debug_program_private(QoreDebugProgram* n_dpgm) : dpgm(n_dpgm) {}
2681
2682 DLLLOCAL ~qore_debug_program_private() {
2683 assert(qore_program_map.empty());
2684 }
2685
2686 DLLLOCAL void addProgram(QoreProgram* pgm, ExceptionSink *xsink) {
2687 if (!pgm->priv->checkAllowDebugging(xsink))
2688 return;
2689 QoreAutoRWWriteLocker al(&tlock);
2690 qore_program_map_t::iterator i = qore_program_map.find(pgm);
2691 printd(5, "qore_debug_program_private::addProgram(), this: %p, pgm: %p, i: %p, end: %p\n", this, pgm, i,
2692 qore_program_map.end());
2693 if (i != qore_program_map.end())
2694 return; // already exists
2695 qore_program_map.insert(qore_program_map_t::value_type(pgm, pgm->priv));
2696 pgm->ref();
2697 pgm->priv->attachDebug(this);
2698 }
2699
2700 DLLLOCAL void removeProgram(QoreProgram* pgm) {
2701 QoreAutoRWWriteLocker al(&tlock);
2702 qore_program_map_t::iterator i = qore_program_map.find(pgm);
2703 printd(5, "qore_debug_program_private::removeProgram(), this: %p, pgm: %p, i: %p, end: %p\n", this, pgm, i,
2704 qore_program_map.end());
2705 if (i == qore_program_map.end())
2706 return;
2707 pgm->priv->detachDebug(this);
2708 qore_program_map.erase(i);
2709 pgm->deref();
2710 // onDetach will not be executed as program is removed
2711 }
2712
2713 DLLLOCAL void removeAllPrograms() {
2714 QoreAutoRWWriteLocker al(&tlock);
2715 printd(5, "qore_debug_program_private::removeAllPrograms(), this: %p\n", this);
2716 qore_program_map_t::iterator i;
2717 while ((i = qore_program_map.begin()) != qore_program_map.end()) {
2718 qore_program_private* qpp = i->second;
2719 QoreProgram* p = i->first;
2720 qore_program_map.erase(i);
2721 qpp->detachDebug(this);
2722 p->deref();
2723 }
2724 }
2725
2726 DLLLOCAL QoreListNode* getAllProgramObjects() {
2727 QoreAutoRWReadLocker al(&tlock);
2728 printd(5, "qore_debug_program_private::getAllProgramObjects(), this: %p\n", this);
2729 ReferenceHolder<QoreListNode> l(new QoreListNode(QC_PROGRAM->getTypeInfo()), nullptr);
2730 qore_program_map_t::iterator i = qore_program_map.begin();
2731 while (i != qore_program_map.end()) {
2733 if (o) {
2734 (*l)->push(o, nullptr);
2735 }
2736 ++i;
2737 }
2738 return l.release();
2739 }
2740
2741 DLLLOCAL void onAttach(QoreProgram* pgm, DebugRunStateEnum& rs, const AbstractStatement*& rts,
2742 ExceptionSink* xsink) {
2743 AutoQoreCounterDec ad(&debug_program_counter);
2744 dpgm->onAttach(pgm, rs, rts, xsink);
2745 }
2746
2747 DLLLOCAL void onDetach(QoreProgram* pgm, DebugRunStateEnum& rs, const AbstractStatement*& rts,
2748 ExceptionSink* xsink) {
2749 AutoQoreCounterDec ad(&debug_program_counter);
2750 dpgm->onDetach(pgm, rs, rts, xsink);
2751 }
2752
2760 DLLLOCAL void onStep(QoreProgram* pgm, const StatementBlock* blockStatement, const AbstractStatement* statement,
2761 unsigned bkptId, int& flow, DebugRunStateEnum& rs, const AbstractStatement*& rts, ExceptionSink* xsink) {
2762 AutoQoreCounterDec ad(&debug_program_counter);
2763 dpgm->onStep(pgm, blockStatement, statement, bkptId, flow, rs, rts, xsink);
2764 }
2765
2766 DLLLOCAL void onFunctionEnter(QoreProgram* pgm, const StatementBlock* statement, DebugRunStateEnum& rs,
2767 const AbstractStatement*& rts, ExceptionSink* xsink) {
2768 AutoQoreCounterDec ad(&debug_program_counter);
2769 dpgm->onFunctionEnter(pgm, statement, rs, rts, xsink);
2770 }
2771
2775 DLLLOCAL void onFunctionExit(QoreProgram* pgm, const StatementBlock* statement, QoreValue& returnValue,
2776 DebugRunStateEnum& rs, const AbstractStatement*& rts, ExceptionSink* xsink) {
2777 AutoQoreCounterDec ad(&debug_program_counter);
2778 dpgm->onFunctionExit(pgm, statement, returnValue, rs, rts, xsink);
2779 }
2783 DLLLOCAL void onException(QoreProgram* pgm, const AbstractStatement* statement, DebugRunStateEnum& rs,
2784 const AbstractStatement*& rts, ExceptionSink* xsink) {
2785 AutoQoreCounterDec ad(&debug_program_counter);
2786 dpgm->onException(pgm, statement, rs, rts, xsink);
2787 }
2791 DLLLOCAL void onExit(QoreProgram* pgm, const StatementBlock* statement, QoreValue& returnValue,
2792 DebugRunStateEnum& rs, const AbstractStatement*& rts, ExceptionSink* xsink) {
2793 AutoQoreCounterDec ad(&debug_program_counter);
2794 dpgm->onExit(pgm, statement, returnValue, rs, rts, xsink);
2795 }
2796
2797 DLLLOCAL int breakProgramThread(QoreProgram* pgm, int tid) {
2798 //printd(5, "breakProgramThread pgm: %p tid: %d po: %lld\n", pgm, tid, pgm->priv->pwo.parse_options);
2799 // do not allow breaking if the Program does not support debugging
2800 if (pgm->priv->pwo.parse_options & PO_NO_DEBUGGING)
2801 return -1;
2802
2803 QoreAutoRWReadLocker al(&tlock);
2804 qore_program_map_t::iterator i = qore_program_map.find(pgm);
2805 printd(5, "qore_debug_program_private::breakProgramThread(), this: %p, pgm: %p, i: %p, end: %p, tid: %d\n",
2806 this, pgm, i, qore_program_map.end(), tid);
2807 if (i == qore_program_map.end())
2808 return -2;
2809 if (i->second->breakProgramThread(tid))
2810 return -3;
2811 return 0;
2812 }
2813
2814 DLLLOCAL int breakProgram(QoreProgram* pgm) {
2815 //printd(5, "breakProgram pgm: %p po: %lld\n", pgm, pgm->priv->pwo.parse_options);
2816 // do not allow breaking if the Program does not support debugging
2817 if (pgm->priv->pwo.parse_options & PO_NO_DEBUGGING)
2818 return -1;
2819
2820 QoreAutoRWReadLocker al(&tlock);
2821 qore_program_map_t::iterator i = qore_program_map.find(pgm);
2822 printd(5, "qore_debug_program_private::breakProgram(), this: %p, pgm: %p, i: %p, end: %p\n", this, pgm, i,
2823 qore_program_map.end());
2824 if (i == qore_program_map.end())
2825 return -2;
2826 i->second->breakProgram();
2827 return 0;
2828 }
2829
2830 DLLLOCAL void waitForTerminationAndClear(ExceptionSink* xsink) {
2831 removeAllPrograms();
2832 // wait till all debug calls finished, avoid deadlock as it might be handled in current thread
2833 debug_program_counter.waitForZero();
2834 }
2835
2836 DLLLOCAL int getInterruptedCount() {
2837 return debug_program_counter.getCount();
2838 }
2839
2840private:
2841 // thread variable data lock, for accessing the program list variable
2842 mutable QoreRWLock tlock;
2843 QoreDebugProgram* dpgm;
2844 qore_program_map_t qore_program_map;
2845 mutable QoreCounter debug_program_counter; // number of thread calls from program instance.
2846};
2847
2848DLLLOCAL TypedHashDecl* init_hashdecl_SourceLocationInfo(QoreNamespace& ns);
2849
2850#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:128
provides a safe and exception-safe way to hold write locks in Qore, only to be used on the stack,...
Definition QoreRWLock.h:167
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