diff --git a/Heady b/Heady deleted file mode 100755 index 42266bd..0000000 Binary files a/Heady and /dev/null differ diff --git a/Testing.py b/Testing.py deleted file mode 100644 index 9a5328d..0000000 --- a/Testing.py +++ /dev/null @@ -1,24 +0,0 @@ -import socket -import sys - -# Create a TCP/IP socket -sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - -# Connect the socket to the port where the server is listening -server_address = ('localhost', 8684) -print(sys.stderr, 'connecting to %s port %s' % server_address) -sock.connect(server_address) - -def send(msg): - b = bytes(msg, "utf-8") - sock.send(bytes("Content-Length: " + str(len(b)) + "\r\n\r\n", "ascii")) - sock.send(b) - -send('{"seq": 1, "type": "request", "command": "setBreakpoints", "arguments": {' - '"source": {"path": "TestScript"},' - '"breakpoints": [{ "line": 14} ]' - '} }') - -while True: - msg = sock.recv(128) - print(msg.decode("utf-8")) \ No newline at end of file diff --git a/include/angelscriptDebugger.hpp b/include/angelscriptDebugger.hpp deleted file mode 100644 index bd20686..0000000 --- a/include/angelscriptDebugger.hpp +++ /dev/null @@ -1,1795 +0,0 @@ - - -// begin --- DebugAdapterProtocol --- - - - -// end --- DebugAdapterProtocol --- - - - -// begin --- AngelscriptDebugger.cpp --- - - - -// begin --- AngelscriptDebugger.hpp --- - -#ifndef ANGELSCRIPTDEBUGGER_ANGELSCRIPTDEBUGGER_HPP -#define ANGELSCRIPTDEBUGGER_ANGELSCRIPTDEBUGGER_HPP -#include -#include -#include -#include - -// begin --- Breakpoint.hpp --- - -#ifndef ANGELSCRIPTDEBUGGER_BREAKPOINT_HPP -#define ANGELSCRIPTDEBUGGER_BREAKPOINT_HPP -#include -#include - -struct Breakpoint { - std::string Section; - int32_t Line; - - bool operator==(const Breakpoint& other) const { return (Section == other.Section && Line == other.Line); } -}; - -namespace std { - - template <> struct hash { - std::size_t operator()(const Breakpoint& k) const { - using std::hash; - using std::size_t; - using std::string; - - return ((hash()(k.Section) ^ (hash()(k.Line) << 1)) >> 1); - } - }; -} - -#endif // ANGELSCRIPTDEBUGGER_BREAKPOINT_HPP - - -// end --- Breakpoint.hpp --- - - - -// begin --- BaseProtocol.hpp --- - -#ifndef ANGELSCRIPTDEBUGGER_BASEPROTOCOL_HPP -#define ANGELSCRIPTDEBUGGER_BASEPROTOCOL_HPP -#include -#include -#include -#include -#include - -// begin --- Utils.hpp --- - -#ifndef ANGELSCRIPTDEBUGGER_UTILS_HPP -#define ANGELSCRIPTDEBUGGER_UTILS_HPP - -#define JsonSerializeOptional(obj, field) \ - if (field.has_value()) \ - obj[#field] = field.value() - -#define JsonDeserializeOptional(obj, field) \ - if (!obj[#field].empty()) \ - field = obj[#field] - -#endif // ANGELSCRIPTDEBUGGER_UTILS_HPP - - -// end --- Utils.hpp --- - - - -namespace DebugAdapterProtocol { - static size_t current_sequence = 0; - struct ProtocolMessage { - explicit ProtocolMessage(std::string t) : seq(current_sequence++), type(std::move(t)) {} - ProtocolMessage(std::string t, size_t s) : seq(s), type(std::move(t)) {} - - size_t seq; - std::string type{"event"}; - - static ProtocolMessage* FromJson(nlohmann::json& j); - [[nodiscard]] virtual nlohmann::json ToJson() const { return {{"seq", seq}, {"type", type}}; } - - virtual ~ProtocolMessage() = default; - }; - - struct RequestArguments { - RequestArguments(){}; - explicit RequestArguments(nlohmann::json&) {} - virtual ~RequestArguments() = default; - virtual nlohmann::json ToJson() const { return nlohmann::json ::object(); } - }; - - struct Request : public ProtocolMessage { - Request() : ProtocolMessage("request") {} - virtual std::string GetCommand() const = 0; - virtual std::optional GetArguments() const = 0; - - static ProtocolMessage* FromJson(nlohmann::json& j); - [[nodiscard]] nlohmann::json ToJson() const override { - auto o = ProtocolMessage::ToJson(); - o["command"] = GetCommand(); - if (GetArguments().has_value()) { - o["body"] = GetArguments().value()->ToJson(); - } - return o; - } - }; - - template - concept IsRequestArguments = std::is_base_of::value; - - template struct DefinedRequest : public Request { - std::optional arguments; - - static ProtocolMessage* FromJson(nlohmann::json& j); - ~DefinedRequest() override { - if (arguments.has_value()) { - delete arguments.value(); - } - } - [[nodiscard]] std::string GetCommand() const override { return S; } - [[nodiscard]] std::optional GetArguments() const override { return arguments; } - }; - - struct EventBody { - EventBody() = default; - explicit EventBody(nlohmann::json&) {} - virtual ~EventBody() = default; - - [[nodiscard]] virtual nlohmann::json ToJson() const { return nlohmann::json::object(); }; - }; - - template - concept IsEventBody = std::is_base_of::value; - - template struct Event : public ProtocolMessage { - Event() : ProtocolMessage("event") {} - explicit Event(T* b) : ProtocolMessage("event"), body(b) {} - - std::optional body; - - ~Event() override { - if (body.has_value()) { - delete body.value(); - } - } - - [[nodiscard]] nlohmann::json ToJson() const override { - auto o = ProtocolMessage::ToJson(); - o["event"] = S; - if (body.has_value()) { - o["body"] = body.value()->ToJson(); - } - return o; - } - }; - - struct ResponseBody { - ResponseBody() = default; - explicit ResponseBody(nlohmann::json&) {} - virtual ~ResponseBody() = default; - - [[nodiscard]] virtual nlohmann::json ToJson() const { return nlohmann::json::object(); }; - }; - - template - concept IsResponseBody = std::is_base_of::value; - - struct Response : public ProtocolMessage { - Response(size_t seq) : ProtocolMessage("response", seq), request_seq(seq) {} - size_t request_seq; - bool success = true; - std::optional message; - - virtual std::string GetCommand() const = 0; - virtual std::optional GetBody() const = 0; - }; - - template struct DefinedResponse : public Response { - DefinedResponse(size_t seq) : Response(seq) {} - - std::optional body; - - [[nodiscard]] std::string GetCommand() const override { return S; } - [[nodiscard]] std::optional GetBody() const override { return body; } - - [[nodiscard]] nlohmann::json ToJson() const override { - auto o = ProtocolMessage::ToJson(); - o["request_seq"] = request_seq; - o["success"] = success; - o["command"] = GetCommand(); - JsonSerializeOptional(o, message); - if (body.has_value()) { - o["body"] = body.value()->ToJson(); - } - return o; - } - }; - -} -#endif // ANGELSCRIPTDEBUGGER_BASEPROTOCOL_HPP - - -// end --- 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>& release) { - _storedVariableReferences.emplace_back(std::make_unique(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); - - asio::io_context _ioContext; - asio::ip::tcp::acceptor* _server; - std::unordered_set _connections; - - std::unordered_map> _breakpoints; - std::vector _pausedContexts; - std::optional _scriptPath; - - 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> Release; - - PointerVariable(void* address, int typeId, std::optional> release) - : Address(address), TypeID(typeId), Release(release) {} - ~PointerVariable() { - if (Release.has_value()) { - Release.value()(Address); - } - } - }; - - std::vector, std::unique_ptr>> _storedVariableReferences; -}; - -#endif // ANGELSCRIPTDEBUGGER_ANGELSCRIPTDEBUGGER_HPP - - -// end --- AngelscriptDebugger.hpp --- - - - -// begin --- ASVariableFormatter.hpp --- - -#ifndef ANGELSCRIPTDEBUGGER_ASVARIABLEFORMATTER_HPP -#define ANGELSCRIPTDEBUGGER_ASVARIABLEFORMATTER_HPP -#include - -// begin --- Types.hpp --- - -#ifndef ANGELSCRIPTDEBUGGER_TYPES_HPP -#define ANGELSCRIPTDEBUGGER_TYPES_HPP -#include -#include - -namespace DebugAdapterProtocol { - struct Source { - std::optional name; - std::optional path; - std::optional sourceReference; - - Source() {} - Source(std::string path) : path(path) {} - explicit Source(nlohmann::json& j) { - JsonDeserializeOptional(j, name); - JsonDeserializeOptional(j, path); - JsonDeserializeOptional(j, sourceReference); - } - - [[nodiscard]] nlohmann::json ToJson() const { - nlohmann::json o = nlohmann::json::object(); - JsonSerializeOptional(o, name); - JsonSerializeOptional(o, path); - JsonSerializeOptional(o, sourceReference); - return o; - } - }; - - struct Breakpoint { - std::optional id; - bool verified; - std::optional message; - std::optional source; - std::optional line; - std::optional column; - std::optional endLine; - std::optional endColumn; - std::optional instructionReference; - std::optional offset; - - Breakpoint(size_t id, Source& s, size_t line) : id(id), verified(true), source(s), line(line) {} - [[nodiscard]] nlohmann::json ToJson() const { - nlohmann::json o; - JsonSerializeOptional(o, id); - o["verified"] = verified; - JsonSerializeOptional(o, message); - if (source.has_value()) { - o["source"] = source.value().ToJson(); - } - JsonSerializeOptional(o, line); - JsonSerializeOptional(o, column); - JsonSerializeOptional(o, endLine); - JsonSerializeOptional(o, endColumn); - JsonSerializeOptional(o, instructionReference); - JsonSerializeOptional(o, offset); - return o; - } - }; - - struct BreakpointLocation { - size_t line; - std::optional column; - std::optional endLine; - std::optional endColumn; - }; - - struct SourceBreakpoint { - size_t line; - std::optional column; - std::optional condition; - std::optional hitCondition; - std::optional logMessage; - - SourceBreakpoint(nlohmann::json j) { - line = j["line"]; - JsonDeserializeOptional(j, column); - JsonDeserializeOptional(j, condition); - JsonDeserializeOptional(j, hitCondition); - JsonDeserializeOptional(j, logMessage); - } - - [[nodiscard]] nlohmann::json ToJson() const { - nlohmann::json o; - o["line"] = line; - JsonSerializeOptional(o, column); - JsonSerializeOptional(o, condition); - JsonSerializeOptional(o, hitCondition); - JsonSerializeOptional(o, logMessage); - return o; - } - }; - - struct Module { - std::string id; - std::string name; - std::optional path; - std::optional isOptimized; - std::optional isUserCode; - std::optional version; - std::optional symbolStatus; - std::optional symbolFilePath; - std::optional dateTimeStamp; - std::optional addressRange; - - [[nodiscard]] nlohmann::json ToJson() const { - nlohmann::json o; - o["id"] = id; - o["name"] = name; - JsonSerializeOptional(o, path); - JsonSerializeOptional(o, isOptimized); - JsonSerializeOptional(o, isUserCode); - JsonSerializeOptional(o, version); - JsonSerializeOptional(o, symbolStatus); - JsonSerializeOptional(o, symbolFilePath); - JsonSerializeOptional(o, dateTimeStamp); - JsonSerializeOptional(o, addressRange); - return o; - } - }; - - struct Thread { - size_t id; - std::string name; - Thread(size_t id, std::string name) : id(id), name(name) {} - - [[nodiscard]] nlohmann::json ToJson() const { - nlohmann::json o; - o["id"] = id; - o["name"] = name; - return o; - } - }; - - struct StackFrame { - size_t id; - std::string name; - std::optional source; - size_t line; - size_t column; - std::optional endLine; - std::optional endColumn; - std::optional canRestart = false; - std::optional instructionPointerReference; - - StackFrame(){}; - StackFrame(size_t id, std::string name, Source source, size_t line, size_t column) - : id(id), name(name), source(source), line(line), column(column) {} - - [[nodiscard]] nlohmann::json ToJson() const { - nlohmann::json o; - o["id"] = id; - o["name"] = name; - if (source.has_value()) { - o["source"] = source.value().ToJson(); - } - o["line"] = line; - o["column"] = column; - JsonSerializeOptional(o, endLine); - JsonSerializeOptional(o, endColumn); - JsonSerializeOptional(o, canRestart); - JsonSerializeOptional(o, instructionPointerReference); - return o; - } - }; - - struct Scope { - std::string name; - std::optional presentationHint; - size_t variablesReference; - size_t namedVariables; - size_t indexedVariables = 0; - bool expensive = false; - std::optional source; - std::optional line; - std::optional column; - std::optional endLine; - std::optional endColumn; - - Scope(std::string name, size_t reference, std::string presentationHint, size_t namedVariables) - : name(std::move(name)), presentationHint(std::move(presentationHint)), variablesReference(reference), - namedVariables(namedVariables) {} - - [[nodiscard]] nlohmann::json ToJson() const { - nlohmann::json o; - o["name"] = name; - JsonSerializeOptional(o, presentationHint); - o["variablesReference"] = variablesReference; - o["namedVariables"] = namedVariables; - o["indexedVariables"] = indexedVariables; - o["expensive"] = expensive; - if (source.has_value()) { - o["source"] = source.value().ToJson(); - } - JsonSerializeOptional(o, line); - JsonSerializeOptional(o, column); - JsonSerializeOptional(o, endLine); - JsonSerializeOptional(o, endColumn); - return o; - } - }; - - struct VariablePresentationHint { - std::optional kind; - std::optional> attributes; - std::optional visibility; - - [[nodiscard]] nlohmann::json ToJson() const { - nlohmann::json o; - JsonSerializeOptional(o, kind); - JsonSerializeOptional(o, attributes); - JsonSerializeOptional(o, visibility); - return o; - } - }; - - struct Variable { - std::string name; - std::string value; - std::optional type; - std::optional presentationHint; - std::optional evaluateName; - std::optional variablesReference = 0; - std::optional namedVariables = 0; - std::optional indexedVariables = 0; - std::optional memoryReference; - - [[nodiscard]] nlohmann::json ToJson() const { - nlohmann::json o; - o["name"] = name; - o["value"] = value; - JsonSerializeOptional(o, type); - if (presentationHint.has_value()) { - o["presentationHint"] = presentationHint.value().ToJson(); - } - JsonSerializeOptional(o, evaluateName); - JsonSerializeOptional(o, variablesReference); - JsonSerializeOptional(o, namedVariables); - JsonSerializeOptional(o, indexedVariables); - JsonSerializeOptional(o, memoryReference); - - return o; - } - - Variable(std::string name, std::string value, std::string type, - std::optional presentationHint = {}) - : name(std::move(name)), value(std::move(value)), type(std::move(type)), - presentationHint(presentationHint) {} - - static Variable FromString(std::string name, std::string value) { - return Variable( - name, value, "string", - VariablePresentationHint{.kind = "data", .attributes = std::vector{"rawString"}}); - } - - static Variable FromNull(std::string name, std::string type) { - return Variable(name, "null", type, - VariablePresentationHint{.kind = "data", .attributes = std::vector{}}); - } - - static Variable FromPointer(std::string name, std::string type, std::string display, size_t variableReference) { - auto v = Variable( - std::move(name), std::move(display), type, - VariablePresentationHint{.kind = "class", .attributes = std::vector{"hasObjectId"}}); - v.variablesReference = variableReference; - return v; - } - }; -} - -#endif // ANGELSCRIPTDEBUGGER_TYPES_HPP - - -// end --- Types.hpp --- - - - -class ASVariableFormatter { -public: - static nlohmann::json GetAsJsonValue(asIScriptEngine* engine, int, void*, int32_t depth = 0); - static DebugAdapterProtocol::Variable GetAsDAPVariable(asIScriptEngine* engine, asIScriptContext* ctx, - AngelscriptDebugger* debugger, const std::string& name, int, - void*); - static void GetChildDAPVariables(std::vector&, asIScriptEngine* engine, - asIScriptContext* ctx, AngelscriptDebugger* debugger, int, void*); -}; - -#endif // ANGELSCRIPTDEBUGGER_ASVARIABLEFORMATTER_HPP - - -// end --- ASVariableFormatter.hpp --- - - - -// begin --- Events.hpp --- - -#ifndef ANGELSCRIPTDEBUGGER_EVENTS_HPP -#define ANGELSCRIPTDEBUGGER_EVENTS_HPP - -namespace DebugAdapterProtocol { -#define EventDefinition(name, jsonName, ...) \ - static const char __c##name[] = jsonName; \ - struct name##EventBody : public EventBody __VA_ARGS__; \ - using name##Event = Event - - EventDefinition(Breakpoint, "breakpoint", { - std::string reason; - Breakpoint breakpoint; - nlohmann::json ToJson() const override { - auto o = EventBody::ToJson(); - o["reason"] = reason; - o["breakpoint"] = breakpoint.ToJson(); - return o; - } - }); - - EventDefinition(Continued, "continued", { - size_t threadId; - std::optional allThreadsContinued; - nlohmann::json ToJson() const override { - auto o = EventBody::ToJson(); - o["threadId"] = threadId; - JsonSerializeOptional(o, allThreadsContinued); - return o; - } - }); - - EventDefinition(Exited, "exited", { - int64_t exitCode; - nlohmann::json ToJson() const override { - auto o = EventBody::ToJson(); - o["exitCode"] = exitCode; - return o; - } - }); - - EventDefinition(Initialized, "initialized", {}); - - EventDefinition(Invalidated, "invalidated", { - std::optional> invalidatedAreas; - std::optional threadId; - std::optional stackFrameId; - - nlohmann::json ToJson() const override { - auto o = EventBody::ToJson(); - JsonSerializeOptional(o, invalidatedAreas); - JsonSerializeOptional(o, threadId); - JsonSerializeOptional(o, stackFrameId); - return o; - } - }); - - EventDefinition(LoadedSource, "loadedSource", { - std::string reason; - Source source; - nlohmann::json ToJson() const override { - auto o = EventBody::ToJson(); - o["reason"] = reason; - o["source"] = source.ToJson(); - return o; - } - }); - - EventDefinition(Memory, "memory", { - std::string memoryReference; - size_t offset; - size_t count; - nlohmann::json ToJson() const override { - auto o = EventBody::ToJson(); - o["memoryReference"] = memoryReference; - o["offset"] = offset; - o["count"] = count; - return o; - } - }); - - EventDefinition(Module, "module", { - std::string reason; - Module module; - nlohmann::json ToJson() const override { - auto o = EventBody::ToJson(); - o["reason"] = reason; - o["module"] = module.ToJson(); - return o; - } - }); - - EventDefinition(Output, "output", { - std::optional category; - std::string output; - std::optional group; - std::optional variablesReference; - std::optional source; - std::optional line; - std::optional column; - nlohmann::json ToJson() const override { - auto o = EventBody::ToJson(); - JsonSerializeOptional(o, category); - o["output"] = output; - JsonSerializeOptional(o, group); - JsonSerializeOptional(o, variablesReference); - if (source.has_value()) { - o["source"] = source.value().ToJson(); - } - JsonSerializeOptional(o, line); - JsonSerializeOptional(o, column); - return o; - } - }); - - EventDefinition(Process, "process", { - std::string name; - std::optional systemProcessId; - std::optional isLocalProcess; - std::optional startMethod; - std::optional pointerSize; - - nlohmann::json ToJson() const override { - auto o = EventBody::ToJson(); - o["name"] = name; - JsonSerializeOptional(o, systemProcessId); - JsonSerializeOptional(o, isLocalProcess); - JsonSerializeOptional(o, startMethod); - JsonSerializeOptional(o, pointerSize); - return o; - } - }); - - EventDefinition(ProgressEnd, "progressEnd", { - std::string progressId; - std::optional message; - - nlohmann::json ToJson() const override { - auto o = EventBody::ToJson(); - o["progressId"] = progressId; - JsonSerializeOptional(o, message); - return o; - } - }); - - EventDefinition(ProgressStart, "progressStart", { - std::string progressId; - std::string title; - std::optional requestId; - std::optional cancellable; - std::optional message; - std::optional percentage; - - nlohmann::json ToJson() const override { - auto o = EventBody::ToJson(); - o["progressId"] = progressId; - o["title"] = title; - JsonSerializeOptional(o, requestId); - JsonSerializeOptional(o, cancellable); - JsonSerializeOptional(o, message); - JsonSerializeOptional(o, percentage); - return o; - } - }); - - EventDefinition(ProgressUpdate, "progressUpdate", { - std::string progressId; - std::optional message; - std::optional percentage; - - nlohmann::json ToJson() const override { - auto o = EventBody::ToJson(); - o["progressId"] = progressId; - JsonSerializeOptional(o, message); - JsonSerializeOptional(o, percentage); - return o; - } - }); - - EventDefinition(Stopped, "stopped", { - std::string reason; - std::optional description; - std::optional number; - std::optional preserveFocusHint = false; - std::optional text; - std::optional allThreadsStopped = true; - std::optional> hitBreakpointIds; - - explicit StoppedEventBody(size_t breakpoint) { - reason = "breakpoint"; - hitBreakpointIds = {breakpoint}; - } - - nlohmann::json ToJson() const override { - auto o = EventBody::ToJson(); - o["reason"] = reason; - JsonSerializeOptional(o, description); - JsonSerializeOptional(o, number); - JsonSerializeOptional(o, preserveFocusHint); - JsonSerializeOptional(o, text); - JsonSerializeOptional(o, allThreadsStopped); - JsonSerializeOptional(o, hitBreakpointIds); - return o; - } - }); - - EventDefinition(terminated, "terminated", {}); - - EventDefinition(Thread, "thread", { - std::string reason; - std::optional threadId; - - nlohmann::json ToJson() const override { - auto o = EventBody::ToJson(); - o["reason"] = reason; - JsonSerializeOptional(o, threadId); - return o; - } - }); - -} - -#endif // ANGELSCRIPTDEBUGGER_EVENTS_HPP - - -// end --- Events.hpp --- - - - -// begin --- Requests.hpp --- - -#ifndef ANGELSCRIPTDEBUGGER_REQUESTS_HPP -#define ANGELSCRIPTDEBUGGER_REQUESTS_HPP -#include -#include -#include -#include - -namespace DebugAdapterProtocol { - -#define RequestDefinition(name, jsonName, ...) \ - static const char __c##name[] = jsonName; \ - struct name##Arguments : public RequestArguments __VA_ARGS__; \ - using name##Request = DefinedRequest - -#define RequestResponseDefinition(name, jsonName, ...) \ - static const char __cResponse##name[] = jsonName; \ - struct name##ResponseBody : public ResponseBody __VA_ARGS__; \ - using name##Response = DefinedResponse - -#define EmptyRequest(name, jsonName) \ - RequestDefinition(name, jsonName, {explicit name##Arguments(nlohmann::json & j) : RequestArguments(j){}}) - - RequestDefinition(Attach, "attach", { - size_t port; - std::string scriptPath; - - explicit AttachArguments(nlohmann::json & j) - : RequestArguments(j), port(j["port"]), scriptPath(j["scriptPath"]) {} - }); - - RequestDefinition(BreakpointLocations, "breakpointLocations", { - Source source; - size_t line; - std::optional column; - std::optional endLine; - std::optional endColumn; - BreakpointLocationsArguments() {} - explicit BreakpointLocationsArguments(nlohmann::json & j) - : RequestArguments(j), source(j["source"]), line(j["line"]) { - JsonDeserializeOptional(j, column); - JsonDeserializeOptional(j, endLine); - JsonDeserializeOptional(j, endColumn); - } - - [[nodiscard]] nlohmann::json ToJson() const override { - auto o = RequestArguments::ToJson(); - o["source"] = source.ToJson(); - o["line"] = line; - JsonSerializeOptional(o, column); - JsonSerializeOptional(o, endLine); - JsonSerializeOptional(o, endColumn); - return o; - } - }); - - EmptyRequest(Completions, "completions"); - EmptyRequest(ConfigurationDone, "configurationDone"); - EmptyRequest(Continue, "continue"); - EmptyRequest(DataBreakpointInfo, "dataBreakpointInfo"); - EmptyRequest(Disassemble, "disassemble"); - - RequestDefinition(Disconnect, "disconnect", { - std::optional restart; - std::optional terminateDebuggee; - std::optional suspendDebuggee; - - explicit DisconnectArguments(nlohmann::json & j) : RequestArguments(j) { - JsonDeserializeOptional(j, restart); - JsonDeserializeOptional(j, terminateDebuggee); - JsonDeserializeOptional(j, suspendDebuggee); - } - }); - - EmptyRequest(Evaluate, "evaluate"); - EmptyRequest(ExceptionInfo, "exceptionInfo"); - EmptyRequest(Goto, "goto"); - EmptyRequest(GotoTargets, "gotoTargets"); - EmptyRequest(Initialize, "initialize"); - RequestResponseDefinition(Initialize, "initialize", { - std::optional supportsConfigurationDoneRequest = true; - std::optional supportsFunctionBreakpoints; - std::optional supportsConditionalBreakpoints; - std::optional supportsHitConditionalBreakpoints; - std::optional supportsEvaluateForHovers; - std::optional supportsStepBack; - std::optional supportsSetVariable; - std::optional supportsRestartFrame; - std::optional supportsGotoTargetsRequest; - std::optional supportsStepInTargetsRequest; - std::optional supportsCompletionsRequest; - std::optional supportsModulesRequest; - std::optional supportsRestartRequest; - std::optional supportsExceptionOptions; - std::optional supportsValueFormattingOptions; - std::optional supportsExceptionInfoRequest; - std::optional supportTerminateDebuggee; - std::optional supportSuspendDebuggee; - std::optional supportsDelayedStackTraceLoading; - std::optional supportsLoadedSourcesRequest; - std::optional supportsLogPoints; - std::optional supportsTerminateThreadsRequest; - std::optional supportsSetExpression; - std::optional supportsTerminateRequest; - std::optional supportsDataBreakpoints; - std::optional supportsReadMemoryRequest; - std::optional supportsWriteMemoryRequest; - std::optional supportsDisassembleRequest; - std::optional supportsCancelRequest; - std::optional supportsBreakpointLocationsRequest; - std::optional supportsClipboardContext; - std::optional supportsSteppingGranularity; - std::optional supportsInstructionBreakpoints; - std::optional supportsExceptionFilterOptions; - - [[nodiscard]] nlohmann::json ToJson() const override { - auto o = ResponseBody::ToJson(); - JsonSerializeOptional(o, supportsConfigurationDoneRequest); - JsonSerializeOptional(o, supportsFunctionBreakpoints); - JsonSerializeOptional(o, supportsConditionalBreakpoints); - JsonSerializeOptional(o, supportsHitConditionalBreakpoints); - JsonSerializeOptional(o, supportsEvaluateForHovers); - JsonSerializeOptional(o, supportsStepBack); - JsonSerializeOptional(o, supportsSetVariable); - JsonSerializeOptional(o, supportsRestartFrame); - JsonSerializeOptional(o, supportsGotoTargetsRequest); - JsonSerializeOptional(o, supportsStepInTargetsRequest); - JsonSerializeOptional(o, supportsCompletionsRequest); - JsonSerializeOptional(o, supportsModulesRequest); - JsonSerializeOptional(o, supportsRestartRequest); - JsonSerializeOptional(o, supportsExceptionOptions); - JsonSerializeOptional(o, supportsValueFormattingOptions); - JsonSerializeOptional(o, supportsExceptionInfoRequest); - JsonSerializeOptional(o, supportTerminateDebuggee); - JsonSerializeOptional(o, supportSuspendDebuggee); - JsonSerializeOptional(o, supportsDelayedStackTraceLoading); - JsonSerializeOptional(o, supportsLoadedSourcesRequest); - JsonSerializeOptional(o, supportsLogPoints); - JsonSerializeOptional(o, supportsTerminateThreadsRequest); - JsonSerializeOptional(o, supportsSetExpression); - JsonSerializeOptional(o, supportsTerminateRequest); - JsonSerializeOptional(o, supportsDataBreakpoints); - JsonSerializeOptional(o, supportsReadMemoryRequest); - JsonSerializeOptional(o, supportsWriteMemoryRequest); - JsonSerializeOptional(o, supportsDisassembleRequest); - JsonSerializeOptional(o, supportsCancelRequest); - JsonSerializeOptional(o, supportsBreakpointLocationsRequest); - JsonSerializeOptional(o, supportsClipboardContext); - JsonSerializeOptional(o, supportsSteppingGranularity); - JsonSerializeOptional(o, supportsInstructionBreakpoints); - JsonSerializeOptional(o, supportsExceptionFilterOptions); - return o; - } - }); - - EmptyRequest(Launch, "launch"); - EmptyRequest(LoadedSources, "loadedSources"); - EmptyRequest(Modules, "modules"); - EmptyRequest(Next, "next"); - EmptyRequest(Pause, "pause"); - EmptyRequest(ReadMemory, "readMemory"); - EmptyRequest(Restart, "restart"); - EmptyRequest(RestartFrame, "restartFrame"); - EmptyRequest(ReverseContinue, "reverseContinue"); - - RequestDefinition(Scopes, "scopes", { - size_t frameId; - explicit ScopesArguments(nlohmann::json & j) : RequestArguments(j), frameId(j["frameId"]) {} - }); - RequestResponseDefinition(Scopes, "scopes", { - std::vector scopes; - - ScopesResponseBody(const std::vector& scopes) : scopes(scopes) {} - - [[nodiscard]] nlohmann::json ToJson() const override { - auto o = ResponseBody::ToJson(); - o["scopes"] = nlohmann::json::array(); - for (auto& t : scopes) { - o["scopes"].push_back(t.ToJson()); - } - return o; - } - }); - - RequestDefinition(SetBreakpoints, "setBreakpoints", { - Source source; - std::optional> breakpoints; - std::optional sourceModified; - - explicit SetBreakpointsArguments(nlohmann::json & j) : RequestArguments(j), source(j["source"]) { - if (!j["breakpoints"].empty()) { - std::vector v; - for (auto& i : j["breakpoints"]) { - v.emplace_back(i); - } - breakpoints = v; - } - JsonDeserializeOptional(j, sourceModified); - } - }); - RequestResponseDefinition(SetBreakpoints, "setBreakpoints", { - std::vector breakpoints; - [[nodiscard]] nlohmann::json ToJson() const override { - auto o = ResponseBody::ToJson(); - o["breakpoints"] = nlohmann::json::array(); - for (auto& bp : breakpoints) { - o["breakpoints"].push_back(bp.ToJson()); - } - return o; - } - }); - - EmptyRequest(SetDataBreakpoints, "setDataBreakpoints"); - EmptyRequest(SetExceptionBreakpoints, "setExceptionBreakpoints"); - EmptyRequest(SetExpression, "setExpression"); - EmptyRequest(SetFunctionBreakpoints, "setFunctionBreakpoints"); - EmptyRequest(SetInstructionBreakpoints, "setInstructionBreakpoints"); - EmptyRequest(SetVariable, "setVariable"); - EmptyRequest(Source, "source"); - EmptyRequest(StackTrace, "stackTrace"); - - RequestResponseDefinition(StackTrace, "stackTrace", { - std::vector stackFrames; - std::optional totalFrames; - - StackTraceResponseBody(std::vector stackFrames) - : stackFrames(stackFrames), totalFrames(stackFrames.size()) {} - - [[nodiscard]] nlohmann::json ToJson() const override { - auto o = ResponseBody::ToJson(); - o["stackFrames"] = nlohmann::json::array(); - for (auto& t : stackFrames) { - o["stackFrames"].push_back(t.ToJson()); - } - JsonSerializeOptional(o, totalFrames); - return o; - } - }); - - EmptyRequest(StepBack, "stepBack"); - EmptyRequest(StepIn, "stepIn"); - EmptyRequest(StepInTargets, "stepInTargets"); - EmptyRequest(StepOut, "stepOut"); - - RequestDefinition(Terminate, "terminate", { - std::optional restart; - explicit TerminateArguments(nlohmann::json & j) : RequestArguments(j), restart(j["restart"]) {} - }); - - EmptyRequest(TerminateThreads, "terminateThreads"); - RequestResponseDefinition(Threads, "threads", { - std::vector threads; - - [[nodiscard]] nlohmann::json ToJson() const override { - auto o = ResponseBody::ToJson(); - o["threads"] = nlohmann::json::array(); - for (auto& t : threads) { - o["threads"].push_back(t.ToJson()); - } - return o; - } - }); - - EmptyRequest(Threads, "threads"); - - RequestDefinition(Variables, "variables", { - size_t variablesReference; - - explicit VariablesArguments(nlohmann::json & j) - : RequestArguments(j), variablesReference(j["variablesReference"]) {} - }); - RequestResponseDefinition(Variables, "variables", { - std::vector variables; - - VariablesResponseBody(const std::vector& v) : variables(v) {} - - [[nodiscard]] nlohmann::json ToJson() const override { - auto o = ResponseBody::ToJson(); - o["variables"] = nlohmann::json::array(); - for (auto& t : variables) { - o["variables"].push_back(t.ToJson()); - } - return o; - } - }); - - EmptyRequest(WriteMemory, "writeMemory"); - -#undef EmptyRequest -#undef RequestDefinition -} - -#endif // ANGELSCRIPTDEBUGGER_REQUESTS_HPP - - -// end --- Requests.hpp --- - - - -void AngelscriptDebugger::Run(uint16_t port) { - // Listen on port 9012 - _server = new asio::ip::tcp::acceptor(_ioContext, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)); - std::thread([this]() { AcceptLoop(); }).detach(); -} - -template std::string string_format(const std::string& format, Args... args) { - int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0' - if (size_s <= 0) { - throw std::runtime_error("Error during formatting."); - } - auto size = static_cast(size_s); - auto buf = std::make_unique(size); - std::snprintf(buf.get(), size, format.c_str(), args...); - return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside -} - -std::string AngelscriptDebugger::GetResolvedScriptPath(const char* scriptSection) { - if (_scriptPath.has_value()) { - return string_format(_scriptPath.value(), scriptSection); - } - return scriptSection; -} - -void AngelscriptDebugger::on_line_callback(asIScriptContext* ctx, AngelscriptDebugger* d) { - if (ctx->GetState() == asEXECUTION_SUSPENDED) { - return; - } - const char* scriptSection = nullptr; - int column = 0; - int line = ctx->GetLineNumber(0, &column, &scriptSection); - if (line == 0) - return; - - auto resolvedScriptPath = d->GetResolvedScriptPath(scriptSection); - auto sectionBreakpoints = d->_breakpoints.find(resolvedScriptPath); - if (sectionBreakpoints != d->_breakpoints.end()) { - if (sectionBreakpoints->second.contains(line)) { - d->EnterBreakpoint(ctx, resolvedScriptPath, line); - } - } -} - -size_t GetBreakpointHash(const std::string& section, size_t line) { - using std::hash; - using std::size_t; - using std::string; - - return ((hash()(section) ^ (hash()(line) << 1)) >> 1); -} - -void AngelscriptDebugger::on_exception_callback(asIScriptContext*, AngelscriptDebugger*) { - return; - // const char* scriptSection = nullptr; - // int column = 0; - // int line = ctx->GetExceptionLineNumber(&column, &scriptSection); - // if (line == 0) - // return; - // auto b = Breakpoint{.Section = scriptSection, .Line = line}; - // d->EnterBreakpoint(ctx, b); -} - -void AngelscriptDebugger::EnterBreakpoint(asIScriptContext* ctx, const std::string& section, size_t line) { - ctx->Suspend(); - _pausedContexts.push_back(ctx); - - auto* o = new DebugAdapterProtocol::StoppedEvent( - new DebugAdapterProtocol::StoppedEventBody(GetBreakpointHash(section, line))); - - Send(o); -} - -void AngelscriptDebugger::RegisterContext(asIScriptContext* ctx) { - ctx->SetLineCallback(asFUNCTION(on_line_callback), this, asCALL_CDECL); - ctx->SetExceptionCallback(asFUNCTION(on_exception_callback), this, asCALL_CDECL); -} - -[[noreturn]] void AngelscriptDebugger::AcceptLoop() { - while (true) { - auto* client = new asio::ip::tcp::socket(_ioContext); - _server->accept(*client); - std::thread([this, client]() { ClientLoop(*client); }).detach(); - } -} - -inline std::string trim(const std::string& s) { - auto wsfront = std::find_if_not(s.begin(), s.end(), [](int c) { return std::isspace(c); }); - auto wsback = std::find_if_not(s.rbegin(), s.rend(), [](int c) { return std::isspace(c); }).base(); - return (wsback <= wsfront ? std::string() : std::string(wsfront, wsback)); -} - -void AngelscriptDebugger::ClientLoop(asio::ip::tcp::socket& client) { - std::unordered_map headers; - bool in_header_mode = true; - asio::streambuf buffer; - while (true) { - if (!client.is_open()) { - break; - } - if (client.available() == 0 && buffer.size() == 0) { - continue; - } - if (client.available() > 0 && buffer.size() == 0) { - asio::error_code error; - asio::read_until(client, buffer, "\r\n", error); - } - - if (in_header_mode) { - std::istream str(&buffer); - std::string s; - std::getline(str, s); - if (s == "\r") { - if (headers.contains("content-length")) { - in_header_mode = false; - } - continue; - } - auto delimiter = s.find(':'); - std::string key = trim(s.substr(0, delimiter)); - std::transform(key.begin(), key.end(), key.begin(), [](unsigned char c) { return std::tolower(c); }); - std::string value = trim(s.substr(delimiter + 1)); - headers[key] = value; - } else { - auto contentLength = headers["content-length"]; - auto size = (size_t)std::strtol(contentLength.c_str(), nullptr, 10); - std::cout << "message length: " << size << std::endl; - std::stringstream msg; - int64_t to_fetch = (int64_t)size - (int64_t)buffer.size(); - auto to_read = std::min(buffer.size(), size); - asio::streambuf::const_buffers_type constBuffer = buffer.data(); - std::copy(asio::buffers_begin(constBuffer), asio::buffers_end(constBuffer), - std::ostream_iterator(msg)); - buffer.consume(to_read); - if (to_fetch > 0) { - std::vector v(to_fetch); - asio::read(client, asio::buffer(v), asio::transfer_exactly(to_fetch)); - msg << std::string(v.begin(), v.end()); - } - - auto m = msg.str(); - std::cout << "found message: " << m << std::endl; - nlohmann::json j = nlohmann::json::parse(m.begin(), m.end()); - auto protocolMessage = DebugAdapterProtocol::ProtocolMessage::FromJson(j); - OnMessage(&client, protocolMessage); - - in_header_mode = true; - } - } -} - -void AngelscriptDebugger::Send(DebugAdapterProtocol::ProtocolMessage* msg) { - auto body = msg->ToJson().dump(); - std::vector vec(body.begin(), body.end()); - for (const auto& conn : _connections) { - if (conn->is_open()) { - asio::error_code ignored_error; - asio::write(*conn, asio::buffer("Content-Length: " + std::to_string(vec.size()) + "\r\n\r\n"), - ignored_error); - asio::write(*conn, asio::buffer(vec), ignored_error); - } - } - std::cout << "Sent message: " << body << std::endl; - delete msg; -} - -void AngelscriptDebugger::Send(asio::ip::tcp::socket* client, DebugAdapterProtocol::ProtocolMessage* msg) { - auto body = msg->ToJson().dump(); - std::vector vec(body.begin(), body.end()); - if (client->is_open()) { - asio::error_code ignored_error; - asio::write(*client, asio::buffer("Content-Length: " + std::to_string(vec.size()) + "\r\n\r\n"), ignored_error); - asio::write(*client, asio::buffer(vec), ignored_error); - } - std::cout << "Sent message: " << body << std::endl; - delete msg; -} - -void AngelscriptDebugger::OnMessage(asio::ip::tcp::socket* client, DebugAdapterProtocol::ProtocolMessage* msg) { - if (msg->type == "request") { - OnRequest(client, dynamic_cast(msg)); - } - delete msg; -} - -const char empty[] = ""; -void AngelscriptDebugger::OnRequest(asio::ip::tcp::socket* client, DebugAdapterProtocol::Request* msg) { - if (msg->GetCommand() == "setBreakpoints") { - auto* t = dynamic_cast(msg); - auto& args = t->arguments.value(); - auto& path = args->source.path.value(); - auto* response = new DebugAdapterProtocol::SetBreakpointsResponse(msg->seq); - auto* body = new DebugAdapterProtocol::SetBreakpointsResponseBody(); - response->body = body; - std::unordered_set sectionBreakpoints; - if (args->breakpoints.has_value()) { - for (auto& bp : args->breakpoints.value()) { - sectionBreakpoints.insert(bp.line); - body->breakpoints.emplace_back(GetBreakpointHash(path, bp.line), args->source, bp.line); - } - } - _breakpoints[path] = sectionBreakpoints; - Send(client, response); - } else if (msg->GetCommand() == "initialize") { - auto response = new DebugAdapterProtocol::InitializeResponse(msg->seq); - auto* body = new DebugAdapterProtocol::InitializeResponseBody(); - response->body = body; - Send(client, response); - - auto r = new DebugAdapterProtocol::InitializedEvent(); - Send(client, r); - - } else if (msg->GetCommand() == "disconnect") { - auto response = new DebugAdapterProtocol::DefinedResponse(msg->seq); - Send(client, response); - _connections.erase(client); - } else if (msg->GetCommand() == "configurationDone") { - _connections.insert(client); - Send(client, new DebugAdapterProtocol::DefinedResponse(msg->seq)); - } else if (msg->GetCommand() == "attach") { - auto t = dynamic_cast(msg); - _scriptPath = t->arguments.value()->scriptPath; - Send(client, new DebugAdapterProtocol::DefinedResponse(msg->seq)); - } else if (msg->GetCommand() == "threads") { - auto response = new DebugAdapterProtocol::ThreadsResponse(msg->seq); - auto* body = new DebugAdapterProtocol::ThreadsResponseBody(); - response->body = body; - body->threads = {DebugAdapterProtocol::Thread(0, "main")}; - Send(client, response); - } else if (msg->GetCommand() == "continue") { - Continue(); - Send(client, new DebugAdapterProtocol::DefinedResponse(msg->seq)); - } else if (msg->GetCommand() == "stackTrace") { - auto ctx = _pausedContexts[0]; - auto response = new DebugAdapterProtocol::StackTraceResponse(msg->seq); - auto stackTrace = std::vector(); - stackTrace.reserve(ctx->GetCallstackSize()); - for (asUINT i = 0; i < ctx->GetCallstackSize(); ++i) { - auto func = ctx->GetFunction(i); - const char* scriptSection = nullptr; - int column = 0; - int line = ctx->GetLineNumber(i, &column, &scriptSection); - - stackTrace.emplace_back(i, func->GetName(), - DebugAdapterProtocol::Source(GetResolvedScriptPath(scriptSection)), line, column); - } - auto body = new DebugAdapterProtocol::StackTraceResponseBody(stackTrace); - response->body = body; - Send(client, response); - } else if (msg->GetCommand() == "scopes") { - auto t = dynamic_cast(msg); - auto ctx = _pausedContexts[0]; - auto response = new DebugAdapterProtocol::ScopesResponse(msg->seq); - auto scopes = std::vector(); - auto frameId = t->arguments.value()->frameId; - auto varCount = 0; - for (int i = 0; i < ctx->GetVarCount(t->arguments.value()->frameId); ++i) { - if (ctx->IsVarInScope(i, frameId)) { - varCount++; - } - } - _storedVariableReferences.emplace_back(std::make_unique(frameId, 0)); - scopes.emplace_back("Locals", _storedVariableReferences.size(), "locals", varCount); - auto body = new DebugAdapterProtocol::ScopesResponseBody(scopes); - response->body = body; - Send(client, response); - } else if (msg->GetCommand() == "variables") { - auto t = dynamic_cast(msg); - auto ctx = _pausedContexts[0]; - auto response = new DebugAdapterProtocol::VariablesResponse(msg->seq); - auto variables = std::vector(); - auto reference = t->arguments.value()->variablesReference - 1; - if (reference >= _storedVariableReferences.size() || reference < 0) - return; - - auto& variant = _storedVariableReferences[reference]; - auto e = ctx->GetEngine(); - // We currently can't use asIScriptContext::PushBack on suspended script contexts due to limitations in - // angelscript. Hence we make a new contenxt - auto c = e->CreateContext(); - - if (holds_alternative>(variant)) { - auto frameId = std::get>(variant)->StackLevel; - // auto scopeType = get(variant).Type; - auto varCount = ctx->GetVarCount(frameId); - for (int i = 0; i < varCount; ++i) { - if (!ctx->IsVarInScope(i, frameId)) { - continue; - } - auto name = ctx->GetVarName(i, frameId); - auto type = ctx->GetVarTypeId(i, frameId); - auto val = ctx->GetAddressOfVar(i, frameId); - variables.push_back(ASVariableFormatter::GetAsDAPVariable(e, c, this, name, type, val)); - } - } else { - auto& v = std::get>(variant); - try { - ASVariableFormatter::GetChildDAPVariables(variables, e, c, this, v->TypeID, v->Address); - } catch (const std::exception& e) { - std::cout << e.what() << std::endl; - } - } - c->Release(); - auto body = new DebugAdapterProtocol::VariablesResponseBody(variables); - response->body = body; - Send(client, response); - } else { - Send(client, new DebugAdapterProtocol::DefinedResponse(msg->seq)); - } -} - - -// end --- AngelscriptDebugger.cpp --- - - - -// begin --- BaseProtocol.cpp --- - - - -namespace DebugAdapterProtocol { - ProtocolMessage* ProtocolMessage::FromJson(nlohmann::json& j) { - auto t = j["type"]; - ProtocolMessage* msg; - if (t == "request") { - msg = Request::FromJson(j); - } else if (t == "event") { - throw std::logic_error("Unhandled type"); - // msg = Event::FromJson(j); - } else if (t == "response") { - msg = Response::FromJson(j); - } else { - throw std::logic_error("Unknown type"); - } - msg->type = t; - msg->seq = j["seq"]; - return msg; - } - - template ProtocolMessage* DefinedRequest::FromJson(nlohmann::json& j) { - std::string c = j["command"]; - auto o = new DefinedRequest(); - auto a = j["arguments"]; - if (!a.empty()) { - o->arguments = dynamic_cast(new T(a)); - } - return o; - } - ProtocolMessage* Request::FromJson(nlohmann::json& j) { - std::string c = j["command"]; - // This is the worst, we need to fix this eventually. - if (c == "attach") - return AttachRequest::FromJson(j); - if (c == "breakpointLocations") - return BreakpointLocationsRequest::FromJson(j); - if (c == "completions") - return CompletionsRequest::FromJson(j); - if (c == "configurationDone") - return ConfigurationDoneRequest ::FromJson(j); - if (c == "continue") - return ContinueRequest ::FromJson(j); - if (c == "dataBreakpointInfo") - return DataBreakpointInfoRequest ::FromJson(j); - if (c == "disassemble") - return DisassembleRequest ::FromJson(j); - if (c == "disconnect") - return DisconnectRequest ::FromJson(j); - if (c == "evaluate") - return EvaluateRequest ::FromJson(j); - if (c == "exceptionInfo") - return ExceptionInfoRequest ::FromJson(j); - if (c == "Goto") - return GotoRequest ::FromJson(j); - if (c == "GotoTargets") - return GotoTargetsRequest ::FromJson(j); - if (c == "initialize") - return InitializeRequest ::FromJson(j); - if (c == "launch") - return LaunchRequest ::FromJson(j); - if (c == "loadedSources") - return LoadedSourcesRequest ::FromJson(j); - if (c == "modules") - return ModulesRequest ::FromJson(j); - if (c == "next") - return NextRequest ::FromJson(j); - if (c == "pause") - return PauseRequest ::FromJson(j); - if (c == "readMemory") - return ReadMemoryRequest ::FromJson(j); - if (c == "restart") - return RestartRequest ::FromJson(j); - if (c == "restartFrame") - return RestartFrameRequest ::FromJson(j); - if (c == "reverseContinue") - return RestartFrameRequest ::FromJson(j); - if (c == "scopes") - return ScopesRequest ::FromJson(j); - if (c == "setBreakpoints") - return SetBreakpointsRequest ::FromJson(j); - if (c == "setDataBreakpoints") - return SetDataBreakpointsRequest ::FromJson(j); - if (c == "setExceptionBreakpoints") - return SetExceptionBreakpointsRequest ::FromJson(j); - if (c == "setExpression") - return SetExpressionRequest ::FromJson(j); - if (c == "setFunctionBreakpoints") - return SetFunctionBreakpointsRequest ::FromJson(j); - if (c == "setInstructionBreakpoints") - return SetInstructionBreakpointsRequest ::FromJson(j); - if (c == "setVariable") - return SetVariableRequest ::FromJson(j); - if (c == "source") - return SourceRequest ::FromJson(j); - if (c == "stackTrace") - return StackTraceRequest ::FromJson(j); - if (c == "stepBack") - return StepBackRequest ::FromJson(j); - if (c == "stepIn") - return StepInRequest ::FromJson(j); - if (c == "stepInTargets") - return StepInTargetsRequest ::FromJson(j); - if (c == "stepOut") - return StepOutRequest ::FromJson(j); - if (c == "terminate") - return TerminateRequest ::FromJson(j); - if (c == "terminateThreads") - return TerminateThreadsRequest ::FromJson(j); - if (c == "threads") - return ThreadsRequest ::FromJson(j); - if (c == "variables") - return VariablesRequest ::FromJson(j); - if (c == "writeMemory") - return WriteMemoryRequest ::FromJson(j); - throw std::logic_error("Unknown command"); - } -} - -// end --- BaseProtocol.cpp --- - - - -// begin --- ASVariableFormatter.cpp --- - - -#include - -DebugAdapterProtocol::Variable ASVariableFormatter::GetAsDAPVariable(asIScriptEngine* engine, asIScriptContext* ctx, - AngelscriptDebugger* debugger, - const std::string& name, int type, void* address) { - if ((type & asTYPEID_OBJHANDLE) != 0) { - address = *static_cast(address); - } - switch (type) { - case asTYPEID_BOOL: { - auto v = *static_cast(address); - return {name, v ? "true" : "false", "bool"}; - }; - - case asTYPEID_INT8: { - auto v = *static_cast(address); - return {name, std::to_string(v), "int8"}; - }; - case asTYPEID_INT16: { - auto v = *static_cast(address); - return {name, std::to_string(v), "int16"}; - } - case asTYPEID_INT32: { - auto v = *static_cast(address); - return {name, std::to_string(v), "int32"}; - }; - case asTYPEID_INT64: { - auto v = *static_cast(address); - return {name, std::to_string(v), "int64"}; - }; - - case asTYPEID_UINT8: { - auto v = *static_cast(address); - return {name, std::to_string(v), "uint8"}; - }; - case asTYPEID_UINT16: { - auto v = *static_cast(address); - return {name, std::to_string(v), "uint16"}; - }; - case asTYPEID_UINT32: { - auto v = *static_cast(address); - return {name, std::to_string(v), "uint32"}; - }; - case asTYPEID_UINT64: { - auto v = *static_cast(address); - return {name, std::to_string(v), "uint64"}; - }; - - case asTYPEID_FLOAT: { - auto v = *static_cast(address); - return {name, std::to_string(v), "float"}; - }; - case asTYPEID_DOUBLE: { - auto v = *static_cast(address); - return {name, std::to_string(v), "double"}; - }; - - default: break; - } - if ((type & asTYPEID_SCRIPTOBJECT) != 0) { - auto* obj = static_cast(address); - std::string typeName = engine->GetTypeInfoById(type)->GetName(); - if (obj == nullptr) { - return DebugAdapterProtocol::Variable::FromNull(name, typeName); - } - auto typeData = obj->GetObjectType(); - std::string stringified = typeName + "(*" + std::to_string((size_t)address) + ")"; - auto stringifiedFunc = typeData->GetMethodByDecl("string opImplConv()"); - if (stringifiedFunc != nullptr) { - ctx->PushState(); - ctx->Prepare(stringifiedFunc); - ctx->SetObject(address); - if (ctx->Execute() == asEXECUTION_FINISHED) { - stringified = *(std::string*)ctx->GetAddressOfReturnValue(); - } - ctx->PopState(); - } - - size_t reference = 0; - if (obj->GetPropertyCount() > 0) { - std::cout << "Storing scriptObj " << name << std::endl; - obj->AddRef(); - reference = debugger->StoreVariable(address, type, [](void* a) { - if (a != nullptr) { - ((asIScriptObject*)a)->Release(); - } - }); - } - return DebugAdapterProtocol::Variable::FromPointer(name, typeName, stringified, reference); - } - // TODO: Caching of type id - if (type == engine->GetTypeIdByDecl("string")) { - auto* str = static_cast(address); - if (str == nullptr) { - return DebugAdapterProtocol::Variable::FromNull(name, "string"); - } - return DebugAdapterProtocol::Variable::FromString(name, "\"" + *str + "\""); - } - auto* typeData = engine->GetTypeInfoById(type); - if ((typeData->GetFlags() & asOBJ_ENUM) != 0) { - const auto* enumValue = typeData->GetEnumValueByIndex(*static_cast(address), nullptr); - return {name, enumValue, typeData->GetName()}; - } - - if (address == nullptr) { - return DebugAdapterProtocol::Variable::FromNull(name, typeData->GetName()); - } - - std::string stringified = std::string(typeData->GetName()) + "(*" + std::to_string((size_t)address) + ")"; - auto stringifiedFunc = typeData->GetMethodByDecl("string opImplConv()"); - if (stringifiedFunc != nullptr) { - ctx->Prepare(stringifiedFunc); - ctx->SetObject(*(void**)address); - if (ctx->Execute() == asEXECUTION_FINISHED) { - stringified = "\"" + *(std::string*)ctx->GetAddressOfReturnValue() + "\""; - } - } - - size_t reference = 0; - size_t varCount = typeData->GetPropertyCount(); - for (asUINT i = 0; i < typeData->GetMethodCount(); ++i) { - if (typeData->GetMethodByIndex(i, true)->IsProperty()) { - varCount++; - } - } - if (varCount > 0) { - - // If we want to re-use an object, we need to make sure to add a reference to it, so it doesn't get accidentally - // cleaned up. This is currently somewhat clunky, we look up the AddRef behaviour, and execute that in a new - // context. - auto b = static_cast(-1); - asIScriptFunction* releaseFunc = nullptr; - bool hasAddRef = false; - for (asUINT i = 0; i < typeData->GetBehaviourCount(); ++i) { - auto* f = typeData->GetBehaviourByIndex(i, &b); - if (b == asBEHAVE_ADDREF) { - // If we push state or re-use the active context, we might lose the reference to the object we have. - // To resolve this we need a new context. - auto* c = ctx->GetEngine()->CreateContext(); - assert(c->Prepare(f) >= 0); - assert(c->SetObject(address) >= 0); - assert(c->Execute() >= 0); - c->Release(); - hasAddRef = true; - } - if (b == asBEHAVE_RELEASE) { - releaseFunc = f; - } - } - if (!hasAddRef) { - releaseFunc = nullptr; - } - - reference = debugger->StoreVariable(address, type, [engine, releaseFunc](void* a) { - if (releaseFunc != nullptr) { - auto* c = engine->CreateContext(); - c->Prepare(releaseFunc); - c->SetObject(a); - c->Execute(); - c->Release(); - } - }); - } - return DebugAdapterProtocol::Variable::FromPointer(name, typeData->GetName(), stringified, reference); -} -void ASVariableFormatter::GetChildDAPVariables(std::vector& vars, - asIScriptEngine* engine, asIScriptContext* ctx, - AngelscriptDebugger* debugger, int type, void* address) { - if (address == nullptr) { - return; - } - - if ((type & asTYPEID_SCRIPTOBJECT) != 0) { - auto* obj = static_cast(address); - for (asUINT i = 0; i < obj->GetPropertyCount(); ++i) { - const auto* propertyName = obj->GetPropertyName(i); - auto propertyType = obj->GetPropertyTypeId(i); - auto* propertyAddress = obj->GetAddressOfProperty(i); - vars.push_back(GetAsDAPVariable(engine, ctx, debugger, propertyName, propertyType, propertyAddress)); - } - return; - } - - auto* typeData = engine->GetTypeInfoById(type); - for (asUINT i = 0; i < typeData->GetPropertyCount(); ++i) { - const char* propertyName = nullptr; - int propertyType = 0; - int offset = 0; - - typeData->GetProperty(i, &propertyName, &propertyType, nullptr, nullptr, &offset); - vars.push_back( - GetAsDAPVariable(engine, ctx, debugger, propertyName, propertyType, (void*)((size_t)address + offset))); - } - for (asUINT i = 0; i < typeData->GetMethodCount(); ++i) { - auto func = typeData->GetMethodByIndex(i, true); - if (func->IsProperty()) { - assert(ctx->Prepare(func) >= 0); - ctx->SetObject(address); - auto state = ctx->Execute(); - if (state == asEXECUTION_FINISHED) { - auto name = std::string(func->GetName()).substr(4); - vars.push_back(GetAsDAPVariable(engine, ctx, debugger, name, func->GetReturnTypeId(), - ctx->GetAddressOfReturnValue())); - } - } - } -} - - -// end --- ASVariableFormatter.cpp --- - diff --git a/src/ASVariableFormatter.cpp b/src/ASVariableFormatter.cpp index ed4c969..b50f289 100644 --- a/src/ASVariableFormatter.cpp +++ b/src/ASVariableFormatter.cpp @@ -164,6 +164,23 @@ DebugAdapterProtocol::Variable ASVariableFormatter::GetAsDAPVariable(asIScriptEn } return DebugAdapterProtocol::Variable::FromPointer(name, typeData->GetName(), stringified, reference); } + +template +void ASVariableFormatter::FormatArrayLike(std::vector& vars, asIScriptEngine* engine, + asIScriptContext* ctx, void* address, AngelscriptDebugger* debugger, + asIScriptFunction* indexFunc) { + auto length = (ctx->*GetLength)(); + for (T i = 0; i < length; ++i) { + assert(ctx->Prepare(indexFunc) >= 0); + ctx->SetObject(address); + (ctx->*SetArg)(0, i); + ctx->Execute(); + auto name = std::to_string(i); + vars.push_back(GetAsDAPVariable(engine, ctx, debugger, name, indexFunc->GetReturnTypeId(), + ctx->GetAddressOfReturnValue())); + } +} + void ASVariableFormatter::GetChildDAPVariables(std::vector& vars, asIScriptEngine* engine, asIScriptContext* ctx, AngelscriptDebugger* debugger, int type, void* address) { @@ -205,4 +222,25 @@ void ASVariableFormatter::GetChildDAPVariables(std::vectorGetMethodByName("get_opIndex", true); + if (indexFunc != nullptr && indexFunc->GetParamCount() == 1) { + auto lengthFunc = typeData->GetMethodByName("get_Length", true); + int t; + indexFunc->GetParam(0, &t); + if (lengthFunc != nullptr && lengthFunc->GetParamCount() == 0 && lengthFunc->GetReturnTypeId() == t) { + assert(ctx->Prepare(lengthFunc) >= 0); + ctx->SetObject(address); + ctx->Execute(); + if (t == asTYPEID_UINT64) { + FormatArrayLike( + vars, engine, ctx, address, debugger, indexFunc); + } + else if (t == asTYPEID_INT32){ + FormatArrayLike( + vars, engine, ctx, address, debugger, indexFunc); + } + } + // vars.push_back(DebugAdapterProtocol::Variable("has_indexer", "true", "string", {})); + } } diff --git a/src/ASVariableFormatter.hpp b/src/ASVariableFormatter.hpp index ec3fad2..af8d175 100644 --- a/src/ASVariableFormatter.hpp +++ b/src/ASVariableFormatter.hpp @@ -11,6 +11,10 @@ public: static DebugAdapterProtocol::Variable GetAsDAPVariable(asIScriptEngine* engine, asIScriptContext* ctx, AngelscriptDebugger* debugger, const std::string& name, int, void*); + template + static void FormatArrayLike(std::vector& vars, asIScriptEngine* engine, + asIScriptContext* ctx, void* address, AngelscriptDebugger* debugger, + asIScriptFunction* indexFunc); static void GetChildDAPVariables(std::vector&, asIScriptEngine* engine, asIScriptContext* ctx, AngelscriptDebugger* debugger, int, void*); };