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