Add stdin and stdout passthrough to VyperDebugCmd.
- Used for implementing cmdline testing.
This commit is contained in:
parent
5b243dde30
commit
093541cac6
|
@ -11,6 +11,9 @@ from vdb.vdb import (
|
|||
set_evm_opcode_debugger,
|
||||
set_evm_opcode_pass
|
||||
)
|
||||
from vdb.source_map import (
|
||||
produce_source_map
|
||||
)
|
||||
from eth_tester import (
|
||||
EthereumTester,
|
||||
)
|
||||
|
@ -123,6 +126,7 @@ if __name__ == '__main__':
|
|||
cast_args = cast_types(args, func_abi)
|
||||
res = getattr(contract.functions, func_name)(*cast_args).call({'gas': func_abi['gas'] + 22000})
|
||||
|
||||
source_map = produce_source_map(code)
|
||||
set_evm_opcode_debugger(source_code=code, source_map=source_map)
|
||||
tx_hash = getattr(contract.functions, func_name)(*cast_args).transact({'gas': func_abi['gas'] + 22000})
|
||||
set_evm_opcode_pass()
|
||||
|
|
80
vdb/vdb.py
80
vdb/vdb.py
|
@ -24,7 +24,7 @@ base_types = ('int128', 'uint256', 'address', 'bytes32')
|
|||
def history():
|
||||
import readline
|
||||
for i in range(1, readline.get_current_history_length() + 1):
|
||||
print("%3d %s" % (i, readline.get_history_item(i)))
|
||||
self.stdout.write("%3d %s" % (i, readline.get_history_item(i)) + '\n')
|
||||
|
||||
|
||||
logo = """
|
||||
|
@ -37,7 +37,7 @@ __ __
|
|||
"""
|
||||
|
||||
|
||||
def print_var(value, var_typ):
|
||||
def print_var(stdout, value, var_typ):
|
||||
|
||||
if isinstance(value, int):
|
||||
v = int_to_big_endian(value)
|
||||
|
@ -46,32 +46,35 @@ def print_var(value, var_typ):
|
|||
|
||||
if isinstance(v, bytes):
|
||||
if var_typ == 'uint256':
|
||||
print(big_endian_to_int(v))
|
||||
stdout.write(str(big_endian_to_int(v)) + '\n')
|
||||
elif var_typ == 'int128':
|
||||
print('TODO!')
|
||||
stdout.write('TODO!' + '\n')
|
||||
elif var_typ == 'address':
|
||||
print(to_hex(v[12:]))
|
||||
stdout.write(to_hex(v[12:]) + '\n')
|
||||
else:
|
||||
print(v)
|
||||
stdout.write(v + '\n')
|
||||
|
||||
|
||||
class VyperDebugCmd(cmd.Cmd):
|
||||
def __init__(self, computation, line_no=None, source_code=None, source_map=None):
|
||||
prompt = '\033[92mvdb\033[0m> '
|
||||
intro = logo
|
||||
|
||||
def __init__(self, computation, line_no=None, source_code=None, source_map=None, stdout=None, stdin=None):
|
||||
if source_map is None:
|
||||
source_map = {}
|
||||
self.computation = computation
|
||||
self.prompt = '\033[92mvdb\033[0m> '
|
||||
self.intro = logo
|
||||
self.source_code = source_code
|
||||
self.line_no = line_no
|
||||
self.globals = source_map.get("globals")
|
||||
self.locals = source_map.get("locals")
|
||||
super().__init__()
|
||||
super().__init__(stdin=stdin, stdout=stdout)
|
||||
if stdout or stdin:
|
||||
self.use_rawinput = False
|
||||
|
||||
def _print_code_position(self):
|
||||
|
||||
if not all((self.source_code, self.line_no)):
|
||||
print('No source loaded')
|
||||
self.stdout.write('No source loaded' + '\n')
|
||||
return
|
||||
|
||||
lines = self.source_code.splitlines()
|
||||
|
@ -80,31 +83,31 @@ class VyperDebugCmd(cmd.Cmd):
|
|||
for idx, line in enumerate(lines[begin - 1:end]):
|
||||
line_number = begin + idx
|
||||
if line_number == self.line_no:
|
||||
print("--> \033[92m{}\033[0m\t{}".format(line_number, line))
|
||||
self.stdout.write("--> \033[92m{}\033[0m\t{}".format(line_number, line) + '\n')
|
||||
else:
|
||||
print(" \033[92m{}\033[0m\t{}".format(line_number, line))
|
||||
self.stdout.write(" \033[92m{}\033[0m\t{}".format(line_number, line) + '\n')
|
||||
|
||||
def preloop(self):
|
||||
super().preloop()
|
||||
self._print_code_position()
|
||||
|
||||
def postloop(self):
|
||||
print('Exiting vdb')
|
||||
self.stdout.write('Exiting vdb' + '\n')
|
||||
super().postloop()
|
||||
|
||||
def do_state(self, *args):
|
||||
""" Show current EVM state information. """
|
||||
print('Block Number => {}'.format(self.computation.state.block_number))
|
||||
print('Program Counter => {}'.format(self.computation.code.pc))
|
||||
print('Memory Size => {}'.format(len(self.computation._memory)))
|
||||
print('Gas Remaining => {}'.format(self.computation.get_gas_remaining()))
|
||||
self.stdout.write('Block Number => {}'.format(self.computation.state.block_number) + '\n')
|
||||
self.stdout.write('Program Counter => {}'.format(self.computation.code.pc) + '\n')
|
||||
self.stdout.write('Memory Size => {}'.format(len(self.computation._memory)) + '\n')
|
||||
self.stdout.write('Gas Remaining => {}'.format(self.computation.get_gas_remaining()) + '\n')
|
||||
|
||||
def do_globals(self, *args):
|
||||
if not self.globals:
|
||||
print('No globals found.')
|
||||
print('Name\t\tType')
|
||||
self.stdout.write('No globals found.' + '\n')
|
||||
self.stdout.write('Name\t\tType' + '\n')
|
||||
for name, info in self.globals.items():
|
||||
print('self.{}\t\t{}'.format(name, info['type']))
|
||||
self.stdout.write('self.{}\t\t{}'.format(name, info['type']) + '\n')
|
||||
|
||||
def _get_fn_name_locals(self):
|
||||
for fn_name, info in self.locals.items():
|
||||
|
@ -114,23 +117,23 @@ class VyperDebugCmd(cmd.Cmd):
|
|||
|
||||
def do_locals(self, *args):
|
||||
if not self.locals:
|
||||
print('No locals found.')
|
||||
self.stdout.write('No locals found.' + '\n')
|
||||
fn_name, variables = self._get_fn_name_locals()
|
||||
print('Function: {}'.format(fn_name))
|
||||
print('Name\t\tType')
|
||||
self.stdout.write('Function: {}'.format(fn_name) + '\n')
|
||||
self.stdout.write('Name\t\tType' + '\n')
|
||||
for name, info in variables.items():
|
||||
print('{}\t\t{}'.format(name, info['type']))
|
||||
self.stdout.write('{}\t\t{}'.format(name, info['type']) + '\n')
|
||||
|
||||
def default(self, line):
|
||||
fn_name, local_variables = self._get_fn_name_locals()
|
||||
|
||||
if line.startswith('self.') and len(line) > 4:
|
||||
if not self.globals:
|
||||
print('No globals found.')
|
||||
self.stdout.write('No globals found.' + '\n')
|
||||
# print global value.
|
||||
name = line.split('.')[1]
|
||||
if name not in self.globals:
|
||||
print('Global named "{}" not found.'.format(name))
|
||||
self.stdout.write('Global named "{}" not found.'.format(name) + '\n')
|
||||
else:
|
||||
global_type = self.globals[name]['type']
|
||||
slot = None
|
||||
|
@ -142,32 +145,32 @@ class VyperDebugCmd(cmd.Cmd):
|
|||
# slot = big_endian_to_int(location_hash)
|
||||
pass
|
||||
else:
|
||||
print('Can not read global of type "{}".'.format(global_type))
|
||||
self.stdout.write('Can not read global of type "{}".'.format(global_type) + '\n')
|
||||
|
||||
if slot is not None:
|
||||
value = self.computation.state.account_db.get_storage(
|
||||
address=self.computation.msg.storage_address,
|
||||
slot=slot,
|
||||
)
|
||||
print_var(value, global_type)
|
||||
print_var(self.stdout, value, global_type)
|
||||
elif line in local_variables:
|
||||
var_info = local_variables[line]
|
||||
local_type = var_info['type']
|
||||
if local_type in base_types:
|
||||
start_position = var_info['position']
|
||||
value = self.computation.memory_read(start_position, 32)
|
||||
print_var(value, local_type)
|
||||
print_var(self.stdout, value, local_type)
|
||||
else:
|
||||
print('Can not read local of type ')
|
||||
self.stdout.write('Can not read local of type ' + '\n')
|
||||
else:
|
||||
self.stdout.write('*** Unknown syntax: %s\n' % line)
|
||||
|
||||
def do_stack(self, *args):
|
||||
""" Show contents of the stack """
|
||||
for idx, value in enumerate(self.computation._stack.values):
|
||||
print("{}\t{}".format(idx, to_hex(value)))
|
||||
self.stdout.write("{}\t{}".format(idx, to_hex(value)) + '\n')
|
||||
else:
|
||||
print("Stack is empty")
|
||||
self.stdout.write("Stack is empty" + '\n')
|
||||
|
||||
def do_pdb(self, *args):
|
||||
# Break out to pdb for vdb debugging.
|
||||
|
@ -198,11 +201,18 @@ class VyperDebugCmd(cmd.Cmd):
|
|||
original_opcodes = evm.vm.forks.byzantium.computation.ByzantiumComputation.opcodes
|
||||
|
||||
|
||||
def set_evm_opcode_debugger(source_code=None, source_map=None):
|
||||
def set_evm_opcode_debugger(source_code=None, source_map=None, stdin=None, stdout=None):
|
||||
|
||||
def debug_opcode(computation):
|
||||
line_no = computation.stack_pop(num_items=1, type_hint=constants.UINT256)
|
||||
VyperDebugCmd(computation, line_no=line_no, source_code=source_code, source_map=source_map).cmdloop()
|
||||
VyperDebugCmd(
|
||||
computation,
|
||||
line_no=line_no,
|
||||
source_code=source_code,
|
||||
source_map=source_map,
|
||||
stdin=stdin,
|
||||
stdout=stdout
|
||||
).cmdloop()
|
||||
|
||||
opcodes = original_opcodes.copy()
|
||||
opcodes[vyper_opcodes['DEBUG'][0]] = as_opcode(
|
||||
|
|
Loading…
Reference in New Issue