mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-08-06 14:49:38 +08:00

It occurred to me recently that gdb's DAP implementation should probably check the types of objects coming from the client. This patch implements this idea by reusing Python's existing type annotations, and supplying a decorator that verifies these at runtime. Python doesn't make it very easy to do runtime type-checking, so the core of the checker is written by hand. I haven't tried to make a fully generic runtime type checker. Instead, this only checks the subset that is needed by DAP. For example, only keyword-only functions are handled. Furthermore, in a few spots, it wasn't convenient to spell out the type that is accepted. I've added a couple of comments to this effect in breakpoint.py. I've tried to make this code compatible with older versions of Python, but I've only been able to try it with 3.9 and 3.10.
92 lines
2.8 KiB
Python
92 lines
2.8 KiB
Python
# Copyright 2022, 2023 Free Software Foundation, Inc.
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import gdb
|
|
import gdb.printing
|
|
|
|
# This is deprecated in 3.9, but required in older versions.
|
|
from typing import Optional
|
|
|
|
from .frames import frame_for_id
|
|
from .server import request
|
|
from .startup import send_gdb_with_response, in_gdb_thread
|
|
from .varref import find_variable, VariableReference
|
|
|
|
|
|
class EvaluateResult(VariableReference):
|
|
def __init__(self, value):
|
|
super().__init__(None, value, "result")
|
|
|
|
|
|
# Helper function to evaluate an expression in a certain frame.
|
|
@in_gdb_thread
|
|
def _evaluate(expr, frame_id):
|
|
global_context = True
|
|
if frame_id is not None:
|
|
frame = frame_for_id(frame_id)
|
|
frame.select()
|
|
global_context = False
|
|
val = gdb.parse_and_eval(expr, global_context=global_context)
|
|
ref = EvaluateResult(val)
|
|
return ref.to_object()
|
|
|
|
|
|
# Helper function to evaluate a gdb command in a certain frame.
|
|
@in_gdb_thread
|
|
def _repl(command, frame_id):
|
|
if frame_id is not None:
|
|
frame = frame_for_id(frame_id)
|
|
frame.select()
|
|
val = gdb.execute(command, from_tty=True, to_string=True)
|
|
return {
|
|
"result": val,
|
|
"variablesReference": 0,
|
|
}
|
|
|
|
|
|
# FIXME supportsVariableType handling
|
|
@request("evaluate")
|
|
def eval_request(
|
|
*,
|
|
expression: str,
|
|
frameId: Optional[int] = None,
|
|
context: str = "variables",
|
|
**args,
|
|
):
|
|
if context in ("watch", "variables"):
|
|
# These seem to be expression-like.
|
|
return send_gdb_with_response(lambda: _evaluate(expression, frameId))
|
|
elif context == "repl":
|
|
return send_gdb_with_response(lambda: _repl(expression, frameId))
|
|
else:
|
|
raise Exception(f'unknown evaluate context "{context}"')
|
|
|
|
|
|
@in_gdb_thread
|
|
def _variables(ref, start, count):
|
|
var = find_variable(ref)
|
|
children = var.fetch_children(start, count)
|
|
return [x.to_object() for x in children]
|
|
|
|
|
|
@request("variables")
|
|
# Note that we ignore the 'filter' field. That seems to be
|
|
# specific to javascript.
|
|
def variables(*, variablesReference: int, start: int = 0, count: int = 0, **args):
|
|
result = send_gdb_with_response(
|
|
lambda: _variables(variablesReference, start, count)
|
|
)
|
|
return {"variables": result}
|