Skip to content

Commit

Permalink
Rework Dexter command imports and get valid commands code to be (#35)
Browse files Browse the repository at this point in the history
simpler and more static.
  • Loading branch information
TomWeaver18 authored and jmorse committed Jul 17, 2019
1 parent e7b192f commit 18fcf75
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 63 deletions.
7 changes: 7 additions & 0 deletions dex/command/CommandBase.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ def __init__(self):
self.path = None
self.lineno = None

@staticmethod
@abc.abstractstaticmethod
def get_name():
"""This abstract method is usually implemented in subclasses as:
return __class__.__name__
"""

@abc.abstractmethod
def eval(self):
pass
Expand Down
47 changes: 16 additions & 31 deletions dex/command/ParseCommand.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,43 +26,28 @@
"""

from collections import defaultdict
import imp
import inspect
import os

from dex.command.CommandBase import CommandBase
from dex.utils.Exceptions import CommandParseError
from dex.dextIR import CommandIR

from dex.command.CommandBase import CommandBase
from dex.command.commands.DexExpectProgramState import DexExpectProgramState
from dex.command.commands.DexExpectStepKind import DexExpectStepKind
from dex.command.commands.DexExpectStepOrder import DexExpectStepOrder
from dex.command.commands.DexExpectWatchValue import DexExpectWatchValue
from dex.command.commands.DexUnreachable import DexUnreachable
from dex.command.commands.DexWatch import DexWatch

def _get_valid_commands():
"""Search the commands subdirectory for any classes which are subclasses of
CommandBase and return a dict in the form of {name: class}.
"""
try:
return _get_valid_commands.cached
except AttributeError:
commands_directory = os.path.join(
os.path.dirname(__file__), 'commands')
potential_modules = [
os.path.splitext(f)[0] for f in os.listdir(commands_directory)
]

commands = {}
for m in potential_modules:
try:
module_info = imp.find_module(m, [commands_directory])
module = imp.load_module(m, *module_info)
except ImportError:
continue

commands.update({
c[0]: c[1]
for c in inspect.getmembers(module, inspect.isclass)
if c[1] != CommandBase and issubclass(c[1], CommandBase)
})
_get_valid_commands.cached = commands
return commands
def _get_valid_commands():
return {
DexExpectProgramState.get_name() : DexExpectProgramState,
DexExpectStepKind.get_name() : DexExpectStepKind,
DexExpectStepOrder.get_name() : DexExpectStepOrder,
DexExpectWatchValue.get_name() : DexExpectWatchValue,
DexUnreachable.get_name() : DexUnreachable,
DexWatch.get_name() : DexWatch
}


def _get_command_name(command_raw: str) -> str:
Expand Down
4 changes: 4 additions & 0 deletions dex/command/commands/DexExpectProgramState.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ def __init__(self, *args, **kwargs):

super(DexExpectProgramState, self).__init__()

@staticmethod
def get_name():
return __class__.__name__

def eval(self, step_collection: DextIR) -> bool:
for step in step_collection.steps:
if self.expected_program_state.match(step.program_state):
Expand Down
4 changes: 4 additions & 0 deletions dex/command/commands/DexExpectStepKind.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,9 @@ def __init__(self, *args):

super(DexExpectStepKind, self).__init__()

@staticmethod
def get_name():
return __class__.__name__

def eval(self):
pass
4 changes: 4 additions & 0 deletions dex/command/commands/DexExpectStepOrder.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ def __init__(self, *args):
self.sequence = [int(x) for x in args]
super(DexExpectStepOrder, self).__init__()

@staticmethod
def get_name():
return __class__.__name__

def eval(self, debugger):
step_info = debugger.get_step_info()
loc = step_info.current_location
Expand Down
21 changes: 20 additions & 1 deletion dex/command/commands/DexExpectWatchValue.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,22 @@
import difflib

from dex.command.CommandBase import CommandBase
from dex.heuristic import StepValueInfo


class StepValueInfo(object):
def __init__(self, step_index, value_info):
self.step_index = step_index
self.value_info = value_info

def __str__(self):
return '{}:{}'.format(self.step_index, self.value_info)

def __eq__(self, other):
return (self.value_info.expression == other.value_info.expression
and self.value_info.value == other.value_info.value)

def __hash__(self):
return hash(self.value_info.expression, self.value_info.value)


def _check_watch_order(actual_watches, expected_values):
Expand Down Expand Up @@ -154,6 +169,10 @@ def _handle_watch(self, step, watch):
except KeyError:
pass

@staticmethod
def get_name():
return __class__.__name__

def eval(self, step_collection):
for step in step_collection.steps:
loc = step.current_location
Expand Down
54 changes: 41 additions & 13 deletions dex/command/commands/DexUnreachable.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,46 @@
# DExTer : Debugging Experience Tester
# ~~~~~~ ~ ~~ ~ ~~
#
# Copyright (c) 2019 by SN Systems Ltd., Sony Interactive Entertainment Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.


from dex.command.CommandBase import CommandBase
from dex.dextIR import ValueIR

class DexUnreachable(CommandBase):
def __init(self):
super(DexUnreachable, self).__init__()
pass
def __init(self):
super(DexUnreachable, self).__init__()
pass

@staticmethod
def get_name():
return __class__.__name__

def eval(self, debugger):
# If we're ever called, at all, then we're evaluating a line that has
# been marked as unreachable. Which means a failure.
vir = ValueIR(expression="Unreachable",
value="True", type_name=None,
error_string=None,
could_evaluate=True,
is_optimized_away=True,
is_irretrievable=False)
return {'DexUnreachable' : vir}
def eval(self, debugger):
# If we're ever called, at all, then we're evaluating a line that has
# been marked as unreachable. Which means a failure.
vir = ValueIR(expression="Unreachable",
value="True", type_name=None,
error_string=None,
could_evaluate=True,
is_optimized_away=True,
is_irretrievable=False)
return {'DexUnreachable' : vir}
4 changes: 4 additions & 0 deletions dex/command/commands/DexWatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,9 @@ def __init__(self, *args):
self._args = args
super(DexWatch, self).__init__()

@staticmethod
def get_name():
return __class__.__name__

def eval(self, debugger):
return {arg: debugger.evaluate_expression(arg) for arg in self._args}
21 changes: 3 additions & 18 deletions dex/heuristic/Heuristic.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,31 +29,16 @@
from collections import defaultdict, namedtuple, Counter
import difflib
import os
from itertools import repeat, chain, groupby
from itertools import groupby
from dex.command.commands.DexExpectWatchValue import StepValueInfo
from dex.command.ParseCommand import get_command_object

from dex.command import get_command_object

PenaltyCommand = namedtuple('PenaltyCommand', ['pen_dict', 'max_penalty'])
# 'meta' field used in different ways by different things
PenaltyInstance = namedtuple('PenaltyInstance', ['meta', 'the_penalty'])


class StepValueInfo(object):
def __init__(self, step_index, value_info):
self.step_index = step_index
self.value_info = value_info

def __str__(self):
return '{}:{}'.format(self.step_index, self.value_info)

def __eq__(self, other):
return (self.value_info.expression == other.value_info.expression
and self.value_info.value == other.value_info.value)

def __hash__(self):
return hash(self.value_info.expression, self.value_info.value)


def add_heuristic_tool_arguments(parser):
parser.add_argument(
'--penalty-variable-optimized',
Expand Down

0 comments on commit 18fcf75

Please sign in to comment.