Top

miasm2.core.sembuilder module

Helper to quickly build instruction's semantic side effects

"Helper to quickly build instruction's semantic side effects"

import inspect
import ast
import re

import miasm2.expression.expression as m2_expr
from miasm2.ir.ir import IRBlock, AssignBlock


class MiasmTransformer(ast.NodeTransformer):
    """AST visitor translating DSL to Miasm expression

    memX[Y]       -> ExprMem(Y, X)
    iX(Y)         -> ExprIntX(Y)
    X if Y else Z -> ExprCond(Y, X, Z)
    'X'(Y)        -> ExprOp('X', Y)
    ('X' % Y)(Z)  -> ExprOp('X' % Y, Z)
    {a, b}        -> ExprCompose(((a, 0, a.size), (b, a.size, a.size + b.size)))
    """

    # Parsers
    parse_integer = re.compile("^i([0-9]+)$")
    parse_mem = re.compile("^mem([0-9]+)$")

    # Visitors
    def visit_Call(self, node):
        """iX(Y) -> ExprIntX(Y),
        'X'(Y) -> ExprOp('X', Y), ('X' % Y)(Z) -> ExprOp('X' % Y, Z)"""

        # Recursive visit
        node = self.generic_visit(node)

        if isinstance(node.func, ast.Name):
            # iX(Y) -> ExprInt(Y, X)
            fc_name = node.func.id

            # Match the function name
            new_name = fc_name
            integer = self.parse_integer.search(fc_name)

            # Do replacement
            if integer is not None:
                size = int(integer.groups()[0])
                new_name = "ExprInt"
                # Replace in the node
                node.func.id = new_name
                node.args.append(ast.Num(n=size))

        elif (isinstance(node.func, ast.Str) or
              (isinstance(node.func, ast.BinOp) and
               isinstance(node.func.op, ast.Mod) and
               isinstance(node.func.left, ast.Str))):
            # 'op'(args...) -> ExprOp('op', args...)
            # ('op' % (fmt))(args...) -> ExprOp('op' % (fmt), args...)
            op_name = node.func

            # Do replacement
            node.func = ast.Name(id="ExprOp", ctx=ast.Load())
            node.args[0:0] = [op_name]

        return node

    def visit_Subscript(self, node):
        """memX[Y] -> ExprMem(Y, X)"""

        # Recursive visit
        node = self.generic_visit(node)

        # Detect the syntax
        if not isinstance(node.value, ast.Name):
            return node
        name = node.value.id
        mem = self.parse_mem.search(name)
        if mem is None:
            return node

        # Do replacement
        addr = self.visit(node.slice.value)
        call = ast.Call(func=ast.Name(id='ExprMem', ctx=ast.Load()),
                        args=[addr, ast.Num(n=int(mem.groups()[0]))],
                        keywords=[], starargs=None, kwargs=None)
        return call

    def visit_IfExp(self, node):
        """X if Y else Z -> ExprCond(Y, X, Z)"""
        # Recursive visit
        node = self.generic_visit(node)

        # Build the new ExprCond
        call = ast.Call(func=ast.Name(id='ExprCond', ctx=ast.Load()),
                        args=[self.visit(node.test),
                              self.visit(node.body),
                              self.visit(node.orelse)],
                        keywords=[], starargs=None, kwargs=None)
        return call

    def visit_Set(self, node):
        "{a, b} -> ExprCompose(a, b)"
        if len(node.elts) == 0:
            return node

        # Recursive visit
        node = self.generic_visit(node)

        return ast.Call(func=ast.Name(id='ExprCompose',
                                      ctx=ast.Load()),
                               args=node.elts,
                               keywords=[],
                               starargs=None,
                               kwargs=None)


class SemBuilder(object):
    """Helper for building instruction's semantic side effects method

    This class provides a decorator @parse to use on them.
    The context in which the function will be parsed must be supplied on
    instanciation
    """

    def __init__(self, ctx):
        """Create a SemBuilder
        @ctx: context dictionary used during parsing
        """
        # Init
        self.transformer = MiasmTransformer()
        self._ctx = dict(m2_expr.__dict__)
        self._ctx["IRBlock"] = IRBlock
        self._ctx["AssignBlock"] = AssignBlock
        self._functions = {}

        # Update context
        self._ctx.update(ctx)

    @property
    def functions(self):
        """Return a dictionary name -> func of parsed functions"""
        return self._functions.copy()

    @staticmethod
    def _create_labels(loc_else=False):
        """Return the AST standing for label creations
        @loc_else (optional): if set, create a label 'loc_else'"""
        loc_end = "loc_end = ir.get_next_loc_key(instr)"
        loc_end_expr = "loc_end_expr = ExprLoc(loc_end, ir.IRDst.size)"
        out = ast.parse(loc_end).body
        out += ast.parse(loc_end_expr).body
        loc_if = "loc_if = ir.loc_db.add_location()"
        loc_if_expr = "loc_if_expr = ExprLoc(loc_if, ir.IRDst.size)"
        out += ast.parse(loc_if).body
        out += ast.parse(loc_if_expr).body
        if loc_else:
            loc_else = "loc_else = ir.loc_db.add_location()"
            loc_else_expr = "loc_else_expr = ExprLoc(loc_else, ir.IRDst.size)"
            out += ast.parse(loc_else).body
            out += ast.parse(loc_else_expr).body
        return out

    def _parse_body(self, body, argument_names):
        """Recursive function transforming a @body to a block expression
        Return:
         - AST to append to body (real python statements)
         - a list of blocks, ie list of affblock, ie list of ExprAff (AST)"""

        # Init
        ## Real instructions
        real_body = []
        ## Final blocks
        blocks = [[[]]]

        for statement in body:

            if isinstance(statement, ast.Assign):
                src = self.transformer.visit(statement.value)
                dst = self.transformer.visit(statement.targets[0])

                if (isinstance(dst, ast.Name) and
                    dst.id not in argument_names and
                    dst.id not in self._ctx and
                    dst.id not in self._local_ctx):

                    # Real variable declaration
                    statement.value = src
                    real_body.append(statement)
                    self._local_ctx[dst.id] = src
                    continue

                dst.ctx = ast.Load()

                res = ast.Call(func=ast.Name(id='ExprAff',
                                             ctx=ast.Load()),
                               args=[dst, src],
                               keywords=[],
                               starargs=None,
                               kwargs=None)

                blocks[-1][-1].append(res)

            elif (isinstance(statement, ast.Expr) and
                  isinstance(statement.value, ast.Str)):
                # String (docstring, comment, ...) -> keep it
                real_body.append(statement)

            elif isinstance(statement, ast.If):
                # Create jumps : ir.IRDst = loc_if if cond else loc_end
                # if .. else .. are also handled
                cond = statement.test
                real_body += self._create_labels(loc_else=True)

                loc_end = ast.Name(id='loc_end_expr', ctx=ast.Load())
                loc_if = ast.Name(id='loc_if_expr', ctx=ast.Load())
                loc_else = ast.Name(id='loc_else_expr', ctx=ast.Load()) \
                           if statement.orelse else loc_end
                dst = ast.Call(func=ast.Name(id='ExprCond',
                                             ctx=ast.Load()),
                               args=[cond,
                                     loc_if,
                                     loc_else],
                               keywords=[],
                               starargs=None,
                               kwargs=None)

                if (isinstance(cond, ast.UnaryOp) and
                    isinstance(cond.op, ast.Not)):
                    ## if not cond -> switch exprCond
                    dst.args[1:] = dst.args[1:][::-1]
                    dst.args[0] = cond.operand

                IRDst = ast.Attribute(value=ast.Name(id='ir',
                                                     ctx=ast.Load()),
                                      attr='IRDst', ctx=ast.Load())
                blocks[-1][-1].append(ast.Call(func=ast.Name(id='ExprAff',
                                                             ctx=ast.Load()),
                                               args=[IRDst, dst],
                                               keywords=[],
                                               starargs=None,
                                               kwargs=None))

                # Create the new blocks
                elements = [(statement.body, 'loc_if')]
                if statement.orelse:
                    elements.append((statement.orelse, 'loc_else'))
                for content, loc_name in elements:
                    sub_blocks, sub_body = self._parse_body(content,
                                                            argument_names)
                    if len(sub_blocks) > 1:
                        raise RuntimeError("Imbricated if unimplemented")

                    ## Close the last block
                    jmp_end = ast.Call(func=ast.Name(id='ExprAff',
                                                     ctx=ast.Load()),
                                       args=[IRDst, loc_end],
                                       keywords=[],
                                       starargs=None,
                                       kwargs=None)
                    sub_blocks[-1][-1].append(jmp_end)


                    instr = ast.Name(id='instr', ctx=ast.Load())
                    effects = ast.List(elts=sub_blocks[-1][-1],
                                       ctx=ast.Load())
                    assignblk = ast.Call(func=ast.Name(id='AssignBlock',
                                                       ctx=ast.Load()),
                                         args=[effects, instr],
                                         keywords=[],
                                         starargs=None,
                                         kwargs=None)


                    ## Replace the block with a call to 'IRBlock'
                    loc_if_name = ast.Name(id=loc_name, ctx=ast.Load())

                    assignblks = ast.List(elts=[assignblk],
                                          ctx=ast.Load())

                    sub_blocks[-1] = ast.Call(func=ast.Name(id='IRBlock',
                                                            ctx=ast.Load()),
                                              args=[loc_if_name,
                                                    assignblks],
                                              keywords=[],
                                              starargs=None,
                                              kwargs=None)
                    blocks += sub_blocks
                    real_body += sub_body

                # Prepare a new block for following statement
                blocks.append([[]])

            else:
                # TODO: real var, +=, /=, -=, <<=, >>=, if/else, ...
                raise RuntimeError("Unimplemented %s" % statement)

        return blocks, real_body

    def parse(self, func):
        """Function decorator, returning a correct method from a pseudo-Python
        one"""

        # Get the function AST
        parsed = ast.parse(inspect.getsource(func))
        fc_ast = parsed.body[0]
        argument_names = [name.id for name in fc_ast.args.args]

        # Init local cache
        self._local_ctx = {}

        # Translate (blocks[0][0] is the current instr)
        blocks, body = self._parse_body(fc_ast.body, argument_names)

        # Build the new function
        fc_ast.args.args[0:0] = [ast.Name(id='ir', ctx=ast.Param()),
                                 ast.Name(id='instr', ctx=ast.Param())]
        cur_instr = blocks[0][0]
        if len(blocks[-1][0]) == 0:
            ## Last block can be empty
            blocks.pop()
        other_blocks = blocks[1:]
        body.append(ast.Return(value=ast.Tuple(elts=[ast.List(elts=cur_instr,
                                                              ctx=ast.Load()),
                                                     ast.List(elts=other_blocks,
                                                              ctx=ast.Load())],
                                               ctx=ast.Load())))

        ret = ast.Module([ast.FunctionDef(name=fc_ast.name,
                                          args=fc_ast.args,
                                          body=body,
                                          decorator_list=[])])

        # To display the generated function, use codegen.to_source
        # codegen: https://github.com/andreif/codegen

        # Compile according to the context
        fixed = ast.fix_missing_locations(ret)
        codeobj = compile(fixed, '<string>', 'exec')
        ctx = self._ctx.copy()
        eval(codeobj, ctx)

        # Get the function back
        self._functions[fc_ast.name] = ctx[fc_ast.name]
        return ctx[fc_ast.name]

Classes

class MiasmTransformer

AST visitor translating DSL to Miasm expression

memX[Y] -> ExprMem(Y, X) iX(Y) -> ExprIntX(Y) X if Y else Z -> ExprCond(Y, X, Z) 'X'(Y) -> ExprOp('X', Y) ('X' % Y)(Z) -> ExprOp('X' % Y, Z) {a, b} -> ExprCompose(((a, 0, a.size), (b, a.size, a.size + b.size)))

class MiasmTransformer(ast.NodeTransformer):
    """AST visitor translating DSL to Miasm expression

    memX[Y]       -> ExprMem(Y, X)
    iX(Y)         -> ExprIntX(Y)
    X if Y else Z -> ExprCond(Y, X, Z)
    'X'(Y)        -> ExprOp('X', Y)
    ('X' % Y)(Z)  -> ExprOp('X' % Y, Z)
    {a, b}        -> ExprCompose(((a, 0, a.size), (b, a.size, a.size + b.size)))
    """

    # Parsers
    parse_integer = re.compile("^i([0-9]+)$")
    parse_mem = re.compile("^mem([0-9]+)$")

    # Visitors
    def visit_Call(self, node):
        """iX(Y) -> ExprIntX(Y),
        'X'(Y) -> ExprOp('X', Y), ('X' % Y)(Z) -> ExprOp('X' % Y, Z)"""

        # Recursive visit
        node = self.generic_visit(node)

        if isinstance(node.func, ast.Name):
            # iX(Y) -> ExprInt(Y, X)
            fc_name = node.func.id

            # Match the function name
            new_name = fc_name
            integer = self.parse_integer.search(fc_name)

            # Do replacement
            if integer is not None:
                size = int(integer.groups()[0])
                new_name = "ExprInt"
                # Replace in the node
                node.func.id = new_name
                node.args.append(ast.Num(n=size))

        elif (isinstance(node.func, ast.Str) or
              (isinstance(node.func, ast.BinOp) and
               isinstance(node.func.op, ast.Mod) and
               isinstance(node.func.left, ast.Str))):
            # 'op'(args...) -> ExprOp('op', args...)
            # ('op' % (fmt))(args...) -> ExprOp('op' % (fmt), args...)
            op_name = node.func

            # Do replacement
            node.func = ast.Name(id="ExprOp", ctx=ast.Load())
            node.args[0:0] = [op_name]

        return node

    def visit_Subscript(self, node):
        """memX[Y] -> ExprMem(Y, X)"""

        # Recursive visit
        node = self.generic_visit(node)

        # Detect the syntax
        if not isinstance(node.value, ast.Name):
            return node
        name = node.value.id
        mem = self.parse_mem.search(name)
        if mem is None:
            return node

        # Do replacement
        addr = self.visit(node.slice.value)
        call = ast.Call(func=ast.Name(id='ExprMem', ctx=ast.Load()),
                        args=[addr, ast.Num(n=int(mem.groups()[0]))],
                        keywords=[], starargs=None, kwargs=None)
        return call

    def visit_IfExp(self, node):
        """X if Y else Z -> ExprCond(Y, X, Z)"""
        # Recursive visit
        node = self.generic_visit(node)

        # Build the new ExprCond
        call = ast.Call(func=ast.Name(id='ExprCond', ctx=ast.Load()),
                        args=[self.visit(node.test),
                              self.visit(node.body),
                              self.visit(node.orelse)],
                        keywords=[], starargs=None, kwargs=None)
        return call

    def visit_Set(self, node):
        "{a, b} -> ExprCompose(a, b)"
        if len(node.elts) == 0:
            return node

        # Recursive visit
        node = self.generic_visit(node)

        return ast.Call(func=ast.Name(id='ExprCompose',
                                      ctx=ast.Load()),
                               args=node.elts,
                               keywords=[],
                               starargs=None,
                               kwargs=None)

Ancestors (in MRO)

Class variables

var parse_integer

var parse_mem

Methods

def generic_visit(

self, node)

def generic_visit(self, node):
    for field, old_value in iter_fields(node):
        old_value = getattr(node, field, None)
        if isinstance(old_value, list):
            new_values = []
            for value in old_value:
                if isinstance(value, AST):
                    value = self.visit(value)
                    if value is None:
                        continue
                    elif not isinstance(value, AST):
                        new_values.extend(value)
                        continue
                new_values.append(value)
            old_value[:] = new_values
        elif isinstance(old_value, AST):
            new_node = self.visit(old_value)
            if new_node is None:
                delattr(node, field)
            else:
                setattr(node, field, new_node)
    return node

def visit(

self, node)

Visit a node.

def visit(self, node):
    """Visit a node."""
    method = 'visit_' + node.__class__.__name__
    visitor = getattr(self, method, self.generic_visit)
    return visitor(node)

def visit_Call(

self, node)

iX(Y) -> ExprIntX(Y), 'X'(Y) -> ExprOp('X', Y), ('X' % Y)(Z) -> ExprOp('X' % Y, Z)

def visit_Call(self, node):
    """iX(Y) -> ExprIntX(Y),
    'X'(Y) -> ExprOp('X', Y), ('X' % Y)(Z) -> ExprOp('X' % Y, Z)"""
    # Recursive visit
    node = self.generic_visit(node)
    if isinstance(node.func, ast.Name):
        # iX(Y) -> ExprInt(Y, X)
        fc_name = node.func.id
        # Match the function name
        new_name = fc_name
        integer = self.parse_integer.search(fc_name)
        # Do replacement
        if integer is not None:
            size = int(integer.groups()[0])
            new_name = "ExprInt"
            # Replace in the node
            node.func.id = new_name
            node.args.append(ast.Num(n=size))
    elif (isinstance(node.func, ast.Str) or
          (isinstance(node.func, ast.BinOp) and
           isinstance(node.func.op, ast.Mod) and
           isinstance(node.func.left, ast.Str))):
        # 'op'(args...) -> ExprOp('op', args...)
        # ('op' % (fmt))(args...) -> ExprOp('op' % (fmt), args...)
        op_name = node.func
        # Do replacement
        node.func = ast.Name(id="ExprOp", ctx=ast.Load())
        node.args[0:0] = [op_name]
    return node

def visit_IfExp(

self, node)

X if Y else Z -> ExprCond(Y, X, Z)

def visit_IfExp(self, node):
    """X if Y else Z -> ExprCond(Y, X, Z)"""
    # Recursive visit
    node = self.generic_visit(node)
    # Build the new ExprCond
    call = ast.Call(func=ast.Name(id='ExprCond', ctx=ast.Load()),
                    args=[self.visit(node.test),
                          self.visit(node.body),
                          self.visit(node.orelse)],
                    keywords=[], starargs=None, kwargs=None)
    return call

def visit_Set(

self, node)

{a, b} -> ExprCompose(a, b)

def visit_Set(self, node):
    "{a, b} -> ExprCompose(a, b)"
    if len(node.elts) == 0:
        return node
    # Recursive visit
    node = self.generic_visit(node)
    return ast.Call(func=ast.Name(id='ExprCompose',
                                  ctx=ast.Load()),
                           args=node.elts,
                           keywords=[],
                           starargs=None,
                           kwargs=None)

def visit_Subscript(

self, node)

memX[Y] -> ExprMem(Y, X)

def visit_Subscript(self, node):
    """memX[Y] -> ExprMem(Y, X)"""
    # Recursive visit
    node = self.generic_visit(node)
    # Detect the syntax
    if not isinstance(node.value, ast.Name):
        return node
    name = node.value.id
    mem = self.parse_mem.search(name)
    if mem is None:
        return node
    # Do replacement
    addr = self.visit(node.slice.value)
    call = ast.Call(func=ast.Name(id='ExprMem', ctx=ast.Load()),
                    args=[addr, ast.Num(n=int(mem.groups()[0]))],
                    keywords=[], starargs=None, kwargs=None)
    return call

class SemBuilder

Helper for building instruction's semantic side effects method

This class provides a decorator @parse to use on them. The context in which the function will be parsed must be supplied on instanciation

class SemBuilder(object):
    """Helper for building instruction's semantic side effects method

    This class provides a decorator @parse to use on them.
    The context in which the function will be parsed must be supplied on
    instanciation
    """

    def __init__(self, ctx):
        """Create a SemBuilder
        @ctx: context dictionary used during parsing
        """
        # Init
        self.transformer = MiasmTransformer()
        self._ctx = dict(m2_expr.__dict__)
        self._ctx["IRBlock"] = IRBlock
        self._ctx["AssignBlock"] = AssignBlock
        self._functions = {}

        # Update context
        self._ctx.update(ctx)

    @property
    def functions(self):
        """Return a dictionary name -> func of parsed functions"""
        return self._functions.copy()

    @staticmethod
    def _create_labels(loc_else=False):
        """Return the AST standing for label creations
        @loc_else (optional): if set, create a label 'loc_else'"""
        loc_end = "loc_end = ir.get_next_loc_key(instr)"
        loc_end_expr = "loc_end_expr = ExprLoc(loc_end, ir.IRDst.size)"
        out = ast.parse(loc_end).body
        out += ast.parse(loc_end_expr).body
        loc_if = "loc_if = ir.loc_db.add_location()"
        loc_if_expr = "loc_if_expr = ExprLoc(loc_if, ir.IRDst.size)"
        out += ast.parse(loc_if).body
        out += ast.parse(loc_if_expr).body
        if loc_else:
            loc_else = "loc_else = ir.loc_db.add_location()"
            loc_else_expr = "loc_else_expr = ExprLoc(loc_else, ir.IRDst.size)"
            out += ast.parse(loc_else).body
            out += ast.parse(loc_else_expr).body
        return out

    def _parse_body(self, body, argument_names):
        """Recursive function transforming a @body to a block expression
        Return:
         - AST to append to body (real python statements)
         - a list of blocks, ie list of affblock, ie list of ExprAff (AST)"""

        # Init
        ## Real instructions
        real_body = []
        ## Final blocks
        blocks = [[[]]]

        for statement in body:

            if isinstance(statement, ast.Assign):
                src = self.transformer.visit(statement.value)
                dst = self.transformer.visit(statement.targets[0])

                if (isinstance(dst, ast.Name) and
                    dst.id not in argument_names and
                    dst.id not in self._ctx and
                    dst.id not in self._local_ctx):

                    # Real variable declaration
                    statement.value = src
                    real_body.append(statement)
                    self._local_ctx[dst.id] = src
                    continue

                dst.ctx = ast.Load()

                res = ast.Call(func=ast.Name(id='ExprAff',
                                             ctx=ast.Load()),
                               args=[dst, src],
                               keywords=[],
                               starargs=None,
                               kwargs=None)

                blocks[-1][-1].append(res)

            elif (isinstance(statement, ast.Expr) and
                  isinstance(statement.value, ast.Str)):
                # String (docstring, comment, ...) -> keep it
                real_body.append(statement)

            elif isinstance(statement, ast.If):
                # Create jumps : ir.IRDst = loc_if if cond else loc_end
                # if .. else .. are also handled
                cond = statement.test
                real_body += self._create_labels(loc_else=True)

                loc_end = ast.Name(id='loc_end_expr', ctx=ast.Load())
                loc_if = ast.Name(id='loc_if_expr', ctx=ast.Load())
                loc_else = ast.Name(id='loc_else_expr', ctx=ast.Load()) \
                           if statement.orelse else loc_end
                dst = ast.Call(func=ast.Name(id='ExprCond',
                                             ctx=ast.Load()),
                               args=[cond,
                                     loc_if,
                                     loc_else],
                               keywords=[],
                               starargs=None,
                               kwargs=None)

                if (isinstance(cond, ast.UnaryOp) and
                    isinstance(cond.op, ast.Not)):
                    ## if not cond -> switch exprCond
                    dst.args[1:] = dst.args[1:][::-1]
                    dst.args[0] = cond.operand

                IRDst = ast.Attribute(value=ast.Name(id='ir',
                                                     ctx=ast.Load()),
                                      attr='IRDst', ctx=ast.Load())
                blocks[-1][-1].append(ast.Call(func=ast.Name(id='ExprAff',
                                                             ctx=ast.Load()),
                                               args=[IRDst, dst],
                                               keywords=[],
                                               starargs=None,
                                               kwargs=None))

                # Create the new blocks
                elements = [(statement.body, 'loc_if')]
                if statement.orelse:
                    elements.append((statement.orelse, 'loc_else'))
                for content, loc_name in elements:
                    sub_blocks, sub_body = self._parse_body(content,
                                                            argument_names)
                    if len(sub_blocks) > 1:
                        raise RuntimeError("Imbricated if unimplemented")

                    ## Close the last block
                    jmp_end = ast.Call(func=ast.Name(id='ExprAff',
                                                     ctx=ast.Load()),
                                       args=[IRDst, loc_end],
                                       keywords=[],
                                       starargs=None,
                                       kwargs=None)
                    sub_blocks[-1][-1].append(jmp_end)


                    instr = ast.Name(id='instr', ctx=ast.Load())
                    effects = ast.List(elts=sub_blocks[-1][-1],
                                       ctx=ast.Load())
                    assignblk = ast.Call(func=ast.Name(id='AssignBlock',
                                                       ctx=ast.Load()),
                                         args=[effects, instr],
                                         keywords=[],
                                         starargs=None,
                                         kwargs=None)


                    ## Replace the block with a call to 'IRBlock'
                    loc_if_name = ast.Name(id=loc_name, ctx=ast.Load())

                    assignblks = ast.List(elts=[assignblk],
                                          ctx=ast.Load())

                    sub_blocks[-1] = ast.Call(func=ast.Name(id='IRBlock',
                                                            ctx=ast.Load()),
                                              args=[loc_if_name,
                                                    assignblks],
                                              keywords=[],
                                              starargs=None,
                                              kwargs=None)
                    blocks += sub_blocks
                    real_body += sub_body

                # Prepare a new block for following statement
                blocks.append([[]])

            else:
                # TODO: real var, +=, /=, -=, <<=, >>=, if/else, ...
                raise RuntimeError("Unimplemented %s" % statement)

        return blocks, real_body

    def parse(self, func):
        """Function decorator, returning a correct method from a pseudo-Python
        one"""

        # Get the function AST
        parsed = ast.parse(inspect.getsource(func))
        fc_ast = parsed.body[0]
        argument_names = [name.id for name in fc_ast.args.args]

        # Init local cache
        self._local_ctx = {}

        # Translate (blocks[0][0] is the current instr)
        blocks, body = self._parse_body(fc_ast.body, argument_names)

        # Build the new function
        fc_ast.args.args[0:0] = [ast.Name(id='ir', ctx=ast.Param()),
                                 ast.Name(id='instr', ctx=ast.Param())]
        cur_instr = blocks[0][0]
        if len(blocks[-1][0]) == 0:
            ## Last block can be empty
            blocks.pop()
        other_blocks = blocks[1:]
        body.append(ast.Return(value=ast.Tuple(elts=[ast.List(elts=cur_instr,
                                                              ctx=ast.Load()),
                                                     ast.List(elts=other_blocks,
                                                              ctx=ast.Load())],
                                               ctx=ast.Load())))

        ret = ast.Module([ast.FunctionDef(name=fc_ast.name,
                                          args=fc_ast.args,
                                          body=body,
                                          decorator_list=[])])

        # To display the generated function, use codegen.to_source
        # codegen: https://github.com/andreif/codegen

        # Compile according to the context
        fixed = ast.fix_missing_locations(ret)
        codeobj = compile(fixed, '<string>', 'exec')
        ctx = self._ctx.copy()
        eval(codeobj, ctx)

        # Get the function back
        self._functions[fc_ast.name] = ctx[fc_ast.name]
        return ctx[fc_ast.name]

Ancestors (in MRO)

Instance variables

var functions

Return a dictionary name -> func of parsed functions

var transformer

Methods

def __init__(

self, ctx)

Create a SemBuilder @ctx: context dictionary used during parsing

def __init__(self, ctx):
    """Create a SemBuilder
    @ctx: context dictionary used during parsing
    """
    # Init
    self.transformer = MiasmTransformer()
    self._ctx = dict(m2_expr.__dict__)
    self._ctx["IRBlock"] = IRBlock
    self._ctx["AssignBlock"] = AssignBlock
    self._functions = {}
    # Update context
    self._ctx.update(ctx)

def parse(

self, func)

Function decorator, returning a correct method from a pseudo-Python one

def parse(self, func):
    """Function decorator, returning a correct method from a pseudo-Python
    one"""
    # Get the function AST
    parsed = ast.parse(inspect.getsource(func))
    fc_ast = parsed.body[0]
    argument_names = [name.id for name in fc_ast.args.args]
    # Init local cache
    self._local_ctx = {}
    # Translate (blocks[0][0] is the current instr)
    blocks, body = self._parse_body(fc_ast.body, argument_names)
    # Build the new function
    fc_ast.args.args[0:0] = [ast.Name(id='ir', ctx=ast.Param()),
                             ast.Name(id='instr', ctx=ast.Param())]
    cur_instr = blocks[0][0]
    if len(blocks[-1][0]) == 0:
        ## Last block can be empty
        blocks.pop()
    other_blocks = blocks[1:]
    body.append(ast.Return(value=ast.Tuple(elts=[ast.List(elts=cur_instr,
                                                          ctx=ast.Load()),
                                                 ast.List(elts=other_blocks,
                                                          ctx=ast.Load())],
                                           ctx=ast.Load())))
    ret = ast.Module([ast.FunctionDef(name=fc_ast.name,
                                      args=fc_ast.args,
                                      body=body,
                                      decorator_list=[])])
    # To display the generated function, use codegen.to_source
    # codegen: https://github.com/andreif/codegen
    # Compile according to the context
    fixed = ast.fix_missing_locations(ret)
    codeobj = compile(fixed, '<string>', 'exec')
    ctx = self._ctx.copy()
    eval(codeobj, ctx)
    # Get the function back
    self._functions[fc_ast.name] = ctx[fc_ast.name]
    return ctx[fc_ast.name]