def _search(self, event): frame = self.active_frame() tree = self.root.main.oplist if not frame.tag_ranges("sel"): tree.clear_selection() return start, stop = frame.tag_ranges("sel") if self._scope and (frame != self._scope[0] or start < self._scope[1] or stop > self._scope[2]): pc = False else: pc = [ k for k, v in self.root.pcMap.items() if "path" in v and frame._label in v["path"] and is_inside_offset((start, stop), v["offset"]) ] if not pc: frame.clear_highlight() tree.clear_selection() return def key(k): return (start - self.root.pcMap[k]["offset"][0]) + ( self.root.pcMap[k]["offset"][1] - stop) id_ = sorted(pc, key=key)[0] tree.selection_set(id_)
def _find_node_by_offset(ast_json: List, offset: Tuple) -> Dict: node = next(i for i in ast_json if is_inside_offset(offset, _convert_src(i["src"]))) if _convert_src(node["src"]) == offset: return node node_list = [i for i in node.values() if isinstance(i, dict) and "ast_type" in i] node_list.extend([x for i in node.values() if isinstance(i, list) for x in i]) if node_list: return _find_node_by_offset(node_list, offset) return _find_node_by_offset(ast_json[ast_json.index(node) + 1 :], offset)
def toggle_on(self): try: op = self.oplist.selection()[0] except IndexError: return False if self.oplist.item(op, 'tags')[0] == "NoSource": return False pc = self.root.pcMap[op] for key, value in sorted(self.root.pcMap.items(), key=lambda k: int(k[0])): if ('path' not in value or value['path'] != pc['path'] or not is_inside_offset(value['offset'], pc['offset'])): self.oplist.detach(key) else: self.oplist.move(key, '', key) self.oplist.see(op) self.root.main.note.apply_scope(*pc['offset']) return True
def _generate_coverage_data(source_map_str: str, opcodes_str: str, contract_name: str, ast_json: List) -> Tuple: if not opcodes_str: return {}, {}, {} source_map = deque(expand_source_map(source_map_str)) opcodes = deque(opcodes_str.split(" ")) fn_nodes = [i for i in ast_json if i["ast_type"] == "FunctionDef"] fn_offsets = dict((i["name"], _convert_src(i["src"])) for i in fn_nodes) stmt_nodes = set( _convert_src(i["src"]) for i in _get_statement_nodes(fn_nodes)) statement_map: Dict = {} branch_map: Dict = {} pc_list: List = [] count, pc = 0, 0 while opcodes: # format of source is [start, stop, contract_id, jump code] source = source_map.popleft() pc_list.append({"op": opcodes.popleft(), "pc": pc}) if source[3] != "-": pc_list[-1]["jump"] = source[3] pc += 1 if opcodes and opcodes[0][:2] == "0x": pc_list[-1]["value"] = opcodes.popleft() pc += int(pc_list[-1]["op"][4:]) # set source offset (-1 means none) if source[0] == -1: if (len(pc_list) > 6 and pc_list[-7]["op"] == "CALLVALUE" and pc_list[-1]["op"] == "REVERT"): # special case - initial nonpayable check on vyper >=0.2.5 pc_list[-1]["dev"] = "Cannot send ether to nonpayable function" # hackiness to prevent the source highlight from showing the entire contract pc_list[-2].update(path="0", offset=[0, 0]) continue offset = (source[0], source[0] + source[1]) pc_list[-1]["path"] = "0" pc_list[-1]["offset"] = offset try: if "offset" in pc_list[-2] and offset == pc_list[-2]["offset"]: pc_list[-1]["fn"] = pc_list[-2]["fn"] else: # statement coverage fn = next(k for k, v in fn_offsets.items() if is_inside_offset(offset, v)) pc_list[-1]["fn"] = f"{contract_name}.{fn}" stmt_offset = next(i for i in stmt_nodes if is_inside_offset(offset, i)) stmt_nodes.remove(stmt_offset) statement_map.setdefault(pc_list[-1]["fn"], {})[count] = stmt_offset pc_list[-1]["statement"] = count count += 1 except (KeyError, IndexError, StopIteration): pass if pc_list[-1]["op"] not in ("JUMPI", "REVERT"): continue node = _find_node_by_offset(ast_json, offset) if pc_list[-1]["op"] == "REVERT": # custom revert error strings if node["ast_type"] == "FunctionDef" and pc_list[-7][ "op"] == "CALLVALUE": pc_list[-1]["dev"] = "Cannot send ether to nonpayable function" elif node["ast_type"] == "Subscript": pc_list[-1]["dev"] = "Index out of range" elif node["ast_type"] in ("AugAssign", "BinOp"): if node["op"]["ast_type"] == "Sub": pc_list[-1]["dev"] = "Integer underflow" elif node["op"]["ast_type"] == "Div": pc_list[-1]["dev"] = "Division by zero" elif node["op"]["ast_type"] == "Mod": pc_list[-1]["dev"] = "Modulo by zero" else: pc_list[-1]["dev"] = "Integer overflow" continue if node["ast_type"] in ("Assert", "If") or ( node["ast_type"] == "Expr" and node["value"]["func"].get( "id", None) == "assert_modifiable"): # branch coverage pc_list[-1]["branch"] = count branch_map.setdefault(pc_list[-1]["fn"], {}) if node["ast_type"] == "If": branch_map[pc_list[-1]["fn"]][count] = _convert_src( node["test"]["src"]) + (False, ) else: branch_map[pc_list[-1]["fn"]][count] = offset + (True, ) count += 1 pc_list[0]["path"] = "0" pc_list[0]["offset"] = [0, _convert_src(ast_json[-1]["src"])[1]] pc_map = dict((i.pop("pc"), i) for i in pc_list) return pc_map, {"0": statement_map}, {"0": branch_map}