Qore Programming Language  0.9.16
LocalVar.h
1 /* -*- mode: c++; indent-tabs-mode: nil -*- */
2 /*
3  LocalVar.h
4 
5  Qore Programming Language
6 
7  Copyright (C) 2003 - 2020 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 private:
307  std::string name;
308  bool closure_use = false,
309  parse_assigned = false;
310  const QoreTypeInfo* typeInfo;
311  const QoreTypeInfo* refTypeInfo;
312 
313  DLLLOCAL LocalVarValue* get_var() const {
314  return thread_find_lvar(name.c_str());
315  }
316 
317 public:
318  DLLLOCAL LocalVar(const char* n_name, const QoreTypeInfo* ti) : name(n_name), typeInfo(ti), refTypeInfo(QoreTypeInfo::getReferenceTarget(ti)) {
319  }
320 
321  DLLLOCAL LocalVar(const LocalVar& old) : name(old.name), closure_use(old.closure_use), parse_assigned(old.parse_assigned), typeInfo(old.typeInfo), refTypeInfo(old.refTypeInfo) {
322  }
323 
324  DLLLOCAL ~LocalVar() {
325  }
326 
327  DLLLOCAL void parseAssigned() {
328  if (!parse_assigned)
329  parse_assigned = true;
330  }
331 
332  DLLLOCAL void parseUnassigned() {
333  if (parse_assigned)
334  parse_assigned = false;
335  }
336 
337  DLLLOCAL void instantiate() {
338  if (getProgram()->getParseOptions64() & PO_STRICT_TYPES) {
339  //printd(5, "LocalVar::instantiate() this: %p '%s' typeInfo: %s\n", this, name.c_str(), QoreTypeInfo::getName(typeInfo));
340  instantiateIntern(QoreTypeInfo::getDefaultQoreValue(typeInfo), true);
341  } else {
342  //printd(5, "LocalVar::instantiate() this: %p '%s' typeInfo: %s NO ASSIGNMENT\n", this, name.c_str(), 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 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));
353 
354  if (!closure_use) {
355  LocalVarValue* val = thread_instantiate_lvar();
356  val->set(name.c_str(), typeInfo, nval, assign, false);
357  } else
358  thread_instantiate_closure_var(name.c_str(), typeInfo, nval, assign);
359  }
360 
361  DLLLOCAL void instantiateSelf(QoreObject* value) const {
362  printd(5, "LocalVar::instantiateSelf(%p) this: %p '%s'\n", value, this, name.c_str());
363  if (!closure_use) {
364  LocalVarValue* val = thread_instantiate_lvar();
365  val->set(name.c_str(), typeInfo, value, true, true);
366  } else {
367  QoreValue val(value->refSelf());
368  thread_instantiate_closure_var(name.c_str(), typeInfo, val, true);
369  }
370  }
371 
372  DLLLOCAL void uninstantiate(ExceptionSink* xsink) const {
373  //printd(5, "LocalVar::uninstantiate() this: %p '%s' closure_use: %s pgm: %p\n", this, name.c_str(), closure_use ? "true" : "false", getProgram());
374 
375  if (!closure_use)
376  thread_uninstantiate_lvar(xsink);
377  else
378  thread_uninstantiate_closure_var(xsink);
379  }
380 
381  DLLLOCAL void uninstantiateSelf() const {
382  if (!closure_use)
383  thread_uninstantiate_self();
384  else // cannot go out of scope here, so no destructor can be run, so we pass a NULL ExceptionSink ptr
385  thread_uninstantiate_closure_var(0);
386  }
387 
388  DLLLOCAL QoreValue eval(bool& needs_deref, ExceptionSink* xsink) const {
389  if (!closure_use) {
390  LocalVarValue* val = get_var();
391  //printd(5, "LocalVar::eval '%s' typeInfo: %p '%s'\n", name.c_str(), typeInfo, QoreTypeInfo::getName(typeInfo));
392  return val->eval(needs_deref, xsink);
393  }
394 
395  ClosureVarValue* val = thread_find_closure_var(name.c_str());
396  return val->eval(needs_deref, xsink);
397  }
398 
399  // returns true if the value could contain an object or a closure
400  DLLLOCAL bool needsScan() const {
401  return QoreTypeInfo::needsScan(typeInfo);
402  }
403 
404  DLLLOCAL const char* getName() const {
405  return name.c_str();
406  }
407 
408  DLLLOCAL const std::string& getNameStr() const {
409  return name;
410  }
411 
412  DLLLOCAL void setClosureUse() {
413  closure_use = true;
414  }
415 
416  DLLLOCAL bool closureUse() const {
417  return closure_use;
418  }
419 
420  DLLLOCAL bool isRef() const {
421  return !closure_use ? get_var()->isRef() : thread_find_closure_var(name.c_str())->isRef();
422  }
423 
424  DLLLOCAL int getLValue(LValueHelper& lvh, bool for_remove, bool initial_assignment) const {
425  //printd(5, "LocalVar::getLValue() this: %p '%s' for_remove: %d closure_use: %d ti: '%s' rti: '%s'\n", this, getName(), for_remove, closure_use, QoreTypeInfo::getName(typeInfo), QoreTypeInfo::getName(refTypeInfo));
426  if (!closure_use) {
427  return get_var()->getLValue(lvh, for_remove, typeInfo, refTypeInfo);
428  }
429 
430  return thread_find_closure_var(name.c_str())->getLValue(lvh, for_remove);
431  }
432 
433  DLLLOCAL void remove(LValueRemoveHelper& lvrh) {
434  if (!closure_use)
435  return get_var()->remove(lvrh, typeInfo);
436 
437  return thread_find_closure_var(name.c_str())->remove(lvrh);
438  }
439 
440  DLLLOCAL const QoreTypeInfo* getTypeInfo() const {
441  return typeInfo;
442  }
443 
444  DLLLOCAL const QoreTypeInfo* parseGetTypeInfo() const {
445  return parse_assigned && refTypeInfo ? refTypeInfo : typeInfo;
446  }
447 
448  DLLLOCAL const QoreTypeInfo* parseGetTypeInfoForInitialAssignment() const {
449  return typeInfo;
450  }
451 
452  DLLLOCAL qore_type_t getValueType() const {
453  return !closure_use ? get_var()->val.getType() : thread_find_closure_var(name.c_str())->val.getType();
454  }
455 
456  DLLLOCAL const char* getValueTypeName() const {
457  return !closure_use ? get_var()->val.getTypeName() : thread_find_closure_var(name.c_str())->val.getTypeName();
458  }
459 };
460 
461 typedef LocalVar* lvar_ptr_t;
462 
463 #endif
ValueEvalRefHolder
evaluates an AbstractQoreNode and dereferences the stored value in the destructor
Definition: QoreValue.h:593
QoreValue
The main value class in Qore, designed to be passed by value.
Definition: QoreValue.h:262
AbstractQoreNode::refSelf
DLLEXPORT AbstractQoreNode * refSelf() const
returns "this" with an incremented reference count
ReferenceHolder
a templated class to manage a reference count of an object that can throw a Qore-language exception w...
Definition: ReferenceHolder.h:52
PO_STRICT_TYPES
#define PO_STRICT_TYPES
enforce strict type checking and setting default values
Definition: Restrictions.h:98
ExceptionSink::raiseException
DLLEXPORT AbstractQoreNode * raiseException(const char *err, const char *fmt,...)
appends a Qore-language exception to the list
ReferenceNode
parse type: reference to a lvalue expression
Definition: ReferenceNode.h:45
QoreObject
the implementation of Qore's object data type, reference counted, dynamically-allocated only
Definition: QoreObject.h:61
NT_REFERENCE
const qore_type_t NT_REFERENCE
type value for ReferenceNode
Definition: node_types.h:64
ExceptionSink
container for holding Qore-language exception information and also for registering a "thread_exit" ca...
Definition: ExceptionSink.h:48
NT_WEAKREF
const qore_type_t NT_WEAKREF
type value for WeakReferenceNode
Definition: node_types.h:87
ReferenceNode::refRefSelf
DLLEXPORT ReferenceNode * refRefSelf() const
returns a reference to itself
getProgram
DLLEXPORT QoreProgram * getProgram()
returns the current QoreProgram
AbstractQoreNode
The base class for all value and parse types in Qore expression trees.
Definition: AbstractQoreNode.h:54
qore_type_t
int16_t qore_type_t
used to identify unique Qore data and parse types (descendents of AbstractQoreNode)
Definition: common.h:70
discard
static void discard(AbstractQoreNode *n, ExceptionSink *xsink)
to deref an AbstractQoreNode (when the pointer may be 0)
Definition: QoreLib.h:324