[Lldb-commits] [lldb] [lldb-dap] Add a new lldb-dap test infrastructure (PR #203978)
Sergei Druzhkov via lldb-commits
lldb-commits at lists.llvm.org
Tue Jun 16 05:29:17 PDT 2026
================
@@ -0,0 +1,2022 @@
+# NOTE: this module must not include `from __future__ import annotations`
+# as the `annotations` import changes some of the types hints to strings.
+# especially when you have a forward declared type reference.
+# see https://peps.python.org/pep-0649/#motivation-for-this-pep
+# https://peps.python.org/pep-0749/#rejected-alternatives
+#
+# This module may not depend on any other module.
+
+from contextlib import suppress
+import copy
+import dataclasses
+import enum
+import json
+import os
+import sys
+import typing
+from dataclasses import asdict, dataclass, field, is_dataclass
+from enum import Enum
+from functools import lru_cache
+from typing import (
+ Any,
+ ClassVar,
+ Dict,
+ List,
+ Literal,
+ Optional,
+ Protocol,
+ Set,
+ Tuple,
+ Type,
+ TypeVar,
+ Union,
+ runtime_checkable,
+)
+
+if sys.version_info >= (3, 11):
+ from enum import StrEnum
+else:
+
+ class StrEnum(str, Enum):
+ """Backport of StrEnum for Python < 3.11."""
+
+ def __str__(self) -> str:
+ return self.value
+
+ def __repr__(self) -> str:
+ return self.value
+
+ @staticmethod
+ def _generate_next_value_(name: str, start, count, last_values) -> str:
+ return name.lower()
+
+
+T = TypeVar("T")
+
+
+class DAPError(AssertionError):
+ """The base error for all DAP related errors
+
+ Inherits from assertion error because of unittests treats assertions outside of a test as a failure
+ instead of a test error. see https://docs.python.org/3/library/unittest.html#unittest.TestCase.setUp
+ """
+
+ @classmethod
+ def history_closed(cls, reason=None, last_event: Optional["Event"] = None):
+ suffix = f" (Reason: {reason})" if reason else ""
+ return cls(
+ f"EventHistory is closed{suffix}. Last recorded event: {last_event}."
+ )
+
+
+RawMessage = Dict[str, Any]
+"""Representation of a json protocol message """
+
+
+class MessageType(StrEnum):
+ REQUEST = "request"
+ RESPONSE = "response"
+ EVENT = "event"
+
+
+class EventName(StrEnum):
+ BREAKPOINT = "breakpoint"
+ CAPABILITIES = "capabilities"
+ CONTINUED = "continued"
+ EXITED = "exited"
+ INITIALIZED = "initialized"
+ INVALIDATED = "invalidated"
+ MEMORY = "memory"
+ MODULE = "module"
+ OUTPUT = "output"
+ PROCESS = "process"
+ PROGRESS_END = "progressEnd"
+ PROGRESS_START = "progressStart"
+ PROGRESS_UPDATE = "progressUpdate"
+ STOPPED = "stopped"
+ TERMINATED = "terminated"
+ THREAD = "thread"
+
+
+ at dataclass(frozen=True)
+class ProtocolMessage:
+ type: MessageType
+ seq: int
+
+ def to_dict(self):
+ return message_to_dict(self)
+
+ @classmethod
+ def from_json(cls: Type[T], json: RawMessage) -> T:
+ if not dataclasses.is_dataclass(cls):
+ raise ValueError(f"{cls.__name__} must be a dataclass")
+
+ return dict_to_message(cls, json)
+
+
+ at dataclass(frozen=True)
+class Request(ProtocolMessage):
+ command: str
+ arguments: Any
+
+ def __post_init__(self):
+ assert (
+ self.type == MessageType.REQUEST
+ ), f"expected request type to be 'request' got '{self.type}' in : {self}"
+
+
+ at dataclass(frozen=True)
+class Response(ProtocolMessage):
+ command: str
+ request_seq: int
+ success: bool
+
+ def __post_init__(self):
+ assert (
+ self.type == MessageType.RESPONSE
+ ), f"expected '{type(self).__name__}' to be of type response: {self}"
+
+ def __init_subclass__(cls, **kwargs):
+ super().__init_subclass__(**kwargs)
+ if not dataclasses.is_dataclass(cls):
+ raise TypeError(f"{cls.__name__} must be a dataclass")
+
+
+AnyResponse = TypeVar("AnyResponse", bound=Response)
+
+
+class RequestError(DAPError):
+ """Raised if a DAP request fails."""
+
+ def __init__(
+ self,
+ request: Union[Request, dict],
+ response: Optional[Union[Response, dict]] = None,
+ ):
+ super().__init__()
+ self.request = request
+ self.response = response
+
+ def __str__(self) -> str:
+ desc = f"request failed request={self.request!r}"
+ if self.response:
+ desc += f" response={self.response!r}"
+ return desc
+
+
+ at dataclass(frozen=True)
+class Event(ProtocolMessage):
+ event: Union[EventName, str]
+ type: MessageType
+ __registry: ClassVar[Dict[str, Type]] = {}
+
+ def __init_subclass__(cls, *, event: str, **kwargs):
+ super().__init_subclass__(**kwargs)
+
+ assert event is not None
+ # Attach metadata (not a field of an event).
+ cls.__message_type__ = event
+
+ # Prevent duplicate types.
+ existing_event_class = Event.__registry.get(event)
+ if existing_event_class is not None:
+ raise Exception(
+ f"cannot register '{event}' event to class '{cls}' because it is already registered to '{existing_event_class}'"
+ )
+
+ Event.__registry[event] = cls
+
+ def __post_init__(self):
+ if self.type != MessageType.EVENT:
+ raise ValueError(f"event must have type of 'event': {self}")
+
+ @classmethod
+ def from_json(cls: Type[T], json: dict, check_class: bool = True) -> T:
+ event_type = json["event"]
+
+ if event_type not in Event.__registry:
+ raise ValueError(f"event type '{event_type}' is not registered")
+
+ event_class = Event.__registry[event_type]
+
+ if check_class and type(cls) != type(event_class):
+ raise ValueError(
+ f"class: {type(cls)} is not of the expected type {type(event_class)}"
+ )
+
+ if not dataclasses.is_dataclass(cls):
+ raise ValueError(f"{cls.__name__} must be a dataclass")
+
+ return dict_to_message(event_class, json)
+
+
+AnyEvent = TypeVar("AnyEvent", bound=Event)
+
+
+class EmptyBodyResponse(Response):
+ body: None = field(init=False, default=None)
+
+
+def args_protocol(cls):
+ """Decorator to check a class conforms to the ArgsProtocol"""
+
+ required_fields = filter(lambda x: not x.startswith("_"), dir(ArgsProtocol))
+ for r_field in required_fields:
+ if not hasattr(cls, r_field):
+ raise AttributeError(
+ f"{cls.__name__} must define '{r_field}' to implement ArgProtocol"
+ )
+
+ command_name = getattr(cls, "command_")
+ if not issubclass(type(command_name), str):
+ raise TypeError(
+ f"the command_ type '{type(command_name)}' for class '{cls.__name__}' must be string like"
+ )
+
+ return cls
+
+
+def _message_to_dict_impl(obj: typing.Any, skip_none: bool = True) -> typing.Any:
+ if dataclasses.is_dataclass(obj):
+ fields = _get_dataclass_fields(type(obj))
+ visited: Dict[str, Any] = {}
+ for f in fields:
+ dict_name = f.metadata.get("alias", f.name)
+ if dict_name in visited or not hasattr(obj, f.name):
+ continue
+ name_attr = getattr(obj, f.name)
+ if skip_none and name_attr is None:
+ continue
+ visited[dict_name] = _message_to_dict_impl(name_attr, skip_none)
+ return visited
+
+ if isinstance(obj, (list, tuple)):
+ return type(obj)(_message_to_dict_impl(item, skip_none) for item in obj)
+
+ if isinstance(obj, dict):
+ result: Dict[str, Any] = {}
+ for key, value in obj.items():
+ result[str(key)] = _message_to_dict_impl(value, skip_none)
+ return result
+
+ # Test enum first as it can also be a subclass of other primitives.
+ if isinstance(obj, Enum):
+ return obj.value
+
+ if isinstance(obj, (bool, int, float, str, bytes, type, type(None))):
+ return obj
+
+ return copy.deepcopy(obj)
+
+
+def message_to_dict(args: Any) -> RawMessage:
+ """
+ Converts DAP types to dictionaries.
+ We always skip optional types in dataclasses during conversion.
+ """
+ if not is_dataclass(args):
+ raise TypeError(
+ f"expected a dataclass instance, got {type(args).__name__}: {args!r}"
+ )
+ result = _message_to_dict_impl(args)
+ assert isinstance(result, dict)
+ return result
+
+
+ at lru_cache
+def _get_dataclass_fields(cls: Type) -> Tuple[dataclasses.Field, ...]:
+ data_class_hints = typing.get_type_hints(cls)
+ result: List[dataclasses.Field] = []
+ for f in dataclasses.fields(cls): # noqa
+ # Ignores 'command_' and 'response_class_'.
+ if f.name.endswith("_"):
+ continue
+ f_copy = copy.copy(f)
+ f_copy.type = data_class_hints[f_copy.name]
+ result.append(f_copy)
+ return tuple(result)
+
+
+ at lru_cache
+def _prepare_dataclass_fields(cls: Type) -> Dict[str, dataclasses.Field]:
+ # Excludes init=False fields since they cannot be passed to __init__.
+ return {f.name: f for f in _get_dataclass_fields(cls) if f.init}
+
+
+def _get_compatible_union_types(data: Any, possible_types: List[Type]) -> List[Type]:
+ """Filters a list of candidate types to find those compatible with the given raw data.
+
+ This helps fail early on type mismatches during parsing. For example, it
+ ensures that a raw payload like `[10, 20]` is correctly matched to `List[int]`
+ rather than a primitive `int`.
+ """
+
+ def _is_type_compatible(candidate_type: Type, data: Any) -> bool:
+ """Helper function to check if a single type is compatible with the data."""
+ origin = typing.get_origin(candidate_type)
+
+ if dataclasses.is_dataclass(candidate_type) or origin is dict:
+ return isinstance(data, dict)
+
+ if origin in (list, tuple):
+ return isinstance(data, (list, tuple))
+
+ if candidate_type is bool:
+ return isinstance(data, bool)
+
+ if candidate_type in (int, float):
+ # Prevent implicit bool conversion as bool is a subclass of int.
+ return isinstance(data, (int, float)) and not isinstance(data, bool)
+
+ if candidate_type is str:
+ return isinstance(data, str)
+
+ if isinstance(candidate_type, type) and issubclass(candidate_type, enum.Enum):
+ if issubclass(candidate_type, enum.IntEnum):
+ return isinstance(data, int) and not isinstance(data, bool)
+ return isinstance(data, str)
+
+ # Fallback for unknown or other generic types
+ return True
+
+ result = [a_type for a_type in possible_types if _is_type_compatible(a_type, data)]
+ return result
+
+
+def _generic_to_message(cls: Optional[Type], data: Any, scope: List[str]) -> Any:
+ origin = typing.get_origin(cls)
+ args = typing.get_args(cls)
+ full_path = ".".join(scope)
+
+ if origin is Literal:
+ unique_literals: Set = set()
+
+ def flatten_literal(args: Any):
+ """'Literal' types can be nested e.g.
+ Literal[Literal["book"], Literal["pen"]] is the same as Literal["book", "pen"]
+ """
+ for val in args:
+ if typing.get_origin(val) is None:
+ unique_literals.add(val)
+ else:
+ flatten_literal(typing.get_args(val))
+
+ flatten_literal(args)
+
+ if data not in unique_literals:
+ raise ValueError(
+ f"expected one of {unique_literals!r} at {full_path}, got {data!r}"
+ )
+ return data
+
+ elif origin is Union:
+ none_type = type(None)
+ is_optional = none_type in args
+ candidate_args = [a for a in args if a is not none_type]
+
+ # Handle Optional.
+ # Optional is represented as Union[T, None].
+ if is_optional and len(candidate_args) == 1:
+ return _dict_to_message_impl(candidate_args[0], data, scope)
+
+ matches = _get_compatible_union_types(data, candidate_args)
+ if not matches:
+ raise TypeError(
+ f"no variant of {cls} is compatible with "
+ f"{type(data).__name__} at {full_path}: {data!r}"
+ )
+
+ # Only one match, try conversion.
+ if len(matches) == 1:
+ return _dict_to_message_impl(matches[0], data, scope)
+
+ for arg in matches:
+ with suppress(TypeError, ValueError, AttributeError, AssertionError):
+ return _dict_to_message_impl(arg, data, scope)
+
+ raise TypeError(
+ f"no variant of {cls} matched {type(data).__name__} at {full_path}: {data!r}"
+ )
+
+ elif origin is list:
+ if not isinstance(data, list):
+ raise TypeError(
+ f"expected list at {full_path}, got {type(data).__name__}: {data!r}"
+ )
+ list_type = args[0]
+ return [
+ _dict_to_message_impl(list_type, val, scope + [str(idx)])
+ for idx, val in enumerate(data)
+ ]
+
+ elif origin is tuple:
+ if not isinstance(data, list):
+ raise TypeError(
+ f"expected list for tuple at {full_path}, got {type(data).__name__}: {data!r}"
+ )
+ item_type = args[0]
+ return tuple(
+ _dict_to_message_impl(item_type, val, scope + [str(idx)])
+ for idx, val in enumerate(data)
+ )
+
+ elif origin is dict:
+ if not isinstance(data, dict):
+ raise TypeError(
+ f"expected dict at {full_path}, got {type(data).__name__}: {data!r}"
+ )
+ if len(args) == 0:
+ args = (str, Any)
+ key_type = args[0]
+ value_type = args[1]
+ return {
+ _dict_to_message_impl(key_type, key, scope): _dict_to_message_impl(
+ value_type, value, scope + [str(key)]
+ )
+ for key, value in data.items()
+ }
+
+ raise TypeError(f"unhandled generic type {cls} at {full_path}: {data!r}")
+
+
+def _dict_to_message_impl(
+ cls: Optional[Type], data: typing.Any, scope: List[str]
+) -> typing.Any:
+ """
+ Recursively deserializes a dictionary into a specified Python type or dataclass.
+
+ Args:
+ cls: The target Python type or dataclass to deserialize the data into.
+ data: The raw data (usually from a dictionary or JSON payload) to be converted.
+ scope: A list of keys representing the current depth in the nested data structure.
+ Used to provide error messages when validation fails.
+
+ Returns:
+ The deserialized data cast to the requested type or instantiated dataclass.
+
+ Raises:
+ TypeError: If the data type does not match the expected type and cannot be coerced.
+ ValueError: If a dataclass field has a required value that does not match the data.
+ """
+ if not cls:
+ return data
+
+ full_path = ".".join(scope)
+ if data is None:
+ # Only pass None through if the declared type permits it.
+ if cls is type(None):
+ return None
+ is_union = typing.get_origin(cls) is Union
+ if is_union and type(None) in typing.get_args(cls): # Is Optional
+ return None
+
+ raise TypeError(f"got None for non-optional type '{cls}' at {full_path}")
+
+ if cls in (bytes, bytearray, Any):
+ return data
+
+ if dataclasses.is_dataclass(cls):
+ if not isinstance(data, dict):
+ raise TypeError(
+ f"expected dict for '{cls.__name__}' at {full_path}, "
+ f"got {type(data).__name__}: {data!r}"
+ )
+ fields = _prepare_dataclass_fields(cls)
+ deserialized = {}
+ for key, f in fields.items():
+ data_key = f.metadata.get("alias", key)
+ if data_key not in data:
+ continue
+
+ required_value = f.metadata.get("required", dataclasses.MISSING)
+ value = _dict_to_message_impl(f.type, data[data_key], scope + [key])
+
+ if required_value is not dataclasses.MISSING and value != required_value:
+ raise ValueError(
+ f"field '{key}' at {full_path} must be {required_value!r}, "
+ f"got {value!r}"
+ )
+ deserialized[key] = value
+ try:
+ return cls(**deserialized)
+ except TypeError as err:
+ msg = f"\n\tfailed to initialize '{cls.__name__}' at '{full_path}' "
+ msg += f'\n\twith dict: "{data}"'
+ err.args = (err.args[0] + msg, *err.args[1:])
+ raise
+
+ if cls in (str, int, float):
+ if isinstance(data, cls):
+ return data
+ # Allow int to float coercion but not bool as bool is a subclass of int:
+ if cls is float and isinstance(data, int) and not isinstance(data, bool):
+ return float(data)
+ raise TypeError(
+ f"expected {cls.__name__} at {full_path}, "
+ f"got {type(data).__name__}: {data!r}"
+ )
+
+ if cls is bool:
+ if not isinstance(data, bool):
+ raise TypeError(
+ f"expected bool at {full_path}, " f"got {type(data).__name__}: {data!r}"
+ )
+ return data
+
+ if isinstance(cls, type) and issubclass(cls, enum.Enum):
+ if issubclass(cls, enum.IntEnum):
+ return cls(int(data))
+ return cls(data)
+
+ # Handle generic types i.e List[T], Tuple[T] e.t.c.
+ if typing.get_origin(cls) is not None:
+ return _generic_to_message(cls, data, scope)
+
+ raise TypeError(
+ f"unexpected type {cls} at {full_path}: " f"got {type(data).__name__}: {data!r}"
+ )
+
+
+def dict_to_message(cls: Type[T], raw_dict: RawMessage) -> T:
+ return _dict_to_message_impl(cls, raw_dict, [cls.__name__])
+
+
+ at runtime_checkable
+class ArgsProtocol(Protocol[T]): # type: ignore[misc]
+ __dataclass_fields__: ClassVar[Dict]
+
+ @property
+ def response_class_(self) -> Type[T]:
+ ...
+
+ @property
+ def command_(self) -> str:
+ ...
+
+
+class Console(StrEnum):
+ INTERNAL = "internalConsole"
+ INTEGRATED_TERMINAL = "integratedTerminal"
+ EXTERNAL_TERMINAL = "externalTerminal"
+
+
+class OutputCategory(StrEnum):
+ CONSOLE = "console"
+ IMPORTANT = "important"
+ STDOUT = "stdout"
+ STDERR = "stderr"
+ TELEMETRY = "telemetry"
+
+
+class StoppedReason(StrEnum):
+ STEP = "step"
+ BREAKPOINT = "breakpoint"
+ EXCEPTION = "exception"
+ PAUSE = "pause"
+ ENTRY = "entry"
+ GOTO = "goto"
+ FUNCTION_BREAKPOINT = "function breakpoint"
+ DATA_BREAKPOINT = "data breakpoint"
+ INSTRUCTION_BREAKPOINT = "instruction breakpoint"
+
+
+class ModuleReason(StrEnum):
+ NEW = "new"
+ CHANGED = "changed"
+ REMOVED = "removed"
+
+
+class BreakpointReason(StrEnum):
+ NEW = "new"
+ CHANGED = "changed"
+ REMOVED = "removed"
+
+
+class InvalidatedAreas(StrEnum):
+ ALL = "all"
+ STACKS = "stacks"
+ THREADS = "threads"
+ VARIABLES = "variables"
+
+
+class StartDebuggingRequestType(StrEnum):
+ LAUNCH = "launch"
+ ATTACH = "attach"
+
+
+ExceptionBreakMode = Literal["never", "always", "unhandled", "userUnhandled"]
+ChecksumAlgorithm = Literal["MD5", "SHA1", "SHA256", "timestamp"]
+
+ScopePresentationHint = Literal["arguments", "locals", "registers"]
+SourcePresentationHint = Literal["normal", "emphasize", "deemphasize"]
+StepInTargetPresentationHint = Literal["normal", "label", "subtle"]
+GotoTargetPresentationHint = StepInTargetPresentationHint
+StackFramePresentationHint = StepInTargetPresentationHint
+
+VariablePresentationHintKind = Literal[
+ "property",
+ "method",
+ "class",
+ "data",
+ "event",
+ "baseClass",
+ "innerClass",
+ "interface",
+ "mostDerivedClass",
+ "virtual",
+ "dataBreakpoint",
+]
+
+VariablePresentationHintVisibility = Literal[
+ "public", "private", "protected", "internal", "final"
+]
+
+
+ at dataclass(frozen=True)
+class Checksum:
+ algorithm: ChecksumAlgorithm
+ checksum: str
+
+
+ at dataclass(frozen=True)
+class Source:
+ name: Optional[str] = None
+ path: Optional[str] = None
+ sourceReference: Optional[int] = None
+ presentationHint: Optional[SourcePresentationHint] = None
+ origin: Optional[str] = None
+ sources: Optional[List["Source"]] = None
+ adapterData: Optional[Any] = None
+ checksums: Optional[List[Checksum]] = None
+
+ def __post_init__(self):
+ if not self.name and not self.path and not self.sourceReference:
+ raise ValueError(
+ f"Source requires either name, path, or source_reference. {self}"
+ )
+
+ @classmethod
+ def create(cls, name: Optional[str] = None, path: Optional[str] = None, **kwargs):
+ if path and not name:
+ name = os.path.basename(path)
+
+ return cls(name=name, path=path, **kwargs)
+
+
+ at dataclass(frozen=True)
+class Breakpoint:
+ verified: bool
+ id: Optional[int] = None
+ message: Optional[str] = None
+ source: Optional[Source] = None
+ line: Optional[int] = None
+ column: Optional[int] = None
+ endLine: Optional[int] = None
+ endColumn: Optional[int] = None
+ instructionReference: Optional[str] = None
+ offset: Optional[int] = None
+ reason: Optional[str] = None
+
+
+ at dataclass(frozen=True)
+class SourceBreakpoint:
+ line: int
+ column: Optional[int] = None
+ condition: Optional[str] = None
+ hitCondition: Optional[str] = None
+ logMessage: Optional[str] = None
+ mode: Optional[str] = None
+
+
+ at dataclass(frozen=True)
+class FunctionBreakpoint:
+ name: str
+ condition: Optional[str] = None
+ hitCondition: Optional[str] = None
+
+
+ at dataclass(frozen=True)
+class DataBreakpoint:
+ dataId: str
+ accessType: Optional[str] = None
+ condition: Optional[str] = None
+ hitCondition: Optional[str] = None
+
+
+ at dataclass(frozen=True)
+class InstructionBreakpoint:
+ instructionReference: str
+ offset: Optional[int] = None
+ condition: Optional[str] = None
+ hitCondition: Optional[str] = None
+ mode: Optional[str] = None
+
+
+ at dataclass(frozen=True)
+class BreakpointLocation:
+ line: int
+ column: Optional[int] = None
+ endLine: Optional[int] = None
+ endColumn: Optional[int] = None
+
+
+ColumnDescriptorType = Literal["string", "number", "boolean", "unixTimestampUTC"]
+
+
+ at dataclass(frozen=True)
+class ColumnDescriptor:
+ attributeName: str
+ label: str
+ format: Optional[str] = None
+ type: Optional[ColumnDescriptorType] = None
+ width: Optional[int] = None
+
+
+ at dataclass(frozen=True)
+class ExceptionFilterOptions:
+ filterId: str
+ condition: Optional[str] = None
+
+
+ at dataclass(frozen=True)
+class ExceptionPathSegment:
+ names: List[str]
+ negate: Optional[bool] = None
+
+
+ at dataclass(frozen=True)
+class ExceptionOptions:
+ breakMode: ExceptionBreakMode
+ path: Optional[List[ExceptionPathSegment]] = None
+
+
+ at dataclass(frozen=True)
+class ExceptionDetails:
+ message: Optional[str] = None
+ typeName: Optional[str] = None
+ fullTypeName: Optional[str] = None
+ evaluateName: Optional[str] = None
+ stackTrace: Optional[str] = None
+ innerException: Optional[List["ExceptionDetails"]] = None
+
+
+ at dataclass(frozen=True)
+class ExceptionBreakpointsFilter:
+ filter: str
+ label: str
+ description: Optional[str] = None
+ default: Optional[bool] = None
+ supportsCondition: Optional[bool] = None
+ conditionDescription: Optional[str] = None
+
+
+ at dataclass(frozen=True)
+class Thread:
+ id: int
+ name: str
+
+
+ at dataclass(frozen=True)
+class StackFrame:
+ id: int
+ name: str
+ line: int
+ column: int
+ source: Optional[Source] = None
+ endLine: Optional[int] = None
+ endColumn: Optional[int] = None
+ canRestart: Optional[bool] = None
+ instructionPointerReference: Optional[str] = None
+ moduleId: Optional[Union[int, str]] = None
+ presentationHint: Optional[StackFramePresentationHint] = None
+
+
+ at dataclass(frozen=True)
+class Scope:
+ name: str
+ variablesReference: int
+ presentationHint: Optional[ScopePresentationHint] = None
+ namedVariables: Optional[int] = None
+ indexedVariables: Optional[int] = None
+ expensive: bool = False
+ source: Optional[Source] = None
+ line: Optional[int] = None
+ column: Optional[int] = None
+ endLine: Optional[int] = None
+ endColumn: Optional[int] = None
+
+
+ at dataclass(frozen=True)
+class VariablePresentationHint:
+ kind: Optional[VariablePresentationHintKind] = None
+ attributes: Optional[List[str]] = None
+ visibility: Optional[VariablePresentationHintVisibility] = None
+ lazy: Optional[bool] = None
+
+
+ at dataclass(frozen=True)
+class Variable:
+ name: str
+ value: str
+ variablesReference: int
+ type: Optional[str] = None
+ presentationHint: Optional[VariablePresentationHint] = None
+ evaluateName: Optional[str] = None
+ namedVariables: Optional[int] = None
+ indexedVariables: Optional[int] = None
+ memoryReference: Optional[str] = None
+ declarationLocationReference: Optional[int] = None
+ valueLocationReference: Optional[int] = None
+
+ @property
+ def value_as_int(self):
+ value = self.value
+ # 'value' may have the variable value and summary.
+ # Extract the variable value since summary can have nonnumeric characters.
+ value = value.split(" ")[0]
+ if value.startswith("0x"):
+ return int(value, 16)
+ elif value.startswith("0"):
+ return int(value, 8)
+ else:
+ return int(value)
+
+
+ at dataclass(frozen=True)
+class Message:
+ id: int
+ format: str
+ variables: Optional[Dict[str, str]] = None
+ sendTelemetry: Optional[bool] = None
+ showUser: Optional[bool] = None
+ url: Optional[str] = None
+ urlLabel: Optional[str] = None
+
+
+ at dataclass(frozen=True)
+class Module:
+ id: str
+ name: str
+ path: Optional[str] = None
+ isOptimized: Optional[bool] = None
+ isUserCode: Optional[bool] = None
+ version: Optional[str] = None
+ symbolStatus: Optional[str] = None
+ symbolFilePath: Optional[str] = None
+ dateTimeStamp: Optional[str] = None
+ addressRange: Optional[str] = None
+ # custom
+ debugInfoSize: Optional[str] = None
+
+
+ at dataclass(frozen=True)
+class CompletionItem:
+ label: str
+ text: Optional[str] = None
+ detail: Optional[str] = None
+ start: Optional[int] = None
+ length: int = 0
+
+ def __repr__(self):
+ # Use json as it is easier to see the diff on failure.
+ return json.dumps(asdict(self), indent=4)
+
+ def clone(self, **kwargs) -> "CompletionItem":
+ """Creates a copy of this CompletionItem with specified fields modified."""
+ return dataclasses.replace(self, **kwargs)
+
+
+ at dataclass(frozen=True)
+class ValueFormat:
+ hex: Optional[bool] = None
+
+
+ at dataclass(frozen=True)
+class StackFrameFormat:
+ hex: Optional[bool] = None
+ parameters: Optional[bool] = None
+ parameterTypes: Optional[bool] = None
+ parameterNames: Optional[bool] = None
+ parameterValues: Optional[bool] = None
+ line: Optional[bool] = None
+ module: Optional[bool] = None
+ includeAll: Optional[bool] = None
+
+
+ at dataclass(frozen=True)
+class GotoTarget:
+ id: int
+ label: str
+ line: int
+ column: Optional[int] = None
+ endLine: Optional[int] = None
+ endColumn: Optional[int] = None
+ instructionPointerReference: Optional[str] = None
+
+
+ at dataclass(frozen=True)
+class StepInTarget:
+ id: int
+ label: str
+ line: Optional[int] = None
+ column: Optional[int] = None
+ endLine: Optional[int] = None
+ endColumn: Optional[int] = None
+
+
+ at dataclass(frozen=True)
+class DisassembledInstruction:
+ address: str
+ instruction: str
+ instructionBytes: Optional[str] = None
+ symbol: Optional[str] = None
+ location: Optional[Source] = None
+ line: Optional[int] = None
+ column: Optional[int] = None
+ endLine: Optional[int] = None
+ endColumn: Optional[int] = None
+ presentationHint: Optional[str] = None
+
+
+ at dataclass(frozen=True)
+class Capabilities:
+ supportsConfigurationDoneRequest: Optional[bool] = None
+ supportsFunctionBreakpoints: Optional[bool] = None
+ supportsConditionalBreakpoints: Optional[bool] = None
+ supportsHitConditionalBreakpoints: Optional[bool] = None
+ supportsEvaluateForHovers: Optional[bool] = None
+ exceptionBreakpointFilters: Optional[List[ExceptionBreakpointsFilter]] = None
+ supportsStepBack: Optional[bool] = None
+ supportsSetVariable: Optional[bool] = None
+ supportsRestartFrame: Optional[bool] = None
+ supportsGotoTargetsRequest: Optional[bool] = None
+ supportsStepInTargetsRequest: Optional[bool] = None
+ supportsCompletionsRequest: Optional[bool] = None
+ completionTriggerCharacters: Optional[List[str]] = None
+ supportsModulesRequest: Optional[bool] = None
+ additionalModuleColumns: Optional[List[ColumnDescriptor]] = None
+ supportedChecksumAlgorithms: Optional[List[ChecksumAlgorithm]] = None
+ supportsRestartRequest: Optional[bool] = None
+ supportsExceptionOptions: Optional[bool] = None
+ supportsValueFormattingOptions: Optional[bool] = None
+ supportsExceptionInfoRequest: Optional[bool] = None
+ supportTerminateDebuggee: Optional[bool] = None
+ supportSuspendDebuggee: Optional[bool] = None
+ supportsDelayedStackTraceLoading: Optional[bool] = None
+ supportsLoadedSourcesRequest: Optional[bool] = None
+ supportsLogPoints: Optional[bool] = None
+ supportsTerminateThreadsRequest: Optional[bool] = None
+ supportsSetExpression: Optional[bool] = None
+ supportsTerminateRequest: Optional[bool] = None
+ supportsDataBreakpoints: Optional[bool] = None
+ supportsReadMemoryRequest: Optional[bool] = None
+ supportsWriteMemoryRequest: Optional[bool] = None
+ supportsDisassembleRequest: Optional[bool] = None
+ supportsCancelRequest: Optional[bool] = None
+ supportsBreakpointLocationsRequest: Optional[bool] = None
+ supportsClipboardContext: Optional[bool] = None
+ supportsSteppingGranularity: Optional[bool] = None
+ supportsInstructionBreakpoints: Optional[bool] = None
+ supportsExceptionFilterOptions: Optional[bool] = None
+ supportsSingleThreadExecutionRequests: Optional[bool] = None
+ supportsDataBreakpointBytes: Optional[bool] = None
+ breakpointModes: Optional[List[Any]] = None
+ lldb_version: Optional[str] = field(
+ metadata={"alias": "$__lldb_version"}, default=None
+ )
+
+ # lldb-dap custom capability.
+ supportsModuleSymbolsRequest: Optional[bool] = None
+
+
+ at dataclass(frozen=True)
+class ErrorResponse(Response):
+ @dataclass(frozen=True)
+ class Body:
+ error: Optional[Message] = None
+
+ message: Optional[str] = None
+ body: Optional[Body] = None
+
+ def __post_init__(self):
+ assert not self.success, f"success field must be 'False' {self}"
+
+
+ at dataclass(frozen=True)
+ at args_protocol
+class CancelArgs:
+ requestId: Optional[int] = None
+ progressId: Optional[str] = None
+
+ command_ = "cancel"
+ response_class_ = EmptyBodyResponse
+
+
+ at dataclass(frozen=True)
+class RunInTerminalResponse(Response):
+ @dataclass(frozen=True)
+ class Body:
+ processId: Optional[int] = None
+ shellProcessId: Optional[int] = None
+
+ body: Body
+
+
+ at dataclass(frozen=True)
+class RunInTerminalArgs:
----------------
DrSergei wrote:
Do we need `@args_protocol`, `command_` and `response_class_`?
https://github.com/llvm/llvm-project/pull/203978
More information about the lldb-commits
mailing list