101 lines
3.4 KiB
C++
101 lines
3.4 KiB
C++
#ifndef ANGELSCRIPTDEBUGGER_ANGELSCRIPTDEBUGGER_HPP
|
|
#define ANGELSCRIPTDEBUGGER_ANGELSCRIPTDEBUGGER_HPP
|
|
#include <angelscript.h>
|
|
#include <iostream>
|
|
#include <unordered_set>
|
|
#include <variant>
|
|
#include "asio.hpp"
|
|
#include "Breakpoint.hpp"
|
|
#include "DebugAdapterProtocol/BaseProtocol.hpp"
|
|
|
|
class AngelscriptDebugger {
|
|
public:
|
|
~AngelscriptDebugger() {
|
|
_server->close();
|
|
delete _server;
|
|
}
|
|
|
|
void Run(uint16_t port);
|
|
void Stop() { _server->close(); }
|
|
|
|
void RegisterContext(asIScriptContext* ctx);
|
|
void Continue() {
|
|
std::cout << "Continuing from suspension" << std::endl;
|
|
_storedVariableReferences.clear();
|
|
while (!_pausedContexts.empty()) {
|
|
auto* ctx = _pausedContexts[_pausedContexts.size() - 1];
|
|
_pausedContexts.pop_back();
|
|
std::thread([](asIScriptContext* ctx) { ctx->Execute(); }, ctx).detach();
|
|
}
|
|
}
|
|
bool HasDebuggerAttached() const {
|
|
for (const auto& c : _connections) {
|
|
if (c->is_open()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
size_t StoreVariable(void* address, int typeId, const std::optional<std::function<void(void* address)>>& release) {
|
|
_storedVariableReferences.emplace_back(std::make_unique<PointerVariable>(address, typeId, release));
|
|
return _storedVariableReferences.size();
|
|
}
|
|
|
|
private:
|
|
static void on_line_callback(asIScriptContext* ctx, AngelscriptDebugger* dbg);
|
|
static void on_exception_callback(asIScriptContext* ctx, AngelscriptDebugger* d);
|
|
|
|
void EnterBreakpoint(asIScriptContext* ctx, const std::string& section, size_t line);
|
|
|
|
[[noreturn]] void AcceptLoop();
|
|
void ClientLoop(asio::ip::tcp::socket&);
|
|
void Send(DebugAdapterProtocol::ProtocolMessage* msg);
|
|
void Send(asio::ip::tcp::socket* client, DebugAdapterProtocol::ProtocolMessage* msg);
|
|
void OnMessage(asio::ip::tcp::socket* client, DebugAdapterProtocol::ProtocolMessage* msg);
|
|
void OnRequest(asio::ip::tcp::socket* client, DebugAdapterProtocol::Request* msg);
|
|
std::string GetResolvedScriptPath(const char* scriptSection);
|
|
|
|
void Next(asIScriptContext* ctx);
|
|
void StepInto(asIScriptContext* ctx);
|
|
void StepOut(asIScriptContext* ctx);
|
|
|
|
asio::io_context _ioContext;
|
|
asio::ip::tcp::acceptor* _server;
|
|
std::unordered_set<asio::ip::tcp::socket*> _connections;
|
|
|
|
std::unordered_map<std::string, std::unordered_set<size_t>> _breakpoints;
|
|
std::vector<asIScriptContext*> _pausedContexts;
|
|
std::optional<std::string> _scriptPath;
|
|
|
|
asIScriptContext* _nextContext;
|
|
bool _next;
|
|
size_t _nextDepth;
|
|
bool _stepInto;
|
|
bool _stepOut;
|
|
|
|
struct StackScope {
|
|
size_t StackLevel;
|
|
uint8_t Type;
|
|
|
|
StackScope(size_t stackLevel, uint8_t type) : StackLevel(stackLevel), Type(type) {}
|
|
};
|
|
struct PointerVariable {
|
|
void* Address;
|
|
int TypeID;
|
|
std::optional<std::function<void(void*)>> Release;
|
|
|
|
PointerVariable(void* address, int typeId, std::optional<std::function<void(void*)>> release)
|
|
: Address(address), TypeID(typeId), Release(release) {}
|
|
~PointerVariable() {
|
|
if (Release.has_value()) {
|
|
Release.value()(Address);
|
|
}
|
|
}
|
|
};
|
|
|
|
std::vector<std::variant<std::unique_ptr<StackScope>, std::unique_ptr<PointerVariable>>> _storedVariableReferences;
|
|
};
|
|
|
|
#endif // ANGELSCRIPTDEBUGGER_ANGELSCRIPTDEBUGGER_HPP
|