Qore Programming Language 1.19.1
Loading...
Searching...
No Matches
LocalVar.h
1/* -*- mode: c++; indent-tabs-mode: nil -*- */
2/*
3 LocalVar.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_LOCALVAR_H
33
34#define _QORE_LOCALVAR_H
35
36#include "qore/intern/qore_thread_intern.h"
37#include "qore/intern/QoreLValue.h"
38#include "qore/intern/RSection.h"
39#include "qore/intern/RSet.h"
40#include "qore/ReferenceNode.h"
41#include "qore/intern/WeakReferenceNode.h"
42
43#include <atomic>
44
45template <class T>
46class LocalRefHelper : public RuntimeReferenceHelper {
47protected:
48 // used to skip the var entry in case it's a recursive reference
49 bool valid;
50
51public:
52 DLLLOCAL LocalRefHelper(const T* val, ReferenceNode& ref, ExceptionSink* xsink)
53 : RuntimeReferenceHelper(ref, xsink),
54 valid(!*xsink) {
55 }
56
57 DLLLOCAL operator bool() const {
58 return valid;
59 }
60};
61
62template <class T>
63class LValueRefHelper : public LocalRefHelper<T> {
64protected:
65 LValueHelper* valp;
66
67public:
68 DLLLOCAL LValueRefHelper(T* val, ExceptionSink* xsink) : LocalRefHelper<T>(val, xsink), valp(this->valid ? new LValueHelper(*((ReferenceNode*)val->v.n), xsink) : nullptr) {
69 }
70
71 DLLLOCAL ~LValueRefHelper() {
72 delete valp;
73 }
74
75 DLLLOCAL operator bool() const {
76 return valp;
77 }
78
79 DLLLOCAL LValueHelper* operator->() {
80 return valp;
81 }
82};
83
84class VarValueBase {
85protected:
86 DLLLOCAL int checkFinalized(ExceptionSink* xsink) const {
87 if (finalized) {
88 xsink->raiseException("DESTRUCTOR-ERROR", "illegal variable assignment after second phase of variable destruction");
89 return -1;
90 }
91 return 0;
92 }
93
94public:
95 QoreLValueGeneric val;
96 const char* id;
97 bool finalized : 1;
98 bool frame_boundary : 1;
99
100 DLLLOCAL VarValueBase(const char* n_id, valtype_t t = QV_Node) : val(t), id(n_id), finalized(false), frame_boundary(false) {
101 }
102
103 DLLLOCAL VarValueBase(const char* n_id, const QoreTypeInfo* varTypeInfo) : val(varTypeInfo), id(n_id), finalized(false), frame_boundary(false) {
104 }
105
106 DLLLOCAL VarValueBase() : val(QV_Bool), id(nullptr), finalized(false), frame_boundary(false) {
107 }
108
109 DLLLOCAL void setFrameBoundary() {
110 assert(!frame_boundary);
111 frame_boundary = true;
112 }
113
114 DLLLOCAL void del(ExceptionSink* xsink) {
115 val.removeValue(true).discard(xsink);
116 }
117
118 DLLLOCAL bool isRef() const {
119 return val.getType() == NT_REFERENCE;
120 }
121
122 DLLLOCAL QoreValue finalize() {
123 if (finalized)
124 return QoreValue();
125
126 finalized = true;
127
128 return val.removeValue(true);
129 }
130};
131
132class LocalVarValue : public VarValueBase {
133public:
134 DLLLOCAL void set(const char* n_id, const QoreTypeInfo* varTypeInfo, QoreValue nval, bool assign, bool static_assignment) {
135 //printd(5, "LocalVarValue::set() this: %p id: '%s' type: '%s' code: %d static_assignment: %d\n", this, n_id, QoreTypeInfo::getName(typeInfo), nval.getType(), static_assignment);
136 assert(!finalized);
137
138 id = n_id;
139
140 // try to set an optimized value type for the value holder if possible
141 val.set(varTypeInfo);
142
143 // no exception is possible here as there was no previous value
144 // also since only basic value types could be returned, no exceptions can occur with the value passed either
145 if (assign) {
146 discard(val.assignAssumeInitial(nval, static_assignment), nullptr);
147 } else {
148 assert(!val.assigned);
149 assert(!nval);
150 }
151 }
152
153 DLLLOCAL void uninstantiate(ExceptionSink* xsink) {
154 del(xsink);
155 }
156
157 DLLLOCAL void uninstantiateSelf() {
158 val.unassignIgnore();
159 }
160
161 DLLLOCAL int getLValue(LValueHelper& lvh, bool for_remove, const QoreTypeInfo* typeInfo, const QoreTypeInfo* refTypeInfo) const;
162 DLLLOCAL void remove(LValueRemoveHelper& lvrh, const QoreTypeInfo* typeInfo);
163
164 DLLLOCAL QoreValue eval(bool& needs_deref, ExceptionSink* xsink) const {
165 //printd(5, "LocalVarValue::eval() this: %p '%s' type: %d '%s'\n", this, id, val.getType(), val.getTypeName());
166 if (val.getType() == NT_REFERENCE) {
167 ReferenceNode* ref = const_cast<ReferenceNode*>(val.get<ReferenceNode>());
168 LocalRefHelper<LocalVarValue> helper(this, *ref, xsink);
169 if (!helper)
170 return QoreValue();
171
172 ValueEvalRefHolder erh(lvalue_ref::get(ref)->vexp, xsink);
173 return erh.takeValue(needs_deref);
174 }
175
176 if (val.getType() == NT_WEAKREF) {
177 needs_deref = false;
178 return val.get<WeakReferenceNode>()->get();
179 }
180
181 return val.getReferencedValue(needs_deref);
182 }
183
184 DLLLOCAL QoreValue eval(ExceptionSink* xsink) const {
185 if (val.getType() == NT_REFERENCE) {
186 ReferenceNode* ref = const_cast<ReferenceNode*>(val.get<ReferenceNode>());
187 LocalRefHelper<LocalVarValue> helper(this, *ref, xsink);
188 if (!helper)
189 return QoreValue();
190
191 ValueEvalRefHolder erh(lvalue_ref::get(ref)->vexp, xsink);
192 return *xsink ? QoreValue() : erh.takeReferencedValue();
193 }
194
195 if (val.getType() == NT_WEAKREF) {
196 return val.get<WeakReferenceNode>()->get()->refSelf();
197 }
198
199 return val.getReferencedValue();
200 }
201};
202
203hashdecl ClosureVarValue : public VarValueBase, public RObject {
204public:
205 const QoreTypeInfo* typeInfo = nullptr; // type restriction for lvalue
206 const QoreTypeInfo* refTypeInfo;
207 // reference count; access serialized with rlck from RObject
208 mutable std::atomic_int references;
209
210 DLLLOCAL ClosureVarValue(const char* n_id, const QoreTypeInfo* varTypeInfo, QoreValue& nval, bool assign) : VarValueBase(n_id, varTypeInfo), RObject(references), typeInfo(varTypeInfo), refTypeInfo(QoreTypeInfo::getReferenceTarget(varTypeInfo)), references(1) {
211 //printd(5, "ClosureVarValue::ClosureVarValue() this: %p refs: 0 -> 1 val: %s\n", this, val.getTypeName());
212 val.setClosure();
213
214 // try to set an optimized value type for the value holder if possible
215 val.set(varTypeInfo);
216
217 //printd(5, "ClosureVarValue::ClosureVarValue() this: %p pgm: %p val: %s\n", this, getProgram(), nval.getTypeName());
218 // also since only basic value types could be returned, no exceptions can occur with the value passed either
219 if (assign)
220 discard(val.assignAssumeInitial(nval), nullptr);
221#ifdef DEBUG
222 else
223 assert(!val.assigned);
224#endif
225 }
226
227 DLLLOCAL virtual ~ClosureVarValue() {
228 //printd(5, "ClosureVarValue::~ClosureVarValue() this: %p\n", this);
229 }
230
231 DLLLOCAL void ref() const;
232
233 DLLLOCAL void deref(ExceptionSink* xsink);
234
235 DLLLOCAL const void* getLValueId() const;
236
237 // returns true if the value could contain an object or a closure
238 DLLLOCAL virtual bool needsScan(bool scan_now) {
239 return QoreTypeInfo::needsScan(typeInfo);
240 }
241
242 DLLLOCAL virtual bool scanMembers(RSetHelper& rsh);
243
244 DLLLOCAL int getLValue(LValueHelper& lvh, bool for_remove) const;
245 DLLLOCAL void remove(LValueRemoveHelper& lvrh);
246
247 DLLLOCAL ClosureVarValue* refSelf() const {
248 ref();
249 return const_cast<ClosureVarValue*>(this);
250 }
251
252 // sets the current variable to finalized, sets the value to 0, and returns the value held (for dereferencing outside the lock)
253 DLLLOCAL QoreValue finalize() {
254 QoreSafeVarRWWriteLocker sl(rml);
255 return VarValueBase::finalize();
256 }
257
258 DLLLOCAL QoreValue eval(bool& needs_deref, ExceptionSink* xsink) const {
259 QoreSafeVarRWReadLocker sl(rml);
260 if (val.getType() == NT_REFERENCE) {
262 sl.unlock();
263 LocalRefHelper<ClosureVarValue> helper(this, **ref, xsink);
264 return helper ? lvalue_ref::get(*ref)->vexp.eval(needs_deref, xsink) : QoreValue();
265 }
266
267 if (val.getType() == NT_WEAKREF) {
268 needs_deref = false;
269 return val.get<WeakReferenceNode>()->get();
270 }
271
272 return val.getReferencedValue();
273 }
274
275 DLLLOCAL QoreValue eval(ExceptionSink* xsink) const {
276 QoreSafeVarRWReadLocker sl(rml);
277 if (val.getType() == NT_REFERENCE) {
279 sl.unlock();
280 LocalRefHelper<ClosureVarValue> helper(this, **ref, xsink);
281 return helper ? lvalue_ref::get(*ref)->vexp.eval(xsink) : QoreValue();
282 }
283
284 if (val.getType() == NT_WEAKREF) {
285 return val.get<WeakReferenceNode>()->get()->refSelf();
286 }
287
288 return val.getReferencedValue();
289 }
290
291 DLLLOCAL AbstractQoreNode* getReference(const QoreProgramLocation* loc, const char* name, const void*& lvalue_id);
292
293 // deletes the object itself
294 DLLLOCAL virtual void deleteObject() {
295 delete this;
296 }
297
298 // returns the name of the object
299 DLLLOCAL virtual const char* getName() const {
300 return id;
301 }
302};
303
304// now shared between parent and child Program objects for top-level local variables with global scope
305class LocalVar {
306public:
307 DLLLOCAL LocalVar(const char* n_name, const QoreTypeInfo* ti)
308 : name(n_name), typeInfo(ti), refTypeInfo(QoreTypeInfo::getReferenceTarget(ti)) {
309 }
310
311 DLLLOCAL LocalVar(const LocalVar& old) : name(old.name), closure_use(old.closure_use),
312 parse_assigned(old.parse_assigned), is_self(old.is_self), typeInfo(old.typeInfo),
313 refTypeInfo(old.refTypeInfo) {
314 }
315
316 DLLLOCAL ~LocalVar() {
317 }
318
319 DLLLOCAL void parseAssigned() {
320 if (!parse_assigned) {
321 parse_assigned = true;
322 }
323 }
324
325 DLLLOCAL void parseUnassigned() {
326 if (parse_assigned) {
327 parse_assigned = false;
328 }
329 }
330
331 DLLLOCAL bool isAssigned() const {
332 return parse_assigned;
333 }
334
335 DLLLOCAL void instantiate() {
336 if (getProgram()->getParseOptions64() & PO_STRICT_TYPES) {
337 //printd(5, "LocalVar::instantiate() this: %p '%s' typeInfo: %s\n", this, name.c_str(),
338 // QoreTypeInfo::getName(typeInfo));
339 instantiateIntern(QoreTypeInfo::getDefaultQoreValue(typeInfo), true);
340 } else {
341 //printd(5, "LocalVar::instantiate() this: %p '%s' typeInfo: %s NO ASSIGNMENT\n", this, name.c_str(),
342 // QoreTypeInfo::getName(typeInfo));
343 instantiateIntern(QoreValue(), false);
344 }
345 }
346
347 DLLLOCAL void instantiate(QoreValue nval) {
348 instantiateIntern(nval, true);
349 }
350
351 DLLLOCAL void instantiateIntern(QoreValue nval, bool assign) {
352 //printd(5, "LocalVar::instantiateIntern(%s, %d) this: %p '%s' value closure_use: %s pgm: %p val: %s "
353 // "type: '%s' rti: '%s'\n", nval.getTypeName(), assign, this, name.c_str(),
354 // closure_use ? "true" : "false", getProgram(), nval.getTypeName(), QoreTypeInfo::getName(typeInfo),
355 // QoreTypeInfo::getName(refTypeInfo));
356
357 if (!closure_use) {
358 LocalVarValue* val = thread_instantiate_lvar();
359 val->set(name.c_str(), typeInfo, nval, assign, false);
360 } else {
361 thread_instantiate_closure_var(name.c_str(), typeInfo, nval, assign);
362 }
363 }
364
365 DLLLOCAL void instantiateSelf(QoreObject* value) const {
366 printd(5, "LocalVar::instantiateSelf(%p) this: %p '%s'\n", value, this, name.c_str());
367 if (!closure_use) {
368 LocalVarValue* val = thread_instantiate_lvar();
369 val->set(name.c_str(), typeInfo, value, true, true);
370 } else {
371 QoreValue val(value->refSelf());
372 thread_instantiate_closure_var(name.c_str(), typeInfo, val, true);
373 }
374 }
375
376 DLLLOCAL void uninstantiate(ExceptionSink* xsink) const {
377 //printd(5, "LocalVar::uninstantiate() this: %p '%s' closure_use: %s pgm: %p\n", this, name.c_str(),
378 // closure_use ? "true" : "false", getProgram());
379
380 if (!closure_use) {
381 thread_uninstantiate_lvar(xsink);
382 } else {
383 thread_uninstantiate_closure_var(xsink);
384 }
385 }
386
387 DLLLOCAL void uninstantiateSelf() const {
388 if (!closure_use) {
389 thread_uninstantiate_self();
390 } else { // cannot go out of scope here, so no destructor can be run, so we pass a nullptr ExceptionSink ptr
391 thread_uninstantiate_closure_var(nullptr);
392 }
393 }
394
395 DLLLOCAL QoreValue eval(bool& needs_deref, ExceptionSink* xsink) const {
396 if (!closure_use) {
397 LocalVarValue* val = get_var();
398 //printd(5, "LocalVar::eval '%s' typeInfo: %p '%s'\n", name.c_str(), typeInfo,
399 // QoreTypeInfo::getName(typeInfo));
400 return val->eval(needs_deref, xsink);
401 }
402
403 ClosureVarValue* val = thread_find_closure_var(name.c_str());
404 return val->eval(needs_deref, xsink);
405 }
406
407 // returns true if the value could contain an object or a closure
408 DLLLOCAL bool needsScan() const {
409 return QoreTypeInfo::needsScan(typeInfo);
410 }
411
412 DLLLOCAL const char* getName() const {
413 return name.c_str();
414 }
415
416 DLLLOCAL const std::string& getNameStr() const {
417 return name;
418 }
419
420 DLLLOCAL void setClosureUse() {
421 closure_use = true;
422 }
423
424 DLLLOCAL bool closureUse() const {
425 return closure_use;
426 }
427
428 DLLLOCAL bool isRef() const {
429 return !closure_use ? get_var()->isRef() : thread_find_closure_var(name.c_str())->isRef();
430 }
431
432 DLLLOCAL int getLValue(LValueHelper& lvh, bool for_remove, bool initial_assignment) const {
433 //printd(5, "LocalVar::getLValue() this: %p '%s' for_remove: %d closure_use: %d ti: '%s' rti: '%s'\n", this,
434 // getName(), for_remove, closure_use, QoreTypeInfo::getName(typeInfo), QoreTypeInfo::getName(refTypeInfo));
435 if (!closure_use) {
436 return get_var()->getLValue(lvh, for_remove, typeInfo, refTypeInfo);
437 }
438
439 return thread_find_closure_var(name.c_str())->getLValue(lvh, for_remove);
440 }
441
442 DLLLOCAL void remove(LValueRemoveHelper& lvrh) {
443 if (!closure_use) {
444 return get_var()->remove(lvrh, typeInfo);
445 }
446
447 return thread_find_closure_var(name.c_str())->remove(lvrh);
448 }
449
450 DLLLOCAL const QoreTypeInfo* getTypeInfo() const {
451 return typeInfo;
452 }
453
454 DLLLOCAL const QoreTypeInfo* parseGetTypeInfo() const {
455 return parse_assigned && refTypeInfo ? refTypeInfo : typeInfo;
456 }
457
458 DLLLOCAL const QoreTypeInfo* parseGetTypeInfoForInitialAssignment() const {
459 return typeInfo;
460 }
461
462 DLLLOCAL qore_type_t getValueType() const {
463 return !closure_use ? get_var()->val.getType() : thread_find_closure_var(name.c_str())->val.getType();
464 }
465
466 DLLLOCAL const char* getValueTypeName() const {
467 return !closure_use ? get_var()->val.getTypeName() : thread_find_closure_var(name.c_str())->val.getTypeName();
468 }
469
470 DLLLOCAL bool isSelf() const {
471 return is_self;
472 }
473
474 DLLLOCAL void setSelf() {
475 assert(!is_self);
476 assert(name == "self");
477 is_self = true;
478 }
479
480private:
481 std::string name;
482 bool closure_use = false,
483 parse_assigned = false,
484 is_self = false;
485 const QoreTypeInfo* typeInfo = nullptr;
486 const QoreTypeInfo* refTypeInfo = nullptr;
487
488 DLLLOCAL LocalVarValue* get_var() const {
489 return thread_find_lvar(name.c_str());
490 }
491};
492
493typedef LocalVar* lvar_ptr_t;
494
495#endif
static void discard(AbstractQoreNode *n, ExceptionSink *xsink)
to deref an AbstractQoreNode (when the pointer may be 0)
Definition: QoreLib.h:327
#define PO_STRICT_TYPES
enforce strict type checking and setting default values
Definition: Restrictions.h:98
The base class for all value and parse types in Qore expression trees.
Definition: AbstractQoreNode.h:57
DLLEXPORT AbstractQoreNode * refSelf() const
returns "this" with an incremented reference count
container for holding Qore-language exception information and also for registering a "thread_exit" ca...
Definition: ExceptionSink.h:50
DLLEXPORT AbstractQoreNode * raiseException(const char *err, const char *fmt,...)
appends a Qore-language exception to the list
the implementation of Qore's object data type, reference counted, dynamically-allocated only
Definition: QoreObject.h:60
a templated class to manage a reference count of an object that can throw a Qore-language exception w...
Definition: ReferenceHolder.h:52
parse type: reference to a lvalue expression
Definition: ReferenceNode.h:45
DLLEXPORT ReferenceNode * refRefSelf() const
returns a reference to itself
evaluates an AbstractQoreNode and dereferences the stored value in the destructor
Definition: QoreValue.h:624
int16_t qore_type_t
used to identify unique Qore data and parse types (descendents of AbstractQoreNode)
Definition: common.h:70
#define QV_Node
for heap-allocated values
Definition: QoreValue.h:46
#define QV_Bool
for boolean values
Definition: QoreValue.h:43
const qore_type_t NT_WEAKREF
type value for WeakReferenceNode
Definition: node_types.h:87
const qore_type_t NT_REFERENCE
type value for ReferenceNode
Definition: node_types.h:64
DLLEXPORT QoreProgram * getProgram()
returns the current QoreProgram
The main value class in Qore, designed to be passed by value.
Definition: QoreValue.h:276