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)
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
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])
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
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
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,])
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