OpenClonk
C4PropList.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2004, Peter Wortmann
5  * Copyright (c) 2007, Günther Brammer
6  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
7  *
8  * Distributed under the terms of the ISC license; see accompanying file
9  * "COPYING" for details.
10  *
11  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12  * See accompanying file "TRADEMARK" for details.
13  *
14  * To redistribute this file separately, substitute the full license texts
15  * for the above references.
16  */
17 
18 #include "C4Include.h"
19 #include "script/C4PropList.h"
20 
21 #include "control/C4Record.h"
22 #include "object/C4GameObjects.h"
23 #include "script/C4Aul.h"
24 
25 void C4PropList::AddRef(C4Value *pRef)
26 {
27  assert(Refs.count(pRef) == 0);
28  Refs.insert(pRef);
29 }
30 
31 void C4PropList::DelRef(C4Value * pRef)
32 {
33  auto erased = Refs.erase(pRef);
34  assert(erased == 1);
35  // Only pure script proplists are garbage collected here, host proplists
36  // like definitions and effects have their own memory management.
37  if (Refs.empty() && Delete()) delete this;
38 }
39 
41 {
42  C4PropList * r = new C4PropListScript(prototype);
43  return r;
44 }
45 
47 {
48  return new C4PropListStatic(prototype, parent, key);
49 }
50 
52 {
53  return PropLists.Get(iNumber);
54 }
55 
57 {
58  if (!pObj) return false;
59  C4PropListNumbered * const * p = PropLists.First();
60  while (p)
61  {
62  if (*p == pObj)
63  return true;
64  p = PropLists.Next(p);
65  }
66  return false;
67 }
68 
69 void C4PropListNumbered::SetEnumerationIndex(int32_t iMaxObjectNumber)
70 {
71  // update object enumeration index now, because calls like OnSynchronized might create objects
72  EnumerationIndex = std::max(EnumerationIndex, iMaxObjectNumber);
73 }
74 
76 {
77  assert(!PropLists.GetSize());
78  EnumerationIndex = 0;
79 }
80 
82 {
83  // unnumber all proplists and put them on the shelve. To be used on remaining objects before a savegame load.
84  assert(ShelvedPropLists.empty());
86  C4PropListNumbered *const* p_next = PropLists.First(), *const* p;
87  while ((p = p_next))
88  {
89  p_next = PropLists.Next(p);
90  C4PropListNumbered *pl = *p;
91  if (pl->Number != -1)
92  {
93  pl->ClearNumber();
94  ShelvedPropLists.push_back(pl);
95  }
96  }
97 }
98 
100 {
101  // re-insert shelved proplists into main list and give them a number
102  for (auto & ShelvedPropList : ShelvedPropLists)
103  ShelvedPropList->AcquireNumber();
104  ShelvedPropLists.clear();
105 }
106 
108 {
109  // cleanup shelve - used in game clear, un unsuccessful section load, etc.
110  ShelvedPropLists.clear();
111 }
112 
114 {
115  // empty all proplists to ensure safe deletion of proplists with circular references
116  // note that this the call to Clear() might delete some prop lists. So it is assumed that
117  // PropLists does not shrink its table as the number of values goes down
118  C4PropListNumbered *const* p_next = PropLists.First(), *const* p;
119  while ((p = p_next))
120  {
121  p_next = PropLists.Next(p);
122  // check *p since it might have been deleted by clearing the previous prop list
123  if (*p) (*p)->Clear();
124  }
125 }
126 
128 {
129 }
130 
132 {
133  // Enumerate object
134  do
136  while (PropLists.Get(Number));
137  // Add to list
138  PropLists.Add(this);
139 }
140 
142 {
143  // Make proplist invisible during denumeration process
144  if (Number != -1)
145  {
146  PropLists.Remove(this);
147  Number = -1;
148  }
149 }
150 
152 {
153  return this;
154 }
155 
157 {
158  int32_t n = Number;
159  pComp->Value(n);
161  C4PropList::CompileFunc(pComp, numbers);
162  if (pComp->isDeserializer())
163  {
164  if (PropLists.Get(n))
165  {
166  pComp->excCorrupt("multiple PropLists with Number %d", n);
167  return;
168  }
169  // Once this proplist has a Number, it has to be in PropLists. See the destructor below.
170  Number = n;
171  PropLists.Add(this);
172  }
173 }
174 
176 {
177  if (Number != -1)
178  PropLists.Remove(this);
179  else
180  Log("removing numbered proplist without number");
181 }
182 
184 {
185  // empty all proplists to ensure safe deletion of proplists with circular references
186  // note that this the call to Clear() might delete some prop lists. So it is assumed that
187  // PropLists does not shrink its table as the number of values goes down
188  // However, some values may be skipped due to table consolidation. Just fix it by iterating over the table until it's empty.
189  C4PropListScript *const* p_next, *const* p;
190  while ((p_next = PropLists.First()))
191  {
192  size_t prev_size = PropLists.GetSize();
193  while ((p = p_next))
194  {
195  p_next = PropLists.Next(p);
196  // check *p since it might have been deleted by clearing the previous prop list
197  if (*p)
198  {
199  C4Value ref(C4VPropList(*p)); // keep a reference because prop list might delete itself within clearing method otherwise
200  (*p)->Clear();
201  }
202  }
203  if (PropLists.GetSize() >= prev_size)
204  {
205  // Looks like there's a rogue C4Value pointer somewhere.
206  // Could just delete the prop list and let ref counting do the job
207  // However, it might be better to keep the dead pointer to find the leak in debug mode
208 #ifdef _DEBUG
209  assert(0);
210 #endif
211  break;
212  }
213  }
214 }
215 
217 {
218  assert(!pComp->isDeserializer());
219  if (Parent)
220  {
221  Parent->RefCompileFunc(pComp, numbers);
223  }
224  if (!ParentKeyName)
225  pComp->excCorrupt("C4PropListStatic::RefCompileFunc without ParentKeyName");
227 }
228 
230 {
231  StdStrBuf r;
232  if (Parent)
233  {
234  r.Take(Parent->GetDataString());
235  r.AppendChar('.');
236  }
237  assert(ParentKeyName);
238  if (ParentKeyName)
240  return r;
241 }
242 
243 const char *C4PropListStatic::GetName() const
244 {
245  const C4String * s = GetPropertyStr(P_Name);
246  if (!s) s = ParentKeyName;
247  if (!s) return "";
248  return s->GetCStr();
249 }
250 
252  prototype(prototype)
253 {
254 #ifdef _DEBUG
255  PropLists.Add(this);
256 #endif
257 }
258 
260 {
261  //thaw self and all owned properties
262  Thaw();
263  //C4PropListStatic *s = IsStatic();
264  //if (s) LogF("Thaw: %s", s->GetDataString().getData());
265  auto prop_names = GetUnsortedProperties(nullptr, ::ScriptEngine.GetPropList());
266  for (auto prop_name : prop_names)
267  {
268  C4Value child_val;
269  GetPropertyByS(prop_name, &child_val);
270  //LogF(" %s=%s", prop_name->GetCStr(), child_val.GetDataString(1).getData());
271  C4PropList *child_proplist = child_val.getPropList();
272  if (child_proplist && child_proplist->IsFrozen())
273  {
274  child_proplist->ThawRecursively();
275  }
276  }
277 }
278 
279 C4PropListStatic *C4PropList::FreezeAndMakeStaticRecursively(std::vector<C4Value>* prop_lists, const C4PropListStatic *parent, C4String * key)
280 {
281  Freeze();
282  // Already static?
283  C4PropListStatic *this_static = IsStatic();
284  if (!this_static)
285  {
286  // Make self static by creating a copy and replacing all references
287  this_static = NewStatic(GetPrototype(), parent, key);
288  this_static->Properties.Swap(&Properties); // grab properties
289  this_static->Status = Status;
290  RefSet pre_freeze_refs{Refs}; // copy to avoid iterator validity headaches
291  C4Value holder = C4VPropList(this); // add another reference to prevent premature deletion
292  for (C4Value * ref : pre_freeze_refs)
293  ref->SetPropList(this_static);
294  // store reference
295  if (prop_lists)
296  prop_lists->push_back(C4VPropList(this_static));
297  // "this" should be deleted as holder goes out of scope
298  }
299  // Iterate over sorted list of elements to make static
300  // Must iterate over sorted list because the order must be defined, just in case it's a network game
301  // and a non-static child proplist is available through different paths it should still get the same name
302  auto prop_names = this_static->GetSortedLocalProperties(false);
303  for (auto prop_name : prop_names)
304  {
305  C4Value child_val;
306  this_static->GetPropertyByS(prop_name, &child_val);
307  C4PropList *child_proplist = child_val.getPropList();
308  if (child_proplist)
309  {
310  // Avoid infinite recursion: Only freeze into unfrozen children and "true" static children
311  C4PropListStatic *child_static = child_proplist->IsStatic();
312  if (!child_static || (child_static->GetParent() == this_static && child_static->GetParentKeyName() == prop_name))
313  {
314  child_proplist->FreezeAndMakeStaticRecursively(prop_lists, this_static, prop_name);
315  }
316  }
317  }
318  return this_static;
319 }
320 
322 {
323  const C4Property * p = Properties.First();
324  while (p)
325  {
326  const_cast<C4Value &>(p->Value).Denumerate(numbers);
327  p = Properties.Next(p);
328  }
329  prototype.Denumerate(numbers);
331 }
332 
334 {
335  for (C4Value * Ref : Refs)
336  {
337  // Manually kill references so DelRef doesn't destroy us again
338  Ref->Data = nullptr; Ref->Type = C4V_Nil;
339  }
340  Refs.clear();
341 #ifdef _DEBUG
342  assert(PropLists.Has(this));
343  PropLists.Remove(this);
344 #endif
345  assert(!C4PropListNumbered::CheckPropList(this));
346 }
347 
349 {
350  // every numbered proplist has a unique number and is only identical to itself
351  if (this == &b) return true;
352  if (IsNumbered() || b.IsNumbered()) return false;
353  if (Properties.GetSize() != b.Properties.GetSize()) return false;
354  if (GetDef() != b.GetDef()) return false;
355  const C4Property * p = Properties.First();
356  while (p)
357  {
358  const C4Property & bp = b.Properties.Get(p->Key);
359  if (!bp) return false;
360  if (p->Value != bp.Value) return false;
361  p = Properties.Next(p);
362  }
363  return true;
364 }
365 
367 {
368  bool oldFormat = false;
369  // constant proplists are not serialized to savegames, but recreated from the game data instead
370  assert(!constant);
371  if (pComp->isDeserializer() && pComp->hasNaming())
372  {
373  // backwards compat to savegames and scenarios before 5.5
374  try
375  {
376  pComp->Value(constant);
377  oldFormat = true;
378  }
379  catch (StdCompiler::NotFoundException *pEx)
380  {
381  delete pEx;
382  pComp->Value(mkParAdapt(prototype, numbers));
383  }
384  }
385  else
386  pComp->Value(mkParAdapt(prototype, numbers));
388  pComp->Value(mkParAdapt(Properties, numbers));
389  if (oldFormat)
390  {
391  if (Properties.Has(&::Strings.P[P_Prototype]))
392  {
393  prototype = Properties.Get(&::Strings.P[P_Prototype]).Value;
394  Properties.Remove(&::Strings.P[P_Prototype]);
395  }
396  }
397 }
398 
400 {
401  // clear any cyclic prototype chains
402  // Use prototype.getPropList() instead of GetPrototype() because denumeration might not be completed yet
403  for(C4PropList * it = prototype.getPropList(); it; it = it->prototype.getPropList())
404  if(it == this)
405  {
406  prototype.Set0();
407  }
408 }
409 
410 void CompileNewFunc(C4PropList *&pStruct, StdCompiler *pComp, C4ValueNumbers *rPar)
411 {
412  std::unique_ptr<C4PropList> temp(C4PropList::New()); // exception-safety
413  pComp->Value(mkParAdapt(*temp, rPar));
414  pStruct = temp.release();
415 }
416 
417 template<typename T>
419 {
420  bool fNaming = pComp->hasNaming();
421  if (pComp->isDeserializer())
422  {
423  // Compiling: Empty previous
424  Clear();
425  // Read size (binary only)
426  uint32_t iSize;
427  if (!pComp->hasNaming()) pComp->Value(iSize);
428  // Read new
429  do
430  {
431  // No entries left to read?
432  if (!fNaming && !iSize--)
433  break;
434  // Read entries
435  try
436  {
437  T e;
438  // This could use the same technique StdArrayAdapt uses
439  // instead of hardcoding mkParAdapt here
440  pComp->Value(mkParAdapt(e, numbers));
441  Add(e);
442  }
443  catch (StdCompiler::NotFoundException *pEx)
444  {
445  // No value found: Stop reading loop
446  delete pEx;
447  break;
448  }
449  }
450  while (pComp->Separator(StdCompiler::SEP_SEP));
451  }
452  else
453  {
454  // Write size (binary only)
455  if (!fNaming)
456  {
457  int32_t iSize = GetSize();
458  pComp->Value(iSize);
459  }
460  // Write all entries
461  const T * p = First();
462  while (p)
463  {
464  pComp->Value(mkParAdapt(*const_cast<T *>(p), numbers));
465  p = Next(p);
466  if (p) pComp->Separator(StdCompiler::SEP_SEP);
467  }
468  }
469 }
470 
472 {
473  StdStrBuf s;
474  if (!pComp->isDeserializer())
475  s = Key->GetData();
476  pComp->Value(s);
477  if (pComp->isDeserializer())
478  {
479  if (Key) Key->DecRef();
481  Key->IncRef();
482  }
484  pComp->Value(mkParAdapt(Value, numbers));
485 }
486 
487 void C4PropList::AppendDataString(StdStrBuf * out, const char * delim, int depth, bool ignore_reference_parent) const
488 {
489  StdStrBuf & DataString = *out;
490  if (depth <= 0 && Properties.GetSize())
491  {
492  DataString.Append("...");
493  return;
494  }
495  bool has_elements = false;
496  // Append prototype
497  if (prototype)
498  {
499  DataString.Append("Prototype = ");
500  DataString.Append(prototype.GetDataString(depth - 1, ignore_reference_parent ? IsStatic() : nullptr));
501  has_elements = true;
502  }
503  // Append other properties
504  std::list<const C4Property *> sorted_props = Properties.GetSortedListOfElementPointers();
505  for (std::list<const C4Property *>::const_iterator p = sorted_props.begin(); p != sorted_props.end(); ++p)
506  {
507  if (has_elements) DataString.Append(delim);
508  DataString.Append((*p)->Key->GetData());
509  DataString.Append(" = ");
510  DataString.Append((*p)->Value.GetDataString(depth - 1, ignore_reference_parent ? IsStatic() : nullptr));
511  has_elements = true;
512  }
513 }
514 
515 StdStrBuf C4PropList::ToJSON(int depth, bool ignore_reference_parent) const
516 {
517  if (depth <= 0 && Properties.GetSize())
518  {
519  throw new C4JSONSerializationError("maximum depth reached");
520  }
521  StdStrBuf DataString;
522  DataString = "{";
523  bool has_elements = false;
524  // Append prototype
525  if (prototype)
526  {
527  DataString.Append("Prototype:");
528  DataString.Append(prototype.ToJSON(depth - 1, ignore_reference_parent ? IsStatic() : nullptr));
529  has_elements = true;
530  }
531  // Append other properties
532  std::list<const C4Property *> sorted_props = Properties.GetSortedListOfElementPointers();
533  for (std::list<const C4Property *>::const_iterator p = sorted_props.begin(); p != sorted_props.end(); ++p)
534  {
535  if (has_elements) DataString.Append(",");
536  DataString.Append(C4Value((*p)->Key).ToJSON());
537  DataString.Append(":");
538  DataString.Append((*p)->Value.ToJSON(depth - 1, ignore_reference_parent ? IsStatic() : nullptr));
539  has_elements = true;
540  }
541  DataString.Append("}");
542  return DataString;
543 }
544 
545 std::vector< C4String * > C4PropList::GetSortedLocalProperties(bool add_prototype) const
546 {
547  // return property list without descending into prototype
548  std::list<const C4Property *> sorted_props = Properties.GetSortedListOfElementPointers();
549  std::vector< C4String * > result;
550  result.reserve(sorted_props.size() + add_prototype);
551  if (add_prototype) result.push_back(&::Strings.P[P_Prototype]); // implicit prototype for every prop list
552  for (auto p : sorted_props) result.push_back(p->Key);
553  return result;
554 }
555 
556 std::vector< C4String * > C4PropList::GetSortedLocalProperties(const char *prefix, const C4PropList *ignore_overridden) const
557 {
558  // return property list without descending into prototype
559  // ignore properties that have been overridden by proplist given in ignore_overridden or any of its prototypes up to and excluding this
560  std::vector< C4String * > result;
561  for (const C4Property *pp = Properties.First(); pp; pp = Properties.Next(pp))
562  if (pp->Key != &::Strings.P[P_Prototype])
563  if (!prefix || pp->Key->GetData().BeginsWith(prefix))
564  {
565  // Override check
566  const C4PropList *check = ignore_overridden;
567  bool overridden = false;
568  if (check && check != this)
569  {
570  if (check->HasProperty(pp->Key)) { overridden = true; break; }
571  check = check->GetPrototype();
572  }
573  result.push_back(pp->Key);
574  }
575  // Sort
576  std::sort(result.begin(), result.end(), [](const C4String *a, const C4String *b) -> bool
577  {
578  return strcmp(a->GetCStr(), b->GetCStr()) < 0;
579  });
580  return result;
581 }
582 
583 std::vector< C4String * > C4PropList::GetUnsortedProperties(const char *prefix, C4PropList *ignore_parent) const
584 {
585  // Return property list with descending into prototype
586  // But do not include Prototype property
587  std::vector< C4String * > result;
588  const C4PropList *p = this;
589  do
590  {
591  for (const C4Property *pp = p->Properties.First(); pp; pp = p->Properties.Next(pp))
592  if (pp->Key != &::Strings.P[P_Prototype])
593  if (!prefix || pp->Key->GetData().BeginsWith(prefix))
594  result.push_back(pp->Key);
595  p = p->GetPrototype();
596  if (p == ignore_parent) break;
597  } while (p);
598  return result;
599 }
600 
601 std::vector< C4String * > C4PropList::GetSortedProperties(const char *prefix, C4PropList *ignore_parent) const
602 {
603  struct sort_cmp {
604  bool operator() (const C4String *a, const C4String *b) const
605  {
606  return strcmp(a->GetCStr(), b->GetCStr()) < 0;
607  }
608  };
609  // Return property list with descending into prototype
610  // But do not include Prototype property
611  std::vector< C4String * > result = GetUnsortedProperties(prefix, ignore_parent);
612  // Sort and remove duplicates
613  std::set< C4String *, sort_cmp > result_set(result.begin(), result.end());
614  result.assign(result_set.begin(), result_set.end());
615  return result;
616 }
617 
618 const char * C4PropList::GetName() const
619 {
621  if (!s) return "";
622  return s->GetCStr();
623 }
624 
625 void C4PropList::SetName(const char* NewName)
626 {
627  if (!NewName)
629  else
630  {
631  SetProperty(P_Name, C4VString(NewName));
632  }
633 }
634 
635 
637 {
638  if (GetPrototype()) return GetPrototype()->GetObject();
639  return nullptr;
640 }
641 
643 {
644  if (GetPrototype()) return GetPrototype()->GetObject();
645  return nullptr;
646 }
647 
649 {
650  if (GetPrototype()) return GetPrototype()->GetDef();
651  return nullptr;
652 }
653 
654 C4Def const * C4PropList::GetDef() const
655 {
656  if (GetPrototype()) return GetPrototype()->GetDef();
657  return nullptr;
658 }
659 
661 {
662  if (GetPrototype()) return GetPrototype()->GetMapScriptLayer();
663  return nullptr;
664 }
665 
667 {
668  if (GetPrototype()) return GetPrototype()->GetMapScriptMap();
669  return nullptr;
670 }
671 
673 {
674  if (GetPrototype()) return GetPrototype()->GetPropListNumbered();
675  return nullptr;
676 }
677 
679 {
680  if (GetPrototype()) return GetPrototype()->GetEffect();
681  return nullptr;
682 }
683 
684 template<> template<>
685 unsigned int C4Set<C4Property>::Hash<const C4String *>(C4String const * const & e)
686 {
687  assert(e);
688  unsigned int hash = 4, tmp;
689  hash += ((uintptr_t)e) >> 16;
690  tmp = ((((uintptr_t)e) & 0xffff) << 11) ^ hash;
691  hash = (hash << 16) ^ tmp;
692  hash += hash >> 11;
693  hash ^= hash << 3;
694  hash += hash >> 5;
695  hash ^= hash << 4;
696  hash += hash >> 17;
697  hash ^= hash << 25;
698  hash += hash >> 6;
699  return hash;
700 }
701 
702 template<> template<>
703 unsigned int C4Set<C4Property>::Hash<C4String *>(C4String * const & e)
704 {
705  return Hash<const C4String *>(e);
706 }
707 
708 template<> template<>
710 {
711  return a.Key == b;
712 }
713 
714 template<> template<>
716 {
717  return a.Key == b;
718 }
719 
720 template<> template<>
721 unsigned int C4Set<C4Property>::Hash<C4Property>(C4Property const & p)
722 {
723  return C4Set<C4Property>::Hash(p.Key);
724 }
725 
726 bool C4PropList::GetPropertyByS(const C4String * k, C4Value *pResult) const
727 {
728  if (Properties.Has(k))
729  {
730  *pResult = Properties.Get(k).Value;
731  return true;
732  }
733  else if (k == &Strings.P[P_Prototype])
734  {
735  *pResult = prototype;
736  return true;
737  }
738  else if(GetPrototype())
739  return GetPrototype()->GetPropertyByS(k, pResult);
740  else
741  return false;
742 }
743 
745 {
746  C4String * k = &Strings.P[n];
747  if (Properties.Has(k))
748  {
749  return Properties.Get(k).Value.getStr();
750  }
751  if (GetPrototype())
752  {
753  return GetPrototype()->GetPropertyStr(n);
754  }
755  return nullptr;
756 }
757 
759 {
760  C4String * k = &Strings.P[n];
761  if (Properties.Has(k))
762  {
763  return Properties.Get(k).Value.getArray();
764  }
765  if (GetPrototype())
766  {
767  return GetPrototype()->GetPropertyArray(n);
768  }
769  return nullptr;
770 }
771 
773 {
774  assert(k);
775  if (Properties.Has(k))
776  {
777  return Properties.Get(k).Value.getFunction();
778  }
779  if (GetPrototype())
780  {
781  return GetPrototype()->GetFunc(k);
782  }
783  return nullptr;
784 }
785 
786 C4AulFunc * C4PropList::GetFunc(const char * s) const
787 {
788  assert(s);
789  if (s[0] == '~') ++s;
790  C4String * k = Strings.FindString(s);
791  // this string is entirely unused
792  if (!k)
793  return nullptr;
794  return GetFunc(k);
795 }
796 
797 C4Value C4PropList::Call(C4String * k, C4AulParSet *Pars, bool fPassErrors)
798 {
799  if (!Status) return C4Value();
800  C4AulFunc *pFn = GetFunc(k);
801  if (!pFn) return C4Value();
802  return pFn->Exec(this, Pars, fPassErrors);
803 }
804 
805 C4Value C4PropList::Call(const char * s, C4AulParSet *Pars, bool fPassErrors)
806 {
807  if (!Status) return C4Value();
808  assert(s && s[0]);
809  C4AulFunc *pFn = GetFunc(s);
810  if (!pFn)
811  {
812  if (s[0] != '~')
813  {
814  C4AulExecError err(FormatString("Undefined function: %s", s).getData());
815  if (fPassErrors)
816  throw err;
818  }
819  return C4Value();
820  }
821  return pFn->Exec(this, Pars, fPassErrors);
822 }
823 
825 {
826  C4String * k = &Strings.P[n];
827  if (Properties.Has(k))
828  {
829  C4String * v = Properties.Get(k).Value.getStr();
830  if (v >= &Strings.P[0] && v < &Strings.P[P_LAST])
831  return C4PropertyName(v - &Strings.P[0]);
832  return P_LAST;
833  }
834  if (GetPrototype())
835  {
836  return GetPrototype()->GetPropertyP(n);
837  }
838  return P_LAST;
839 }
840 
841 int32_t C4PropList::GetPropertyBool(C4PropertyName n, bool default_val) const
842 {
843  C4String * k = &Strings.P[n];
844  if (Properties.Has(k))
845  {
846  return Properties.Get(k).Value.getBool();
847  }
848  if (GetPrototype())
849  {
850  return GetPrototype()->GetPropertyBool(n, default_val);
851  }
852  return default_val;
853 }
854 
855 int32_t C4PropList::GetPropertyInt(C4PropertyName n, int32_t default_val) const
856 {
857  C4String * k = &Strings.P[n];
858  if (Properties.Has(k))
859  {
860  return Properties.Get(k).Value.getInt();
861  }
862  if (GetPrototype())
863  {
864  return GetPrototype()->GetPropertyInt(n, default_val);
865  }
866  return default_val;
867 }
868 
870 {
871  C4String * k = &Strings.P[n];
872  if (Properties.Has(k))
873  {
874  return Properties.Get(k).Value.getPropList();
875  }
876  if (GetPrototype())
877  {
878  return GetPrototype()->GetPropertyPropList(n);
879  }
880  return nullptr;
881 }
882 
884 {
885  C4ValueArray * a;
886  int i = 0;
887  const bool hasInheritedProperties = GetPrototype() != nullptr;
888  if (hasInheritedProperties)
889  {
891  i = a->GetSize();
892  a->SetSize(i + Properties.GetSize());
893  }
894  else
895  {
896  a = new C4ValueArray(Properties.GetSize());
897  i = 0;
898  }
899  const C4Property * p = Properties.First();
900  while (p)
901  {
902  C4String *newPropertyName = p->Key;
903  assert(newPropertyName != nullptr && "Proplist key is nullpointer");
904  // Do we need to check for duplicate property names?
905  bool skipProperty = false;
906  if (hasInheritedProperties)
907  {
908  for (size_t j = 0; j < i; ++j)
909  {
910  if ((*a)[j].getStr() != newPropertyName) continue;
911  skipProperty = true;
912  break;
913  }
914  }
915  if (!skipProperty)
916  {
917  (*a)[i++] = C4VString(newPropertyName);
918  assert(((*a)[i - 1].GetType() == C4V_String) && "Proplist key is non-string");
919  }
920  p = Properties.Next(p);
921  }
922  // We might have added less properties than initially intended.
923  if (hasInheritedProperties)
924  a->SetSize(i);
925  return a;
926 }
927 
929 {
930  const C4Property * p = prev ? Properties.Next(&Properties.Get(prev)) : Properties.First();
931  while (p)
932  {
933  if (p->Value.getFunction())
934  return p->Key;
935  p = Properties.Next(p);
936  }
937  return nullptr;
938 }
939 
941 {
942  assert(!constant);
943  if (k == &Strings.P[P_Prototype])
944  {
945  C4PropList * newpt = to.getPropList();
946  for(C4PropList * it = newpt; it; it = it->GetPrototype())
947  if(it == this)
948  throw C4AulExecError("Trying to create cyclic prototype structure");
949  prototype.SetPropList(newpt);
950  }
951  else if (Properties.Has(k))
952  {
953  Properties.Get(k).Value = to;
954  }
955  else
956  {
957  Properties.Add(C4Property(k, to));
958  }
959 }
960 
962 {
963  if (k == &Strings.P[P_Prototype])
964  prototype.Set0();
965  else
966  Properties.Remove(k);
967 }
968 
969 void C4PropList::Iterator::Init()
970 {
971  iter = properties->begin();
972 }
973 
974 void C4PropList::Iterator::Reserve(size_t additionalAmount)
975 {
976  properties->reserve(properties->size() + additionalAmount);
977 }
978 
979 void C4PropList::Iterator::AddProperty(const C4Property * prop)
980 {
981  std::vector<const C4Property*>::size_type i = 0, len = properties->size();
982  for(;i < len; ++i)
983  {
984  const C4Property *oldProperty = (*properties)[i];
985  if (oldProperty->Key == prop->Key)
986  {
987  (*properties)[i] = prop;
988  return;
989  }
990  }
991  // not already in vector?
992  properties->push_back(prop);
993 }
994 
996 {
998 
999  if (GetPrototype())
1000  {
1001  iter = GetPrototype()->begin();
1002  }
1003  else
1004  {
1005  iter.properties = std::make_shared<std::vector<const C4Property*> >();
1006  }
1007  iter.Reserve(Properties.GetSize());
1008 
1009  const C4Property * p = Properties.First();
1010  while (p)
1011  {
1012  iter.AddProperty(p);
1013  p = Properties.Next(p);
1014  }
1015 
1016  iter.Init();
1017  return iter;
1018 }
1019 
1020 
1021 template<> template<>
1022 unsigned int C4Set<C4PropListNumbered *>::Hash<int>(int const & e)
1023 {
1024  unsigned int hash = 4, tmp;
1025  hash += e >> 16;
1026  tmp = ((e & 0xffff) << 11) ^ hash;
1027  hash = (hash << 16) ^ tmp;
1028  hash += hash >> 11;
1029  hash ^= hash << 3;
1030  hash += hash >> 5;
1031  hash ^= hash << 4;
1032  hash += hash >> 17;
1033  hash ^= hash << 25;
1034  hash += hash >> 6;
1035  return hash;
1036 }
1037 
1038 template<> template<>
1040 {
1041  return Hash(e->GetPropListNumbered()->Number);
1042 }
1043 
1044 template<> template<>
1046 {
1047  return Hash(e->Number);
1048 }
1049 
1050 template<> template<>
1052 {
1053  return a->Number == b;
1054 }
1055 
1056 template<> template<>
1058 {
1059  return a == b;
1060 }
1061 
1062 template<> template<>
1064 {
1065  return C4Set<C4PropListNumbered *>::Hash(static_cast<int>(reinterpret_cast<intptr_t>(e)));
1066 }
1067 
1068 template<> template<>
1070 {
1071  // since script prop lists are only put in the set for reference keeping, just hash by pointer
1072  // but use only some of the more significant bits because
1073  uintptr_t hash = reinterpret_cast<uintptr_t>(e);
1074  return (unsigned int)(hash / 63);
1075 }
#define s
C4AulScriptEngine ScriptEngine
Definition: C4Globals.cpp:43
C4StringTable Strings
Definition: C4Globals.cpp:42
bool Log(const char *szMessage)
Definition: C4Log.cpp:204
#define a
#define b
void CompileNewFunc(C4PropList *&pStruct, StdCompiler *pComp, C4ValueNumbers *rPar)
Definition: C4PropList.cpp:410
C4PropertyName
@ P_Name
@ P_Prototype
@ P_LAST
@ C4V_Nil
Definition: C4Value.h:25
@ C4V_String
Definition: C4Value.h:29
C4Value C4VPropList(C4PropList *p)
Definition: C4Value.h:242
C4Value C4VString(C4String *pStr)
Definition: C4Value.h:243
StdParameterAdapt< T, P > mkParAdapt(T &&rObj, P &&rPar)
Definition: StdAdaptors.h:490
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:270
int iSize
Definition: TstC4NetIO.cpp:32
virtual void OnError(const char *msg)=0
const char * what() const noexcept override
Definition: C4Aul.cpp:59
C4Value Exec(C4PropList *p=nullptr, C4AulParSet *pPars=nullptr, bool fPassErrors=false)
Definition: C4AulFunc.h:72
C4AulErrorHandler * GetErrorHandler() const
Definition: C4Aul.h:173
C4PropListStatic * GetPropList()
Definition: C4Aul.h:151
Definition: C4Def.h:99
bool IsFrozen() const
Definition: C4PropList.h:135
virtual void SetName(const char *NewName=nullptr)
Definition: C4PropList.cpp:625
int32_t GetPropertyInt(C4PropertyName k, int32_t default_val=0) const
Definition: C4PropList.cpp:855
int32_t GetPropertyBool(C4PropertyName n, bool default_val=false) const
Definition: C4PropList.cpp:841
virtual C4Object * GetObject()
Definition: C4PropList.cpp:636
void CompileFunc(StdCompiler *pComp, C4ValueNumbers *)
Definition: C4PropList.cpp:366
C4AulFunc * GetFunc(C4PropertyName k) const
Definition: C4PropList.h:109
bool operator==(const C4PropList &b) const
Definition: C4PropList.cpp:348
void Thaw()
Definition: C4PropList.h:133
virtual const char * GetName() const
Definition: C4PropList.cpp:618
virtual C4ValueArray * GetProperties() const
Definition: C4PropList.cpp:883
virtual bool Delete()
Definition: C4PropList.h:94
virtual class C4PropListStatic * IsStatic()
Definition: C4PropList.h:89
int32_t Status
Definition: C4PropList.h:173
C4String * EnumerateOwnFuncs(C4String *prev=nullptr) const
Definition: C4PropList.cpp:928
virtual bool GetPropertyByS(const C4String *k, C4Value *pResult) const
Definition: C4PropList.cpp:726
C4ValueArray * GetPropertyArray(C4PropertyName n) const
Definition: C4PropList.cpp:758
virtual bool IsNumbered() const
Definition: C4PropList.h:92
virtual C4Effect * GetEffect()
Definition: C4PropList.cpp:678
bool HasProperty(C4String *k) const
Definition: C4PropList.h:122
C4PropertyName GetPropertyP(C4PropertyName k) const
Definition: C4PropList.cpp:824
void RemoveCyclicPrototypes()
Definition: C4PropList.cpp:399
void ThawRecursively()
Definition: C4PropList.cpp:259
C4PropListStatic * FreezeAndMakeStaticRecursively(std::vector< C4Value > *prop_lists, const C4PropListStatic *parent=nullptr, C4String *key=nullptr)
Definition: C4PropList.cpp:279
virtual void ResetProperty(C4String *k)
Definition: C4PropList.cpp:961
std::vector< C4String * > GetSortedLocalProperties(bool add_prototype=true) const
Definition: C4PropList.cpp:545
C4PropList * GetPrototype() const
Definition: C4PropList.h:85
StdStrBuf ToJSON(int depth=10, bool ignore_reference_parent=false) const
Definition: C4PropList.cpp:515
C4PropList * GetPropertyPropList(C4PropertyName k) const
Definition: C4PropList.cpp:869
void AppendDataString(StdStrBuf *out, const char *delim, int depth=3, bool ignore_reference_parent=false) const
Definition: C4PropList.cpp:487
C4PropList(C4PropList *prototype=nullptr)
Definition: C4PropList.cpp:251
void Freeze()
Definition: C4PropList.h:132
C4String * GetPropertyStr(C4PropertyName k) const
Definition: C4PropList.cpp:744
Iterator begin()
Definition: C4PropList.cpp:995
virtual void Denumerate(C4ValueNumbers *)
Definition: C4PropList.cpp:321
virtual C4PropListNumbered * GetPropListNumbered()
Definition: C4PropList.cpp:672
virtual C4Def const * GetDef() const
Definition: C4PropList.cpp:654
Iterator end()
Definition: C4PropList.h:211
C4Value Call(C4PropertyName k, C4AulParSet *pPars=nullptr, bool fPassErrors=false)
Definition: C4PropList.h:114
virtual void SetPropertyByS(C4String *k, const C4Value &to)
Definition: C4PropList.cpp:940
virtual ~C4PropList()
Definition: C4PropList.cpp:333
static C4PropList * New(C4PropList *prototype=nullptr)
Definition: C4PropList.cpp:40
virtual class C4MapScriptLayer * GetMapScriptLayer()
Definition: C4PropList.cpp:660
std::vector< C4String * > GetSortedProperties(const char *prefix, C4PropList *ignore_parent=nullptr) const
Definition: C4PropList.cpp:601
friend class C4Value
Definition: C4PropList.h:170
void Clear()
Definition: C4PropList.h:70
virtual class C4MapScriptMap * GetMapScriptMap()
Definition: C4PropList.cpp:666
void SetProperty(C4PropertyName k, const C4Value &to)
Definition: C4PropList.h:124
static C4PropListStatic * NewStatic(C4PropList *prototype, const C4PropListStatic *parent, C4String *key)
Definition: C4PropList.cpp:46
std::vector< C4String * > GetUnsortedProperties(const char *prefix, C4PropList *ignore_parent=nullptr) const
Definition: C4PropList.cpp:583
C4PropListNumbered(C4PropList *prototype=nullptr)
Definition: C4PropList.cpp:127
C4PropListNumbered * GetPropListNumbered() override
Definition: C4PropList.cpp:151
~C4PropListNumbered() override
Definition: C4PropList.cpp:175
static void ClearShelve()
Definition: C4PropList.cpp:107
static std::vector< C4PropListNumbered * > ShelvedPropLists
Definition: C4PropList.h:242
void CompileFunc(StdCompiler *pComp, C4ValueNumbers *numbers)
Definition: C4PropList.cpp:156
static bool CheckPropList(C4PropList *)
Definition: C4PropList.cpp:56
static C4Set< C4PropListNumbered * > PropLists
Definition: C4PropList.h:241
static void ShelveNumberedPropLists()
Definition: C4PropList.cpp:81
static void UnshelveNumberedPropLists()
Definition: C4PropList.cpp:99
static void ClearNumberedPropLists()
Definition: C4PropList.cpp:113
static void SetEnumerationIndex(int32_t iMaxObjectNumber)
Definition: C4PropList.cpp:69
static C4PropList * GetByNumber(int32_t iNumber)
Definition: C4PropList.cpp:51
static void ResetEnumerationIndex()
Definition: C4PropList.cpp:75
static int32_t EnumerationIndex
Definition: C4PropList.h:243
static C4Set< C4PropListScript * > PropLists
Definition: C4PropList.h:259
static void ClearScriptPropLists()
Definition: C4PropList.cpp:183
void RefCompileFunc(StdCompiler *pComp, C4ValueNumbers *numbers) const
Definition: C4PropList.cpp:216
C4RefCntPointer< C4String > ParentKeyName
Definition: C4PropList.h:279
C4String * GetParentKeyName()
Definition: C4PropList.h:276
StdStrBuf GetDataString() const
Definition: C4PropList.cpp:229
const C4PropListStatic * GetParent() const
Definition: C4PropList.h:275
const char * GetName() const override
Definition: C4PropList.cpp:243
const C4PropListStatic * Parent
Definition: C4PropList.h:278
C4Value Value
Definition: C4PropList.h:54
C4String * Key
Definition: C4PropList.h:53
void CompileFunc(StdCompiler *pComp, C4ValueNumbers *)
Definition: C4PropList.cpp:471
void DecRef()
Definition: C4StringTable.h:28
void IncRef()
Definition: C4StringTable.h:27
void CompileFunc(class StdCompiler *pComp, class C4ValueNumbers *)
Definition: C4PropList.cpp:418
T & Get(H e) const
T const * Next(T const *p) const
bool Has(H e) const
std::list< const T * > GetSortedListOfElementPointers() const
T * Add(T const &e)
unsigned int GetSize() const
static unsigned int Hash(const H &)
void Remove(H e)
T const * First() const
StdStrBuf GetData() const
Definition: C4StringTable.h:50
C4String * FindString(const char *strString) const
C4String P[P_LAST]
C4String * RegString(StdStrBuf String)
C4ValueArray * getArray() const
Definition: C4Value.h:118
void SetPropList(C4PropList *PropList)
Definition: C4Value.h:141
int32_t getInt() const
Definition: C4Value.h:112
StdStrBuf ToJSON(int depth=10, const class C4PropListStatic *ignore_reference_parent=nullptr) const
Definition: C4Value.cpp:189
StdStrBuf GetDataString(int depth=10, const class C4PropListStatic *ignore_reference_parent=nullptr) const
Definition: C4Value.cpp:131
C4String * getStr() const
Definition: C4Value.h:117
void Set0()
Definition: C4Value.h:332
C4AulFunc * getFunction() const
Definition: C4Value.h:119
bool getBool() const
Definition: C4Value.h:113
C4PropList * getPropList() const
Definition: C4Value.h:116
void Denumerate(C4ValueNumbers *)
Definition: C4Value.cpp:251
virtual bool Separator(Sep eSep=SEP_SEP)
Definition: StdCompiler.h:119
void excCorrupt(const char *szMessage,...)
Definition: StdCompiler.h:249
void Value(const T &rStruct)
Definition: StdCompiler.h:161
virtual bool isDeserializer()
Definition: StdCompiler.h:53
virtual bool hasNaming()
Definition: StdCompiler.h:58
void AppendChar(char cChar)
Definition: StdBuf.h:588
void Append(const char *pnData, size_t iChars)
Definition: StdBuf.h:519
void Take(char *pnData)
Definition: StdBuf.h:457