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
# ______________________________________________________________________
# Module imports
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 .control_flow import ControlFlowGraph
@ -251,24 +254,39 @@ class ControlFlowBuilder (BenignBytecodeVisitorMixin, BasicBlockVisitor):
return super(ControlFlowBuilder, self).op_SETUP_WITH(i, op, arg, *args,
**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):
'''Given a Python function, create a bytecode flow, visit the flow
object, and return a control flow graph.'''
co_obj = opcode_util.get_code_object(func)
return ControlFlowBuilder().visit(opcode_util.build_basic_blocks(co_obj),
co_obj.co_argcount)
return ControlFlowBuilder.build_cfg(func)
# ______________________________________________________________________
# Main (self-test) routine
def main (*args, **kws):
from tests import llfuncs
if not args:
args = ('doslice',)
for arg in args:
build_cfg(getattr(llfuncs, arg)).pprint()
def _visit(obj):
print("_" * 70)
print(obj)
if type(obj) == types.FunctionType:
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
import dis
import opcode
import pprint
import types
from .bytecode_visitor import BasicBlockVisitor
from . import opcode_util
@ -293,7 +295,16 @@ class BytecodeFlowBuilder (BasicBlockVisitor):
op_UNARY_NOT = _op
op_UNARY_POSITIVE = _op
#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_YIELD_VALUE = _op
@ -308,9 +319,7 @@ class BytecodeFlowBuilder (BasicBlockVisitor):
def build_flow_from_co(cls, code_obj):
'''Given a Python code object, return a flow representation of
that code object.'''
bbs = opcode_util.build_basic_blocks(code_obj)
cfg = byte_control.ControlFlowBuilder().visit(bbs,
code_obj.co_argcount)
cfg = byte_control.ControlFlowBuilder.build_cfg_from_co(code_obj)
return cls().visit_cfg(cfg)
@classmethod
@ -333,23 +342,15 @@ def build_flow(func):
def demo_flow_builder(builder_cls, *args):
import pprint
try:
from .tests import llfuncs
except ImportError:
llfuncs = object()
if not args:
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))
def _visit(obj):
print("_" * 70)
print(obj)
if type(obj) == types.FunctionType:
flow = builder_cls.build_flow(obj)
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

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):
"""Extend the set of jump target labels to account for the
passthrough targets of conditional branches.