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