1 /*
2 * OpenClonk, http://www.openclonk.org
3 *
4 * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5 * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6 *
7 * Distributed under the terms of the ISC license; see accompanying file
8 * "COPYING" for details.
9 *
10 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11 * See accompanying file "TRADEMARK" for details.
12 *
13 * To redistribute this file separately, substitute the full license texts
14 * for the above references.
15 */
16 #ifndef INC_C4Value
17 #define INC_C4Value
18
19 #include "object/C4ObjectPtr.h"
20 #include "script/C4StringTable.h"
21
22 // C4Value type
23 enum C4V_Type
24 {
25 C4V_Nil=0,
26 C4V_Int=1,
27 C4V_Bool=2,
28 C4V_PropList=3,
29 C4V_String=4,
30 C4V_Array=5,
31 C4V_Function=6,
32
33 C4V_Enum=8, // enumerated array or proplist
34 C4V_C4ObjectEnum=9, // enumerated object
35
36 // for typechecks
37 C4V_Any,
38 C4V_Object,
39 C4V_Def,
40 C4V_Effect,
41 };
42 // last C4V_Type that doesn't vanish in Denumerate
43 #define C4V_Last ((int) C4V_Array)
44 // a C4V_Type >= C4V_FirstPointer and <= C4V_Last is a pointer
45 #define C4V_FirstPointer C4V_PropList
46
47 const char* GetC4VName(const C4V_Type Type);
48 template<typename T> class Nillable;
49
50 union C4V_Data
51 {
52 intptr_t Int;
53 void * Ptr;
54 C4PropList * PropList;
55 C4String * Str;
56 C4ValueArray * Array;
57 C4AulFunc * Fn;
58 // cheat a little - assume that all members have the same length
59 operator void * () { return Ptr; }
60 operator const void * () const { return Ptr; }
61 C4V_Data &operator = (void *p) { assert(!p); Ptr = p; return *this; }
62 };
63
64 class C4JSONSerializationError : public std::exception
65 {
66 std::string msg;
67 public:
C4JSONSerializationError(const std::string & msg)68 C4JSONSerializationError(const std::string& msg) : msg(msg) {}
what()69 const char* what() const noexcept override { return msg.c_str(); }
70 };
71
72 class C4Value
73 {
74 public:
75
C4Value()76 C4Value() { Data = nullptr; }
77
C4Value(const C4Value & nValue)78 C4Value(const C4Value &nValue) : Data(nValue.Data), NextRef(nullptr), Type(nValue.Type)
79 { AddDataRef(); }
80 C4Value(C4Value && nValue) noexcept;
81
C4Value(bool data)82 explicit C4Value(bool data): NextRef(nullptr), Type(C4V_Bool)
83 { Data.Int = data; }
C4Value(int data)84 explicit C4Value(int data): NextRef(nullptr), Type(C4V_Int)
85 { Data.Int = data; }
C4Value(long data)86 explicit C4Value(long data): NextRef(nullptr), Type(C4V_Int)
87 { Data.Int = int32_t(data); }
88 explicit C4Value(C4PropListStatic *p);
89 explicit C4Value(C4Def *p);
90 explicit C4Value(C4Object *pObj);
91 explicit C4Value(C4Effect *p);
C4Value(C4String * pStr)92 explicit C4Value(C4String *pStr): NextRef(nullptr), Type(pStr ? C4V_String : C4V_Nil)
93 { Data.Str = pStr; AddDataRef(); }
C4Value(const char * s)94 explicit C4Value(const char * s): NextRef(nullptr), Type(s ? C4V_String : C4V_Nil)
95 { Data.Str = s ? ::Strings.RegString(s) : nullptr; AddDataRef(); }
C4Value(const StdStrBuf & s)96 explicit C4Value(const StdStrBuf & s): NextRef(nullptr), Type(s.isNull() ? C4V_Nil : C4V_String)
97 { Data.Str = s.isNull() ? nullptr: ::Strings.RegString(s); AddDataRef(); }
C4Value(C4ValueArray * pArray)98 explicit C4Value(C4ValueArray *pArray): NextRef(nullptr), Type(pArray ? C4V_Array : C4V_Nil)
99 { Data.Array = pArray; AddDataRef(); }
C4Value(C4AulFunc * pFn)100 explicit C4Value(C4AulFunc * pFn): NextRef(nullptr), Type(pFn ? C4V_Function : C4V_Nil)
101 { Data.Fn = pFn; AddDataRef(); }
C4Value(C4PropList * p)102 explicit C4Value(C4PropList *p): NextRef(nullptr), Type(p ? C4V_PropList : C4V_Nil)
103 { Data.PropList = p; AddDataRef(); }
C4Value(C4ObjectPtr p)104 C4Value(C4ObjectPtr p): C4Value(p.operator C4Object *()) {}
C4Value(Nillable<T> v)105 template<typename T> C4Value(Nillable<T> v): C4Value(v.IsNil() ? C4Value() : C4Value(v.operator T())) {}
106
107 C4Value& operator = (const C4Value& nValue) { Set(nValue); return *this; }
108
~C4Value()109 ~C4Value() { DelDataRef(Data, Type, NextRef); }
110
111 // Checked getters
getInt()112 int32_t getInt() const { return CheckConversion(C4V_Int) ? Data.Int : 0; }
getBool()113 bool getBool() const { return CheckConversion(C4V_Bool) ? !! Data : false; }
114 C4Object * getObj() const;
115 C4Def * getDef() const;
getPropList()116 C4PropList * getPropList() const { return CheckConversion(C4V_PropList) ? Data.PropList : nullptr; }
getStr()117 C4String * getStr() const { return CheckConversion(C4V_String) ? Data.Str : nullptr; }
getArray()118 C4ValueArray * getArray() const { return CheckConversion(C4V_Array) ? Data.Array : nullptr; }
getFunction()119 C4AulFunc * getFunction() const { return CheckConversion(C4V_Function) ? Data.Fn : nullptr; }
120
121 // Unchecked getters
_getInt()122 int32_t _getInt() const { return Data.Int; }
_getBool()123 bool _getBool() const { return !! Data.Int; }
124 C4Object *_getObj() const;
125 C4Def *_getDef() const;
_getStr()126 C4String *_getStr() const { return Data.Str; }
_getArray()127 C4ValueArray *_getArray() const { return Data.Array; }
_getFunction()128 C4AulFunc *_getFunction() const { return Data.Fn; }
_getPropList()129 C4PropList *_getPropList() const { return Data.PropList; }
130
131 bool operator ! () const { return !GetData(); }
132 inline operator const void* () const { return GetData() ? this : nullptr; } // To allow use of C4Value in conditions
133
Set(const C4Value & nValue)134 void Set(const C4Value &nValue) { Set(nValue.Data, nValue.Type); }
135
SetInt(int32_t i)136 void SetInt(int32_t i) { C4V_Data d; d.Int = i; Set(d, C4V_Int); }
SetBool(bool b)137 void SetBool(bool b) { C4V_Data d; d.Int = b; Set(d, C4V_Bool); }
SetString(C4String * Str)138 void SetString(C4String * Str) { C4V_Data d; d.Str = Str; Set(d, C4V_String); }
SetArray(C4ValueArray * Array)139 void SetArray(C4ValueArray * Array) { C4V_Data d; d.Array = Array; Set(d, C4V_Array); }
SetFunction(C4AulFunc * Fn)140 void SetFunction(C4AulFunc * Fn) { C4V_Data d; d.Fn = Fn; Set(d, C4V_Function); }
SetPropList(C4PropList * PropList)141 void SetPropList(C4PropList * PropList) { C4V_Data d; d.PropList = PropList; Set(d, C4V_PropList); }
SetObjectEnum(int i)142 void SetObjectEnum(int i) { C4V_Data d; d.Int = i; Set(d, C4V_C4ObjectEnum); }
143 void Set0();
144
145 bool operator == (const C4Value& Value2) const;
146 bool operator != (const C4Value& Value2) const;
147
148 // Identical comparison
IsIdenticalTo(const C4Value & cmp)149 bool IsIdenticalTo(const C4Value &cmp) const { return GetType()==cmp.GetType() && GetData()==cmp.GetData(); }
150
151 // Change and set Type to int in case it was nil or bool before
152 // Use with care: These don't handle int32_t overflow
153 C4Value & operator += (int32_t by) { Data.Int += by; Type=C4V_Int; return *this; }
154 C4Value & operator ++ () { Data.Int++; Type=C4V_Int; return *this; }
155 C4Value operator ++ (int) { C4Value old = *this; ++(*this); return old; }
156 C4Value & operator -- () { Data.Int--; Type=C4V_Int; return *this; }
157 C4Value operator -- (int) { C4Value old = *this; --(*this); return old; }
158
159 // getters
GetData()160 C4V_Data GetData() const { return Data; }
GetType()161 C4V_Type GetType() const { return Type; }
162 C4V_Type GetTypeEx() const; // Return type including types derived from prop list types (such as C4V_Def)
163
GetTypeName()164 const char *GetTypeName() const { return GetC4VName(GetType()); }
165
166 void Denumerate(C4ValueNumbers *);
167
168 StdStrBuf GetDataString(int depth = 10, const class C4PropListStatic *ignore_reference_parent = nullptr) const;
169 StdStrBuf ToJSON(int depth = 10, const class C4PropListStatic *ignore_reference_parent = nullptr) const;
170
CheckParConversion(C4V_Type vtToType)171 ALWAYS_INLINE bool CheckParConversion(C4V_Type vtToType) const // convert to dest type
172 {
173 switch (vtToType)
174 {
175 case C4V_Nil: return Type == C4V_Nil || (Type == C4V_Int && !*this);
176 case C4V_Int: return Type == C4V_Int || Type == C4V_Nil || Type == C4V_Bool;
177 case C4V_Bool: return true;
178 case C4V_PropList: return Type == C4V_PropList || Type == C4V_Nil || (Type == C4V_Int && !*this);
179 case C4V_String: return Type == C4V_String || Type == C4V_Nil || (Type == C4V_Int && !*this);
180 case C4V_Array: return Type == C4V_Array || Type == C4V_Nil || (Type == C4V_Int && !*this);
181 case C4V_Function: return Type == C4V_Function || Type == C4V_Nil || (Type == C4V_Int && !*this);
182 case C4V_Any: return true;
183 case C4V_Object: return (Type == C4V_PropList && FnCnvObject()) || Type == C4V_Nil || (Type == C4V_Int && !*this);
184 case C4V_Def: return (Type == C4V_PropList && FnCnvDef()) || Type == C4V_Nil || (Type == C4V_Int && !*this);
185 case C4V_Effect: return (Type == C4V_PropList && FnCnvEffect()) || Type == C4V_Nil || (Type == C4V_Int && !*this);
186 default: assert(!"C4Value::CheckParConversion: impossible conversion target"); return false;
187 }
188 }
CheckConversion(C4V_Type vtToType)189 ALWAYS_INLINE bool CheckConversion(C4V_Type vtToType) const // convert to dest type
190 {
191 switch (vtToType)
192 {
193 case C4V_Nil: return Type == C4V_Nil;
194 case C4V_Int: return Type == C4V_Nil || Type == C4V_Int || Type == C4V_Bool;
195 case C4V_Bool: return true;
196 case C4V_PropList: return Type == C4V_PropList;
197 case C4V_String: return Type == C4V_String;
198 case C4V_Array: return Type == C4V_Array;
199 case C4V_Function: return Type == C4V_Function;
200 case C4V_Any: return true;
201 case C4V_Object: return Type == C4V_PropList && FnCnvObject();
202 case C4V_Def: return Type == C4V_PropList && FnCnvDef();
203 case C4V_Effect: return Type == C4V_PropList && FnCnvEffect();
204 default: assert(!"C4Value::CheckConversion: impossible conversion target"); return false;
205 }
206 }
207 static bool WarnAboutConversion(C4V_Type Type, C4V_Type vtToType);
208
209 // Compilation
210 void CompileFunc(StdCompiler *pComp, C4ValueNumbers *);
211
IsNullableType(C4V_Type Type)212 static inline constexpr bool IsNullableType(C4V_Type Type)
213 { return Type == C4V_Int || Type == C4V_Bool; }
214
215 private:
216 // data
217 C4V_Data Data;
218
219 // proplist reference list
220 C4Value * NextRef{nullptr};
221
222 // data type
223 C4V_Type Type{C4V_Nil};
224
225 void Set(C4V_Data nData, C4V_Type nType);
226
227 void AddDataRef();
228 void DelDataRef(C4V_Data Data, C4V_Type Type, C4Value *pNextRef);
229
230 bool FnCnvObject() const;
231 bool FnCnvDef() const;
232 bool FnCnvEffect() const;
233 void LogDeletedObjectWarning(C4PropList *);
234
235 // Prevent unintended type conversions
236 template<typename T> explicit C4Value(T);
237
238 friend class C4PropList;
239 };
240
241 // converter
C4VInt(int32_t i)242 inline C4Value C4VInt(int32_t i) { return C4Value(i); }
C4VBool(bool b)243 inline C4Value C4VBool(bool b) { return C4Value(b); }
244 C4Value C4VObj(C4Object *pObj);
C4VPropList(C4PropList * p)245 inline C4Value C4VPropList(C4PropList * p) { return C4Value(p); }
C4VString(C4String * pStr)246 inline C4Value C4VString(C4String *pStr) { return C4Value(pStr); }
C4VString(StdStrBuf strString)247 inline C4Value C4VString(StdStrBuf strString) { return C4Value(strString); }
C4VString(const char * strString)248 inline C4Value C4VString(const char *strString) { return C4Value(strString); }
C4VArray(C4ValueArray * pArray)249 inline C4Value C4VArray(C4ValueArray *pArray) { return C4Value(pArray); }
C4VFunction(C4AulFunc * pFn)250 inline C4Value C4VFunction(C4AulFunc * pFn) { return C4Value(pFn); }
251
252 extern const C4Value C4VNull;
253
254 // C4Values can contain data structures that have to maintain their
255 // identity across a save/load. During serialization, these get a number
256 class C4ValueNumbers
257 {
258 public:
259 C4ValueNumbers() = default;
260 uint32_t GetNumberForValue(C4Value * v);
261 const C4Value & GetValue(uint32_t);
262 void Denumerate();
263 void CompileFunc(StdCompiler *);
264 void CompileValue(StdCompiler *, C4Value *);
265 private:
266 std::list<C4Value *> ValuesToSave;
267 std::vector<C4Value> LoadedValues;
268 std::map<void *, uint32_t> ValueNumbers;
269 };
270
271 /* These are by far the most often called C4Value functions.
272 They also often do redundant checks the compiler should be able to optimize away
273 in common situations because the Type of the new value is known. In any case,
274 inlining them does speed up the script engine on at least one artificial benchmark. */
275
276 #include "script/C4ValueArray.h"
277 #include "script/C4PropList.h"
278 #include "script/C4AulFunc.h"
279
AddDataRef()280 ALWAYS_INLINE void C4Value::AddDataRef()
281 {
282 assert(Type < C4V_Any);
283 assert(Type != C4V_Nil || !Data);
284 switch (Type)
285 {
286 case C4V_PropList:
287 #ifdef _DEBUG
288 assert(C4PropList::PropLists.Has(Data.PropList));
289 if (!Data.PropList->Status)
290 {
291 LogDeletedObjectWarning(Data.PropList);
292 }
293 #endif
294 Data.PropList->AddRef(this);
295 break;
296 case C4V_String: Data.Str->IncRef(); break;
297 case C4V_Array: Data.Array->IncRef(); break;
298 case C4V_Function: Data.Fn->IncRef(); break;
299 default: break;
300 }
301 }
302
DelDataRef(C4V_Data Data,C4V_Type Type,C4Value * pNextRef)303 ALWAYS_INLINE void C4Value::DelDataRef(C4V_Data Data, C4V_Type Type, C4Value *pNextRef)
304 {
305 assert(Type < C4V_Any);
306 assert(Type != C4V_Nil || !Data);
307 // clean up
308 switch (Type)
309 {
310 case C4V_PropList: Data.PropList->DelRef(this, pNextRef); break;
311 case C4V_String: Data.Str->DecRef(); break;
312 case C4V_Array: Data.Array->DecRef(); break;
313 case C4V_Function: Data.Fn->DecRef(); break;
314 default: break;
315 }
316 }
317
Set(C4V_Data nData,C4V_Type nType)318 ALWAYS_INLINE void C4Value::Set(C4V_Data nData, C4V_Type nType)
319 {
320 // Do not add this to the same linked list twice.
321 if (Data == nData && Type >= C4V_FirstPointer) return;
322
323 C4V_Data oData = Data;
324 C4V_Type oType = Type;
325 C4Value * oNextRef = NextRef;
326
327 // change
328 Data = nData;
329 Type = nData || IsNullableType(nType) ? nType : C4V_Nil;
330
331 // hold new data & clean up old
332 AddDataRef();
333 DelDataRef(oData, oType, oNextRef);
334 }
335
Set0()336 ALWAYS_INLINE void C4Value::Set0()
337 {
338 C4V_Data oData = Data;
339 C4V_Type oType = Type;
340
341 // change
342 Data = nullptr;
343 Type = C4V_Nil;
344
345 // clean up (save even if Data was 0 before)
346 DelDataRef(oData, oType, NextRef);
347 }
348
C4Value(C4Value && nValue)349 ALWAYS_INLINE C4Value::C4Value(C4Value && nValue) noexcept:
350 Data(nValue.Data), NextRef(nullptr), Type(nValue.Type)
351 {
352 if (Type == C4V_PropList)
353 {
354 Data.PropList->AddRef(this);
355 Data.PropList->DelRef(&nValue, nValue.NextRef);
356 }
357 nValue.Type = C4V_Nil; nValue.Data = nullptr; nValue.NextRef = nullptr;
358 }
359
360 #endif
361
362