summaryrefslogtreecommitdiff
path: root/gdb/python/lib/gdb/dap/scopes.py
blob: be4f8fc28a09a2b2f2a9f6061f385cb4c2f76ae9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# 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

from .frames import frame_for_id
from .startup import send_gdb_with_response, in_gdb_thread
from .server import request
from .varref import BaseReference


# Helper function to return a frame's block without error.
@in_gdb_thread
def _safe_block(frame):
    try:
        return frame.block()
    except gdb.error:
        return None


# Helper function to return two lists of variables of block, up to the
# enclosing function.  The result is of the form (ARGS, LOCALS), where
# each element is itself a list.
@in_gdb_thread
def _block_vars(block):
    args = []
    locs = []
    while True:
        for var in block:
            if var.is_argument:
                args.append(var)
            elif var.is_variable or var.is_constant:
                locs.append(var)
        if block.function is not None:
            break
        block = block.superblock
    return (args, locs)


class ScopeReference(BaseReference):
    def __init__(self, name, frame, var_list):
        super().__init__(name)
        self.frame = frame
        self.func = frame.function()
        self.var_list = var_list

    def to_object(self):
        result = super().to_object()
        # How would we know?
        result["expensive"] = False
        result["namedVariables"] = len(self.var_list)
        if self.func is not None:
            result["line"] = self.func.line
            # FIXME construct a Source object
        return result

    def child_count(self):
        return len(self.var_list)

    @in_gdb_thread
    def fetch_one_child(self, idx):
        sym = self.var_list[idx]
        if sym.needs_frame:
            val = sym.value(self.frame)
        else:
            val = sym.value()
        return (sym.print_name, val)


# Helper function to create a DAP scopes for a given frame ID.
@in_gdb_thread
def _get_scope(id):
    frame = frame_for_id(id)
    block = _safe_block(frame)
    scopes = []
    if block is not None:
        (args, locs) = _block_vars(block)
        if args:
            scopes.append(ScopeReference("Arguments", frame, args))
        if locs:
            scopes.append(ScopeReference("Locals", frame, locs))
    return [x.to_object() for x in scopes]


@request("scopes")
def scopes(*, frameId, **extra):
    scopes = send_gdb_with_response(lambda: _get_scope(frameId))
    return {"scopes": scopes}