Exemplo n.º 1
0
    def test_IRScope(self):
        filename = "<?>"
        top = ir.Scope(parent=None, loc=ir.Loc(filename=filename, line=1))
        local = ir.Scope(parent=top, loc=ir.Loc(filename=filename, line=2))

        apple = local.define('apple', loc=ir.Loc(filename=filename, line=3))
        self.assertIs(local.get('apple'), apple)
        self.assertEqual(len(local.localvars), 1)

        orange = top.define('orange', loc=ir.Loc(filename=filename, line=4))
        self.assertEqual(len(local.localvars), 1)
        self.assertEqual(len(top.localvars), 1)
        self.assertIs(top.get('orange'), orange)
        self.assertIs(local.get('orange'), orange)

        more_orange = local.define('orange',
                                   loc=ir.Loc(filename=filename, line=5))
        self.assertIs(top.get('orange'), orange)
        self.assertIsNot(local.get('orange'), not orange)
        self.assertIs(local.get('orange'), more_orange)

        try:
            local.define('orange', loc=ir.Loc(filename=filename, line=5))
        except ir.RedefinedError:
            pass
        else:
            self.fail("Expecting an %s" % ir.RedefinedError)
Exemplo n.º 2
0
    def interpret(self, bytecode):
        """
        Generate IR for this bytecode.
        """
        self.bytecode = bytecode

        self.scopes = []
        global_scope = ir.Scope(parent=None, loc=self.loc)
        self.scopes.append(global_scope)

        if PYVERSION < (3, 7):
            # Control flow analysis
            self.cfa = controlflow.ControlFlowAnalysis(bytecode)
            self.cfa.run()
            if config.DUMP_CFG:
                self.cfa.dump()

            # Data flow analysis
            self.dfa = dataflow.DataFlowAnalysis(self.cfa)
            self.dfa.run()
        else:
            flow = Flow(bytecode)
            flow.run()
            self.dfa = AdaptDFA(flow)
            self.cfa = AdaptCFA(flow)
            if config.DUMP_CFG:
                self.cfa.dump()

        # Temp states during interpretation
        self.current_block = None
        self.current_block_offset = None
        self.syntax_blocks = []
        self.dfainfo = None

        firstblk = min(self.cfa.blocks.keys())
        self.scopes.append(ir.Scope(parent=self.current_scope, loc=self.loc))
        # Interpret loop
        for inst, kws in self._iter_inst():
            self._dispatch(inst, kws)

        self._legalize_exception_vars()

        # Prepare FunctionIR
        fir = ir.FunctionIR(
            self.blocks,
            self.is_generator,
            self.func_id,
            self.first_loc,
            self.definitions,
            self.arg_count,
            self.arg_names,
        )
        _logger.debug(fir.dump_to_string())
        return fir
Exemplo n.º 3
0
 def test_var(self):
     a = ir.Var(None, 'foo', self.loc1)
     b = ir.Var(None, 'foo', self.loc1)
     c = ir.Var(None, 'foo', self.loc2)
     d = ir.Var(ir.Scope(None, ir.unknown_loc), 'foo', self.loc1)
     e = ir.Var(None, 'bar', self.loc1)
     self.check(a, same=[b, c, d], different=[e])
Exemplo n.º 4
0
 def run_pass(self, state):
     func_ir = state.func_ir
     # walk the blocks
     for blk in func_ir.blocks.values():
         oldscope = blk.scope
         # put in an empty Scope
         blk.scope = ir.Scope(parent=oldscope.parent,
                              loc=oldscope.loc)
     return True
Exemplo n.º 5
0
 def gen_block():
     parent = ir.Scope(None, self.loc1)
     tmp = ir.Block(parent, self.loc2)
     assign1 = ir.Assign(self.var_a, self.var_b, self.loc3)
     assign2 = ir.Assign(self.var_a, self.var_c, self.loc3)
     assign3 = ir.Assign(self.var_c, self.var_b, self.loc3)
     tmp.append(assign1)
     tmp.append(assign2)
     tmp.append(assign3)
     return tmp
Exemplo n.º 6
0
    def test_scope(self):
        parent1 = ir.Scope(None, self.loc1)
        parent2 = ir.Scope(None, self.loc1)
        parent3 = ir.Scope(None, self.loc2)
        self.check(parent1, same=[parent2, parent3,])

        a = ir.Scope(parent1, self.loc1)
        b = ir.Scope(parent1, self.loc1)
        c = ir.Scope(parent1, self.loc2)
        d = ir.Scope(parent3, self.loc1)
        self.check(a, same=[b, c, d])

        # parent1 and parent2 are equal, so children referring to either parent
        # should be equal
        e = ir.Scope(parent2, self.loc1)
        self.check(a, same=[e,])
Exemplo n.º 7
0
def _rewrite_return(func_ir, target_block_label):
    """Rewrite a return block inside a with statement.

    Arguments
    ---------

    func_ir: Function IR
      the CFG to transform
    target_block_label: int
      the block index/label of the block containing the POP_BLOCK statement


    This implements a CFG transformation to insert a block between two other
    blocks.

    The input situation is:

    ┌───────────────┐
    │   top         │
    │   POP_BLOCK   │
    │   bottom      │
    └───────┬───────┘
            │
    ┌───────▼───────┐
    │               │
    │    RETURN     │
    │               │
    └───────────────┘

    If such a pattern is detected in IR, it means there is a `return` statement
    within a `with` context. The basic idea is to rewrite the CFG as follows:

    ┌───────────────┐
    │   top         │
    │   POP_BLOCK   │
    │               │
    └───────┬───────┘
            │
    ┌───────▼───────┐
    │               │
    │     bottom    │
    │               │
    └───────┬───────┘
            │
    ┌───────▼───────┐
    │               │
    │    RETURN     │
    │               │
    └───────────────┘

    We split the block that contains the `POP_BLOCK` statement into two blocks.
    Everything from the beginning of the block up to and including the
    `POP_BLOCK` statement is considered the 'top' and everything below is
    considered 'bottom'. Finally the jump statements are re-wired to make sure
    the CFG remains valid.

    """
    # the block itself from the index
    target_block = func_ir.blocks[target_block_label]
    # get the index of the block containing the return
    target_block_successor_label = target_block.terminator.get_targets()[0]
    # the return block
    target_block_successor = func_ir.blocks[target_block_successor_label]

    # create the new return block with an appropriate label
    max_label = ir_utils.find_max_label(func_ir.blocks)
    new_label = max_label + 1
    # create the new return block
    new_block_loc = target_block_successor.loc
    new_block_scope = ir.Scope(None, loc=new_block_loc)
    new_block = ir.Block(new_block_scope, loc=new_block_loc)

    # Split the block containing the POP_BLOCK into top and bottom
    # Block must be of the form:
    # -----------------
    # <some stmts>
    # POP_BLOCK
    # <some more stmts>
    # JUMP
    # -----------------
    top_body, bottom_body = [], []
    pop_blocks = [*target_block.find_insts(ir.PopBlock)]
    assert len(pop_blocks) == 1
    assert len([*target_block.find_insts(ir.Jump)]) == 1
    assert isinstance(target_block.body[-1], ir.Jump)
    pb_marker = pop_blocks[0]
    pb_is = target_block.body.index(pb_marker)
    top_body.extend(target_block.body[:pb_is])
    top_body.append(ir.Jump(target_block_successor_label, target_block.loc))
    bottom_body.extend(target_block.body[pb_is:-1])
    bottom_body.append(ir.Jump(new_label, target_block.loc))

    # get the contents of the return block
    return_body = func_ir.blocks[target_block_successor_label].body
    # finally, re-assign all blocks
    new_block.body.extend(return_body)
    target_block_successor.body.clear()
    target_block_successor.body.extend(bottom_body)
    target_block.body.clear()
    target_block.body.extend(top_body)

    # finally, append the new return block and rebuild the IR properties
    func_ir.blocks[new_label] = new_block
    func_ir._definitions = ir_utils.build_definitions(func_ir.blocks)
    return func_ir