91 lines
2.9 KiB
Python
91 lines
2.9 KiB
Python
# 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, StepExpectInfo
|
|
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 = set()
|
|
for idx, frame in enumerate(self.expected_program_state.frames):
|
|
path = (
|
|
frame.location.path
|
|
if frame.location and frame.location.path
|
|
else self.path
|
|
)
|
|
line_range = (
|
|
range(frame.location.lineno, frame.location.lineno + 1)
|
|
if frame.location and frame.location.lineno
|
|
else None
|
|
)
|
|
for watch in frame.watches:
|
|
frame_expects.add(
|
|
StepExpectInfo(
|
|
expression=watch,
|
|
path=path,
|
|
frame_idx=idx,
|
|
line_range=line_range,
|
|
)
|
|
)
|
|
return 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
|
|
)
|