/* * OpenClonk, http://www.openclonk.org * * Copyright (c) 2001, 2006-2007, Sven Eberhardt * Copyright (c) 2006, Peter Wortmann * Copyright (c) 2007, Günther Brammer * Copyright (c) 2009-2016, The OpenClonk Team and contributors * * Distributed under the terms of the ISC license; see accompanying file * "COPYING" for details. * * "Clonk" is a registered trademark of Matthes Bender, used with permission. * See accompanying file "TRADEMARK" for details. * * To redistribute this file separately, substitute the full license texts * for the above references. */ #ifndef C4AULEXEC_H #define C4AULEXEC_H #include "platform/C4TimeMilliseconds.h" #include "script/C4Aul.h" #include "script/C4AulScriptFunc.h" const int MAX_CONTEXT_STACK = 512; const int MAX_VALUE_STACK = 1024; /* The Stack layout is as follows: first parameter ... last parameter first named var ... last named var temporary values */ // execution context struct C4AulScriptContext { C4PropList *Obj; C4Value *Return; C4Value *Pars; C4AulScriptFunc *Func; C4AulBCC *CPos; C4TimeMilliseconds tTime; // initialized only by profiler if active void dump(StdStrBuf Dump = StdStrBuf("")); StdStrBuf ReturnDump(StdStrBuf Dump = StdStrBuf("")); }; class C4AulExec { public: C4AulExec() : pCurCtx(Contexts - 1), pCurVal(Values - 1) { } private: C4AulScriptContext *pCurCtx; C4Value *pCurVal; int iTraceStart{-1}; bool fProfiling; C4TimeMilliseconds tDirectExecStart; uint32_t tDirectExecTotal; // profiler time for DirectExec C4ScriptHost *pProfiledScript; C4AulScriptContext Contexts[MAX_CONTEXT_STACK]; C4Value Values[MAX_VALUE_STACK]; void StartProfiling(C4ScriptHost *pScript); // starts recording the times bool IsProfiling() { return fProfiling; } void StopProfiling() { fProfiling=false; } friend class C4AulProfiler; public: C4Value Exec(C4AulScriptFunc *pSFunc, C4PropList * p, C4Value pPars[], bool fPassErrors); C4Value DirectExec(C4PropList *p, const char *szScript, const char *szContext, bool fPassErrors = false, C4AulScriptContext* context = nullptr, bool parse_function = false); void StartTrace(); inline void StartDirectExec() { if (fProfiling) tDirectExecStart = C4TimeMilliseconds::Now(); } inline void StopDirectExec() { if (fProfiling) tDirectExecTotal += C4TimeMilliseconds::Now() - tDirectExecStart; } int GetContextDepth() const { return pCurCtx - Contexts + 1; } C4AulScriptContext *GetContext(int iLevel) { return iLevel >= 0 && iLevel < GetContextDepth() ? Contexts + iLevel : nullptr; } void LogCallStack(); static C4String *FnTranslate(C4PropList * _this, C4String *text); static bool FnLogCallStack(C4PropList * _this); void ClearPointers(C4Object *); private: C4Value Exec(C4AulBCC *pCPos); void PushContext(const C4AulScriptContext &rContext); void PopContext(); void CheckOverflow(int iCnt) { if (pCurVal - Values >= MAX_VALUE_STACK - iCnt) throw C4AulExecError("value stack overflow, probably due to too deep recursion"); } void PushInt(int32_t i) { CheckOverflow(1); (++pCurVal)->SetInt(i); } void PushBool(bool b) { CheckOverflow(1); (++pCurVal)->SetBool(b); } void PushString(C4String * Str) { CheckOverflow(1); (++pCurVal)->SetString(Str); } void PushArray(C4ValueArray * Array) { CheckOverflow(1); (++pCurVal)->SetArray(Array); } void PushFunction(C4AulFunc * Fn) { CheckOverflow(1); (++pCurVal)->SetFunction(Fn); } void PushPropList(C4PropList * PropList) { CheckOverflow(1); (++pCurVal)->SetPropList(PropList); } void PushValue(const C4Value &rVal) { CheckOverflow(1); (++pCurVal)->Set(rVal); } void PushNullVals(int iCnt) { CheckOverflow(iCnt); pCurVal += iCnt; } bool PopValue() { assert (LocalValueStackSize() >= 1); (pCurVal--)->Set0(); return true; } void PopValues(int n) { assert (LocalValueStackSize() >= n); while (n--) (pCurVal--)->Set0(); } void PopValuesUntil(C4Value *pUntilVal) { assert (pUntilVal >= Values - 1); while (pCurVal > pUntilVal) (pCurVal--)->Set0(); } int ContextStackSize() const { return pCurCtx - Contexts + 1; } int ValueStackSize() const { return pCurVal - Values + 1; } int LocalValueStackSize() const { return ContextStackSize() ? pCurVal - pCurCtx->Pars - pCurCtx->Func->GetParCount() - pCurCtx->Func->VarNamed.iSize + 1 : pCurVal - Values + 1; } ALWAYS_INLINE void CheckOpPars(C4V_Type Type1, C4V_Type Type2, const char * opname) { // Get parameters C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal; // Typecheck parameters if (!pPar1->CheckParConversion(Type1)) throw C4AulExecError(FormatString(R"(operator "%s" left side got %s, but expected %s)", opname, pPar1->GetTypeName(), GetC4VName(Type1)).getData()); if (!pPar2->CheckParConversion(Type2)) throw C4AulExecError(FormatString(R"(operator "%s" right side got %s, but expected %s)", opname, pPar2->GetTypeName(), GetC4VName(Type2)).getData()); } ALWAYS_INLINE void CheckOpPar(C4V_Type Type1, const char * opname) { // Typecheck parameter if (!pCurVal->CheckParConversion(Type1)) throw C4AulExecError(FormatString(R"(operator "%s": got %s, but expected %s)", opname, pCurVal->GetTypeName(), GetC4VName(Type1)).getData()); } C4V_Type CheckArrayAccess(C4Value *pStructure, C4Value *pIndex) { if (pStructure->CheckConversion(C4V_Array)) { if (!pIndex->CheckConversion(C4V_Int)) throw C4AulExecError(FormatString("array access: index of type %s, but expected int", pIndex->GetTypeName()).getData()); return C4V_Array; } else if (pStructure->CheckConversion(C4V_PropList)) { if (!pIndex->CheckConversion(C4V_String)) throw C4AulExecError(FormatString("proplist access: index of type %s, but expected string", pIndex->GetTypeName()).getData()); return C4V_PropList; } else throw C4AulExecError(FormatString("can't access %s as array or proplist", pStructure->GetTypeName()).getData()); } C4AulBCC *Call(C4AulFunc *pFunc, C4Value *pReturn, C4Value *pPars, C4PropList * pContext = nullptr); }; extern C4AulExec AulExec; // script profiler entry class C4AulProfiler { private: // map entry struct Entry { C4AulScriptFunc *pFunc; uint32_t tProfileTime; bool operator < (const Entry &e2) const { return tProfileTime < e2.tProfileTime ; } }; // items std::vector Times; void CollectEntry(C4AulScriptFunc *pFunc, uint32_t tProfileTime); void CollectTimes(C4PropListStatic * p); void CollectTimes(); static void ResetTimes(C4PropListStatic * p); static void ResetTimes(); void Show(); public: static void Abort() { AulExec.StopProfiling(); } static void StartProfiling(C4ScriptHost *pScript); // reset times and start collecting new ones static void StopProfiling(); // stop the profiler and displays results }; #endif // C4AULEXEC_H