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