1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2001, 2006-2007, Sven Eberhardt
5  * Copyright (c) 2006, Peter Wortmann
6  * Copyright (c) 2007, Günther Brammer
7  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
8  *
9  * Distributed under the terms of the ISC license; see accompanying file
10  * "COPYING" for details.
11  *
12  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
13  * See accompanying file "TRADEMARK" for details.
14  *
15  * To redistribute this file separately, substitute the full license texts
16  * for the above references.
17  */
18 
19 #ifndef C4AULEXEC_H
20 #define C4AULEXEC_H
21 
22 #include "platform/C4TimeMilliseconds.h"
23 #include "script/C4Aul.h"
24 #include "script/C4AulScriptFunc.h"
25 
26 const int MAX_CONTEXT_STACK = 512;
27 const int MAX_VALUE_STACK = 1024;
28 
29 /*
30  The Stack layout is as follows:
31  first parameter
32  ...
33  last parameter
34  first named var
35  ...
36  last named var
37  temporary values
38  */
39 
40 // execution context
41 struct C4AulScriptContext
42 {
43 	C4PropList *Obj;
44 	C4Value *Return;
45 	C4Value *Pars;
46 	C4AulScriptFunc *Func;
47 	C4AulBCC *CPos;
48 	C4TimeMilliseconds tTime; // initialized only by profiler if active
49 
50 	void dump(StdStrBuf Dump = StdStrBuf(""));
51 	StdStrBuf ReturnDump(StdStrBuf Dump = StdStrBuf(""));
52 };
53 
54 class C4AulExec
55 {
56 
57 public:
C4AulExec()58 	C4AulExec()
59 			: pCurCtx(Contexts - 1), pCurVal(Values - 1)
60 	{ }
61 
62 private:
63 	C4AulScriptContext *pCurCtx;
64 	C4Value *pCurVal;
65 
66 	int iTraceStart{-1};
67 	bool fProfiling;
68 	C4TimeMilliseconds tDirectExecStart;
69 	uint32_t tDirectExecTotal; // profiler time for DirectExec
70 	C4ScriptHost *pProfiledScript;
71 
72 	C4AulScriptContext Contexts[MAX_CONTEXT_STACK];
73 	C4Value Values[MAX_VALUE_STACK];
74 
75 	void StartProfiling(C4ScriptHost *pScript); // starts recording the times
IsProfiling()76 	bool IsProfiling() { return fProfiling; }
StopProfiling()77 	void StopProfiling() { fProfiling=false; }
78 	friend class C4AulProfiler;
79 public:
80 	C4Value Exec(C4AulScriptFunc *pSFunc, C4PropList * p, C4Value pPars[], bool fPassErrors);
81 	C4Value DirectExec(C4PropList *p, const char *szScript, const char *szContext, bool fPassErrors = false, C4AulScriptContext* context = nullptr, bool parse_function = false);
82 
83 	void StartTrace();
StartDirectExec()84 	inline void StartDirectExec() { if (fProfiling) tDirectExecStart = C4TimeMilliseconds::Now(); }
StopDirectExec()85 	inline void StopDirectExec() { if (fProfiling) tDirectExecTotal += C4TimeMilliseconds::Now() - tDirectExecStart; }
86 
GetContextDepth()87 	int GetContextDepth() const { return pCurCtx - Contexts + 1; }
GetContext(int iLevel)88 	C4AulScriptContext *GetContext(int iLevel) { return iLevel >= 0 && iLevel < GetContextDepth() ? Contexts + iLevel : nullptr; }
89 	void LogCallStack();
90 	static C4String *FnTranslate(C4PropList * _this, C4String *text);
91 	static bool FnLogCallStack(C4PropList * _this);
92 	void ClearPointers(C4Object *);
93 
94 private:
95 	C4Value Exec(C4AulBCC *pCPos);
96 	void PushContext(const C4AulScriptContext &rContext);
97 	void PopContext();
98 
CheckOverflow(int iCnt)99 	void CheckOverflow(int iCnt)
100 	{
101 		if (pCurVal - Values >= MAX_VALUE_STACK - iCnt)
102 			throw C4AulExecError("value stack overflow, probably due to too deep recursion");
103 	}
104 
PushInt(int32_t i)105 	void PushInt(int32_t i)
106 	{
107 		CheckOverflow(1);
108 		(++pCurVal)->SetInt(i);
109 	}
110 
PushBool(bool b)111 	void PushBool(bool b)
112 	{
113 		CheckOverflow(1);
114 		(++pCurVal)->SetBool(b);
115 	}
116 
PushString(C4String * Str)117 	void PushString(C4String * Str)
118 	{
119 		CheckOverflow(1);
120 		(++pCurVal)->SetString(Str);
121 	}
122 
PushArray(C4ValueArray * Array)123 	void PushArray(C4ValueArray * Array)
124 	{
125 		CheckOverflow(1);
126 		(++pCurVal)->SetArray(Array);
127 	}
128 
PushFunction(C4AulFunc * Fn)129 	void PushFunction(C4AulFunc * Fn)
130 	{
131 		CheckOverflow(1);
132 		(++pCurVal)->SetFunction(Fn);
133 	}
134 
PushPropList(C4PropList * PropList)135 	void PushPropList(C4PropList * PropList)
136 	{
137 		CheckOverflow(1);
138 		(++pCurVal)->SetPropList(PropList);
139 	}
140 
PushValue(const C4Value & rVal)141 	void PushValue(const C4Value &rVal)
142 	{
143 		CheckOverflow(1);
144 		(++pCurVal)->Set(rVal);
145 	}
146 
PushNullVals(int iCnt)147 	void PushNullVals(int iCnt)
148 	{
149 		CheckOverflow(iCnt);
150 		pCurVal += iCnt;
151 	}
152 
PopValue()153 	bool PopValue()
154 	{
155 		assert (LocalValueStackSize() >= 1);
156 		(pCurVal--)->Set0();
157 		return true;
158 	}
159 
PopValues(int n)160 	void PopValues(int n)
161 	{
162 		assert (LocalValueStackSize() >= n);
163 		while (n--)
164 			(pCurVal--)->Set0();
165 	}
166 
PopValuesUntil(C4Value * pUntilVal)167 	void PopValuesUntil(C4Value *pUntilVal)
168 	{
169 		assert (pUntilVal >= Values - 1);
170 		while (pCurVal > pUntilVal)
171 			(pCurVal--)->Set0();
172 	}
173 
ContextStackSize()174 	int ContextStackSize() const
175 	{
176 		return pCurCtx - Contexts + 1;
177 	}
178 
ValueStackSize()179 	int ValueStackSize() const
180 	{
181 		return pCurVal - Values + 1;
182 	}
183 
LocalValueStackSize()184 	int LocalValueStackSize() const
185 	{
186 		return ContextStackSize()
187 		       ? pCurVal - pCurCtx->Pars - pCurCtx->Func->GetParCount() - pCurCtx->Func->VarNamed.iSize + 1
188 		       : pCurVal - Values + 1;
189 	}
190 
CheckOpPars(C4V_Type Type1,C4V_Type Type2,const char * opname)191 	ALWAYS_INLINE void CheckOpPars(C4V_Type Type1, C4V_Type Type2, const char * opname)
192 	{
193 		// Get parameters
194 		C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
195 
196 		// Typecheck parameters
197 		if (!pPar1->CheckParConversion(Type1))
198 			throw C4AulExecError(FormatString(R"(operator "%s" left side got %s, but expected %s)",
199 			                                      opname, pPar1->GetTypeName(), GetC4VName(Type1)).getData());
200 		if (!pPar2->CheckParConversion(Type2))
201 			throw C4AulExecError(FormatString(R"(operator "%s" right side got %s, but expected %s)",
202 			                                      opname, pPar2->GetTypeName(), GetC4VName(Type2)).getData());
203 	}
CheckOpPar(C4V_Type Type1,const char * opname)204 	ALWAYS_INLINE void CheckOpPar(C4V_Type Type1, const char * opname)
205 	{
206 		// Typecheck parameter
207 		if (!pCurVal->CheckParConversion(Type1))
208 			throw C4AulExecError(FormatString(R"(operator "%s": got %s, but expected %s)",
209 			                                      opname, pCurVal->GetTypeName(), GetC4VName(Type1)).getData());
210 	}
211 
CheckArrayAccess(C4Value * pStructure,C4Value * pIndex)212 	C4V_Type CheckArrayAccess(C4Value *pStructure, C4Value *pIndex)
213 	{
214 		if (pStructure->CheckConversion(C4V_Array))
215 		{
216 			if (!pIndex->CheckConversion(C4V_Int))
217 				throw C4AulExecError(FormatString("array access: index of type %s, but expected int", pIndex->GetTypeName()).getData());
218 			return C4V_Array;
219 		}
220 		else if (pStructure->CheckConversion(C4V_PropList))
221 		{
222 			if (!pIndex->CheckConversion(C4V_String))
223 				throw C4AulExecError(FormatString("proplist access: index of type %s, but expected string", pIndex->GetTypeName()).getData());
224 			return C4V_PropList;
225 		}
226 		else
227 			throw C4AulExecError(FormatString("can't access %s as array or proplist", pStructure->GetTypeName()).getData());
228 	}
229 	C4AulBCC *Call(C4AulFunc *pFunc, C4Value *pReturn, C4Value *pPars, C4PropList * pContext = nullptr);
230 };
231 
232 extern C4AulExec AulExec;
233 
234 // script profiler entry
235 class C4AulProfiler
236 {
237 private:
238 	// map entry
239 	struct Entry
240 	{
241 		C4AulScriptFunc *pFunc;
242 		uint32_t tProfileTime;
243 
244 		bool operator < (const Entry &e2) const { return tProfileTime < e2.tProfileTime ; }
245 	};
246 
247 	// items
248 	std::vector<Entry> Times;
249 
250 	void CollectEntry(C4AulScriptFunc *pFunc, uint32_t tProfileTime);
251 	void CollectTimes(C4PropListStatic * p);
252 	void CollectTimes();
253 	static void ResetTimes(C4PropListStatic * p);
254 	static void ResetTimes();
255 	void Show();
256 public:
Abort()257 	static void Abort() { AulExec.StopProfiling(); }
258 	static void StartProfiling(C4ScriptHost *pScript); // reset times and start collecting new ones
259 	static void StopProfiling(); // stop the profiler and displays results
260 };
261 
262 #endif // C4AULEXEC_H
263