Qore Programming Language  1.12.1
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 bool isAssigned() const {
329  return parse_assigned;
330  }
331 
332  DLLLOCAL void instantiate() {
333  if (getProgram()->getParseOptions64() & PO_STRICT_TYPES) {
334  //printd(5, "LocalVar::instantiate() this: %p '%s' typeInfo: %s\n", this, name.c_str(), QoreTypeInfo::getName(typeInfo));
335  instantiateIntern(QoreTypeInfo::getDefaultQoreValue(typeInfo), true);
336  } else {
337  //printd(5, "LocalVar::instantiate() this: %p '%s' typeInfo: %s NO ASSIGNMENT\n", this, name.c_str(), QoreTypeInfo::getName(typeInfo));
338  instantiateIntern(QoreValue(), false);
339  }
340  }
341 
342  DLLLOCAL void instantiate(QoreValue nval) {
343  instantiateIntern(nval, true);
344  }
345 
346  DLLLOCAL void instantiateIntern(QoreValue nval, bool assign) {
347  //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));
348 
349  if (!closure_use) {
350  LocalVarValue* val = thread_instantiate_lvar();
351  val->set(name.c_str(), typeInfo, nval, assign, false);
352  } else
353  thread_instantiate_closure_var(name.c_str(), typeInfo, nval, assign);
354  }
355 
356  DLLLOCAL void instantiateSelf(QoreObject* value) const {
357  printd(5, "LocalVar::instantiateSelf(%p) this: %p '%s'\n", value, this, name.c_str());
358  if (!closure_use) {
359  LocalVarValue* val = thread_instantiate_lvar();
360  val->set(name.c_str(), typeInfo, value, true, true);
361  } else {
362  QoreValue val(value->refSelf());
363  thread_instantiate_closure_var(name.c_str(), typeInfo, val, true);
364  }
365  }
366 
367  DLLLOCAL void uninstantiate(ExceptionSink* xsink) const {
368  //printd(5, "LocalVar::uninstantiate() this: %p '%s' closure_use: %s pgm: %p\n", this, name.c_str(), closure_use ? "true" : "false", getProgram());
369 
370  if (!closure_use)
371  thread_uninstantiate_lvar(xsink);
372  else
373  thread_uninstantiate_closure_var(xsink);
374  }
375 
376  DLLLOCAL void uninstantiateSelf() const {
377  if (!closure_use)
378  thread_uninstantiate_self();
379  else // cannot go out of scope here, so no destructor can be run, so we pass a NULL ExceptionSink ptr
380  thread_uninstantiate_closure_var(0);
381  }
382 
383  DLLLOCAL QoreValue eval(bool& needs_deref, ExceptionSink* xsink) const {
384  if (!closure_use) {
385  LocalVarValue* val = get_var();
386  //printd(5, "LocalVar::eval '%s' typeInfo: %p '%s'\n", name.c_str(), typeInfo, QoreTypeInfo::getName(typeInfo));
387  return val->eval(needs_deref, xsink);
388  }
389 
390  ClosureVarValue* val = thread_find_closure_var(name.c_str());
391  return val->eval(needs_deref, xsink);
392  }
393 
394  // returns true if the value could contain an object or a closure
395  DLLLOCAL bool needsScan() const {
396  return QoreTypeInfo::needsScan(typeInfo);
397  }
398 
399  DLLLOCAL const char* getName() const {
400  return name.c_str();
401  }
402 
403  DLLLOCAL const std::string& getNameStr() const {
404  return name;
405  }
406 
407  DLLLOCAL void setClosureUse() {
408  closure_use = true;
409  }
410 
411  DLLLOCAL bool closureUse() const {
412  return closure_use;
413  }
414 
415  DLLLOCAL bool isRef() const {
416  return !closure_use ? get_var()->isRef() : thread_find_closure_var(name.c_str())->isRef();
417  }
418 
419  DLLLOCAL int getLValue(LValueHelper& lvh, bool for_remove, bool initial_assignment) const {
420  //printd(5, "LocalVar::getLValue() this: %p '%s' for_remove: %d closure_use: %d ti: '%s' rti: '%s'\n", this,
421  // getName(), for_remove, closure_use, QoreTypeInfo::getName(typeInfo), QoreTypeInfo::getName(refTypeInfo));
422  if (!closure_use) {
423  return get_var()->getLValue(lvh, for_remove, typeInfo, refTypeInfo);
424  }
425 
426  return thread_find_closure_var(name.c_str())->getLValue(lvh, for_remove);
427  }
428 
429  DLLLOCAL void remove(LValueRemoveHelper& lvrh) {
430  if (!closure_use)
431  return get_var()->remove(lvrh, typeInfo);
432 
433  return thread_find_closure_var(name.c_str())->remove(lvrh);
434  }
435 
436  DLLLOCAL const QoreTypeInfo* getTypeInfo() const {
437  return typeInfo;
438  }
439 
440  DLLLOCAL const QoreTypeInfo* parseGetTypeInfo() const {
441  return parse_assigned && refTypeInfo ? refTypeInfo : typeInfo;
442  }
443 
444  DLLLOCAL const QoreTypeInfo* parseGetTypeInfoForInitialAssignment() const {
445  return typeInfo;
446  }
447 
448  DLLLOCAL qore_type_t getValueType() const {
449  return !closure_use ? get_var()->val.getType() : thread_find_closure_var(name.c_str())->val.getType();
450  }
451 
452  DLLLOCAL const char* getValueTypeName() const {
453  return !closure_use ? get_var()->val.getTypeName() : thread_find_closure_var(name.c_str())->val.getTypeName();
454  }
455 
456 private:
457  std::string name;
458  bool closure_use = false,
459  parse_assigned = false;
460  const QoreTypeInfo* typeInfo = nullptr;
461  const QoreTypeInfo* refTypeInfo = nullptr;
462 
463  DLLLOCAL LocalVarValue* get_var() const {
464  return thread_find_lvar(name.c_str());
465  }
466 };
467 
468 typedef LocalVar* lvar_ptr_t;
469 
470 #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