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