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