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
|
#! /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)
|
||||||
|
|
||||||
# ______________________________________________________________________
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue