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 typing import List 15 16class SourceLocation: 17 def __init__(self, path: str = None, lineno: int = None, column: int = None): 18 if path: 19 path = os.path.normcase(path) 20 self.path = path 21 self.lineno = lineno 22 self.column = column 23 24 def __str__(self): 25 return '{}({}:{})'.format(self.path, self.lineno, self.column) 26 27 def match(self, other) -> bool: 28 """Returns true iff all the properties that appear in `self` have the 29 same value in `other`, but not necessarily vice versa. 30 """ 31 if not other or not isinstance(other, SourceLocation): 32 return False 33 34 if self.path and (self.path != other.path): 35 return False 36 37 if self.lineno and (self.lineno != other.lineno): 38 return False 39 40 if self.column and (self.column != other.column): 41 return False 42 43 return True 44 45 46class StackFrame: 47 def __init__(self, 48 function: str = None, 49 is_inlined: bool = None, 50 location: SourceLocation = None, 51 watches: OrderedDict = None): 52 if watches is None: 53 watches = {} 54 55 self.function = function 56 self.is_inlined = is_inlined 57 self.location = location 58 self.watches = watches 59 60 def __str__(self): 61 return '{}{}: {} | {}'.format( 62 self.function, 63 ' (inlined)' if self.is_inlined else '', 64 self.location, 65 {k: str(self.watches[k]) for k in self.watches}) 66 67 def match(self, other) -> bool: 68 """Returns true iff all the properties that appear in `self` have the 69 same value in `other`, but not necessarily vice versa. 70 """ 71 if not other or not isinstance(other, StackFrame): 72 return False 73 74 if self.location and not self.location.match(other.location): 75 return False 76 77 if self.watches: 78 for name in iter(self.watches): 79 try: 80 if isinstance(self.watches[name], dict): 81 for attr in iter(self.watches[name]): 82 if (getattr(other.watches[name], attr, None) != 83 self.watches[name][attr]): 84 return False 85 else: 86 if other.watches[name].value != self.watches[name]: 87 return False 88 except KeyError: 89 return False 90 91 return True 92 93class ProgramState: 94 def __init__(self, frames: List[StackFrame] = None): 95 self.frames = frames 96 97 def __str__(self): 98 return '\n'.join(map( 99 lambda enum: 'Frame {}: {}'.format(enum[0], enum[1]), 100 enumerate(self.frames))) 101 102 def match(self, other) -> bool: 103 """Returns true iff all the properties that appear in `self` have the 104 same value in `other`, but not necessarily vice versa. 105 """ 106 if not other or not isinstance(other, ProgramState): 107 return False 108 109 if self.frames: 110 for idx, frame in enumerate(self.frames): 111 try: 112 if not frame.match(other.frames[idx]): 113 return False 114 except (IndexError, KeyError): 115 return False 116 117 return True 118