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