mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
fahhh
This commit is contained in:
@@ -0,0 +1,400 @@
|
||||
#pragma once
|
||||
|
||||
#include "V8Tree/Instance.h"
|
||||
#include "V8Tree/Service.h"
|
||||
#include "script/ThreadRef.h"
|
||||
#include "script/ScriptContext.h"
|
||||
|
||||
struct lua_State;
|
||||
struct lua_Debug;
|
||||
struct Table;
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
class Script;
|
||||
class ModuleScript;
|
||||
class DataModel;
|
||||
|
||||
namespace Scripting
|
||||
{
|
||||
class ScriptDebugger;
|
||||
|
||||
enum BreakOnErrorMode
|
||||
{
|
||||
BreakOnErrorMode_Never = 0,
|
||||
BreakOnErrorMode_AllExceptions,
|
||||
BreakOnErrorMode_UnhandledExceptions
|
||||
};
|
||||
|
||||
enum ExecutionMode
|
||||
{
|
||||
ExecutionMode_Continue = 0,
|
||||
ExecutionMode_Break
|
||||
};
|
||||
|
||||
struct ISpecialBreakpoint
|
||||
{
|
||||
virtual ~ISpecialBreakpoint() {}
|
||||
virtual bool hitTest(lua_State* L, lua_Debug *ar) = 0;
|
||||
lua_State* baseThread;
|
||||
};
|
||||
|
||||
extern const char* const sDebuggerManager;
|
||||
// Contains all data related to Lua debugging of Scripts
|
||||
class DebuggerManager
|
||||
: public DescribedNonCreatable<DebuggerManager, Instance, sDebuggerManager, Reflection::ClassDescriptor::INTERNAL_LOCAL, Security::LocalUser>
|
||||
{
|
||||
typedef DescribedNonCreatable<DebuggerManager, Instance, sDebuggerManager, Reflection::ClassDescriptor::INTERNAL_LOCAL, Security::LocalUser> Super;
|
||||
public:
|
||||
typedef boost::unordered_map<const Instance*, ScriptDebugger*> Debuggers;
|
||||
private:
|
||||
bool enabled;
|
||||
Debuggers debuggers;
|
||||
|
||||
rbx::signals::connection errorSignalConnection;
|
||||
rbx::signals::connection descendantAddedSignalConnection;
|
||||
|
||||
typedef boost::unordered_map<const Instance*, boost::shared_ptr<ScriptDebugger> > UnaddedDebuggers;
|
||||
UnaddedDebuggers unaddedDebuggers;
|
||||
|
||||
typedef boost::unordered_map<const lua_State*, ScriptDebugger*> DebuggersLookup;
|
||||
DebuggersLookup debuggersLookup;
|
||||
|
||||
RBX::DataModel *dataModel;
|
||||
BreakOnErrorMode breakOnErrorMode;
|
||||
|
||||
boost::scoped_ptr<ISpecialBreakpoint> specialBreakpoint;
|
||||
ExecutionMode executionMode;
|
||||
std::list<Lua::WeakThreadRef> pausedThreads, resumingPausedThreads, errorThreads;
|
||||
bool resuming;
|
||||
bool scriptAutoResume;
|
||||
|
||||
public:
|
||||
DebuggerManager();
|
||||
~DebuggerManager();
|
||||
|
||||
static DebuggerManager& singleton();
|
||||
|
||||
void setDataModel(RBX::DataModel *pDataModel);
|
||||
RBX::DataModel* getDataModel();
|
||||
|
||||
void enableDebugging();
|
||||
bool getEnabled() const { return enabled; }
|
||||
|
||||
BreakOnErrorMode getBreakOnErrorMode() const { return breakOnErrorMode; }
|
||||
void setBreakOnErrorMode(BreakOnErrorMode mode);
|
||||
|
||||
static Reflection::Variant readWatchValue(std::string expression, int stackFrame, lua_State* L);
|
||||
|
||||
const Debuggers& getDebuggers()
|
||||
{
|
||||
return debuggers;
|
||||
}
|
||||
shared_ptr<const Instances> getDebuggers_Reflection();
|
||||
|
||||
ScriptDebugger* findDebugger(lua_State* L);
|
||||
ScriptDebugger* findDebugger(Instance* script);
|
||||
|
||||
shared_ptr<ScriptDebugger> addDebugger(Instance* script);
|
||||
shared_ptr<Instance> addDebugger_Reflection(shared_ptr<Instance> script);
|
||||
void addDebugger(shared_ptr<ScriptDebugger> debugger);
|
||||
|
||||
void populateForLookup(lua_State* L, ScriptDebugger* debugger);
|
||||
|
||||
void pause();
|
||||
void resume();
|
||||
void stepOver();
|
||||
void stepInto();
|
||||
void stepOut();
|
||||
|
||||
void reset();
|
||||
void setScriptAutoResume(bool state) { scriptAutoResume = state; }
|
||||
|
||||
static void hook(lua_State* L, lua_Debug *ar);
|
||||
|
||||
rbx::signal<void(shared_ptr<Instance>)> debuggerAdded;
|
||||
rbx::signal<void(shared_ptr<Instance>)> debuggerRemoved;
|
||||
|
||||
protected:
|
||||
/*override*/ bool askForbidChild(const Instance* instance) const;
|
||||
/*override*/ void verifyAddChild(const Instance* newChild) const;
|
||||
/*override*/ void onChildAdded(Instance* child);
|
||||
/*override*/ void onChildRemoved(Instance* child);
|
||||
/*override*/ void onChildChanged(Instance* instance, const PropertyChanged& event);
|
||||
|
||||
void addScriptDebugger(Instance* instance);
|
||||
void onErrorSignal(lua_State* L);
|
||||
void onHook(lua_State* L, lua_Debug *ar);
|
||||
|
||||
void addUnaddedDebuggerForAddedDescendant(shared_ptr<RBX::Instance> instance);
|
||||
};
|
||||
|
||||
class DebuggerBreakpoint;
|
||||
class DebuggerWatch;
|
||||
|
||||
extern const char* const sScriptDebugger;
|
||||
// Debugs an RBX::Script
|
||||
class ScriptDebugger
|
||||
: public DescribedCreatable<ScriptDebugger, Instance, sScriptDebugger, Reflection::ClassDescriptor::PERSISTENT_HIDDEN, Security::LocalUser>
|
||||
{
|
||||
public:
|
||||
typedef boost::unordered_map<int, DebuggerBreakpoint*> Breakpoints;
|
||||
typedef std::vector<DebuggerWatch*> Watches;
|
||||
struct PausedThreadData;
|
||||
typedef boost::unordered_map<long, PausedThreadData> PausedThreads;
|
||||
private:
|
||||
typedef DescribedCreatable<ScriptDebugger, Instance, sScriptDebugger, Reflection::ClassDescriptor::PERSISTENT_HIDDEN, Security::LocalUser> Super;
|
||||
|
||||
Breakpoints breakpoints;
|
||||
Watches watches;
|
||||
|
||||
boost::scoped_ptr<ISpecialBreakpoint> specialBreakpoint;
|
||||
|
||||
shared_ptr<Instance> script;
|
||||
rbx::signals::scoped_connection scriptStartedConnection;
|
||||
rbx::signals::scoped_connection scriptStoppedConnection;
|
||||
rbx::signals::scoped_connection scriptParentChangedConnection;
|
||||
rbx::signals::scoped_connection scriptClonedConnection;
|
||||
Lua::WeakThreadRef rootThread; // The root thread of script. Set when the Script starts and reset when it stops
|
||||
|
||||
typedef boost::function<void(lua_State* L, lua_Debug *ar)> HookFunction;
|
||||
HookFunction hookFunction; // used to overload the hook function
|
||||
|
||||
Lua::WeakThreadRef pausedThread; // the thread that a breakpoint hit
|
||||
Lua::WeakThreadRef errorThread; // the thread that has error
|
||||
lua_Debug *breakpointHookData; // set for a short period of time during the hook when we encounter a breakpoint
|
||||
void* globalRawScriptPtr;
|
||||
Table* prevFuncTable;
|
||||
void* prevRawScriptPtr;
|
||||
int currentLine; // the current line when a breakpoint is hit
|
||||
bool ignoreDebuggerBreak; // whether to ignore breakpoint at the current line
|
||||
long currentThreadID;
|
||||
PausedThreads pausedThreads;
|
||||
bool rootThreadResumed;
|
||||
|
||||
public:
|
||||
ScriptDebugger()
|
||||
:currentLine(0)
|
||||
,breakpointHookData(NULL)
|
||||
,globalRawScriptPtr(NULL)
|
||||
,prevFuncTable(NULL)
|
||||
,prevRawScriptPtr(NULL)
|
||||
,ignoreDebuggerBreak(false)
|
||||
,currentThreadID(0)
|
||||
,rootThreadResumed(false)
|
||||
{}
|
||||
ScriptDebugger(Instance& script);
|
||||
~ScriptDebugger();
|
||||
|
||||
Instance* getScript() const { return script.get(); }
|
||||
|
||||
void setScript(Script* value);
|
||||
void setScript(ModuleScript* value);
|
||||
|
||||
std::string getScriptPath() const;
|
||||
void setScriptPath(std::string scriptPath);
|
||||
|
||||
void setIgnoreDebuggerBreak(bool state) { ignoreDebuggerBreak = state; }
|
||||
|
||||
DebuggerBreakpoint* findBreakpoint(int line);
|
||||
shared_ptr<DebuggerBreakpoint> setBreakpoint(int line);
|
||||
shared_ptr<Instance> setBreakpoint_Reflection(int line);
|
||||
const Breakpoints& getBreakpoints()
|
||||
{
|
||||
return breakpoints;
|
||||
}
|
||||
shared_ptr<const Instances> getBreakpoints_Reflection();
|
||||
|
||||
shared_ptr<DebuggerWatch> addWatch(std::string expression);
|
||||
shared_ptr<Instance> addWatch_Reflection(std::string expression);
|
||||
const Watches& getWatches()
|
||||
{
|
||||
return watches;
|
||||
}
|
||||
shared_ptr<const Instances> getWatches_Reflection();
|
||||
Reflection::Variant getWatchValue(DebuggerWatch* watch, int stackFrame = 0);
|
||||
Reflection::Variant getWatchValue_Reflection(shared_ptr<Instance> watch);
|
||||
|
||||
Reflection::Variant getKeyValue(std::string key, int stackFrame);
|
||||
|
||||
bool isDebugging() const
|
||||
{
|
||||
return !rootThread.empty();
|
||||
}
|
||||
|
||||
bool isPaused() const;
|
||||
|
||||
bool hasError() const
|
||||
{
|
||||
return !errorThread.empty();
|
||||
}
|
||||
|
||||
int getCurrentLine() const
|
||||
{
|
||||
return currentLine;
|
||||
}
|
||||
void pause();
|
||||
void resume();
|
||||
void resumeTo(int line);
|
||||
void stepOver();
|
||||
void stepInto();
|
||||
void stepOut();
|
||||
|
||||
struct FunctionInfo
|
||||
{
|
||||
boost::shared_ptr<RBX::Instance> script;
|
||||
int frame;
|
||||
std::string name;
|
||||
std::string what;
|
||||
std::string namewhat;
|
||||
std::string short_src;
|
||||
int currentline;
|
||||
int linedefined;
|
||||
int lastlinedefined;
|
||||
};
|
||||
typedef std::vector<FunctionInfo> Stack;
|
||||
Stack getStack();
|
||||
shared_ptr<const Reflection::ValueArray> getStack_Reflection();
|
||||
shared_ptr<const Reflection::ValueMap> getLocals(int stackIndex);
|
||||
shared_ptr<const Reflection::ValueMap> getUpvalues(int stackIndex);
|
||||
shared_ptr<const Reflection::ValueMap> getGlobals();
|
||||
void setLocal(std::string name, Reflection::Variant value, int stackFrame = 0);
|
||||
void setUpvalue(std::string name, Reflection::Variant value, int stackFrame = 0);
|
||||
void setGlobal(std::string name, Reflection::Variant value);
|
||||
|
||||
void handleError(lua_State* L);
|
||||
void updateHook();
|
||||
|
||||
ScriptContext::Result resumeThread(lua_State* L, bool evalLineHookForCurrentLine = false);
|
||||
bool handleHook(lua_State* L, lua_Debug *ar);
|
||||
bool onLineHook(lua_State* L, lua_Debug *ar);
|
||||
void debuggerBreak(lua_State* L, lua_Debug *ar);
|
||||
|
||||
struct PausedThreadData
|
||||
{
|
||||
int pausedLine;
|
||||
Lua::WeakThreadRef thread;
|
||||
|
||||
bool hasError;
|
||||
Stack callStack;
|
||||
std::string errorMessage;
|
||||
|
||||
PausedThreadData()
|
||||
:hasError(false)
|
||||
,pausedLine(0)
|
||||
{
|
||||
}
|
||||
};
|
||||
const PausedThreads& getPausedThreads() { return pausedThreads; }
|
||||
|
||||
bool isPausedThread(long threadID);
|
||||
bool isErrorThread(long threadID);
|
||||
|
||||
bool isRootThread(long threadID);
|
||||
bool isRootThreadResumed() { return rootThreadResumed; }
|
||||
|
||||
void setCurrentThread(long threadID);
|
||||
long getCurrentThread() { return currentThreadID; }
|
||||
|
||||
rbx::signal<void(int)> encounteredBreak;
|
||||
rbx::signal<void()> resuming;
|
||||
|
||||
rbx::signal<void(shared_ptr<Instance>)> breakpointAdded;
|
||||
rbx::signal<void(shared_ptr<Instance>)> breakpointRemoved;
|
||||
rbx::signal<void(shared_ptr<Instance>)> watchAdded;
|
||||
rbx::signal<void(shared_ptr<Instance>)> watchRemoved;
|
||||
rbx::signal<void(int, std::string, Stack)> scriptErrorDetected;
|
||||
|
||||
protected:
|
||||
/*override*/ bool askForbidChild(const Instance* instance) const;
|
||||
/*override*/ void verifySetParent(const Instance* newParent) const;
|
||||
/*override*/ void verifyAddChild(const Instance* newChild) const;
|
||||
/*override*/ void onChildAdded(Instance* child);
|
||||
/*override*/ void onChildRemoved(Instance* child);
|
||||
|
||||
private:
|
||||
void onScriptStarting(lua_State* L);
|
||||
void onScriptStopped();
|
||||
void onScriptParentChanged(shared_ptr<RBX::Instance> newParent);
|
||||
void onScriptCloned(boost::shared_ptr<Instance> clonedScript);
|
||||
bool shouldBreak(DebuggerBreakpoint* bp, lua_State* L);
|
||||
bool hasDifferentScriptInstances(lua_State* L);
|
||||
void handleError(std::string errorMessage, const Stack& stack = Stack());
|
||||
void setScript(Instance* value);
|
||||
boost::shared_ptr<ScriptDebugger> createClone(boost::shared_ptr<Instance> clonedScript);
|
||||
|
||||
template<class R>
|
||||
void withPausedThreadHook(lua_State* L, lua_Debug *ar, boost::function<R(lua_State* L, lua_Debug *ar)> f, R& r, shared_ptr<std::string>& error);
|
||||
|
||||
// TODO: template specialization for R=void
|
||||
template<class R>
|
||||
R withPausedThread(boost::function<R(lua_State* L, lua_Debug *ar)> f);
|
||||
static shared_ptr<Reflection::ValueMap> readLocals(int stackIndex, lua_State* L);
|
||||
static shared_ptr<Reflection::ValueMap> readUpvalues(int stackIndex, lua_State* L);
|
||||
static shared_ptr<Reflection::ValueMap> readGlobals(lua_State* L);
|
||||
static Stack readStack(lua_State* L);
|
||||
static RBX::Instance* getScriptForLuaState(lua_State* L);
|
||||
static void updateRootThread(ScriptDebugger* scriptDebugger, lua_State *L);
|
||||
static void setLuaHook(ScriptDebugger* scriptDebugger, int hookMask, lua_State *L);
|
||||
};
|
||||
|
||||
extern const char* const sDebuggerBreakpoint;
|
||||
class DebuggerBreakpoint
|
||||
: public DescribedCreatable<DebuggerBreakpoint, Instance, sDebuggerBreakpoint, Reflection::ClassDescriptor::PERSISTENT_HIDDEN, Security::LocalUser>
|
||||
{
|
||||
bool enabled;
|
||||
int line;
|
||||
std::string condition;
|
||||
public:
|
||||
DebuggerBreakpoint();
|
||||
DebuggerBreakpoint(int line);
|
||||
~DebuggerBreakpoint();
|
||||
|
||||
int getLine() const { return line; }
|
||||
|
||||
bool isEnabled() const { return enabled; }
|
||||
|
||||
const std::string& getCondition() const { return condition; }
|
||||
|
||||
static Reflection::BoundProp<bool> prop_Enabled;
|
||||
static Reflection::BoundProp<std::string> prop_Condition;
|
||||
|
||||
protected:
|
||||
/*override*/ void verifySetParent(const Instance* newParent) const;
|
||||
/*override*/ bool askForbidChild(const Instance* instance) const { return true; }
|
||||
/*override*/ void verifyAddChild(const Instance* newChild) const
|
||||
{
|
||||
throw std::runtime_error("DebuggerBreakpoint can have no children");
|
||||
}
|
||||
private:
|
||||
void setLine(int line);
|
||||
static Reflection::BoundProp<int> prop_Line_Data;
|
||||
|
||||
};
|
||||
|
||||
extern const char* const sDebuggerWatch;
|
||||
class DebuggerWatch
|
||||
: public DescribedCreatable<DebuggerWatch, Instance, sDebuggerWatch, Reflection::ClassDescriptor::PERSISTENT, Security::LocalUser>
|
||||
{
|
||||
std::string expression;
|
||||
public:
|
||||
DebuggerWatch() {}
|
||||
DebuggerWatch(std::string expression);
|
||||
const std::string& getCondition() const { return expression; }
|
||||
void checkExpressionSyntax();
|
||||
|
||||
const std::string& getExpression() const { return expression; }
|
||||
|
||||
static Reflection::BoundProp<std::string> prop_Expression;
|
||||
|
||||
protected:
|
||||
/*override*/ void verifySetParent(const Instance* newParent) const;
|
||||
/*override*/ bool askForbidChild(const Instance* instance) const { return true; }
|
||||
/*override*/ void verifyAddChild(const Instance* newChild) const
|
||||
{
|
||||
throw std::runtime_error("DebuggerWatch can have no children");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user