Source code for boa.code.method

import inspect

from bytecode import Instr, Bytecode, Label

from boa import abi
from boa.code.vmtoken import VMTokenizer, Nep8VMTokenizer
from boa.code.expression import Expression
from boa.code import pyop
from boa.code.ast_preprocess import preprocess_method_body
from uuid import UUID, uuid3

import pdb
import dis


[docs]class method(object): code = None bytecode = None block = None blocks = [] stack_size = 0 tokens = [] tokenizer = None address = 0 module = None name = None module_name = None dictionary_defs = None start_line_no = None _blocks = None _expressions = None _scope = None _forloop_counter = 0 _extra = None _id = None code_object = None @property def id(self): return self._id @property def forloop_counter(self): self._forloop_counter += 1 return self._forloop_counter @property def vm_tokens(self): """ Returns a list of all vm tokens in this method. :return: a list of vm tokens in this method :rtype: list """ return self.tokenizer.vm_tokens @property def is_interop(self): if 'boa.interop' in self.full_name: return True if 'boa.builtins' in self.full_name and self.full_name != 'boa.builtins.range': return True return False @property def is_abi_decorator(self): if 'boa.abi' in self.full_name: return True return False @property def full_name(self): if len(self.module_name): return '%s.%s' % (self.module_name, self.name) return self.name @property def scope(self): return self._scope @property def args(self): return self.bytecode.argnames @property def stacksize(self): return self.bytecode.argcount + len(self._blocks) + 2 def __init__(self, module, block, module_name, extra): self.module = module self.block = block self.module_name = module_name self._extra = extra method_block_index = self.get_code_block_index(self.block) if method_block_index is None: raise Exception('Block of code of a method from %s module was not found', self.module_name) self.name = self.block[method_block_index + 1].arg self._id = uuid3(UUID('{baa187e0-2c51-4ef6-aa42-b3421c22d5e1}'), self.full_name) self.start_line_no = self.block[method_block_index].lineno self.code_object = self.block[method_block_index].arg # dis.dis(code_object) self.code, self.dictionary_defs = preprocess_method_body(self.code_object) self.bytecode = Bytecode.from_code(self.code) self.evaluate_annotations(method_block_index) self.setup()
[docs] def setup(self): self._scope = {} for index, name in enumerate(self.bytecode.argnames): self._scope[name] = index blocks = [] # find LOAD_GLOBALS gbl = [] for instr in self.bytecode: if isinstance(instr, Instr) and instr.opcode == pyop.LOAD_GLOBAL: gbl.append(instr.arg) # if there are global things passed in # we want to check if they are used in the method # and if so, load them in global_blocks = [] if len(self._extra): for item in self._extra: if item[-1].opcode == pyop.STORE_NAME: if item[-1].arg in gbl: global_blocks.append(item) self.add_to_scope(item[-1].arg) if item[0].opcode == pyop.LOAD_NAME: item[0].opcode = pyop.LOAD_GLOBAL blocks = global_blocks instructions = [] last_ln = self.bytecode[0].lineno for instr in self.bytecode: if not isinstance(instr, Label) and instr.lineno != last_ln: last_ln = instr.lineno if len(instructions): blocks.append(instructions) instructions = [] if not isinstance(instr, Label) and instr.opcode == pyop.STORE_FAST: self.add_to_scope(instr.arg) instructions.append(instr) if len(instructions): blocks.append(instructions) self._blocks = blocks from ..compiler import Compiler if Compiler.instance().nep8: self.tokenizer = Nep8VMTokenizer(self) else: self.tokenizer = VMTokenizer(self) self._expressions = []
[docs] def evaluate_annotations(self, index): block_index = 0 args_types = [] while block_index < index: if self.block[block_index].opcode == pyop.LOAD_NAME and 'abi' in self.block[block_index].arg: block_index = self.include_abi_info(block_index) else: block_index = block_index + 1
[docs] def include_abi_info(self, start_index): index = start_index load_method_instr = self.block[index] while load_method_instr.opcode != pyop.LOAD_METHOD and load_method_instr.opcode != pyop.LOAD_NAME: index = index + 1 load_method_instr = self.block[index] args_types = [] if load_method_instr.arg == 'abi_method' or load_method_instr.arg == 'abi_entry_point': index = index + 1 arg_instr = self.block[index] while arg_instr.opcode == pyop.LOAD_NAME or arg_instr.opcode == pyop.LOAD_ATTR: if abi.is_abi_type(arg_instr.arg): args_types.append(arg_instr.arg) index = index + 1 arg_instr = self.block[index] # return type not specified if len(args_types) == len(self.args): args_types.append(abi.Void) if arg_instr.opcode == pyop.CALL_METHOD: index = index + 1 if load_method_instr.arg == 'abi_entry_point': self.module.set_abi_entry_point(self, args_types) else: self.module.include_abi_method(self, args_types) return index
[docs] def get_code_block_index(self, blocks): for index, block in enumerate(blocks): if inspect.iscode(block.arg): return index
[docs] def add_to_scope(self, argname): if argname not in self.scope.keys(): current_total = len(self._scope) self._scope[argname] = current_total
[docs] def prepare(self): last_exp = None for block in self._blocks: exp = Expression(block, self.tokenizer, self) self._expressions.append(exp) if last_exp: last_exp.next = exp last_exp = exp for exp in self._expressions: exp.tokenize() self.convert_breaks() self.convert_jumps()
[docs] def convert_jumps(self): filtered = [] for vmtoken in self.tokenizer.vm_tokens.values(): if vmtoken.pytoken: filtered.append(vmtoken) for vmtoken in filtered: if vmtoken.pytoken.jump_from and not vmtoken.pytoken.jump_found: for vmtoken2 in filtered: if vmtoken2.pytoken.jump_target == vmtoken.pytoken.jump_from: diff = vmtoken2.addr - vmtoken.addr vmtoken.data = diff.to_bytes(2, 'little', signed=True) vmtoken2.pytoken.jump_from_addr = vmtoken.addr vmtoken.pytoken.jump_to_addr = vmtoken2.addr
[docs] def convert_breaks(self): tokens = list(self.tokenizer.vm_tokens.values()) setup_token_label = None for tkn in tokens: if tkn.pytoken: if tkn.pytoken.pyop == pyop.SETUP_LOOP: setup_token_label = tkn.pytoken.jump_from if tkn.pytoken.pyop == pyop.BREAK_LOOP: if not setup_token_label: raise Exception("No loopsetup for break") tkn.pytoken.jump_from = setup_token_label