forked from Mirrors/openclonk
+ Initial C4Aul debugging support
parent
1fe837cc34
commit
1d5d8b7d19
|
@ -42,6 +42,7 @@ class C4AulScriptFunc;
|
|||
class C4AulDefFunc;
|
||||
class C4AulScript;
|
||||
class C4AulScriptEngine;
|
||||
class C4AulDebug;
|
||||
|
||||
struct C4AulContext;
|
||||
struct C4AulBCC;
|
||||
|
@ -198,6 +199,7 @@ enum C4AulBCCType
|
|||
AB_FOREACH_NEXT, // foreach: next element
|
||||
AB_RETURN, // return statement
|
||||
AB_ERR, // parse error at this position
|
||||
AB_DEBUG, // debug break
|
||||
AB_EOFN, // end of function
|
||||
AB_EOF, // end of file
|
||||
};
|
||||
|
@ -248,13 +250,16 @@ struct C4AulScriptContext : public C4AulContext
|
|||
C4Value *Return;
|
||||
C4Value *Pars;
|
||||
C4Value *Vars;
|
||||
C4AulScriptFunc *Func;
|
||||
C4AulFunc *Func;
|
||||
bool TemporaryScript;
|
||||
C4ValueList NumVars;
|
||||
C4AulBCC *CPos;
|
||||
time_t tTime; // initialized only by profiler if active
|
||||
|
||||
C4AulScriptFunc *SFunc();
|
||||
int ParCnt() const { return Vars - Pars; }
|
||||
StdStrBuf Dump(StdStrBuf Dump = StdStrBuf(""));
|
||||
StdStrBuf DumpVerbose(StdStrBuf Dump = StdStrBuf(""));
|
||||
void dump(StdStrBuf Dump = StdStrBuf(""));
|
||||
};
|
||||
|
||||
|
@ -298,6 +303,8 @@ class C4AulFunc
|
|||
|
||||
};
|
||||
|
||||
inline C4AulScriptFunc *C4AulScriptContext::SFunc() { return Func->SFunc(); }
|
||||
|
||||
// script function class
|
||||
class C4AulScriptFunc : public C4AulFunc
|
||||
{
|
||||
|
@ -351,7 +358,6 @@ class C4AulScriptFunc : public C4AulFunc
|
|||
friend class C4AulScript;
|
||||
};
|
||||
|
||||
|
||||
// defined function class
|
||||
class C4AulDefFunc : C4AulFunc
|
||||
{
|
||||
|
@ -518,7 +524,6 @@ class C4AulScript
|
|||
friend class C4AulParseState;
|
||||
};
|
||||
|
||||
|
||||
// holds all C4AulScripts
|
||||
class C4AulScriptEngine : public C4AulScript
|
||||
{
|
||||
|
@ -526,6 +531,7 @@ class C4AulScriptEngine : public C4AulScript
|
|||
C4AList itbl; // include table
|
||||
C4AList atbl; // append table
|
||||
C4AulFuncMap FuncLookUp;
|
||||
C4AulDebug *pDebug;
|
||||
|
||||
public:
|
||||
int warnCnt, errCnt; // number of warnings/errors
|
||||
|
@ -568,6 +574,10 @@ class C4AulScriptEngine : public C4AulScript
|
|||
|
||||
BOOL DenumerateVariablePointers();
|
||||
void UnLink(); // called when a script is being reloaded (clears string table)
|
||||
|
||||
bool InitDebug(uint16_t iPort, const char *szPassword, const char *szHost, bool fWait);
|
||||
C4AulDebug *GetDebugger() const { return pDebug; }
|
||||
|
||||
// Compile scenario script data (without strings and constants)
|
||||
void CompileFunc(StdCompiler *pComp);
|
||||
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
|
||||
#ifndef C4AULDEBUG_H
|
||||
#define C4AULDEBUG_H
|
||||
|
||||
#include "C4Aul.h"
|
||||
#include "C4NetIO.h"
|
||||
|
||||
// manages a debugging interface
|
||||
class C4AulDebug : public C4NetIOTCP, private C4NetIO::CBClass
|
||||
{
|
||||
public:
|
||||
C4AulDebug();
|
||||
~C4AulDebug();
|
||||
|
||||
private:
|
||||
bool fInit, fConnected;
|
||||
class C4AulExec *pExec;
|
||||
C4NetIO::addr_t PeerAddr, AllowedAddr;
|
||||
StdCopyStrBuf Password;
|
||||
|
||||
enum DebugState
|
||||
{
|
||||
DS_Go, // execute until next break point
|
||||
DS_Stop, // execution stopped
|
||||
DS_Step, // executing until next step point
|
||||
DS_StepOver, // executung until next step point on same level or above (or break point)
|
||||
DS_StepOut // executing until next step point above (or break point)
|
||||
};
|
||||
DebugState eState;
|
||||
int iStepCallDepth;
|
||||
|
||||
private:
|
||||
|
||||
// Overridden
|
||||
virtual void PackPacket(const C4NetIOPacket &rPacket, StdBuf &rOutBuf);
|
||||
virtual size_t UnpackPacket(const StdBuf &rInBuf, const C4NetIO::addr_t &addr);
|
||||
|
||||
// Callbacks
|
||||
bool OnConn(const C4NetIO::addr_t &AddrPeer, const C4NetIO::addr_t &AddrConnect, const addr_t *pOwnAddr, C4NetIO *pNetIO);
|
||||
void OnDisconn(const C4NetIO::addr_t &AddrPeer, C4NetIO *pNetIO, const char *szReason);
|
||||
void OnPacket(const class C4NetIOPacket &rPacket, C4NetIO *pNetIO);
|
||||
|
||||
public:
|
||||
bool isConnected() const { return fConnected; }
|
||||
|
||||
void SetPassword(const char *szPassword) { Password = szPassword; }
|
||||
bool SetAllowed(const char *szHost);
|
||||
void SetEngine(class C4AulExec *pnExec) { pExec = pnExec; }
|
||||
|
||||
bool Init(uint16_t iPort);
|
||||
bool Close();
|
||||
|
||||
void OnLog(const char *szLine);
|
||||
void DebugStep(C4AulBCC *pCPos);
|
||||
void DebugStepIn(C4AulBCC *pCPos);
|
||||
void DebugStepOut(C4AulBCC *pCPos, C4AulScriptContext *pRetCtx, C4Value *pRVal);
|
||||
|
||||
private:
|
||||
void StepPoint(C4AulBCC *pCPos, C4AulScriptContext *pRetCtx = NULL, C4Value *pRVal = NULL);
|
||||
|
||||
StdStrBuf FormatCodePos(C4AulScriptContext *pCtx, C4AulBCC *pCPos);
|
||||
|
||||
void ProcessLine(const StdStrBuf &Line);
|
||||
|
||||
bool SendLine(const char *szType, const char *szData = NULL);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,152 @@
|
|||
|
||||
#ifndef C4AULEXEC_H
|
||||
#define C4AULEXEC_H
|
||||
|
||||
const int MAX_CONTEXT_STACK = 512;
|
||||
const int MAX_VALUE_STACK = 1024;
|
||||
|
||||
class C4AulExec
|
||||
{
|
||||
|
||||
public:
|
||||
C4AulExec()
|
||||
: pCurCtx(Contexts - 1), pCurVal(Values - 1), iTraceStart(-1)
|
||||
{ }
|
||||
|
||||
private:
|
||||
C4AulScriptContext Contexts[MAX_CONTEXT_STACK];
|
||||
C4Value Values[MAX_VALUE_STACK];
|
||||
|
||||
C4AulScriptContext *pCurCtx;
|
||||
C4Value *pCurVal;
|
||||
|
||||
int iTraceStart;
|
||||
bool fProfiling;
|
||||
time_t tDirectExecStart, tDirectExecTotal; // profiler time for DirectExec
|
||||
C4AulScript *pProfiledScript;
|
||||
|
||||
public:
|
||||
C4Value Exec(C4AulScriptFunc *pSFunc, C4Object *pObj, C4Value pPars[], bool fPassErrors, bool fTemporaryScript = false);
|
||||
C4Value Exec(C4AulBCC *pCPos, bool fPassErrors);
|
||||
|
||||
void StartTrace();
|
||||
void StartProfiling(C4AulScript *pScript); // resets profling times and starts recording the times
|
||||
void StopProfiling(); // stop the profiler and displays results
|
||||
void AbortProfiling() { fProfiling=false; }
|
||||
inline void StartDirectExec() { if (fProfiling) tDirectExecStart = timeGetTime(); }
|
||||
inline void StopDirectExec() { if (fProfiling) tDirectExecTotal += timeGetTime() - tDirectExecStart; }
|
||||
|
||||
int GetContextDepth() const { return pCurCtx - Contexts + 1; }
|
||||
C4AulScriptContext *GetContext(int iLevel) { return iLevel >= 0 && iLevel < GetContextDepth() ? Contexts + iLevel : NULL; }
|
||||
|
||||
private:
|
||||
|
||||
void PushContext(const C4AulScriptContext &rContext);
|
||||
void PopContext();
|
||||
|
||||
void CheckOverflow(int iCnt)
|
||||
{
|
||||
if(ValueStackSize() + iCnt > MAX_VALUE_STACK)
|
||||
throw new C4AulExecError(pCurCtx->Obj, "internal error: value stack overflow!");
|
||||
}
|
||||
|
||||
void PushString(C4String * Str)
|
||||
{
|
||||
CheckOverflow(1);
|
||||
(++pCurVal)->SetString(Str);
|
||||
}
|
||||
|
||||
void PushArray(C4ValueArray * Array)
|
||||
{
|
||||
CheckOverflow(1);
|
||||
(++pCurVal)->SetArray(Array);
|
||||
}
|
||||
|
||||
void PushValue(const C4Value &rVal)
|
||||
{
|
||||
CheckOverflow(1);
|
||||
(++pCurVal)->Set(rVal);
|
||||
}
|
||||
|
||||
void PushValueRef(C4Value &rVal)
|
||||
{
|
||||
CheckOverflow(1);
|
||||
(++pCurVal)->SetRef(&rVal);
|
||||
}
|
||||
|
||||
void PushNullVals(int iCnt)
|
||||
{
|
||||
CheckOverflow(iCnt);
|
||||
pCurVal += iCnt;
|
||||
}
|
||||
|
||||
bool PopValue()
|
||||
{
|
||||
if(LocalValueStackSize() < 1)
|
||||
throw new C4AulExecError(pCurCtx->Obj, "internal error: value stack underflow!");
|
||||
(pCurVal--)->Set0();
|
||||
return true;
|
||||
}
|
||||
|
||||
void PopValues(int n)
|
||||
{
|
||||
if(LocalValueStackSize() < n)
|
||||
throw new C4AulExecError(pCurCtx->Obj, "internal error: value stack underflow!");
|
||||
while(n--)
|
||||
(pCurVal--)->Set0();
|
||||
}
|
||||
|
||||
void PopValuesUntil(C4Value *pUntilVal)
|
||||
{
|
||||
if(pUntilVal < Values - 1)
|
||||
throw new C4AulExecError(pCurCtx->Obj, "internal error: value stack underflow!");
|
||||
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->Vars - pCurCtx->Func->SFunc()->VarNamed.iSize + 1
|
||||
: pCurVal - Values + 1;
|
||||
}
|
||||
|
||||
void CheckOpPars(int iOpID)
|
||||
{
|
||||
// Get parameters
|
||||
C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
|
||||
|
||||
// Typecheck parameters
|
||||
if(!pPar1->ConvertTo(C4ScriptOpMap[iOpID].Type1))
|
||||
throw new C4AulExecError(pCurCtx->Obj,
|
||||
FormatString("operator \"%s\" left side: got \"%s\", but expected \"%s\"!",
|
||||
C4ScriptOpMap[iOpID].Identifier, pPar1->GetTypeInfo(), GetC4VName(C4ScriptOpMap[iOpID].Type1)).getData());
|
||||
if(!pPar2->ConvertTo(C4ScriptOpMap[iOpID].Type2))
|
||||
throw new C4AulExecError(pCurCtx->Obj,
|
||||
FormatString("operator \"%s\" right side: got \"%s\", but expected \"%s\"!",
|
||||
C4ScriptOpMap[iOpID].Identifier, pPar2->GetTypeInfo(), GetC4VName(C4ScriptOpMap[iOpID].Type2)).getData());
|
||||
}
|
||||
void CheckOpPar(int iOpID)
|
||||
{
|
||||
// Typecheck parameter
|
||||
if(!pCurVal->ConvertTo(C4ScriptOpMap[iOpID].Type1))
|
||||
throw new C4AulExecError(pCurCtx->Obj,
|
||||
FormatString("operator \"%s\": got \"%s\", but expected \"%s\"!",
|
||||
C4ScriptOpMap[iOpID].Identifier, pCurVal->GetTypeInfo(), GetC4VName(C4ScriptOpMap[iOpID].Type1)).getData());
|
||||
}
|
||||
C4AulBCC *Call(C4AulFunc *pFunc, C4Value *pReturn, C4Value *pPars, C4Object *pObj = NULL, C4Def *pDef = NULL);
|
||||
};
|
||||
|
||||
extern C4AulExec AulExec;
|
||||
|
||||
#endif // C4AULEXEC_H
|
|
@ -191,7 +191,11 @@ class C4Game
|
|||
bool DebugMode;
|
||||
// next mission to be played after this one
|
||||
StdCopyStrBuf NextMission, NextMissionText, NextMissionDesc;
|
||||
|
||||
|
||||
private:
|
||||
// debug settings
|
||||
uint16_t DebugPort; StdStrBuf DebugPassword, DebugHost; bool DebugWait;
|
||||
|
||||
public:
|
||||
// Init and execution
|
||||
void Default();
|
||||
|
|
|
@ -18,8 +18,11 @@
|
|||
|
||||
#include <C4Include.h>
|
||||
#include <C4Aul.h>
|
||||
#include <C4AulExec.h>
|
||||
#include <C4AulDebug.h>
|
||||
|
||||
#ifndef BIG_C4INCLUDE
|
||||
#include <C4Application.h>
|
||||
#include <C4Config.h>
|
||||
#include <C4Def.h>
|
||||
#include <C4Log.h>
|
||||
|
@ -421,6 +424,8 @@ C4AulScriptEngine::C4AulScriptEngine():
|
|||
GlobalConstNames.Reset();
|
||||
GlobalConsts.Reset();
|
||||
GlobalConsts.SetNameList(&GlobalConstNames);
|
||||
|
||||
pDebug = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
@ -428,6 +433,8 @@ C4AulScriptEngine::~C4AulScriptEngine() { Clear(); }
|
|||
|
||||
void C4AulScriptEngine::Clear()
|
||||
{
|
||||
// stop debugger
|
||||
delete pDebug; pDebug = NULL;
|
||||
// clear inherited
|
||||
C4AulScript::Clear();
|
||||
// clear own stuff
|
||||
|
@ -490,6 +497,7 @@ BOOL C4AulScriptEngine::DenumerateVariablePointers()
|
|||
// runtime data only: don't denumerate consts
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void C4AulScriptEngine::CompileFunc(StdCompiler *pComp)
|
||||
{
|
||||
pComp->Value(mkNamingAdapt(Global, "Globals" , C4ValueList()));
|
||||
|
@ -497,6 +505,33 @@ void C4AulScriptEngine::CompileFunc(StdCompiler *pComp)
|
|||
GlobalNamedDefault.SetNameList(&GlobalNamedNames);
|
||||
pComp->Value(mkNamingAdapt(GlobalNamed, "GlobalNamed" , GlobalNamedDefault));
|
||||
}
|
||||
|
||||
bool C4AulScriptEngine::InitDebug(uint16_t iPort, const char *szPassword, const char *szHost, bool fWait)
|
||||
{
|
||||
// Create debug object
|
||||
if(!pDebug) pDebug = new C4AulDebug();
|
||||
// Initialize
|
||||
pDebug->SetPassword(szPassword);
|
||||
pDebug->SetAllowed(szHost);
|
||||
pDebug->SetEngine(&AulExec);
|
||||
if(!pDebug->Init(iPort))
|
||||
{ LogFatal("C4Aul debugger failed to initialize!"); return false; }
|
||||
// Log
|
||||
LogF("C4Aul debugger initialized on port %d", iPort);
|
||||
// Add to application
|
||||
Application.Add(pDebug);
|
||||
// Wait for connection
|
||||
if(fWait)
|
||||
{
|
||||
Log("C4Aul debugger waiting for connection...");
|
||||
while(!pDebug->isConnected())
|
||||
if(!Application.ScheduleProcs())
|
||||
return false;
|
||||
}
|
||||
// Done
|
||||
return true;
|
||||
}
|
||||
|
||||
/*--- C4AulFuncMap ---*/
|
||||
static const size_t CapacityInc = 1024;
|
||||
|
||||
|
|
|
@ -0,0 +1,329 @@
|
|||
|
||||
#include "C4Include.h"
|
||||
|
||||
#include "C4AulDebug.h"
|
||||
#include "C4AulExec.h"
|
||||
|
||||
#ifndef BIG_C4INCLUDE
|
||||
#include "C4Control.h"
|
||||
#include "C4Game.h"
|
||||
#endif
|
||||
|
||||
// *** C4AulDebug
|
||||
|
||||
C4AulDebug::C4AulDebug()
|
||||
: fInit(false), fConnected(false)
|
||||
{
|
||||
ZeroMem(&PeerAddr, sizeof PeerAddr);
|
||||
}
|
||||
|
||||
C4AulDebug::~C4AulDebug()
|
||||
{
|
||||
}
|
||||
|
||||
void C4AulDebug::PackPacket(const C4NetIOPacket &rPacket, StdBuf &rOutBuf)
|
||||
{
|
||||
// Enlarge buffer
|
||||
int iSize = rPacket.getSize(),
|
||||
iPos = rOutBuf.getSize();
|
||||
rOutBuf.Grow(iSize + 2);
|
||||
// Write packet
|
||||
rOutBuf.Write(rPacket, iPos);
|
||||
// Terminate
|
||||
uint8_t *pPos = getMBufPtr<uint8_t>(rOutBuf, iPos + iSize);
|
||||
*pPos = '\r'; *(pPos + 1) = '\n';
|
||||
}
|
||||
|
||||
size_t C4AulDebug::UnpackPacket(const StdBuf &rInBuf, const C4NetIO::addr_t &addr)
|
||||
{
|
||||
// Find line seperation
|
||||
const char *pSep = reinterpret_cast<const char *>(memchr(rInBuf.getData(), '\n', rInBuf.getSize()));
|
||||
if(!pSep)
|
||||
return 0;
|
||||
// Check if it's windows-style seperation
|
||||
int iSize = pSep - getBufPtr<char>(rInBuf) + 1,
|
||||
iLength = iSize - 1;
|
||||
if(iLength && *(pSep - 1) == '\r')
|
||||
iLength--;
|
||||
// Copy the line
|
||||
StdStrBuf Buf; Buf.Copy(getBufPtr<char>(rInBuf), iLength);
|
||||
// Password line?
|
||||
if(fConnected)
|
||||
ProcessLine(Buf);
|
||||
else if(!Password.getSize() || Password == Buf)
|
||||
{
|
||||
fConnected = true;
|
||||
SendLine("HLO", "This is " C4ENGINEINFOLONG ", " C4VERSION);
|
||||
Log("C4Aul debugger connected successfully!");
|
||||
}
|
||||
else
|
||||
C4NetIOTCP::Close(PeerAddr);
|
||||
// Consume line
|
||||
return iSize;
|
||||
}
|
||||
|
||||
bool C4AulDebug::OnConn(const C4NetIO::addr_t &AddrPeer, const C4NetIO::addr_t &AddrConnect, const addr_t *pOwnAddr, C4NetIO *pNetIO)
|
||||
{
|
||||
assert(pNetIO == this);
|
||||
// Already have a connection?
|
||||
if(fConnected) return false;
|
||||
// Check address
|
||||
if(AllowedAddr.sin_addr.s_addr)
|
||||
if(AllowedAddr.sin_addr.s_addr != AddrPeer.sin_addr.s_addr ||
|
||||
(AllowedAddr.sin_port && AllowedAddr.sin_port != AddrPeer.sin_port))
|
||||
{
|
||||
LogF("C4AulDebug blocked connection from %s:%d", inet_ntoa(AddrPeer.sin_addr), htons(AddrPeer.sin_port));
|
||||
return false;
|
||||
}
|
||||
// Log
|
||||
LogF("C4AulDebug got connection from %s:%d", inet_ntoa(AddrPeer.sin_addr), htons(AddrPeer.sin_port));
|
||||
// Accept connection
|
||||
PeerAddr = AddrPeer;
|
||||
return true;
|
||||
}
|
||||
|
||||
void C4AulDebug::OnDisconn(const C4NetIO::addr_t &AddrPeer, C4NetIO *pNetIO, const char *szReason)
|
||||
{
|
||||
LogF("C4AulDebug lost connection (%s)", szReason);
|
||||
fConnected = false;
|
||||
eState = DS_Go;
|
||||
ZeroMem(&PeerAddr, sizeof PeerAddr);
|
||||
}
|
||||
|
||||
void C4AulDebug::OnPacket(const class C4NetIOPacket &rPacket, C4NetIO *pNetIO)
|
||||
{
|
||||
// Won't get called
|
||||
}
|
||||
|
||||
bool C4AulDebug::SetAllowed(const char *szHost)
|
||||
{
|
||||
// Clear
|
||||
ZeroMem(&AllowedAddr, sizeof(AllowedAddr));
|
||||
// No host?
|
||||
if(!szHost || !*szHost) return true;
|
||||
// Resolve the address
|
||||
return ResolveAddress(szHost, &AllowedAddr, 0);
|
||||
}
|
||||
|
||||
bool C4AulDebug::Init(uint16_t iPort)
|
||||
{
|
||||
if(fInit) Close();
|
||||
if(iPort == P_NONE) return false;
|
||||
|
||||
// Register self as callback for network events
|
||||
C4NetIOTCP::SetCallback(this);
|
||||
|
||||
// Start listening
|
||||
if(!C4NetIOTCP::Init(iPort))
|
||||
return false;
|
||||
|
||||
// Okay
|
||||
fInit = true;
|
||||
eState = DS_Go;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C4AulDebug::Close()
|
||||
{
|
||||
if(!fInit) return true;
|
||||
fInit = fConnected = false;
|
||||
return C4NetIOTCP::Close();
|
||||
}
|
||||
|
||||
void C4AulDebug::OnLog(const char *szLine)
|
||||
{
|
||||
if(!fConnected) return;
|
||||
SendLine("LOG", szLine);
|
||||
}
|
||||
|
||||
void C4AulDebug::ProcessLine(const StdStrBuf &Line)
|
||||
{
|
||||
// Get command
|
||||
StdStrBuf Cmd;
|
||||
Cmd.CopyUntil(Line.getData(), ' ');
|
||||
// Get data
|
||||
const char *szData = Line.getPtr(Cmd.getLength());
|
||||
if(*szData) szData++;
|
||||
// Identify command
|
||||
const char *szCmd = Cmd.getData();
|
||||
bool fOkay = true;
|
||||
const char *szAnswer = NULL;
|
||||
if(SEqualNoCase(szCmd, "HELP"))
|
||||
{
|
||||
fOkay = false; szAnswer = "Yeah, like I'm going to explain that /here/";
|
||||
}
|
||||
else if(SEqualNoCase(szCmd, "BYE") || SEqualNoCase(szCmd, "QUIT"))
|
||||
C4NetIOTCP::Close(PeerAddr);
|
||||
else if(SEqualNoCase(szCmd, "SAY"))
|
||||
Game.Control.DoInput(CID_Message, new C4ControlMessage(C4CMT_Normal, szData), CDT_Direct);
|
||||
else if(SEqualNoCase(szCmd, "CMD"))
|
||||
Game.MessageInput.ProcessCommand(szData);
|
||||
else if(SEqualNoCase(szCmd, "STP") || SEqualNoCase(szCmd, "S"))
|
||||
eState = DS_Step;
|
||||
else if(SEqualNoCase(szCmd, "GO") || SEqualNoCase(szCmd, "G"))
|
||||
eState = DS_Go;
|
||||
else if(SEqualNoCase(szCmd, "STO") || SEqualNoCase(szCmd, "O"))
|
||||
eState = DS_StepOver;
|
||||
else if(SEqualNoCase(szCmd, "STR") || SEqualNoCase(szCmd, "R"))
|
||||
eState = DS_StepOut;
|
||||
else if(SEqualNoCase(szCmd, "EXC") || SEqualNoCase(szCmd, "E"))
|
||||
Game.Control.DoInput(CID_Script, new C4ControlScript(szData, C4ControlScript::SCOPE_Console, false), CDT_Decide);
|
||||
else if(SEqualNoCase(szCmd, "PSE"))
|
||||
if(Game.IsPaused())
|
||||
{
|
||||
Game.Unpause();
|
||||
szAnswer = "Game unpaused.";
|
||||
}
|
||||
else
|
||||
{
|
||||
Game.Pause();
|
||||
szAnswer = "Game paused.";
|
||||
}
|
||||
else
|
||||
{
|
||||
fOkay = false;
|
||||
szAnswer = "Can't do that";
|
||||
}
|
||||
// Send answer
|
||||
SendLine(fOkay ? "OK" : "ERR", szAnswer);
|
||||
}
|
||||
|
||||
bool C4AulDebug::SendLine(const char *szType, const char *szData)
|
||||
{
|
||||
StdStrBuf Line = szData ? FormatString("%s %s", szType, szData) : StdStrBuf(szType);
|
||||
return Send(C4NetIOPacket(Line.getData(), Line.getSize(), false, PeerAddr));
|
||||
}
|
||||
|
||||
void C4AulDebug::DebugStep(C4AulBCC *pCPos)
|
||||
{
|
||||
// Get top context
|
||||
C4AulScriptContext *pCtx = pExec->GetContext(pExec->GetContextDepth() - 1);
|
||||
|
||||
// Already stopped? Ignore.
|
||||
// This means we are doing some calculation with suspended script engine.
|
||||
// We do /not/ want to have recursive suspensions...
|
||||
if(eState == DS_Stop)
|
||||
return;
|
||||
|
||||
// Have break point?
|
||||
if(pCPos->Par.i)
|
||||
eState = DS_Step;
|
||||
|
||||
StepPoint(pCPos);
|
||||
}
|
||||
|
||||
void C4AulDebug::DebugStepIn(C4AulBCC *pCPos)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void C4AulDebug::DebugStepOut(C4AulBCC *pCPos, C4AulScriptContext *pRetCtx, C4Value *pRVal)
|
||||
{
|
||||
|
||||
// Ignore if already suspended, see above.
|
||||
if(eState == DS_Stop)
|
||||
return;
|
||||
|
||||
// This counts as a regular step point
|
||||
StepPoint(pCPos, pRetCtx, pRVal);
|
||||
|
||||
}
|
||||
|
||||
void C4AulDebug::StepPoint(C4AulBCC *pCPos, C4AulScriptContext *pRetCtx, C4Value *pRVal)
|
||||
{
|
||||
|
||||
// Maybe got a command in the meantime?
|
||||
Execute(0);
|
||||
|
||||
// Get current script context
|
||||
int iCallDepth = pExec->GetContextDepth();
|
||||
C4AulScriptContext *pCtx = pExec->GetContext(iCallDepth-1);
|
||||
|
||||
// When we're stepping out of a script function, the context of the returning
|
||||
// function hasn't been removed yet.
|
||||
if(pCtx == pRetCtx)
|
||||
{
|
||||
iCallDepth--;
|
||||
// Check if we are returning to a script function
|
||||
if(pCtx->Return)
|
||||
pCtx--;
|
||||
else
|
||||
pCtx = NULL;
|
||||
}
|
||||
|
||||
// Stepped out?
|
||||
if(pRVal && eState != DS_Go && iCallDepth <= iStepCallDepth)
|
||||
{
|
||||
StdStrBuf FuncDump = pRetCtx->Dump();
|
||||
StdStrBuf ReturnDump = pRVal->GetDataString();
|
||||
SendLine("RET", FormatString("%s = %s", FuncDump.getData(), ReturnDump.getData()).getData());
|
||||
|
||||
// Ignore as step point if we didn't "really" step out
|
||||
if(iCallDepth >= iStepCallDepth) return;
|
||||
}
|
||||
|
||||
// Stop?
|
||||
switch(eState)
|
||||
{
|
||||
// Continue normally
|
||||
case DS_Go: return;
|
||||
|
||||
// Always stop
|
||||
case DS_Stop: break;
|
||||
case DS_Step: break;
|
||||
|
||||
// Only stop for same level or above
|
||||
case DS_StepOver:
|
||||
if(iCallDepth > iStepCallDepth)
|
||||
return;
|
||||
break;
|
||||
|
||||
// Only stop above
|
||||
case DS_StepOut:
|
||||
if(iCallDepth >= iStepCallDepth)
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
// Let's stop here
|
||||
eState = DS_Stop;
|
||||
iStepCallDepth = iCallDepth;
|
||||
Game.HaltCount++;
|
||||
|
||||
// No valid stop position? Just continue
|
||||
if(!pCtx)
|
||||
{
|
||||
Game.HaltCount--;
|
||||
eState = DS_Step;
|
||||
return;
|
||||
}
|
||||
|
||||
// Signal
|
||||
if(pCPos && pCPos->bccType == AB_DEBUG && pCPos->Par.i)
|
||||
SendLine("STP", FormatString("Stopped on breakpoint %d", pCPos->Par.i).getData());
|
||||
else
|
||||
SendLine("STP", "Stepped");
|
||||
|
||||
// Position
|
||||
SendLine("POS", FormatCodePos(pCtx, pCPos).getData());
|
||||
|
||||
// Suspend until we get some command
|
||||
while(eState == DS_Stop)
|
||||
if(!Application.ScheduleProcs())
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Do whatever we've been told.
|
||||
Game.HaltCount--;
|
||||
}
|
||||
|
||||
StdStrBuf C4AulDebug::FormatCodePos(C4AulScriptContext *pCtx, C4AulBCC *pCPos)
|
||||
{
|
||||
// Get position in script
|
||||
const char *szScript = pCtx->SFunc()->pOrgScript->GetScript();
|
||||
int iLine = SGetLine(szScript, pCPos->SPos);
|
||||
// Format
|
||||
return FormatString("%s:%d", Config.AtRelativePath(pCtx->Func->Owner->ScriptName.getData()), iLine);
|
||||
}
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
#include <C4Include.h>
|
||||
#include <C4Aul.h>
|
||||
#include <C4AulExec.h>
|
||||
#include <C4AulDebug.h>
|
||||
|
||||
#ifndef BIG_C4INCLUDE
|
||||
#include <C4Object.h>
|
||||
|
@ -26,6 +28,8 @@
|
|||
#include <C4Wrappers.h>
|
||||
#endif
|
||||
|
||||
C4AulExec AulExec;
|
||||
|
||||
C4AulExecError::C4AulExecError(C4Object *pObj, const char *szError) : cObj(pObj)
|
||||
{
|
||||
// direct error message string
|
||||
|
@ -46,38 +50,35 @@ void C4AulExecError::show()
|
|||
#endif
|
||||
}
|
||||
|
||||
const int MAX_CONTEXT_STACK = 512;
|
||||
const int MAX_VALUE_STACK = 1024;
|
||||
|
||||
void C4AulScriptContext::dump(StdStrBuf Dump)
|
||||
{
|
||||
StdStrBuf C4AulScriptContext::Dump(StdStrBuf Dump)
|
||||
{
|
||||
bool fDirectExec = !*Func->Name;
|
||||
if(!fDirectExec)
|
||||
{
|
||||
{
|
||||
// Function name
|
||||
Dump.Append(Func->Name);
|
||||
// Parameters
|
||||
Dump.AppendChar('(');
|
||||
int iNullPars = 0;
|
||||
for(int i = 0; i < C4AUL_MAX_Par; i++)
|
||||
for(int i = 0; i < Func->GetParCount(); i++)
|
||||
if(Pars + i < Vars)
|
||||
if(!Pars[i])
|
||||
iNullPars++;
|
||||
else
|
||||
{
|
||||
{
|
||||
if(i > iNullPars)
|
||||
Dump.AppendChar(',');
|
||||
// Insert missing null parameters
|
||||
while(iNullPars > 0)
|
||||
{
|
||||
{
|
||||
Dump.Append("0,");
|
||||
iNullPars--;
|
||||
}
|
||||
}
|
||||
// Insert parameter
|
||||
Dump.Append(Pars[i].GetDataString());
|
||||
}
|
||||
}
|
||||
Dump.AppendChar(')');
|
||||
}
|
||||
}
|
||||
else
|
||||
Dump.Append(Func->Owner->ScriptName);
|
||||
// Context
|
||||
|
@ -85,192 +86,26 @@ void C4AulScriptContext::dump(StdStrBuf Dump)
|
|||
Dump.AppendFormat(" (obj %s)", C4VObj(Obj).GetDataString().getData());
|
||||
else if(Func->Owner->Def != NULL)
|
||||
Dump.AppendFormat(" (def %s)", Func->Owner->Def->Name.getData());
|
||||
return Dump;
|
||||
}
|
||||
|
||||
StdStrBuf C4AulScriptContext::DumpVerbose(StdStrBuf Dump)
|
||||
{
|
||||
bool fDirectExec = !*Func->Name;
|
||||
// Dump function and context
|
||||
Dump = this->Dump(Dump);
|
||||
// Script
|
||||
if(!fDirectExec && Func->Owner)
|
||||
if(!fDirectExec && Func->Owner && SFunc())
|
||||
Dump.AppendFormat(" (%s:%d)",
|
||||
Func->pOrgScript->ScriptName.getData(),
|
||||
SGetLine(Func->pOrgScript->GetScript(), CPos ? CPos->SPos : Func->Script));
|
||||
// Log it
|
||||
DebugLog(Dump.getData());
|
||||
}
|
||||
SFunc()->pOrgScript->ScriptName.getData(),
|
||||
SGetLine(SFunc()->pOrgScript->GetScript(), CPos ? CPos->SPos : SFunc()->Script));
|
||||
return Dump;
|
||||
}
|
||||
|
||||
class C4AulExec
|
||||
{
|
||||
|
||||
public:
|
||||
C4AulExec()
|
||||
: pCurCtx(Contexts - 1), pCurVal(Values - 1), iTraceStart(-1)
|
||||
{ }
|
||||
|
||||
private:
|
||||
C4AulScriptContext Contexts[MAX_CONTEXT_STACK];
|
||||
C4Value Values[MAX_VALUE_STACK];
|
||||
|
||||
C4AulScriptContext *pCurCtx;
|
||||
C4Value *pCurVal;
|
||||
|
||||
int iTraceStart;
|
||||
bool fProfiling;
|
||||
time_t tDirectExecStart, tDirectExecTotal; // profiler time for DirectExec
|
||||
C4AulScript *pProfiledScript;
|
||||
|
||||
public:
|
||||
C4Value Exec(C4AulScriptFunc *pSFunc, C4Object *pObj, C4Value pPars[], bool fPassErrors, bool fTemporaryScript = false);
|
||||
C4Value Exec(C4AulBCC *pCPos, bool fPassErrors);
|
||||
|
||||
void StartTrace();
|
||||
void StartProfiling(C4AulScript *pScript); // resets profling times and starts recording the times
|
||||
void StopProfiling(); // stop the profiler and displays results
|
||||
void AbortProfiling() { fProfiling=false; }
|
||||
inline void StartDirectExec() { if (fProfiling) tDirectExecStart = timeGetTime(); }
|
||||
inline void StopDirectExec() { if (fProfiling) tDirectExecTotal += timeGetTime() - tDirectExecStart; }
|
||||
|
||||
private:
|
||||
|
||||
void PushContext(const C4AulScriptContext &rContext)
|
||||
{
|
||||
if(pCurCtx >= Contexts + MAX_CONTEXT_STACK - 1)
|
||||
throw new C4AulExecError(pCurCtx->Obj, "context stack overflow!");
|
||||
*++pCurCtx = rContext;
|
||||
// Trace?
|
||||
if(iTraceStart >= 0)
|
||||
{
|
||||
StdStrBuf Buf("T");
|
||||
Buf.AppendChars('>', ContextStackSize() - iTraceStart);
|
||||
pCurCtx->dump(Buf);
|
||||
}
|
||||
// Profiler: Safe time to measure difference afterwards
|
||||
if (fProfiling) pCurCtx->tTime = timeGetTime();
|
||||
}
|
||||
|
||||
void PopContext()
|
||||
{
|
||||
if(pCurCtx < Contexts)
|
||||
throw new C4AulExecError(pCurCtx->Obj, "context stack underflow!");
|
||||
// Profiler adding up times
|
||||
if (fProfiling)
|
||||
{
|
||||
time_t dt = timeGetTime() - pCurCtx->tTime;
|
||||
if (dt && pCurCtx->Func)
|
||||
pCurCtx->Func->tProfileTime += dt;
|
||||
}
|
||||
// Trace done?
|
||||
if(iTraceStart >= 0)
|
||||
{
|
||||
if(ContextStackSize() <= iTraceStart)
|
||||
{
|
||||
iTraceStart = -1;
|
||||
}
|
||||
}
|
||||
if(pCurCtx->TemporaryScript)
|
||||
delete pCurCtx->Func->Owner;
|
||||
pCurCtx--;
|
||||
}
|
||||
|
||||
void CheckOverflow(int iCnt)
|
||||
{
|
||||
if(ValueStackSize() + iCnt > MAX_VALUE_STACK)
|
||||
throw new C4AulExecError(pCurCtx->Obj, "internal error: value stack overflow!");
|
||||
}
|
||||
|
||||
void PushString(C4String * Str)
|
||||
{
|
||||
CheckOverflow(1);
|
||||
(++pCurVal)->SetString(Str);
|
||||
}
|
||||
|
||||
void PushArray(C4ValueArray * Array)
|
||||
{
|
||||
CheckOverflow(1);
|
||||
(++pCurVal)->SetArray(Array);
|
||||
}
|
||||
|
||||
void PushValue(const C4Value &rVal)
|
||||
{
|
||||
CheckOverflow(1);
|
||||
(++pCurVal)->Set(rVal);
|
||||
}
|
||||
|
||||
void PushValueRef(C4Value &rVal)
|
||||
{
|
||||
CheckOverflow(1);
|
||||
(++pCurVal)->SetRef(&rVal);
|
||||
}
|
||||
|
||||
void PushNullVals(int iCnt)
|
||||
{
|
||||
CheckOverflow(iCnt);
|
||||
pCurVal += iCnt;
|
||||
}
|
||||
|
||||
bool PopValue()
|
||||
{
|
||||
if(LocalValueStackSize() < 1)
|
||||
throw new C4AulExecError(pCurCtx->Obj, "internal error: value stack underflow!");
|
||||
(pCurVal--)->Set0();
|
||||
return true;
|
||||
}
|
||||
|
||||
void PopValues(int n)
|
||||
{
|
||||
if(LocalValueStackSize() < n)
|
||||
throw new C4AulExecError(pCurCtx->Obj, "internal error: value stack underflow!");
|
||||
while(n--)
|
||||
(pCurVal--)->Set0();
|
||||
}
|
||||
|
||||
void PopValuesUntil(C4Value *pUntilVal)
|
||||
{
|
||||
if(pUntilVal < Values - 1)
|
||||
throw new C4AulExecError(pCurCtx->Obj, "internal error: value stack underflow!");
|
||||
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->Vars - pCurCtx->Func->VarNamed.iSize + 1
|
||||
: pCurVal - Values + 1;
|
||||
}
|
||||
|
||||
void CheckOpPars(int iOpID)
|
||||
{
|
||||
// Get parameters
|
||||
C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
|
||||
|
||||
// Typecheck parameters
|
||||
if(!pPar1->ConvertTo(C4ScriptOpMap[iOpID].Type1))
|
||||
throw new C4AulExecError(pCurCtx->Obj,
|
||||
FormatString("operator \"%s\" left side: got \"%s\", but expected \"%s\"!",
|
||||
C4ScriptOpMap[iOpID].Identifier, pPar1->GetTypeInfo(), GetC4VName(C4ScriptOpMap[iOpID].Type1)).getData());
|
||||
if(!pPar2->ConvertTo(C4ScriptOpMap[iOpID].Type2))
|
||||
throw new C4AulExecError(pCurCtx->Obj,
|
||||
FormatString("operator \"%s\" right side: got \"%s\", but expected \"%s\"!",
|
||||
C4ScriptOpMap[iOpID].Identifier, pPar2->GetTypeInfo(), GetC4VName(C4ScriptOpMap[iOpID].Type2)).getData());
|
||||
}
|
||||
void CheckOpPar(int iOpID)
|
||||
{
|
||||
// Typecheck parameter
|
||||
if(!pCurVal->ConvertTo(C4ScriptOpMap[iOpID].Type1))
|
||||
throw new C4AulExecError(pCurCtx->Obj,
|
||||
FormatString("operator \"%s\": got \"%s\", but expected \"%s\"!",
|
||||
C4ScriptOpMap[iOpID].Identifier, pCurVal->GetTypeInfo(), GetC4VName(C4ScriptOpMap[iOpID].Type1)).getData());
|
||||
}
|
||||
C4AulBCC *Call(C4AulFunc *pFunc, C4Value *pReturn, C4Value *pPars, C4Object *pObj = NULL, C4Def *pDef = NULL);
|
||||
};
|
||||
|
||||
C4AulExec AulExec;
|
||||
void C4AulScriptContext::dump(StdStrBuf Dump)
|
||||
{
|
||||
DebugLog(DumpVerbose(Dump).getData());
|
||||
}
|
||||
|
||||
C4Value C4AulExec::Exec(C4AulScriptFunc *pSFunc, C4Object *pObj, C4Value *pnPars, bool fPassErrors, bool fTemporaryScript)
|
||||
{
|
||||
|
@ -312,6 +147,10 @@ C4Value C4AulExec::Exec(C4AulScriptFunc *pSFunc, C4Object *pObj, C4Value *pnPars
|
|||
C4Value C4AulExec::Exec(C4AulBCC *pCPos, bool fPassErrors)
|
||||
{
|
||||
|
||||
// Debugger pointer
|
||||
C4AulDebug * const pDebug = Game.ScriptEngine.GetDebugger();
|
||||
pDebug->DebugStepIn(pCPos);
|
||||
|
||||
// Save start context
|
||||
C4AulScriptContext *pOldCtx = pCurCtx;
|
||||
|
||||
|
@ -821,7 +660,8 @@ C4Value C4AulExec::Exec(C4AulBCC *pCPos, bool fPassErrors)
|
|||
case AB_RETURN:
|
||||
{
|
||||
// Resolve reference
|
||||
if(!pCurCtx->Func->SFunc()->bReturnRef)
|
||||
C4AulFunc *pFunc = pCurCtx->Func;
|
||||
if(!pFunc->SFunc()->bReturnRef)
|
||||
pCurVal->Deref();
|
||||
|
||||
// Trace
|
||||
|
@ -832,8 +672,12 @@ C4Value C4AulExec::Exec(C4AulBCC *pCPos, bool fPassErrors)
|
|||
LogF("%s%s returned %s", Buf.getData(), pCurCtx->Func->Name, pCurVal->GetDataString().getData());
|
||||
}
|
||||
|
||||
// External call?
|
||||
// Notify debugger
|
||||
C4Value *pReturn = pCurCtx->Return;
|
||||
if(pDebug)
|
||||
pDebug->DebugStepOut(pReturn ? (pCurCtx-1)->CPos + 1 : NULL, pCurCtx, pCurVal);
|
||||
|
||||
// External call?
|
||||
if(!pReturn)
|
||||
{
|
||||
// Get return value and stop executing.
|
||||
|
@ -1011,6 +855,10 @@ C4Value C4AulExec::Exec(C4AulBCC *pCPos, bool fPassErrors)
|
|||
break;
|
||||
}
|
||||
|
||||
case AB_DEBUG:
|
||||
if(pDebug) pDebug->DebugStep(pCPos);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
@ -1094,6 +942,10 @@ C4AulBCC *C4AulExec::Call(C4AulFunc *pFunc, C4Value *pReturn, C4Value *pPars, C4
|
|||
ctx.CPos = NULL;
|
||||
PushContext(ctx);
|
||||
|
||||
// Notify debugger
|
||||
if(C4AulDebug *pDebug = Game.ScriptEngine.GetDebugger())
|
||||
pDebug->DebugStepIn(pSFunc->Code);
|
||||
|
||||
// Jump to code
|
||||
return pSFunc->Code;
|
||||
}
|
||||
|
@ -1106,7 +958,6 @@ C4AulBCC *C4AulExec::Call(C4AulFunc *pFunc, C4Value *pReturn, C4Value *pPars, C4
|
|||
CallCtx.Def = pDef;
|
||||
CallCtx.Caller = pCurCtx;
|
||||
|
||||
|
||||
#ifdef DEBUGREC_SCRIPT
|
||||
if (Game.FrameCounter >= DEBUGREC_START_FRAME)
|
||||
{
|
||||
|
@ -1139,20 +990,43 @@ C4AulBCC *C4AulExec::Call(C4AulFunc *pFunc, C4Value *pReturn, C4Value *pPars, C4
|
|||
AddDbgRec(RCT_AulFunc, sCallText.getData(), sCallText.getLength()+1);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Execute
|
||||
#ifdef _DEBUG
|
||||
C4AulScriptContext *pCtx = pCurCtx;
|
||||
#endif
|
||||
if(pReturn > pCurVal)
|
||||
PushValue(pFunc->Exec(&CallCtx, pPars, true));
|
||||
else
|
||||
pReturn->Set(pFunc->Exec(&CallCtx, pPars, true));
|
||||
#endif
|
||||
C4Value RVal(pFunc->Exec(&CallCtx, pPars, true));
|
||||
#ifdef _DEBUG
|
||||
assert(pCtx == pCurCtx);
|
||||
#endif
|
||||
|
||||
// Remove parameters from stack
|
||||
PopValuesUntil(pReturn);
|
||||
// Notify debugger
|
||||
if(C4AulDebug *pDebug = Game.ScriptEngine.GetDebugger())
|
||||
{
|
||||
// Make dummy context
|
||||
C4AulScriptContext ctx;
|
||||
ctx.Obj = pObj;
|
||||
ctx.Def = pDef;
|
||||
ctx.Caller = pCurCtx;
|
||||
ctx.Return = pReturn;
|
||||
ctx.Pars = pPars;
|
||||
ctx.Vars = pPars + pFunc->GetParCount();
|
||||
ctx.Func = pFunc;
|
||||
ctx.TemporaryScript = false;
|
||||
ctx.CPos = NULL;
|
||||
pDebug->DebugStepOut(pCurCtx->CPos + 1, &ctx, &RVal);
|
||||
}
|
||||
|
||||
// Save return value onto stack
|
||||
if(pReturn > pCurVal)
|
||||
PushValue(pFunc->Exec(&CallCtx, pPars, true));
|
||||
else
|
||||
{
|
||||
pReturn->Set(pFunc->Exec(&CallCtx, pPars, true));
|
||||
|
||||
// Remove parameters from stack
|
||||
PopValuesUntil(pReturn);
|
||||
}
|
||||
|
||||
// Continue
|
||||
return NULL;
|
||||
|
@ -1198,6 +1072,46 @@ void C4AulExec::StopProfiling()
|
|||
Profiler.Show();
|
||||
}
|
||||
|
||||
void C4AulExec::PushContext(const C4AulScriptContext &rContext)
|
||||
{
|
||||
if(pCurCtx >= Contexts + MAX_CONTEXT_STACK - 1)
|
||||
throw new C4AulExecError(pCurCtx->Obj, "context stack overflow!");
|
||||
*++pCurCtx = rContext;
|
||||
// Trace?
|
||||
if(iTraceStart >= 0)
|
||||
{
|
||||
StdStrBuf Buf("T");
|
||||
Buf.AppendChars('>', ContextStackSize() - iTraceStart);
|
||||
pCurCtx->dump(Buf);
|
||||
}
|
||||
// Profiler: Safe time to measure difference afterwards
|
||||
if (fProfiling) pCurCtx->tTime = timeGetTime();
|
||||
}
|
||||
|
||||
void C4AulExec::PopContext()
|
||||
{
|
||||
if(pCurCtx < Contexts)
|
||||
throw new C4AulExecError(pCurCtx->Obj, "context stack underflow!");
|
||||
// Profiler adding up times
|
||||
if (fProfiling)
|
||||
{
|
||||
time_t dt = timeGetTime() - pCurCtx->tTime;
|
||||
if (dt && pCurCtx->Func)
|
||||
pCurCtx->SFunc()->tProfileTime += dt;
|
||||
}
|
||||
// Trace done?
|
||||
if(iTraceStart >= 0)
|
||||
{
|
||||
if(ContextStackSize() <= iTraceStart)
|
||||
{
|
||||
iTraceStart = -1;
|
||||
}
|
||||
}
|
||||
if(pCurCtx->TemporaryScript)
|
||||
delete pCurCtx->Func->Owner;
|
||||
pCurCtx--;
|
||||
}
|
||||
|
||||
void C4AulProfiler::StartProfiling(C4AulScript *pScript)
|
||||
{
|
||||
AulExec.StartProfiling(pScript);
|
||||
|
|
|
@ -1102,6 +1102,7 @@ void C4AulParseState::AddBCC(C4AulBCCType eType, intptr_t X)
|
|||
case AB_EOF:
|
||||
case AB_JUMP:
|
||||
case AB_CALLNS:
|
||||
case AB_DEBUG:
|
||||
break;
|
||||
|
||||
case AB_STACK:
|
||||
|
@ -1777,6 +1778,7 @@ void C4AulParseState::Parse_Function()
|
|||
C4AulBCC * CPos = a->GetCodeByPos(Max(a->GetCodePos() - 1,0));
|
||||
if (!CPos || CPos->bccType != AB_RETURN || fJump)
|
||||
{
|
||||
AddBCC(AB_DEBUG);
|
||||
AddBCC(AB_INT);
|
||||
AddBCC(AB_RETURN);
|
||||
}
|
||||
|
@ -1819,6 +1821,7 @@ void C4AulParseState::Parse_Block()
|
|||
}
|
||||
void C4AulParseState::Parse_Statement()
|
||||
{
|
||||
AddBCC(AB_DEBUG);
|
||||
switch (TokenType)
|
||||
{
|
||||
// do we have a block start?
|
||||
|
|
|
@ -835,7 +835,6 @@ const char* C4Config::AtDataReadPath(const char *szFilename, bool fPreferWorkdir
|
|||
|
||||
const char* C4Config::AtDataReadPathCore(const char *szFilename, bool fPreferWorkdir)
|
||||
{
|
||||
const char *szPath;
|
||||
if (fPreferWorkdir && FileExists(szFilename))
|
||||
{
|
||||
SCopy(GetWorkingDirectory(),AtPathFilename,_MAX_PATH-1);
|
||||
|
|
|
@ -1706,6 +1706,10 @@ void C4Game::Default()
|
|||
pNetworkStatistics = NULL;
|
||||
iMusicLevel = 100;
|
||||
PlayList.Clear();
|
||||
DebugPort = 0;
|
||||
DebugPassword.Clear();
|
||||
DebugHost.Clear();
|
||||
DebugWait = false;
|
||||
}
|
||||
|
||||
void C4Game::Evaluate()
|
||||
|
@ -2475,6 +2479,11 @@ BOOL C4Game::LinkScriptEngine()
|
|||
// Set name list for globals
|
||||
ScriptEngine.GlobalNamed.SetNameList(&ScriptEngine.GlobalNamedNames);
|
||||
|
||||
// Activate debugger if requested
|
||||
if(DebugPort)
|
||||
if(!ScriptEngine.InitDebug(DebugPort, DebugPassword.getData(), DebugHost.getData(), DebugWait))
|
||||
return false;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@ -2967,6 +2976,15 @@ void C4Game::ParseCommandLine(const char *szCmdLine)
|
|||
// additional read-only data path
|
||||
if (SEqual2NoCase(szParameter, "/data:"))
|
||||
Config.General.AddAdditionalDataPath(szParameter + 6);
|
||||
// debug options
|
||||
if (SEqual2NoCase(szParameter, "/debug:"))
|
||||
DebugPort = atoi(szParameter + 7);
|
||||
if (SEqual2NoCase(szParameter, "/debugpass:"))
|
||||
DebugPassword = szParameter + 11;
|
||||
if (SEqual2NoCase(szParameter, "/debughost:"))
|
||||
DebugHost = szParameter + 11;
|
||||
if (SEqual2NoCase(szParameter, "/debugwait"))
|
||||
DebugWait = true;
|
||||
#ifdef _DEBUG
|
||||
// debug configs
|
||||
if (SEqualNoCase(szParameter, "/host"))
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <C4Include.h>
|
||||
#include <C4Log.h>
|
||||
#include <C4AulDebug.h>
|
||||
|
||||
#ifndef BIG_C4INCLUDE
|
||||
#include <C4Console.h>
|
||||
|
@ -149,6 +150,10 @@ bool Log(const char *szMessage)
|
|||
if (iDisableLog) return true;
|
||||
// security
|
||||
if(!szMessage) return false;
|
||||
|
||||
// Pass on to debugger
|
||||
if(C4AulDebug *pDebug = Game.ScriptEngine.GetDebugger())
|
||||
pDebug->OnLog(szMessage);
|
||||
// Pass on to console
|
||||
Console.Out(szMessage);
|
||||
// pass on to lobby
|
||||
|
|
|
@ -1405,7 +1405,7 @@ static long FnGetBase(C4AulContext *cthr, C4Object *pObj)
|
|||
|
||||
static C4ID FnGetMenu(C4AulContext *cthr, C4Object *pObj)
|
||||
{
|
||||
if (!pObj) pObj=cthr->Obj; if (!pObj) return C4ID(-1);
|
||||
if (!pObj) pObj=cthr->Obj; if (!pObj) return C4ID(0);
|
||||
if (pObj->Menu && pObj->Menu->IsActive())
|
||||
return pObj->Menu->GetIdentification();
|
||||
return C4MN_None;
|
||||
|
@ -4637,7 +4637,7 @@ static C4Value FnEval(C4AulContext *cthr, C4Value *strScript_C4V)
|
|||
// execute script in the same object
|
||||
enum C4AulScript::Strict Strict = C4AulScript::MAXSTRICT;
|
||||
if (cthr->Caller)
|
||||
Strict = cthr->Caller->Func->pOrgScript->Strict;
|
||||
Strict = cthr->Caller->SFunc()->pOrgScript->Strict;
|
||||
if (cthr->Obj)
|
||||
return cthr->Obj->Def->Script.DirectExec(cthr->Obj, FnStringPar(strScript_C4V->getStr()), "eval", true, Strict);
|
||||
else if (cthr->Def)
|
||||
|
@ -4715,7 +4715,7 @@ static C4Value FnVarN(C4AulContext *cthr, C4Value *strName_C4V)
|
|||
if(!cthr->Caller) return C4VNull;
|
||||
|
||||
// find variable
|
||||
int32_t iID = cthr->Caller->Func->VarNamed.GetItemNr(strName);
|
||||
int32_t iID = cthr->Caller->SFunc()->VarNamed.GetItemNr(strName);
|
||||
if(iID < 0)
|
||||
return C4VNull;
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
AFFAA14E0C2A896200444B95 /* C4ChatDlg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFFAA14B0C2A896200444B95 /* C4ChatDlg.cpp */; };
|
||||
AFFAA14F0C2A896200444B95 /* C4InteractiveThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFFAA14C0C2A896200444B95 /* C4InteractiveThread.cpp */; };
|
||||
AFFAA1500C2A896200444B95 /* C4Network2IRC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFFAA14D0C2A896200444B95 /* C4Network2IRC.cpp */; };
|
||||
D0AEFAD70FBF8EC400E2229D /* C4AulDebug.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0AEFAD60FBF8EC400E2229D /* C4AulDebug.cpp */; };
|
||||
D406269A0BB9440C00815E5D /* c4group_ng.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D40626990BB9440C00815E5D /* c4group_ng.cpp */; };
|
||||
D40626A00BB9477A00815E5D /* C4Config.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D439CADC0B8CB129001D508B /* C4Config.cpp */; };
|
||||
D40626A10BB9477A00815E5D /* C4Group.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D439CAF80B8CB12A001D508B /* C4Group.cpp */; };
|
||||
|
@ -284,6 +285,9 @@
|
|||
AFFAA14B0C2A896200444B95 /* C4ChatDlg.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = C4ChatDlg.cpp; sourceTree = "<group>"; };
|
||||
AFFAA14C0C2A896200444B95 /* C4InteractiveThread.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = C4InteractiveThread.cpp; sourceTree = "<group>"; };
|
||||
AFFAA14D0C2A896200444B95 /* C4Network2IRC.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = C4Network2IRC.cpp; sourceTree = "<group>"; };
|
||||
D0AEFAA10FBF8E5500E2229D /* C4AulDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = C4AulDebug.h; sourceTree = "<group>"; };
|
||||
D0AEFAA20FBF8E5500E2229D /* C4AulExec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = C4AulExec.h; sourceTree = "<group>"; };
|
||||
D0AEFAD60FBF8EC400E2229D /* C4AulDebug.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = C4AulDebug.cpp; sourceTree = "<group>"; };
|
||||
D40626910BB9418400815E5D /* c4group */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = c4group; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D40626990BB9440C00815E5D /* c4group_ng.cpp */ = {isa = PBXFileReference; fileEncoding = 12; lastKnownFileType = sourcecode.cpp.cpp; name = c4group_ng.cpp; path = ../group/c4group_ng.cpp; sourceTree = SOURCE_ROOT; };
|
||||
D40626AA0BB947A700815E5D /* C4Update.cpp */ = {isa = PBXFileReference; fileEncoding = 12; lastKnownFileType = sourcecode.cpp.cpp; path = C4Update.cpp; sourceTree = "<group>"; };
|
||||
|
@ -886,6 +890,7 @@
|
|||
D439CAD10B8CB129001D508B /* src */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D0AEFAD60FBF8EC400E2229D /* C4AulDebug.cpp */,
|
||||
3562890E0DA62F9000FB1482 /* C4MainMenu.cpp */,
|
||||
3562890F0DA62F9000FB1482 /* C4ObjectMenu.cpp */,
|
||||
3588FB750DA0FFD2001544C0 /* C4FileSelDlg.cpp */,
|
||||
|
@ -1068,6 +1073,8 @@
|
|||
D45B48A40D6B333A001A7AE2 /* inc */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D0AEFAA10FBF8E5500E2229D /* C4AulDebug.h */,
|
||||
D0AEFAA20FBF8E5500E2229D /* C4AulExec.h */,
|
||||
356289130DA62FA600FB1482 /* C4MainMenu.h */,
|
||||
356288E60DA62CAD00FB1482 /* C4ObjectMenu.h */,
|
||||
3588FB730DA0FFBE001544C0 /* C4FileSelDlg.h */,
|
||||
|
@ -1515,6 +1522,7 @@
|
|||
356289100DA62F9000FB1482 /* C4MainMenu.cpp in Sources */,
|
||||
356289110DA62F9000FB1482 /* C4ObjectMenu.cpp in Sources */,
|
||||
3514CB3A0DBCCE5F00534809 /* OpenURL.cpp in Sources */,
|
||||
D0AEFAD70FBF8EC400E2229D /* C4AulDebug.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue