llvm/lldb/tools/lldb-dap/JSONUtils.cpp

//===-- JSONUtils.cpp -------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include <algorithm>
#include <iomanip>
#include <optional>
#include <sstream>
#include <string.h>

#include "llvm/ADT/StringRef.h"
#include "llvm/Support/FormatAdapters.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/ScopedPrinter.h"

#include "lldb/API/SBBreakpoint.h"
#include "lldb/API/SBBreakpointLocation.h"
#include "lldb/API/SBDeclaration.h"
#include "lldb/API/SBStringList.h"
#include "lldb/API/SBStructuredData.h"
#include "lldb/API/SBValue.h"
#include "lldb/Host/PosixApi.h"

#include "DAP.h"
#include "ExceptionBreakpoint.h"
#include "JSONUtils.h"
#include "LLDBUtils.h"

namespace lldb_dap {

void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key,
                       llvm::StringRef str) {}

llvm::StringRef GetAsString(const llvm::json::Value &value) {}

// Gets a string from a JSON object using the key, or returns an empty string.
llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key,
                          llvm::StringRef defaultValue) {}

llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key,
                          llvm::StringRef defaultValue) {}

// Gets an unsigned integer from a JSON object using the key, or returns the
// specified fail value.
uint64_t GetUnsigned(const llvm::json::Object &obj, llvm::StringRef key,
                     uint64_t fail_value) {}

uint64_t GetUnsigned(const llvm::json::Object *obj, llvm::StringRef key,
                     uint64_t fail_value) {}

bool GetBoolean(const llvm::json::Object &obj, llvm::StringRef key,
                bool fail_value) {}

bool GetBoolean(const llvm::json::Object *obj, llvm::StringRef key,
                bool fail_value) {}

int64_t GetSigned(const llvm::json::Object &obj, llvm::StringRef key,
                  int64_t fail_value) {}

int64_t GetSigned(const llvm::json::Object *obj, llvm::StringRef key,
                  int64_t fail_value) {}

bool ObjectContainsKey(const llvm::json::Object &obj, llvm::StringRef key) {}

std::string EncodeMemoryReference(lldb::addr_t addr) {}

std::optional<lldb::addr_t>
DecodeMemoryReference(llvm::StringRef memoryReference) {}

std::vector<std::string> GetStrings(const llvm::json::Object *obj,
                                    llvm::StringRef key) {}

static bool IsClassStructOrUnionType(lldb::SBType t) {}

/// Create a short summary for a container that contains the summary of its
/// first children, so that the user can get a glimpse of its contents at a
/// glance.
static std::optional<std::string>
TryCreateAutoSummaryForContainer(lldb::SBValue &v) {}

/// Try to create a summary string for the given value that doesn't have a
/// summary of its own.
static std::optional<std::string> TryCreateAutoSummary(lldb::SBValue value) {}

void FillResponse(const llvm::json::Object &request,
                  llvm::json::Object &response) {}

// "Scope": {
//   "type": "object",
//   "description": "A Scope is a named container for variables. Optionally
//                   a scope can map to a source or a range within a source.",
//   "properties": {
//     "name": {
//       "type": "string",
//       "description": "Name of the scope such as 'Arguments', 'Locals'."
//     },
//     "presentationHint": {
//       "type": "string",
//       "description": "An optional hint for how to present this scope in the
//                       UI. If this attribute is missing, the scope is shown
//                       with a generic UI.",
//       "_enum": [ "arguments", "locals", "registers" ],
//     },
//     "variablesReference": {
//       "type": "integer",
//       "description": "The variables of this scope can be retrieved by
//                       passing the value of variablesReference to the
//                       VariablesRequest."
//     },
//     "namedVariables": {
//       "type": "integer",
//       "description": "The number of named variables in this scope. The
//                       client can use this optional information to present
//                       the variables in a paged UI and fetch them in chunks."
//     },
//     "indexedVariables": {
//       "type": "integer",
//       "description": "The number of indexed variables in this scope. The
//                       client can use this optional information to present
//                       the variables in a paged UI and fetch them in chunks."
//     },
//     "expensive": {
//       "type": "boolean",
//       "description": "If true, the number of variables in this scope is
//                       large or expensive to retrieve."
//     },
//     "source": {
//       "$ref": "#/definitions/Source",
//       "description": "Optional source for this scope."
//     },
//     "line": {
//       "type": "integer",
//       "description": "Optional start line of the range covered by this
//                       scope."
//     },
//     "column": {
//       "type": "integer",
//       "description": "Optional start column of the range covered by this
//                       scope."
//     },
//     "endLine": {
//       "type": "integer",
//       "description": "Optional end line of the range covered by this scope."
//     },
//     "endColumn": {
//       "type": "integer",
//       "description": "Optional end column of the range covered by this
//                       scope."
//     }
//   },
//   "required": [ "name", "variablesReference", "expensive" ]
// }
llvm::json::Value CreateScope(const llvm::StringRef name,
                              int64_t variablesReference,
                              int64_t namedVariables, bool expensive) {}

// "Breakpoint": {
//   "type": "object",
//   "description": "Information about a Breakpoint created in setBreakpoints
//                   or setFunctionBreakpoints.",
//   "properties": {
//     "id": {
//       "type": "integer",
//       "description": "An optional unique identifier for the breakpoint."
//     },
//     "verified": {
//       "type": "boolean",
//       "description": "If true breakpoint could be set (but not necessarily
//                       at the desired location)."
//     },
//     "message": {
//       "type": "string",
//       "description": "An optional message about the state of the breakpoint.
//                       This is shown to the user and can be used to explain
//                       why a breakpoint could not be verified."
//     },
//     "source": {
//       "$ref": "#/definitions/Source",
//       "description": "The source where the breakpoint is located."
//     },
//     "line": {
//       "type": "integer",
//       "description": "The start line of the actual range covered by the
//                       breakpoint."
//     },
//     "column": {
//       "type": "integer",
//       "description": "An optional start column of the actual range covered
//                       by the breakpoint."
//     },
//     "endLine": {
//       "type": "integer",
//       "description": "An optional end line of the actual range covered by
//                       the breakpoint."
//     },
//     "endColumn": {
//       "type": "integer",
//       "description": "An optional end column of the actual range covered by
//                       the breakpoint. If no end line is given, then the end
//                       column is assumed to be in the start line."
//     }
//   },
//   "required": [ "verified" ]
// }
llvm::json::Value CreateBreakpoint(BreakpointBase *bp,
                                   std::optional<llvm::StringRef> request_path,
                                   std::optional<uint32_t> request_line,
                                   std::optional<uint32_t> request_column) {}

static uint64_t GetDebugInfoSizeInSection(lldb::SBSection section) {}

static uint64_t GetDebugInfoSize(lldb::SBModule module) {}

static std::string ConvertDebugInfoSizeToString(uint64_t debug_info) {}
llvm::json::Value CreateModule(lldb::SBModule &module) {}

void AppendBreakpoint(BreakpointBase *bp, llvm::json::Array &breakpoints,
                      std::optional<llvm::StringRef> request_path,
                      std::optional<uint32_t> request_line) {}

// "Event": {
//   "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
//     "type": "object",
//     "description": "Server-initiated event.",
//     "properties": {
//       "type": {
//         "type": "string",
//         "enum": [ "event" ]
//       },
//       "event": {
//         "type": "string",
//         "description": "Type of event."
//       },
//       "body": {
//         "type": [ "array", "boolean", "integer", "null", "number" ,
//                   "object", "string" ],
//         "description": "Event-specific information."
//       }
//     },
//     "required": [ "type", "event" ]
//   }]
// },
// "ProtocolMessage": {
//   "type": "object",
//   "description": "Base class of requests, responses, and events.",
//   "properties": {
//         "seq": {
//           "type": "integer",
//           "description": "Sequence number."
//         },
//         "type": {
//           "type": "string",
//           "description": "Message type.",
//           "_enum": [ "request", "response", "event" ]
//         }
//   },
//   "required": [ "seq", "type" ]
// }
llvm::json::Object CreateEventObject(const llvm::StringRef event_name) {}

// "ExceptionBreakpointsFilter": {
//   "type": "object",
//   "description": "An ExceptionBreakpointsFilter is shown in the UI as an
//                   option for configuring how exceptions are dealt with.",
//   "properties": {
//     "filter": {
//       "type": "string",
//       "description": "The internal ID of the filter. This value is passed
//                       to the setExceptionBreakpoints request."
//     },
//     "label": {
//       "type": "string",
//       "description": "The name of the filter. This will be shown in the UI."
//     },
//     "default": {
//       "type": "boolean",
//       "description": "Initial value of the filter. If not specified a value
//                       'false' is assumed."
//     }
//   },
//   "required": [ "filter", "label" ]
// }
llvm::json::Value
CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) {}

// "Source": {
//   "type": "object",
//   "description": "A Source is a descriptor for source code. It is returned
//                   from the debug adapter as part of a StackFrame and it is
//                   used by clients when specifying breakpoints.",
//   "properties": {
//     "name": {
//       "type": "string",
//       "description": "The short name of the source. Every source returned
//                       from the debug adapter has a name. When sending a
//                       source to the debug adapter this name is optional."
//     },
//     "path": {
//       "type": "string",
//       "description": "The path of the source to be shown in the UI. It is
//                       only used to locate and load the content of the
//                       source if no sourceReference is specified (or its
//                       value is 0)."
//     },
//     "sourceReference": {
//       "type": "number",
//       "description": "If sourceReference > 0 the contents of the source must
//                       be retrieved through the SourceRequest (even if a path
//                       is specified). A sourceReference is only valid for a
//                       session, so it must not be used to persist a source."
//     },
//     "presentationHint": {
//       "type": "string",
//       "description": "An optional hint for how to present the source in the
//                       UI. A value of 'deemphasize' can be used to indicate
//                       that the source is not available or that it is
//                       skipped on stepping.",
//       "enum": [ "normal", "emphasize", "deemphasize" ]
//     },
//     "origin": {
//       "type": "string",
//       "description": "The (optional) origin of this source: possible values
//                       'internal module', 'inlined content from source map',
//                       etc."
//     },
//     "sources": {
//       "type": "array",
//       "items": {
//         "$ref": "#/definitions/Source"
//       },
//       "description": "An optional list of sources that are related to this
//                       source. These may be the source that generated this
//                       source."
//     },
//     "adapterData": {
//       "type":["array","boolean","integer","null","number","object","string"],
//       "description": "Optional data that a debug adapter might want to loop
//                       through the client. The client should leave the data
//                       intact and persist it across sessions. The client
//                       should not interpret the data."
//     },
//     "checksums": {
//       "type": "array",
//       "items": {
//         "$ref": "#/definitions/Checksum"
//       },
//       "description": "The checksums associated with this file."
//     }
//   }
// }
llvm::json::Value CreateSource(const lldb::SBFileSpec &file) {}

llvm::json::Value CreateSource(const lldb::SBLineEntry &line_entry) {}

llvm::json::Value CreateSource(llvm::StringRef source_path) {}

std::optional<llvm::json::Value> CreateSource(lldb::SBFrame &frame) {}

// "StackFrame": {
//   "type": "object",
//   "description": "A Stackframe contains the source location.",
//   "properties": {
//     "id": {
//       "type": "integer",
//       "description": "An identifier for the stack frame. It must be unique
//                       across all threads. This id can be used to retrieve
//                       the scopes of the frame with the 'scopesRequest' or
//                       to restart the execution of a stackframe."
//     },
//     "name": {
//       "type": "string",
//       "description": "The name of the stack frame, typically a method name."
//     },
//     "source": {
//       "$ref": "#/definitions/Source",
//       "description": "The optional source of the frame."
//     },
//     "line": {
//       "type": "integer",
//       "description": "The line within the file of the frame. If source is
//                       null or doesn't exist, line is 0 and must be ignored."
//     },
//     "column": {
//       "type": "integer",
//       "description": "The column within the line. If source is null or
//                       doesn't exist, column is 0 and must be ignored."
//     },
//     "endLine": {
//       "type": "integer",
//       "description": "An optional end line of the range covered by the
//                       stack frame."
//     },
//     "endColumn": {
//       "type": "integer",
//       "description": "An optional end column of the range covered by the
//                       stack frame."
//     },
//     "instructionPointerReference": {
// 	     "type": "string",
// 	     "description": "A memory reference for the current instruction
//                         pointer in this frame."
//     },
//     "moduleId": {
//       "type": ["integer", "string"],
//       "description": "The module associated with this frame, if any."
//     },
//     "presentationHint": {
//       "type": "string",
//       "enum": [ "normal", "label", "subtle" ],
//       "description": "An optional hint for how to present this frame in
//                       the UI. A value of 'label' can be used to indicate
//                       that the frame is an artificial frame that is used
//                       as a visual label or separator. A value of 'subtle'
//                       can be used to change the appearance of a frame in
//                       a 'subtle' way."
//     }
//   },
//   "required": [ "id", "name", "line", "column" ]
// }
llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) {}

llvm::json::Value CreateExtendedStackFrameLabel(lldb::SBThread &thread) {}

// Response to `setInstructionBreakpoints` request.
// "Breakpoint": {
//   "type": "object",
//   "description": "Response to `setInstructionBreakpoints` request.",
//   "properties": {
//     "id": {
//       "type": "number",
//       "description": "The identifier for the breakpoint. It is needed if
//       breakpoint events are used to update or remove breakpoints."
//     },
//     "verified": {
//       "type": "boolean",
//       "description": "If true, the breakpoint could be set (but not
//       necessarily at the desired location."
//     },
//     "message": {
//       "type": "string",
//       "description": "A message about the state of the breakpoint.
//       This is shown to the user and can be used to explain why a breakpoint
//       could not be verified."
//     },
//     "source": {
//       "type": "Source",
//       "description": "The source where the breakpoint is located."
//     },
//     "line": {
//       "type": "number",
//       "description": "The start line of the actual range covered by the
//       breakpoint."
//     },
//     "column": {
//       "type": "number",
//       "description": "The start column of the actual range covered by the
//       breakpoint."
//     },
//     "endLine": {
//       "type": "number",
//       "description": "The end line of the actual range covered by the
//       breakpoint."
//     },
//     "endColumn": {
//       "type": "number",
//       "description": "The end column of the actual range covered by the
//       breakpoint. If no end line is given, then the end column is assumed to
//       be in the start line."
//     },
//     "instructionReference": {
//       "type": "string",
//       "description": "A memory reference to where the breakpoint is set."
//     },
//     "offset": {
//       "type": "number",
//       "description": "The offset from the instruction reference.
//       This can be negative."
//     },
//   },
//   "required": [ "id", "verified", "line"]
// }
llvm::json::Value CreateInstructionBreakpoint(BreakpointBase *ibp) {}

// "Thread": {
//   "type": "object",
//   "description": "A Thread",
//   "properties": {
//     "id": {
//       "type": "integer",
//       "description": "Unique identifier for the thread."
//     },
//     "name": {
//       "type": "string",
//       "description": "A name of the thread."
//     }
//   },
//   "required": [ "id", "name" ]
// }
llvm::json::Value CreateThread(lldb::SBThread &thread) {}

// "StoppedEvent": {
//   "allOf": [ { "$ref": "#/definitions/Event" }, {
//     "type": "object",
//     "description": "Event message for 'stopped' event type. The event
//                     indicates that the execution of the debuggee has stopped
//                     due to some condition. This can be caused by a break
//                     point previously set, a stepping action has completed,
//                     by executing a debugger statement etc.",
//     "properties": {
//       "event": {
//         "type": "string",
//         "enum": [ "stopped" ]
//       },
//       "body": {
//         "type": "object",
//         "properties": {
//           "reason": {
//             "type": "string",
//             "description": "The reason for the event. For backward
//                             compatibility this string is shown in the UI if
//                             the 'description' attribute is missing (but it
//                             must not be translated).",
//             "_enum": [ "step", "breakpoint", "exception", "pause", "entry" ]
//           },
//           "description": {
//             "type": "string",
//             "description": "The full reason for the event, e.g. 'Paused
//                             on exception'. This string is shown in the UI
//                             as is."
//           },
//           "threadId": {
//             "type": "integer",
//             "description": "The thread which was stopped."
//           },
//           "text": {
//             "type": "string",
//             "description": "Additional information. E.g. if reason is
//                             'exception', text contains the exception name.
//                             This string is shown in the UI."
//           },
//           "allThreadsStopped": {
//             "type": "boolean",
//             "description": "If allThreadsStopped is true, a debug adapter
//                             can announce that all threads have stopped.
//                             The client should use this information to
//                             enable that all threads can be expanded to
//                             access their stacktraces. If the attribute
//                             is missing or false, only the thread with the
//                             given threadId can be expanded."
//           }
//         },
//         "required": [ "reason" ]
//       }
//     },
//     "required": [ "event", "body" ]
//   }]
// }
llvm::json::Value CreateThreadStopped(lldb::SBThread &thread,
                                      uint32_t stop_id) {}

const char *GetNonNullVariableName(lldb::SBValue v) {}

std::string CreateUniqueVariableNameForDisplay(lldb::SBValue v,
                                               bool is_name_duplicated) {}

VariableDescription::VariableDescription(lldb::SBValue v, bool format_hex,
                                         bool is_name_duplicated,
                                         std::optional<std::string> custom_name)
    :{}

llvm::json::Object VariableDescription::GetVariableExtensionsJSON() {}

std::string VariableDescription::GetResult(llvm::StringRef context) {}

// "Variable": {
//   "type": "object",
//   "description": "A Variable is a name/value pair. Optionally a variable
//                   can have a 'type' that is shown if space permits or when
//                   hovering over the variable's name. An optional 'kind' is
//                   used to render additional properties of the variable,
//                   e.g. different icons can be used to indicate that a
//                   variable is public or private. If the value is
//                   structured (has children), a handle is provided to
//                   retrieve the children with the VariablesRequest. If
//                   the number of named or indexed children is large, the
//                   numbers should be returned via the optional
//                   'namedVariables' and 'indexedVariables' attributes. The
//                   client can use this optional information to present the
//                   children in a paged UI and fetch them in chunks.",
//   "properties": {
//     "name": {
//       "type": "string",
//       "description": "The variable's name."
//     },
//     "value": {
//       "type": "string",
//       "description": "The variable's value. This can be a multi-line text,
//                       e.g. for a function the body of a function."
//     },
//     "type": {
//       "type": "string",
//       "description": "The type of the variable's value. Typically shown in
//                       the UI when hovering over the value."
//     },
//     "presentationHint": {
//       "$ref": "#/definitions/VariablePresentationHint",
//       "description": "Properties of a variable that can be used to determine
//                       how to render the variable in the UI."
//     },
//     "evaluateName": {
//       "type": "string",
//       "description": "Optional evaluatable name of this variable which can
//                       be passed to the 'EvaluateRequest' to fetch the
//                       variable's value."
//     },
//     "variablesReference": {
//       "type": "integer",
//       "description": "If variablesReference is > 0, the variable is
//                       structured and its children can be retrieved by
//                       passing variablesReference to the VariablesRequest."
//     },
//     "namedVariables": {
//       "type": "integer",
//       "description": "The number of named child variables. The client can
//                       use this optional information to present the children
//                       in a paged UI and fetch them in chunks."
//     },
//     "indexedVariables": {
//       "type": "integer",
//       "description": "The number of indexed child variables. The client
//                       can use this optional information to present the
//                       children in a paged UI and fetch them in chunks."
//     },
//     "memoryReference": {
//        "type": "string",
//        "description": "A memory reference associated with this variable.
//                        For pointer type variables, this is generally a
//                        reference to the memory address contained in the
//                        pointer. For executable data, this reference may later
//                        be used in a `disassemble` request. This attribute may
//                        be returned by a debug adapter if corresponding
//                        capability `supportsMemoryReferences` is true."
//     },
//     "declarationLocationReference": {
//       "type": "integer",
//       "description": "A reference that allows the client to request the
//                       location where the variable is declared. This should be
//                       present only if the adapter is likely to be able to
//                       resolve the location.\n\nThis reference shares the same
//                       lifetime as the `variablesReference`. See 'Lifetime of
//                       Object References' in the Overview section for
//                       details."
//     },
//
//     "$__lldb_extensions": {
//       "description": "Unofficial extensions to the protocol",
//       "properties": {
//         "declaration": {
//           "type": "object",
//           "description": "The source location where the variable was
//                           declared. This value won't be present if no
//                           declaration is available.
//                           Superseded by `declarationLocationReference`",
//           "properties": {
//             "path": {
//               "type": "string",
//               "description": "The source file path where the variable was
//                              declared."
//             },
//             "line": {
//               "type": "number",
//               "description": "The 1-indexed source line where the variable
//                               was declared."
//             },
//             "column": {
//               "type": "number",
//               "description": "The 1-indexed source column where the variable
//                               was declared."
//             }
//           }
//         },
//         "value": {
//           "type": "string",
//           "description": "The internal value of the variable as returned by
//                            This is effectively SBValue.GetValue(). The other
//                            `value` entry in the top-level variable response
//                            is, on the other hand, just a display string for
//                            the variable."
//         },
//         "summary": {
//           "type": "string",
//           "description": "The summary string of the variable. This is
//                           effectively SBValue.GetSummary()."
//         },
//         "autoSummary": {
//           "type": "string",
//           "description": "The auto generated summary if using
//                           `enableAutoVariableSummaries`."
//         },
//         "error": {
//           "type": "string",
//           "description": "An error message generated if LLDB couldn't inspect
//                           the variable."
//         }
//       }
//     }
//   },
//   "required": [ "name", "value", "variablesReference" ]
// }
llvm::json::Value CreateVariable(lldb::SBValue v, int64_t var_ref,
                                 bool format_hex, bool is_name_duplicated,
                                 std::optional<std::string> custom_name) {}

llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit unit) {}

/// See
/// https://microsoft.github.io/debug-adapter-protocol/specification#Reverse_Requests_RunInTerminal
llvm::json::Object
CreateRunInTerminalReverseRequest(const llvm::json::Object &launch_request,
                                  llvm::StringRef debug_adaptor_path,
                                  llvm::StringRef comm_file,
                                  lldb::pid_t debugger_pid) {}

// Keep all the top level items from the statistics dump, except for the
// "modules" array. It can be huge and cause delay
// Array and dictionary value will return as <key, JSON string> pairs
void FilterAndGetValueForKey(const lldb::SBStructuredData data, const char *key,
                             llvm::json::Object &out) {}

void addStatistic(llvm::json::Object &event) {}

llvm::json::Object CreateTerminatedEventObject() {}

std::string JSONToString(const llvm::json::Value &json) {}

} // namespace lldb_dap