Continued generalization of byte_flow.demo_flow_builder(), moving traversal logic into visitor function opcode_util.visit_code_args(). Modified byte_control.main() to use visit_code_args().

This commit is contained in:
Jon Riehl 2013-06-06 14:08:09 -05:00
commit 722453502c
3 changed files with 78 additions and 31 deletions

View file

@ -1,11 +1,14 @@
#! /usr/bin/env python #! /usr/bin/env python
# ______________________________________________________________________ # ______________________________________________________________________
# Module imports
from __future__ import absolute_import from __future__ import absolute_import
import opcode
from . import opcode_util
import pprint
import opcode
import pprint
import types
from . import opcode_util
from .bytecode_visitor import BasicBlockVisitor, BenignBytecodeVisitorMixin from .bytecode_visitor import BasicBlockVisitor, BenignBytecodeVisitorMixin
from .control_flow import ControlFlowGraph from .control_flow import ControlFlowGraph
@ -251,24 +254,39 @@ class ControlFlowBuilder (BenignBytecodeVisitorMixin, BasicBlockVisitor):
return super(ControlFlowBuilder, self).op_SETUP_WITH(i, op, arg, *args, return super(ControlFlowBuilder, self).op_SETUP_WITH(i, op, arg, *args,
**kws) **kws)
# ____________________________________________________________
# Class convenience methods
@classmethod
def build_cfg_from_co(cls, co_obj):
return cls().visit(opcode_util.build_basic_blocks(co_obj),
co_obj.co_argcount)
@classmethod
def build_cfg(cls, func):
co_obj = opcode_util.get_code_object(func)
return cls.build_cfg_from_co(co_obj)
# ______________________________________________________________________ # ______________________________________________________________________
def build_cfg (func): def build_cfg (func):
'''Given a Python function, create a bytecode flow, visit the flow '''Given a Python function, create a bytecode flow, visit the flow
object, and return a control flow graph.''' object, and return a control flow graph.'''
co_obj = opcode_util.get_code_object(func) return ControlFlowBuilder.build_cfg(func)
return ControlFlowBuilder().visit(opcode_util.build_basic_blocks(co_obj),
co_obj.co_argcount)
# ______________________________________________________________________ # ______________________________________________________________________
# Main (self-test) routine # Main (self-test) routine
def main (*args, **kws): def main (*args, **kws):
from tests import llfuncs def _visit(obj):
if not args: print("_" * 70)
args = ('doslice',) print(obj)
for arg in args: if type(obj) == types.FunctionType:
build_cfg(getattr(llfuncs, arg)).pprint() cfg = build_cfg(obj)
else:
cfg = ControlFlowBuilder.build_cfg_from_co(obj)
cfg.pprint()
return opcode_util.visit_code_args(_visit, *args, **kws)
# ______________________________________________________________________ # ______________________________________________________________________

View file

@ -4,6 +4,8 @@
from __future__ import absolute_import from __future__ import absolute_import
import dis import dis
import opcode import opcode
import pprint
import types
from .bytecode_visitor import BasicBlockVisitor from .bytecode_visitor import BasicBlockVisitor
from . import opcode_util from . import opcode_util
@ -293,7 +295,16 @@ class BytecodeFlowBuilder (BasicBlockVisitor):
op_UNARY_NOT = _op op_UNARY_NOT = _op
op_UNARY_POSITIVE = _op op_UNARY_POSITIVE = _op
#op_UNPACK_EX = _not_implemented #op_UNPACK_EX = _not_implemented
#op_UNPACK_SEQUENCE = _not_implemented
def op_UNPACK_SEQUENCE (self, i, op, arg):
seq = self.stack.pop()
opname = self.opnames[op]
while arg > 0:
arg -= 1
ret_val = i, op, opname, arg, [seq]
self.stack.append(ret_val)
return ret_val
#op_WITH_CLEANUP = _not_implemented #op_WITH_CLEANUP = _not_implemented
op_YIELD_VALUE = _op op_YIELD_VALUE = _op
@ -308,9 +319,7 @@ class BytecodeFlowBuilder (BasicBlockVisitor):
def build_flow_from_co(cls, code_obj): def build_flow_from_co(cls, code_obj):
'''Given a Python code object, return a flow representation of '''Given a Python code object, return a flow representation of
that code object.''' that code object.'''
bbs = opcode_util.build_basic_blocks(code_obj) cfg = byte_control.ControlFlowBuilder.build_cfg_from_co(code_obj)
cfg = byte_control.ControlFlowBuilder().visit(bbs,
code_obj.co_argcount)
return cls().visit_cfg(cfg) return cls().visit_cfg(cfg)
@classmethod @classmethod
@ -333,23 +342,15 @@ def build_flow(func):
def demo_flow_builder(builder_cls, *args): def demo_flow_builder(builder_cls, *args):
import pprint import pprint
try: def _visit(obj):
from .tests import llfuncs print("_" * 70)
except ImportError: print(obj)
llfuncs = object() if type(obj) == types.FunctionType:
if not args: flow = builder_cls.build_flow(obj)
args = ('pymod',)
for arg in args:
if arg.endswith('.py'):
with open(arg) as in_file:
in_source = in_file.read()
in_codeobj = compile(in_source, arg, 'exec')
for codeobj in opcode_util.itercodeobjs(in_codeobj):
print("_" * 70)
print(codeobj)
pprint.pprint(builder_cls.build_flow_from_co(codeobj))
else: else:
pprint.pprint(builder_cls.build_flow(getattr(llfuncs, arg))) flow = builder_cls.build_flow_from_co(obj)
pprint.pprint(flow)
return opcode_util.visit_code_args(_visit, *args)
# ______________________________________________________________________ # ______________________________________________________________________
# Main (self-test) routine # Main (self-test) routine

View file

@ -214,6 +214,34 @@ def itercodeobjs(codeobj):
# ______________________________________________________________________ # ______________________________________________________________________
def visit_code_args(visitor, *args, **kws):
"""Utility function for testing or demonstrating various code
analysis passes in llpython.
Takes a visitor function and a sequence of command line arguments.
The visitor function should be able to handle either function
objects or code objects."""
try:
from .tests import llfuncs
except ImportError:
llfuncs = object()
if not args:
if 'default_args' in kws:
args = kws['default_args']
else:
args = ('pymod',)
for arg in args:
if arg.endswith('.py'):
with open(arg) as in_file:
in_source = in_file.read()
in_codeobj = compile(in_source, arg, 'exec')
for codeobj in itercodeobjs(in_codeobj):
visitor(codeobj)
else:
visitor(getattr(llfuncs, arg))
# ______________________________________________________________________
def extendlabels(code, labels = None): def extendlabels(code, labels = None):
"""Extend the set of jump target labels to account for the """Extend the set of jump target labels to account for the
passthrough targets of conditional branches. passthrough targets of conditional branches.