diff --git a/.travis.yml b/.travis.yml index 8f32b37..c722707 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,9 @@ matrix: - python: 3.7 dist: xenial sudo: true + - python: 3.8 + dist: xenial + sudo: true env: - SKIP_DEPS_CHECK=1 diff --git a/boa/code/method.py b/boa/code/method.py index 7171d40..10e0dd6 100644 --- a/boa/code/method.py +++ b/boa/code/method.py @@ -103,11 +103,12 @@ def args(self): def stacksize(self): return self.bytecode.argcount + len(self._blocks) + 2 - def __init__(self, module, block, module_name, extra): + def __init__(self, module, block, module_name, extra, related_blocks=None): self.module = module self.block = block self.module_name = module_name self._extra = extra + self._related_blocks = related_blocks method_block_index = self.get_code_block_index(self.block) if method_block_index is None: @@ -182,37 +183,45 @@ def setup(self): self._expressions = [] def evaluate_annotations(self, index): + if self._related_blocks: + blocks = [] + for block in self._related_blocks: + blocks.extend(block) + index = len(blocks) + else: + blocks = self.block + 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) + if blocks[block_index].opcode == pyop.LOAD_NAME and 'abi' in blocks[block_index].arg: + block_index = self.include_abi_info(blocks, block_index) else: block_index = block_index + 1 - def include_abi_info(self, start_index): + def include_abi_info(self, block, start_index): index = start_index - load_method_instr = self.block[index] + load_method_instr = 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] + load_method_instr = 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] + arg_instr = 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] + arg_instr = 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: + if arg_instr.opcode in [pyop.CALL_METHOD, pyop.CALL_FUNCTION]: index = index + 1 if load_method_instr.arg == 'abi_entry_point': diff --git a/boa/code/module.py b/boa/code/module.py index 45fdce1..ad5b994 100644 --- a/boa/code/module.py +++ b/boa/code/module.py @@ -201,12 +201,30 @@ def build(self): elif type == BlockType.APPCALL_REG: self.app_call_registrations.append(BoaAppcall(blk)) + last_m = None for m in new_method_blks: - new_method = BoaMethod(self, m, self.module_name, self._extra_instr) + new_method = BoaMethod(self, m, self.module_name, self._extra_instr, self._method_related_blocks(m, last_m)) if not self.has_method(new_method.full_name): self.methods.append(new_method) + last_m = m + + def _method_related_blocks(self, method_block, last_method_block): + # decorators were included in the same block as the method until python 3.7 + # in python 3.8, they're in different blocks, that are in the `_extra_instr` + if sys.version_info >= (3, 8): + block = method_block + if last_method_block: + block = last_method_block.next_block + elif len(self._extra_instr) > 0: + block = self._extra_instr[0] + + extra_instr = [] + while block is not None and block is not method_block: + extra_instr.append(block) + block = block.next_block + return extra_instr def write(self): """ @@ -375,19 +393,20 @@ def to_s(self): return "\n".join(output) def include_abi_method(self, method, types): - num_methods = len(method.args) - num_types = len(types) - - args_types = {} - # params and return types - if num_types == num_methods + 1: - for index, arg in enumerate(method.args): - args_types[arg] = types[index] - args_types['return'] = types[num_types - 1] - else: - raise Exception("Number of arguments for the abi is incompatible with the function '%s'" % method.full_name) - - self.abi_methods[method.full_name] = args_types + if method.full_name not in self.abi_methods: + num_methods = len(method.args) + num_types = len(types) + + args_types = {} + # params and return types + if num_types == num_methods + 1: + for index, arg in enumerate(method.args): + args_types[arg] = types[index] + args_types['return'] = types[num_types - 1] + else: + raise Exception("Number of arguments for the abi is incompatible with the function '%s'" % method.full_name) + + self.abi_methods[method.full_name] = args_types def set_abi_entry_point(self, method, types): if self.abi_entry_point is None: