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