forked from Mirrors/openclonk
263 lines
6.8 KiB
C++
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
|