Merge with debug

stable-5.1
Martin Plicht 2010-02-24 17:30:32 +01:00
commit a6a12309e9
11 changed files with 900 additions and 196 deletions

View File

@ -1591,6 +1591,10 @@ void C4Game::Default()
pNetworkStatistics = NULL;
iMusicLevel = 100;
PlayList.Clear();
DebugPort = 0;
DebugPassword.Clear();
DebugHost.Clear();
DebugWait = false;
}
void C4Game::Evaluate()
@ -2361,6 +2365,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;
}
@ -2853,6 +2862,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"))

View File

@ -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();

View File

@ -24,6 +24,7 @@
#include <C4Include.h>
#include <C4Log.h>
#include <C4AulDebug.h>
#include <C4Console.h>
#include <C4GameLobby.h>
@ -158,6 +159,12 @@ bool Log(const char *szMessage)
if (iDisableLog) return true;
// security
if(!szMessage) return false;
#ifndef NOAULDEBUG
// Pass on to debugger
if(C4AulDebug *pDebug = ::ScriptEngine.GetDebugger())
pDebug->OnLog(szMessage);
#endif
// Pass on to console
Console.Out(szMessage);
// pass on to lobby

View File

@ -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,10 @@ C4AulScriptEngine::C4AulScriptEngine():
GlobalConstNames.Reset();
GlobalConsts.Reset();
GlobalConsts.SetNameList(&GlobalConstNames);
#ifndef NOAULDEBUG
pDebug = NULL;
#endif
}
@ -447,6 +454,10 @@ C4AulScriptEngine::~C4AulScriptEngine() { Clear(); }
void C4AulScriptEngine::Clear()
{
#ifndef NOAULDEBUG
// stop debugger
delete pDebug; pDebug = NULL;
#endif
// clear inherited
C4AulScript::Clear();
// clear own stuff
@ -507,12 +518,42 @@ 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)
{
#ifndef NOAULDEBUG
// 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;
}
#endif
// Done
return true;
}
/*--- C4AulFuncMap ---*/
static const size_t CapacityInc = 1024;

View File

@ -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
{
@ -438,6 +440,7 @@ class C4AulScript
#ifdef _MSC_VER
friend class C4AulScript;
#endif
friend class C4AulDebug;
public:
C4AulScript(); // constructor
virtual ~C4AulScript(); // destructor
@ -529,14 +532,17 @@ class C4AulScript
C4LangStringTable *stringTable;
};
// holds all C4AulScripts
class C4AulScriptEngine : public C4AulScript
{
protected:
C4AList itbl; // include table
C4AList atbl; // append table
C4AulFuncMap FuncLookUp;
#ifndef NOAULDEBUG
C4AulDebug *pDebug;
#endif
public:
int warnCnt, errCnt; // number of warnings/errors
@ -577,11 +583,18 @@ 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);
#ifndef NOAULDEBUG
inline C4AulDebug *GetDebugger() const { return pDebug; }
#endif
// Compile scenario script data (without strings and constants)
void CompileFunc(StdCompiler *pComp);
friend class C4AulFunc;
friend class C4AulParseState;
friend class C4AulDebug;
};
extern C4AulScriptEngine ScriptEngine;

View File

@ -0,0 +1,458 @@
#ifndef NOAULDEBUG
#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 if (SEqualNoCase(szCmd, "VAR"))
{
C4Value *val = NULL;
int varIndex;
C4AulScriptContext* pCtx = pExec->GetContext(pExec->GetContextDepth() - 1);
if (pCtx)
{
if ((varIndex = pCtx->Func->ParNamed.GetItemNr(szData)) != -1)
{
val = &pCtx->Pars[varIndex];
}
else if ((varIndex = pCtx->Func->VarNamed.GetItemNr(szData)) != 1)
{
val = &pCtx->Vars[varIndex];
}
}
StdStrBuf output = FormatString("%s=%s", szData, val ? val->GetDataString().getData() : "Unknown");
SendLine("VAR", output.getData());
}
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);
}
#endif

View File

@ -0,0 +1,77 @@
#ifndef C4AULDEBUG_H
#define C4AULDEBUG_H
#ifndef NOAULDEBUG
#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
#endif

View File

@ -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
@ -50,11 +55,10 @@ void C4AulExecError::show()
}
}
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)
{
@ -96,193 +100,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)
{
@ -325,6 +151,13 @@ C4Value C4AulExec::Exec(C4AulScriptFunc *pSFunc, C4Object *pObj, C4Value *pnPars
C4Value C4AulExec::Exec(C4AulBCC *pCPos, bool fPassErrors)
{
#ifndef NOAULDEBUG
// Debugger pointer
C4AulDebug * const pDebug = ::ScriptEngine.GetDebugger();
if (pDebug)
pDebug->DebugStepIn(pCPos);
#endif
// Save start context
C4AulScriptContext *pOldCtx = pCurCtx;
@ -860,8 +693,14 @@ C4Value C4AulExec::Exec(C4AulBCC *pCPos, bool fPassErrors)
LogF("%s%s returned %s", Buf.getData(), pCurCtx->Func->Name, pCurVal->GetDataString().getData());
}
// External call?
#ifndef NOAULDEBUG
// Notify debugger
C4Value *pReturn = pCurCtx->Return;
if(pDebug)
pDebug->DebugStepOut(pReturn ? (pCurCtx-1)->CPos + 1 : NULL, pCurCtx, pCurVal);
#endif
// External call?
if(!pReturn)
{
// Get return value and stop executing.
@ -1029,6 +868,12 @@ C4Value C4AulExec::Exec(C4AulBCC *pCPos, bool fPassErrors)
break;
}
case AB_DEBUG:
#ifndef NOAULDEBUG
if(pDebug) pDebug->DebugStep(pCPos);
#endif
break;
default:
assert(false);
}
@ -1112,6 +957,12 @@ C4AulBCC *C4AulExec::Call(C4AulFunc *pFunc, C4Value *pReturn, C4Value *pPars, C4
ctx.CPos = NULL;
PushContext(ctx);
#ifndef NOAULDEBUG
// Notify debugger
if(C4AulDebug *pDebug = ::ScriptEngine.GetDebugger())
pDebug->DebugStepIn(pSFunc->Code);
#endif
// Jump to code
return pSFunc->Code;
}
@ -1124,7 +975,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)
{
@ -1157,10 +1007,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
@ -1169,6 +1020,25 @@ C4AulBCC *C4AulExec::Call(C4AulFunc *pFunc, C4Value *pReturn, C4Value *pPars, C4
assert(pCtx == pCurCtx);
#endif
#ifndef NOAULDEBUG
// 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);
}
#endif
// Remove parameters from stack
PopValuesUntil(pReturn);

View File

@ -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

View File

@ -1104,6 +1104,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?

View File

@ -201,6 +201,7 @@
5804A5E810C4BBC600A26AD0 /* C4Globals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 58CBBCB710BDF19400FBA25E /* C4Globals.cpp */; };
5830903A10E11D2200F1AECF /* C4PlayerControl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5830903910E11D2200F1AECF /* C4PlayerControl.cpp */; };
583F3F53110A7E6D00C5E281 /* C4MeshAnimation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 583F3F52110A7E6D00C5E281 /* C4MeshAnimation.cpp */; };
58437DEE112EDF2200DFE775 /* C4AulDebug.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 58437DED112EDF2200DFE775 /* C4AulDebug.cpp */; };
5862E1191032E57A002CAE9E /* C4Application.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5862DF3B1032E57A002CAE9E /* C4Application.cpp */; };
5862E11A1032E57A002CAE9E /* C4FullScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5862DF3D1032E57A002CAE9E /* C4FullScreen.cpp */; };
5862E11B1032E57A002CAE9E /* C4Game.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5862DF3F1032E57A002CAE9E /* C4Game.cpp */; };
@ -501,6 +502,7 @@
5830903910E11D2200F1AECF /* C4PlayerControl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = C4PlayerControl.cpp; sourceTree = "<group>"; };
583F3F51110A7E6D00C5E281 /* C4MeshAnimation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = C4MeshAnimation.h; sourceTree = "<group>"; };
583F3F52110A7E6D00C5E281 /* C4MeshAnimation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = C4MeshAnimation.cpp; sourceTree = "<group>"; };
58437DED112EDF2200DFE775 /* C4AulDebug.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = C4AulDebug.cpp; sourceTree = "<group>"; };
58508E1C103482EF00533E7D /* StdMacApp.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = StdMacApp.mm; sourceTree = "<group>"; };
5862DF3B1032E57A002CAE9E /* C4Application.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = C4Application.cpp; sourceTree = "<group>"; };
5862DF3C1032E57A002CAE9E /* C4Application.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = C4Application.h; sourceTree = "<group>"; };
@ -1810,6 +1812,7 @@
5862E1021032E57A002CAE9E /* script */ = {
isa = PBXGroup;
children = (
58437DED112EDF2200DFE775 /* C4AulDebug.cpp */,
588FCF9D103637B700BCF686 /* C4StringTable.h */,
588FCF9E103637B700BCF686 /* C4StringTable.cpp */,
5862E1031032E57A002CAE9E /* C4AList.cpp */,
@ -2402,6 +2405,7 @@
58E9D4B710F2B267005760F5 /* tinyxmlerror.cpp in Sources */,
58E9D4B810F2B267005760F5 /* tinyxmlparser.cpp in Sources */,
583F3F53110A7E6D00C5E281 /* C4MeshAnimation.cpp in Sources */,
58437DEE112EDF2200DFE775 /* C4AulDebug.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};