xref: /llvm-project/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/ProgramState.py (revision f98ee40f4b5d7474fc67e82824bf6abbaedb7b1c)
1# DExTer : Debugging Experience Tester
2# ~~~~~~   ~         ~~         ~   ~~
3#
4# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5# See https://llvm.org/LICENSE.txt for license information.
6# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7"""Set of data classes for representing the complete debug program state at a
8fixed point in execution.
9"""
10
11import os
12
13from collections import OrderedDict
14from pathlib import PurePath
15from typing import List
16
17
18class SourceLocation:
19    def __init__(self, path: str = None, lineno: int = None, column: int = None):
20        if path:
21            path = os.path.normcase(path)
22        self.path = path
23        self.lineno = lineno
24        self.column = column
25
26    def __str__(self):
27        return "{}({}:{})".format(self.path, self.lineno, self.column)
28
29    def match(self, other) -> bool:
30        """Returns true iff all the properties that appear in `self` have the
31        same value in `other`, but not necessarily vice versa.
32        """
33        if not other or not isinstance(other, SourceLocation):
34            return False
35
36        if self.path and (
37            other.path is None or (PurePath(self.path) != PurePath(other.path))
38        ):
39            return False
40
41        if self.lineno and (self.lineno != other.lineno):
42            return False
43
44        if self.column and (self.column != other.column):
45            return False
46
47        return True
48
49
50class StackFrame:
51    def __init__(
52        self,
53        function: str = None,
54        is_inlined: bool = None,
55        location: SourceLocation = None,
56        watches: OrderedDict = None,
57    ):
58        if watches is None:
59            watches = {}
60
61        self.function = function
62        self.is_inlined = is_inlined
63        self.location = location
64        self.watches = watches
65
66    def __str__(self):
67        return "{}{}: {} | {}".format(
68            self.function,
69            " (inlined)" if self.is_inlined else "",
70            self.location,
71            {k: str(self.watches[k]) for k in self.watches},
72        )
73
74    def match(self, other) -> bool:
75        """Returns true iff all the properties that appear in `self` have the
76        same value in `other`, but not necessarily vice versa.
77        """
78        if not other or not isinstance(other, StackFrame):
79            return False
80
81        if self.location and not self.location.match(other.location):
82            return False
83
84        if self.watches:
85            for name in iter(self.watches):
86                try:
87                    if isinstance(self.watches[name], dict):
88                        for attr in iter(self.watches[name]):
89                            if (
90                                getattr(other.watches[name], attr, None)
91                                != self.watches[name][attr]
92                            ):
93                                return False
94                    else:
95                        if other.watches[name].value != self.watches[name]:
96                            return False
97                except KeyError:
98                    return False
99
100        return True
101
102
103class ProgramState:
104    def __init__(self, frames: List[StackFrame] = None):
105        self.frames = frames
106
107    def __str__(self):
108        return "\n".join(
109            map(
110                lambda enum: "Frame {}: {}".format(enum[0], enum[1]),
111                enumerate(self.frames),
112            )
113        )
114
115    def match(self, other) -> bool:
116        """Returns true iff all the properties that appear in `self` have the
117        same value in `other`, but not necessarily vice versa.
118        """
119        if not other or not isinstance(other, ProgramState):
120            return False
121
122        if self.frames:
123            for idx, frame in enumerate(self.frames):
124                try:
125                    if not frame.match(other.frames[idx]):
126                        return False
127                except (IndexError, KeyError):
128                    return False
129
130        return True
131