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:
parent
c55198095f
commit
722453502c
3 changed files with 78 additions and 31 deletions
|
|
@ -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)
|
||||
|
||||
# ______________________________________________________________________
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue