Qore Programming Language  1.12.0
LocalVar.h
1 /* -*- mode: c++; indent-tabs-mode: nil -*- */
2 /*
3  LocalVar.h
4 
5  Qore Programming Language
6 
7  Copyright (C) 2003 - 2022 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 
45 template <class T>
46 class LocalRefHelper : public RuntimeReferenceHelper {
47 protected:
48  // used to skip the var entry in case it's a recursive reference
49  bool valid;
50 
51 public:
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 
62 template <class T>
63 class LValueRefHelper : public LocalRefHelper<T> {
64 protected:
65  LValueHelper* valp;
66 
67 public:
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 
84 class VarValueBase {
85 protected:
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 
94 public:
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 
132 class LocalVarValue : public VarValueBase {
133 public:
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 
203 hashdecl ClosureVarValue : public VarValueBase, public RObject {
204 public:
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) {
261  ReferenceHolder<ReferenceNode> ref(val.get<ReferenceNode>()->refRefSelf(), xsink);
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) {
278  ReferenceHolder<ReferenceNode> ref(val.get<ReferenceNode>()->refRefSelf(), xsink);
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
305 class LocalVar {
306 public:
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), typeInfo(old.typeInfo), refTypeInfo(old.refTypeInfo) {
313  }
314 
315  DLLLOCAL ~LocalVar() {
316  }
317 
318  DLLLOCAL void parseAssigned() {
319  if (!parse_assigned)
320  parse_assigned = true;
321  }
322 
323  DLLLOCAL void parseUnassigned() {
324  if (parse_assigned)
325  parse_assigned = false;
326  }
327 
328  DLLLOCAL void instantiate() {
329  if (getProgram()->getParseOptions64() & PO_STRICT_TYPES) {
330  //printd(5, "LocalVar::instantiate() this: %p '%s' typeInfo: %s\n", this, name.c_str(), QoreTypeInfo::getName(typeInfo));
331  instantiateIntern(QoreTypeInfo::getDefaultQoreValue(typeInfo), true);
332  } else {
333  //printd(5, "LocalVar::instantiate() this: %p '%s' typeInfo: %s NO ASSIGNMENT\n", this, name.c_str(), QoreTypeInfo::getName(typeInfo));
334  instantiateIntern(QoreValue(), false);
335  }
336  }
337 
338  DLLLOCAL void instantiate(QoreValue nval) {
339  instantiateIntern(nval, true);
340  }
341 
342  DLLLOCAL void instantiateIntern(QoreValue nval, bool assign) {
343  //printd(5, "LocalVar::instantiateIntern(%s, %d) this: %p '%s' value closure_use: %s pgm: %p val: %s type: '%s' rti: '%s'\n", nval.getTypeName(), assign, this, name.c_str(), closure_use ? "true" : "false", getProgram(), nval.getTypeName(), QoreTypeInfo::getName(typeInfo), QoreTypeInfo::getName(refTypeInfo));
344 
345  if (!closure_use) {
346  LocalVarValue* val = thread_instantiate_lvar();
347  val->set(name.c_str(), typeInfo, nval, assign, false);
348  } else
349  thread_instantiate_closure_var(name.c_str(), typeInfo, nval, assign);
350  }
351 
352  DLLLOCAL void instantiateSelf(QoreObject* value) const {
353  printd(5, "LocalVar::instantiateSelf(%p) this: %p '%s'\n", value, this, name.c_str());
354  if (!closure_use) {
355  LocalVarValue* val = thread_instantiate_lvar();
356  val->set(name.c_str(), typeInfo, value, true, true);
357  } else {
358  QoreValue val(value->refSelf());
359  thread_instantiate_closure_var(name.c_str(), typeInfo, val, true);
360  }
361  }
362 
363  DLLLOCAL void uninstantiate(ExceptionSink* xsink) const {
364  //printd(5, "LocalVar::uninstantiate() this: %p '%s' closure_use: %s pgm: %p\n", this, name.c_str(), closure_use ? "true" : "false", getProgram());
365 
366  if (!closure_use)
367  thread_uninstantiate_lvar(xsink);
368  else
369  thread_uninstantiate_closure_var(xsink);
370  }
371 
372  DLLLOCAL void uninstantiateSelf() const {
373  if (!closure_use)
374  thread_uninstantiate_self();
375  else // cannot go out of scope here, so no destructor can be run, so we pass a NULL ExceptionSink ptr
376  thread_uninstantiate_closure_var(0);
377  }
378 
379  DLLLOCAL QoreValue eval(bool& needs_deref, ExceptionSink* xsink) const {
380  if (!closure_use) {
381  LocalVarValue* val = get_var();
382  //printd(5, "LocalVar::eval '%s' typeInfo: %p '%s'\n", name.c_str(), typeInfo, QoreTypeInfo::getName(typeInfo));
383  return val->eval(needs_deref, xsink);
384  }
385 
386  ClosureVarValue* val = thread_find_closure_var(name.c_str());
387  return val->eval(needs_deref, xsink);
388  }
389 
390  // returns true if the value could contain an object or a closure
391  DLLLOCAL bool needsScan() const {
392  return QoreTypeInfo::needsScan(typeInfo);
393  }
394 
395  DLLLOCAL const char* getName() const {
396  return name.c_str();
397  }
398 
399  DLLLOCAL const std::string& getNameStr() const {
400  return name;
401  }
402 
403  DLLLOCAL void setClosureUse() {
404  closure_use = true;
405  }
406 
407  DLLLOCAL bool closureUse() const {
408  return closure_use;
409  }
410 
411  DLLLOCAL bool isRef() const {
412  return !closure_use ? get_var()->isRef() : thread_find_closure_var(name.c_str())->isRef();
413  }
414 
415  DLLLOCAL int getLValue(LValueHelper& lvh, bool for_remove, bool initial_assignment) const {
416  //printd(5, "LocalVar::getLValue() this: %p '%s' for_remove: %d closure_use: %d ti: '%s' rti: '%s'\n", this,
417  // getName(), for_remove, closure_use, QoreTypeInfo::getName(typeInfo), QoreTypeInfo::getName(refTypeInfo));
418  if (!closure_use) {
419  return get_var()->getLValue(lvh, for_remove, typeInfo, refTypeInfo);
420  }
421 
422  return thread_find_closure_var(name.c_str())->getLValue(lvh, for_remove);
423  }
424 
425  DLLLOCAL void remove(LValueRemoveHelper& lvrh) {
426  if (!closure_use)
427  return get_var()->remove(lvrh, typeInfo);
428 
429  return thread_find_closure_var(name.c_str())->remove(lvrh);
430  }
431 
432  DLLLOCAL const QoreTypeInfo* getTypeInfo() const {
433  return typeInfo;
434  }
435 
436  DLLLOCAL const QoreTypeInfo* parseGetTypeInfo() const {
437  return parse_assigned && refTypeInfo ? refTypeInfo : typeInfo;
438  }
439 
440  DLLLOCAL const QoreTypeInfo* parseGetTypeInfoForInitialAssignment() const {
441  return typeInfo;
442  }
443 
444  DLLLOCAL qore_type_t getValueType() const {
445  return !closure_use ? get_var()->val.getType() : thread_find_closure_var(name.c_str())->val.getType();
446  }
447 
448  DLLLOCAL const char* getValueTypeName() const {
449  return !closure_use ? get_var()->val.getTypeName() : thread_find_closure_var(name.c_str())->val.getTypeName();
450  }
451 
452 private:
453  std::string name;
454  bool closure_use = false,
455  parse_assigned = false;
456  const QoreTypeInfo* typeInfo = nullptr;
457  const QoreTypeInfo* refTypeInfo = nullptr;
458 
459  DLLLOCAL LocalVarValue* get_var() const {
460  return thread_find_lvar(name.c_str());
461  }
462 };
463 
464 typedef LocalVar* lvar_ptr_t;
465 
466 #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:48
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:613
int16_t qore_type_t
used to identify unique Qore data and parse types (descendents of AbstractQoreNode)
Definition: common.h:70
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:275