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