Add returnValue scope to DAP

The DAP spec recently changed to add a new scope for the return value
from a "stepOut" request.  This new scope uses the "returnValue"
presentation hint.  See:

    https://github.com/microsoft/debug-adapter-protocol/issues/458

This patch implements this for gdb.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31945
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
This commit is contained in:
Tom Tromey
2024-07-02 10:43:51 -06:00
parent fa8c46f5ad
commit c51fb38679
3 changed files with 25 additions and 16 deletions

View File

@@ -8,6 +8,10 @@
** The "scopes" request will now return a scope holding global ** The "scopes" request will now return a scope holding global
variables from the stack frame's compilation unit. variables from the stack frame's compilation unit.
** The "scopes" request will return a "returnValue" scope holding
the return value from the latest "stepOut" command, when
appropriate.
* For ARM targets, the offset of the pc in the jmp_buf has been fixed to match * For ARM targets, the offset of the pc in the jmp_buf has been fixed to match
glibc 2.20 and later. This should only matter when not using libc probes. glibc 2.20 and later. This should only matter when not using libc probes.
This may cause breakage when using an incompatible libc, like uclibc or This may cause breakage when using an incompatible libc, like uclibc or

View File

@@ -111,20 +111,19 @@ class _ScopeReference(BaseReference):
return symbol_value(self.var_list[idx], self.frame) return symbol_value(self.var_list[idx], self.frame)
# A _ScopeReference that prepends the most recent return value. Note # A _ScopeReference that wraps the 'finish' value. Note that this
# that this object is only created if such a value actually exists. # object is only created if such a value actually exists.
class _FinishScopeReference(_ScopeReference): class _FinishScopeReference(_ScopeReference):
def __init__(self, *args): def __init__(self, frame):
super().__init__(*args) super().__init__("Return", "returnValue", frame, ())
def child_count(self): def child_count(self):
return super().child_count() + 1 return 1
def fetch_one_child(self, idx): def fetch_one_child(self, idx):
if idx == 0: assert idx == 0
global _last_return_value global _last_return_value
return ("(return)", _last_return_value) return ("(return)", _last_return_value)
return super().fetch_one_child(idx - 1)
class _RegisterReference(_ScopeReference): class _RegisterReference(_ScopeReference):
@@ -159,11 +158,11 @@ def scopes(*, frameId: int, **extra):
# Make sure to handle the None case as well as the empty # Make sure to handle the None case as well as the empty
# iterator case. # iterator case.
locs = tuple(frame.frame_locals() or ()) locs = tuple(frame.frame_locals() or ())
if has_return_value: if locs:
scopes.append(_FinishScopeReference("Locals", "locals", frame, locs))
elif locs:
scopes.append(_ScopeReference("Locals", "locals", frame, locs)) scopes.append(_ScopeReference("Locals", "locals", frame, locs))
scopes.append(_RegisterReference("Registers", frame)) scopes.append(_RegisterReference("Registers", frame))
if has_return_value:
scopes.append(_FinishScopeReference(frame))
frame_to_scope[frameId] = scopes frame_to_scope[frameId] = scopes
global_scope = get_global_scope(frame) global_scope = get_global_scope(frame)
if global_scope is not None: if global_scope is not None:

View File

@@ -59,15 +59,21 @@ set scopes [dap_check_request_and_response "get scopes" scopes \
[format {o frameId [i %d]} $frame_id]] [format {o frameId [i %d]} $frame_id]]
set scopes [dict get [lindex $scopes 0] body scopes] set scopes [dict get [lindex $scopes 0] body scopes]
gdb_assert {[llength $scopes] == 2} "two scopes" gdb_assert {[llength $scopes] == 3} "three scopes"
lassign $scopes scope reg_scope lassign $scopes scope reg_scope return_scope
gdb_assert {[dict get $scope name] == "Locals"} "scope is locals" gdb_assert {[dict get $scope name] == "Locals"} "scope is locals"
gdb_assert {[dict get $scope presentationHint] == "locals"} \ gdb_assert {[dict get $scope presentationHint] == "locals"} \
"locals presentation hint" "locals presentation hint"
gdb_assert {[dict get $scope namedVariables] == 2} "two vars in scope" gdb_assert {[dict get $scope namedVariables] == 1} "one var in scope"
set num [dict get $scope variablesReference] gdb_assert {[dict get $return_scope name] == "Return"} "scope is return"
gdb_assert {[dict get $return_scope presentationHint] == "returnValue"} \
"returnValue presentation hint"
gdb_assert {[dict get $return_scope namedVariables] == 1} \
"one var in return scope"
set num [dict get $return_scope variablesReference]
set refs [lindex [dap_check_request_and_response "fetch arguments" \ set refs [lindex [dap_check_request_and_response "fetch arguments" \
"variables" \ "variables" \
[format {o variablesReference [i %d]} $num]] \ [format {o variablesReference [i %d]} $num]] \