11364750dSJames Henderson# DExTer : Debugging Experience Tester 21364750dSJames Henderson# ~~~~~~ ~ ~~ ~ ~~ 31364750dSJames Henderson# 41364750dSJames Henderson# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 51364750dSJames Henderson# See https://llvm.org/LICENSE.txt for license information. 61364750dSJames Henderson# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 71364750dSJames Henderson"""Command for specifying a partial or complete state for the program to enter 81364750dSJames Hendersonduring execution. 91364750dSJames Henderson""" 101364750dSJames Henderson 111364750dSJames Hendersonfrom itertools import chain 121364750dSJames Henderson 137e46a721SStephen Tozerfrom dex.command.CommandBase import CommandBase, StepExpectInfo 141364750dSJames Hendersonfrom dex.dextIR import ProgramState, SourceLocation, StackFrame, DextIR 151364750dSJames Henderson 16*f98ee40fSTobias Hieta 171364750dSJames Hendersondef frame_from_dict(source: dict) -> StackFrame: 18*f98ee40fSTobias Hieta if "location" in source: 19*f98ee40fSTobias Hieta assert isinstance(source["location"], dict) 20*f98ee40fSTobias Hieta source["location"] = SourceLocation(**source["location"]) 211364750dSJames Henderson return StackFrame(**source) 221364750dSJames Henderson 23*f98ee40fSTobias Hieta 241364750dSJames Hendersondef state_from_dict(source: dict) -> ProgramState: 25*f98ee40fSTobias Hieta if "frames" in source: 26*f98ee40fSTobias Hieta assert isinstance(source["frames"], list) 27*f98ee40fSTobias Hieta source["frames"] = list(map(frame_from_dict, source["frames"])) 281364750dSJames Henderson return ProgramState(**source) 291364750dSJames Henderson 30*f98ee40fSTobias Hieta 311364750dSJames Hendersonclass DexExpectProgramState(CommandBase): 321364750dSJames Henderson """Expect to see a given program `state` a certain numer of `times`. 331364750dSJames Henderson 341364750dSJames Henderson DexExpectProgramState(state [,**times]) 351364750dSJames Henderson 361364750dSJames Henderson See Commands.md for more info. 371364750dSJames Henderson """ 381364750dSJames Henderson 391364750dSJames Henderson def __init__(self, *args, **kwargs): 401364750dSJames Henderson if len(args) != 1: 41*f98ee40fSTobias Hieta raise TypeError("expected exactly one unnamed arg") 421364750dSJames Henderson 431364750dSJames Henderson self.program_state_text = str(args[0]) 441364750dSJames Henderson 451364750dSJames Henderson self.expected_program_state = state_from_dict(args[0]) 461364750dSJames Henderson 47*f98ee40fSTobias Hieta self.times = kwargs.pop("times", -1) 481364750dSJames Henderson if kwargs: 49*f98ee40fSTobias Hieta raise TypeError("unexpected named args: {}".format(", ".join(kwargs))) 501364750dSJames Henderson 511364750dSJames Henderson # Step indices at which the expected program state was encountered. 521364750dSJames Henderson self.encounters = [] 531364750dSJames Henderson 541364750dSJames Henderson super(DexExpectProgramState, self).__init__() 551364750dSJames Henderson 561364750dSJames Henderson @staticmethod 571364750dSJames Henderson def get_name(): 581364750dSJames Henderson return __class__.__name__ 591364750dSJames Henderson 601364750dSJames Henderson def get_watches(self): 617e46a721SStephen Tozer frame_expects = set() 627e46a721SStephen Tozer for idx, frame in enumerate(self.expected_program_state.frames): 63*f98ee40fSTobias Hieta path = ( 64*f98ee40fSTobias Hieta frame.location.path 65*f98ee40fSTobias Hieta if frame.location and frame.location.path 66*f98ee40fSTobias Hieta else self.path 67*f98ee40fSTobias Hieta ) 687e46a721SStephen Tozer line_range = ( 697e46a721SStephen Tozer range(frame.location.lineno, frame.location.lineno + 1) 70*f98ee40fSTobias Hieta if frame.location and frame.location.lineno 71*f98ee40fSTobias Hieta else None 72*f98ee40fSTobias Hieta ) 737e46a721SStephen Tozer for watch in frame.watches: 747e46a721SStephen Tozer frame_expects.add( 757e46a721SStephen Tozer StepExpectInfo( 767e46a721SStephen Tozer expression=watch, 777e46a721SStephen Tozer path=path, 787e46a721SStephen Tozer frame_idx=idx, 79*f98ee40fSTobias Hieta line_range=line_range, 807e46a721SStephen Tozer ) 817e46a721SStephen Tozer ) 827e46a721SStephen Tozer return frame_expects 831364750dSJames Henderson 841364750dSJames Henderson def eval(self, step_collection: DextIR) -> bool: 851364750dSJames Henderson for step in step_collection.steps: 861364750dSJames Henderson if self.expected_program_state.match(step.program_state): 871364750dSJames Henderson self.encounters.append(step.step_index) 881364750dSJames Henderson 89*f98ee40fSTobias Hieta return ( 90*f98ee40fSTobias Hieta self.times < 0 < len(self.encounters) or len(self.encounters) == self.times 91*f98ee40fSTobias Hieta ) 92