| # DExTer : Debugging Experience Tester |
| # ~~~~~~ ~ ~~ ~ ~~ |
| # |
| # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| # See https://llvm.org/LICENSE.txt for license information. |
| # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| """Command for specifying a partial or complete state for the program to enter |
| during execution. |
| """ |
| |
| from itertools import chain |
| |
| from dex.command.CommandBase import CommandBase |
| from dex.dextIR import ProgramState, SourceLocation, StackFrame, DextIR |
| |
| def frame_from_dict(source: dict) -> StackFrame: |
| if 'location' in source: |
| assert isinstance(source['location'], dict) |
| source['location'] = SourceLocation(**source['location']) |
| return StackFrame(**source) |
| |
| def state_from_dict(source: dict) -> ProgramState: |
| if 'frames' in source: |
| assert isinstance(source['frames'], list) |
| source['frames'] = list(map(frame_from_dict, source['frames'])) |
| return ProgramState(**source) |
| |
| class DexExpectProgramState(CommandBase): |
| """Expect to see a given program `state` a certain numer of `times`. |
| |
| DexExpectProgramState(state [,**times]) |
| |
| See Commands.md for more info. |
| """ |
| |
| def __init__(self, *args, **kwargs): |
| if len(args) != 1: |
| raise TypeError('expected exactly one unnamed arg') |
| |
| self.program_state_text = str(args[0]) |
| |
| self.expected_program_state = state_from_dict(args[0]) |
| |
| self.times = kwargs.pop('times', -1) |
| if kwargs: |
| raise TypeError('unexpected named args: {}'.format( |
| ', '.join(kwargs))) |
| |
| # Step indices at which the expected program state was encountered. |
| self.encounters = [] |
| |
| super(DexExpectProgramState, self).__init__() |
| |
| @staticmethod |
| def get_name(): |
| return __class__.__name__ |
| |
| def get_watches(self): |
| frame_expects = chain.from_iterable(frame.watches |
| for frame in self.expected_program_state.frames) |
| return set(frame_expects) |
| |
| def eval(self, step_collection: DextIR) -> bool: |
| for step in step_collection.steps: |
| if self.expected_program_state.match(step.program_state): |
| self.encounters.append(step.step_index) |
| |
| return self.times < 0 < len(self.encounters) or len(self.encounters) == self.times |