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