forked from Mirrors/openclonk
Merge
commit
48229a8550
|
@ -1587,6 +1587,10 @@ void C4Game::Default()
|
|||
pNetworkStatistics = NULL;
|
||||
iMusicLevel = 100;
|
||||
PlayList.Clear();
|
||||
DebugPort = 0;
|
||||
DebugPassword.Clear();
|
||||
DebugHost.Clear();
|
||||
DebugWait = false;
|
||||
}
|
||||
|
||||
void C4Game::Evaluate()
|
||||
|
@ -2355,6 +2359,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;
|
||||
}
|
||||
|
||||
|
@ -2847,6 +2856,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"))
|
||||
|
|
|
@ -153,7 +153,9 @@ 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();
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include <C4Include.h>
|
||||
#include <C4Log.h>
|
||||
#include <C4AulDebug.h>
|
||||
|
||||
#include <C4Console.h>
|
||||
#include <C4GameLobby.h>
|
||||
|
@ -158,6 +159,10 @@ bool Log(const char *szMessage)
|
|||
if (iDisableLog) return true;
|
||||
// security
|
||||
if(!szMessage) return false;
|
||||
|
||||
// Pass on to debugger
|
||||
if(C4AulDebug *pDebug = ::ScriptEngine.GetDebugger())
|
||||
pDebug->OnLog(szMessage);
|
||||
// Pass on to console
|
||||
Console.Out(szMessage);
|
||||
// pass on to lobby
|
||||
|
|
|
@ -422,7 +422,7 @@ bool C4NetIOTCP::Execute(int iMaxTime, pollfd * fds) // (mt-safe)
|
|||
if(ret == 0)
|
||||
return true;
|
||||
// flush pipe
|
||||
assert(fdvec[cfd].fd == Pipe[0]);
|
||||
//assert(fdvec[cfd].fd == Pipe[0]);
|
||||
if(fdvec[cfd].events & fdvec[cfd].revents)
|
||||
{
|
||||
char c;
|
||||
|
@ -446,7 +446,7 @@ bool C4NetIOTCP::Execute(int iMaxTime, pollfd * fds) // (mt-safe)
|
|||
if(wsaEvents.lNetworkEvents & FD_ACCEPT)
|
||||
#else
|
||||
++cfd;
|
||||
assert(fds[cfd].fd == lsock);
|
||||
//assert(fds[cfd].fd == lsock);
|
||||
// a connection waiting for accept?
|
||||
if(fds[cfd].events & fds[cfd].revents)
|
||||
#endif
|
||||
|
@ -480,7 +480,7 @@ bool C4NetIOTCP::Execute(int iMaxTime, pollfd * fds) // (mt-safe)
|
|||
#else
|
||||
// got connection?
|
||||
++cfd;
|
||||
assert(fds[cfd].fd == pWait->sock);
|
||||
//assert(fds[cfd].fd == pWait->sock);
|
||||
if(fds[cfd].events & fds[cfd].revents)
|
||||
#endif
|
||||
{
|
||||
|
@ -535,7 +535,7 @@ bool C4NetIOTCP::Execute(int iMaxTime, pollfd * fds) // (mt-safe)
|
|||
#else
|
||||
// something to read from socket?
|
||||
++cfd;
|
||||
assert(fds[cfd].fd == sock);
|
||||
//assert(fds[cfd].fd == sock);
|
||||
if(POLLIN & fds[cfd].revents)
|
||||
#endif
|
||||
for(;;)
|
||||
|
@ -587,7 +587,7 @@ bool C4NetIOTCP::Execute(int iMaxTime, pollfd * fds) // (mt-safe)
|
|||
#ifdef STDSCHEDULER_USE_EVENTS
|
||||
if(wsaEvents.lNetworkEvents & FD_WRITE)
|
||||
#else
|
||||
assert(fds[cfd].fd == sock);
|
||||
//assert(fds[cfd].fd == sock);
|
||||
if(POLLOUT & fds[cfd].revents)
|
||||
#endif
|
||||
// send remaining data
|
||||
|
|
|
@ -22,12 +22,15 @@
|
|||
|
||||
#include <C4Include.h>
|
||||
#include <C4Aul.h>
|
||||
#include <C4AulExec.h>
|
||||
#include <C4AulDebug.h>
|
||||
|
||||
#include <C4Config.h>
|
||||
#include <C4Def.h>
|
||||
#include <C4Log.h>
|
||||
#include <C4Components.h>
|
||||
#include "C4LangStringTable.h"
|
||||
#include <C4Application.h>
|
||||
#include <C4LangStringTable.h>
|
||||
|
||||
C4AulError::C4AulError() {}
|
||||
|
||||
|
@ -440,6 +443,8 @@ C4AulScriptEngine::C4AulScriptEngine():
|
|||
GlobalConstNames.Reset();
|
||||
GlobalConsts.Reset();
|
||||
GlobalConsts.SetNameList(&GlobalConstNames);
|
||||
|
||||
pDebug = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
@ -447,6 +452,8 @@ C4AulScriptEngine::~C4AulScriptEngine() { Clear(); }
|
|||
|
||||
void C4AulScriptEngine::Clear()
|
||||
{
|
||||
// stop debugger
|
||||
delete pDebug; pDebug = NULL;
|
||||
// clear inherited
|
||||
C4AulScript::Clear();
|
||||
// clear own stuff
|
||||
|
@ -507,12 +514,40 @@ bool C4AulScriptEngine::DenumerateVariablePointers()
|
|||
// runtime data only: don't denumerate consts
|
||||
return true;
|
||||
}
|
||||
|
||||
void C4AulScriptEngine::CompileFunc(StdCompiler *pComp)
|
||||
{
|
||||
C4ValueMapData GlobalNamedDefault;
|
||||
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;
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ class C4AulScriptFunc;
|
|||
class C4AulDefFunc;
|
||||
class C4AulScript;
|
||||
class C4AulScriptEngine;
|
||||
class C4AulDebug;
|
||||
class C4LangStringTable;
|
||||
|
||||
struct C4AulContext;
|
||||
|
@ -206,6 +207,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
|
||||
};
|
||||
|
@ -263,6 +265,7 @@ struct C4AulScriptContext : public C4AulContext
|
|||
|
||||
int ParCnt() const { return Vars - Pars; }
|
||||
void dump(StdStrBuf Dump = StdStrBuf(""));
|
||||
StdStrBuf ReturnDump(StdStrBuf Dump = StdStrBuf(""));
|
||||
};
|
||||
|
||||
// base function class
|
||||
|
@ -357,7 +360,6 @@ class C4AulScriptFunc : public C4AulFunc
|
|||
friend class C4AulScript;
|
||||
};
|
||||
|
||||
|
||||
// defined function class
|
||||
class C4AulDefFunc : C4AulFunc
|
||||
{
|
||||
|
@ -437,6 +439,7 @@ class C4AulScript
|
|||
#ifdef _MSC_VER
|
||||
friend class C4AulScript;
|
||||
#endif
|
||||
friend class C4AulDebug;
|
||||
public:
|
||||
C4AulScript(); // constructor
|
||||
virtual ~C4AulScript(); // destructor
|
||||
|
@ -528,14 +531,15 @@ class C4AulScript
|
|||
C4LangStringTable *stringTable;
|
||||
};
|
||||
|
||||
|
||||
// holds all C4AulScripts
|
||||
class C4AulScriptEngine : public C4AulScript
|
||||
{
|
||||
|
||||
protected:
|
||||
C4AList itbl; // include table
|
||||
C4AList atbl; // append table
|
||||
C4AulFuncMap FuncLookUp;
|
||||
C4AulDebug *pDebug;
|
||||
|
||||
public:
|
||||
int warnCnt, errCnt; // number of warnings/errors
|
||||
|
@ -575,11 +579,16 @@ 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);
|
||||
inline C4AulDebug *GetDebugger() const { return pDebug; }
|
||||
|
||||
// Compile scenario script data (without strings and constants)
|
||||
void CompileFunc(StdCompiler *pComp);
|
||||
|
||||
friend class C4AulFunc;
|
||||
friend class C4AulParseState;
|
||||
friend class C4AulDebug;
|
||||
};
|
||||
|
||||
extern C4AulScriptEngine ScriptEngine;
|
||||
|
|
|
@ -0,0 +1,436 @@
|
|||
|
||||
#include <C4Include.h>
|
||||
#include <C4Version.h>
|
||||
#include <C4GameControl.h>
|
||||
#include <C4Game.h>
|
||||
#include <C4MessageInput.h>
|
||||
#include <C4Log.h>
|
||||
|
||||
#include "C4AulDebug.h"
|
||||
#include "C4AulExec.h"
|
||||
|
||||
// *** C4AulDebug
|
||||
|
||||
C4AulDebug::C4AulDebug()
|
||||
: fInit(false), fConnected(false)
|
||||
{
|
||||
ZeroMem(&PeerAddr, sizeof PeerAddr);
|
||||
}
|
||||
|
||||
C4AulDebug::~C4AulDebug()
|
||||
{
|
||||
for (std::list<StdStrBuf*>::iterator it = StackTrace.begin(); it != StackTrace.end(); it++)
|
||||
{delete *it;}
|
||||
}
|
||||
|
||||
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"))
|
||||
::Control.DoInput(CID_Message, new C4ControlMessage(C4CMT_Normal, szData), CDT_Direct);
|
||||
else if(SEqualNoCase(szCmd, "CMD"))
|
||||
::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"))
|
||||
::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 if (SEqualNoCase(szCmd, "LST"))
|
||||
{
|
||||
for (C4AulScript* script = ScriptEngine.Child0; script; script = script->Next) {
|
||||
SendLine(RelativePath(script->ScriptName));
|
||||
}
|
||||
}
|
||||
|
||||
// toggle breakpoint
|
||||
else if (SEqualNoCase(szCmd, "TBR"))
|
||||
{
|
||||
StdStrBuf scriptPath;
|
||||
scriptPath.CopyUntil(szData, ':');
|
||||
const char* lineStart = szData+1+scriptPath.getLength();
|
||||
int line = strtol(szData+1+scriptPath.getLength(), const_cast<char**>(&lineStart), 10);
|
||||
|
||||
C4AulScript* script;
|
||||
for (script = ScriptEngine.Child0; script; script = script->Next)
|
||||
{
|
||||
if (SEqualNoCase(RelativePath(script->ScriptName), scriptPath.getData()))
|
||||
break;
|
||||
}
|
||||
|
||||
if (script)
|
||||
{
|
||||
C4AulBCC* foundDebugChunk = NULL;
|
||||
const char* scriptText = script->GetScript();
|
||||
for (C4AulBCC* chunk = script->Code; chunk; chunk++)
|
||||
{
|
||||
switch (chunk->bccType) {
|
||||
case AB_DEBUG:
|
||||
int lineOfThisOne;
|
||||
if ((lineOfThisOne = SGetLine(scriptText, chunk->SPos)) == line)
|
||||
{
|
||||
foundDebugChunk = chunk;
|
||||
goto Done;
|
||||
}
|
||||
/*else {
|
||||
DebugLogF("Debug chunk at %d", lineOfThisOne);
|
||||
}*/
|
||||
|
||||
break;
|
||||
case AB_EOF:
|
||||
goto Done;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
Done:
|
||||
if (foundDebugChunk)
|
||||
{
|
||||
foundDebugChunk->Par.i = !foundDebugChunk->Par.i; // activate breakpoint
|
||||
}
|
||||
else
|
||||
{
|
||||
szAnswer = "Can't set breakpoint (wrong line?)";
|
||||
fOkay = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fOkay = false;
|
||||
szAnswer = "Can't find script";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else if (SEqualNoCase(szCmd, "SST"))
|
||||
{
|
||||
std::list<StdStrBuf*>::iterator it = StackTrace.begin();
|
||||
for (it++; it != StackTrace.end(); it++)
|
||||
{
|
||||
SendLine("AT", (*it)->getData());
|
||||
}
|
||||
SendLine("EST");
|
||||
}
|
||||
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->ReturnDump();
|
||||
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
|
||||
ObtainStackTrace(pCtx, pCPos);
|
||||
SendLine("POS", StackTrace.front()->getData());
|
||||
|
||||
// Suspend until we get some command
|
||||
while(eState == DS_Stop)
|
||||
if(!Application.ScheduleProcs())
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Do whatever we've been told.
|
||||
Game.HaltCount--;
|
||||
}
|
||||
|
||||
const char* C4AulDebug::RelativePath(StdStrBuf &path)
|
||||
{
|
||||
const char* p = path.getData();
|
||||
const char* result = Config.AtRelativePath(p);
|
||||
if (p != result)
|
||||
return result;
|
||||
// try path relative to scenario container
|
||||
StdStrBuf scenarioContainerPath;
|
||||
GetParentPath(::Game.ScenarioFile.GetName(), &scenarioContainerPath);
|
||||
return GetRelativePathS(p, scenarioContainerPath.getData());
|
||||
}
|
||||
|
||||
void C4AulDebug::ObtainStackTrace(C4AulScriptContext* pCtx, C4AulBCC* pCPos)
|
||||
{
|
||||
for (std::list<StdStrBuf*>::iterator it = StackTrace.begin(); it != StackTrace.end(); it++)
|
||||
{delete *it;}
|
||||
StackTrace.clear();
|
||||
for (int ctxNum = pExec->GetContextDepth()-1; ctxNum >= 0; ctxNum--)
|
||||
{
|
||||
C4AulScriptContext* c = pExec->GetContext(ctxNum);
|
||||
C4AulBCC* _cpos = c == pCtx ? pCPos : c->CPos;
|
||||
if (_cpos)
|
||||
{
|
||||
StdStrBuf* format = new StdStrBuf(FormatCodePos(c, _cpos));
|
||||
StackTrace.push_back(format);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StdStrBuf C4AulDebug::FormatCodePos(C4AulScriptContext *pCtx, C4AulBCC *pCPos)
|
||||
{
|
||||
// Get position in script
|
||||
const char *szScript = pCtx->Func->pOrgScript->GetScript();
|
||||
int iLine = SGetLine(szScript, pCPos->SPos);
|
||||
// Format
|
||||
return FormatString("%s:%d", RelativePath(pCtx->Func->pOrgScript->ScriptName), iLine);
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
|
||||
#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;
|
||||
|
||||
// temporary stuff
|
||||
std::list<StdStrBuf*> StackTrace;
|
||||
|
||||
void ObtainStackTrace(C4AulScriptContext* pCtx, C4AulBCC* pCPos);
|
||||
const char* RelativePath(StdStrBuf &path);
|
||||
|
||||
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
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include <C4Include.h>
|
||||
#include <C4Aul.h>
|
||||
#include <C4AulDebug.h>
|
||||
|
||||
#include <C4Object.h>
|
||||
#include <C4Config.h>
|
||||
|
@ -30,6 +31,10 @@
|
|||
#include <C4Record.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include <C4AulExec.h>
|
||||
|
||||
C4AulExec AulExec;
|
||||
|
||||
C4AulExecError::C4AulExecError(C4Object *pObj, const char *szError) : cObj(pObj)
|
||||
{
|
||||
// direct error message string
|
||||
|
@ -48,11 +53,10 @@ void C4AulExecError::show()
|
|||
::Messages.New(C4GM_Global,sMessage,NULL,ANY_OWNER);
|
||||
}
|
||||
|
||||
const int MAX_CONTEXT_STACK = 512;
|
||||
const int MAX_VALUE_STACK = 1024;
|
||||
|
||||
void C4AulScriptContext::dump(StdStrBuf Dump)
|
||||
StdStrBuf C4AulScriptContext::ReturnDump(StdStrBuf Dump)
|
||||
{
|
||||
if (!Func)
|
||||
return StdStrBuf("");
|
||||
bool fDirectExec = !*Func->Name;
|
||||
if(!fDirectExec)
|
||||
{
|
||||
|
@ -92,193 +96,15 @@ void C4AulScriptContext::dump(StdStrBuf Dump)
|
|||
Dump.AppendFormat(" (%s:%d)",
|
||||
Func->pOrgScript->ScriptName.getData(),
|
||||
SGetLine(Func->pOrgScript->GetScript(), CPos ? CPos->SPos : Func->Script));
|
||||
// Log it
|
||||
DebugLog(Dump.getData());
|
||||
// Return it
|
||||
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 PushPropList(C4PropList * PropList)
|
||||
{
|
||||
CheckOverflow(1);
|
||||
(++pCurVal)->SetPropList(PropList);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// Log it
|
||||
DebugLog(ReturnDump(Dump).getData());
|
||||
}
|
||||
|
||||
C4Value C4AulExec::Exec(C4AulScriptFunc *pSFunc, C4Object *pObj, C4Value *pnPars, bool fPassErrors, bool fTemporaryScript)
|
||||
{
|
||||
|
@ -320,6 +146,11 @@ C4Value C4AulExec::Exec(C4AulScriptFunc *pSFunc, C4Object *pObj, C4Value *pnPars
|
|||
C4Value C4AulExec::Exec(C4AulBCC *pCPos, bool fPassErrors)
|
||||
{
|
||||
|
||||
// Debugger pointer
|
||||
C4AulDebug * const pDebug = ::ScriptEngine.GetDebugger();
|
||||
if (pDebug)
|
||||
pDebug->DebugStepIn(pCPos);
|
||||
|
||||
// Save start context
|
||||
C4AulScriptContext *pOldCtx = pCurCtx;
|
||||
|
||||
|
@ -855,8 +686,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.
|
||||
|
@ -1024,6 +859,10 @@ C4Value C4AulExec::Exec(C4AulBCC *pCPos, bool fPassErrors)
|
|||
break;
|
||||
}
|
||||
|
||||
case AB_DEBUG:
|
||||
if(pDebug) pDebug->DebugStep(pCPos);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
@ -1107,6 +946,10 @@ C4AulBCC *C4AulExec::Call(C4AulFunc *pFunc, C4Value *pReturn, C4Value *pPars, C4
|
|||
ctx.CPos = NULL;
|
||||
PushContext(ctx);
|
||||
|
||||
// Notify debugger
|
||||
if(C4AulDebug *pDebug = ::ScriptEngine.GetDebugger())
|
||||
pDebug->DebugStepIn(pSFunc->Code);
|
||||
|
||||
// Jump to code
|
||||
return pSFunc->Code;
|
||||
}
|
||||
|
@ -1119,7 +962,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)
|
||||
{
|
||||
|
@ -1152,10 +994,11 @@ 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
|
||||
#endif
|
||||
if(pReturn > pCurVal)
|
||||
PushValue(pFunc->Exec(&CallCtx, pPars, true));
|
||||
else
|
||||
|
@ -1164,6 +1007,23 @@ C4AulBCC *C4AulExec::Call(C4AulFunc *pFunc, C4Value *pReturn, C4Value *pPars, C4
|
|||
assert(pCtx == pCurCtx);
|
||||
#endif
|
||||
|
||||
// Notify debugger
|
||||
if(C4AulDebug *pDebug = ::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 = pSFunc;
|
||||
ctx.TemporaryScript = false;
|
||||
ctx.CPos = NULL;
|
||||
pDebug->DebugStepOut(pCurCtx->CPos + 1, pCurCtx, pReturn);
|
||||
}
|
||||
|
||||
// Remove parameters from stack
|
||||
PopValuesUntil(pReturn);
|
||||
|
||||
|
|
|
@ -0,0 +1,195 @@
|
|||
|
||||
#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)
|
||||
{
|
||||
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 PushPropList(C4PropList * PropList)
|
||||
{
|
||||
CheckOverflow(1);
|
||||
(++pCurVal)->SetPropList(PropList);
|
||||
}
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
extern C4AulExec AulExec;
|
||||
|
||||
#endif // C4AULEXEC_H
|
|
@ -1107,6 +1107,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:
|
||||
|
@ -1661,6 +1662,23 @@ void C4AulParseState::Parse_Function()
|
|||
C4AulBCC * CPos = a->GetCodeByPos(Max(a->GetCodePos() - 1,0));
|
||||
if (!CPos || CPos->bccType != AB_RETURN || fJump)
|
||||
{
|
||||
|
||||
/*
|
||||
// all ok, insert a return
|
||||
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);
|
||||
}
|
||||
// and break
|
||||
Done = TRUE;
|
||||
// Do not blame this function for script errors between functions
|
||||
Fn = 0;
|
||||
return; */
|
||||
|
||||
AddBCC(AB_DEBUG);
|
||||
AddBCC(AB_NIL);
|
||||
AddBCC(AB_RETURN);
|
||||
}
|
||||
|
@ -1702,6 +1720,7 @@ void C4AulParseState::Parse_Block()
|
|||
|
||||
void C4AulParseState::Parse_Statement()
|
||||
{
|
||||
AddBCC(AB_DEBUG);
|
||||
switch (TokenType)
|
||||
{
|
||||
// do we have a block start?
|
||||
|
|
Loading…
Reference in New Issue