openclonk/src/script/C4AulExec.h

263 lines
6.8 KiB
C++

/*
* 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<Entry> 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