mirror of
https://github.com/python/cpython.git
synced 2025-09-19 07:00:59 +00:00
bpo-45152: refactor the dis module to make handling of hasconst opcodes more generic (GH-28258)
This commit is contained in:
parent
1afc7b3219
commit
40d2ac92f9
1 changed files with 35 additions and 19 deletions
54
Lib/dis.py
54
Lib/dis.py
|
@ -26,6 +26,7 @@ FORMAT_VALUE_CONVERTERS = (
|
||||||
MAKE_FUNCTION = opmap['MAKE_FUNCTION']
|
MAKE_FUNCTION = opmap['MAKE_FUNCTION']
|
||||||
MAKE_FUNCTION_FLAGS = ('defaults', 'kwdefaults', 'annotations', 'closure')
|
MAKE_FUNCTION_FLAGS = ('defaults', 'kwdefaults', 'annotations', 'closure')
|
||||||
|
|
||||||
|
LOAD_CONST = opmap['LOAD_CONST']
|
||||||
|
|
||||||
def _try_compile(source, name):
|
def _try_compile(source, name):
|
||||||
"""Attempts to compile the given source, first as an expression and
|
"""Attempts to compile the given source, first as an expression and
|
||||||
|
@ -317,19 +318,32 @@ def get_instructions(x, *, first_line=None):
|
||||||
co.co_names, co.co_consts,
|
co.co_names, co.co_consts,
|
||||||
linestarts, line_offset, co_positions=co.co_positions())
|
linestarts, line_offset, co_positions=co.co_positions())
|
||||||
|
|
||||||
def _get_const_info(const_index, const_list):
|
def _get_const_value(op, arg, co_consts):
|
||||||
|
"""Helper to get the value of the const in a hasconst op.
|
||||||
|
|
||||||
|
Returns the dereferenced constant if this is possible.
|
||||||
|
Otherwise (if it is a LOAD_CONST and co_consts is not
|
||||||
|
provided) returns the dis.UNKNOWN sentinel.
|
||||||
|
"""
|
||||||
|
assert op in hasconst
|
||||||
|
|
||||||
|
argval = UNKNOWN
|
||||||
|
if op == LOAD_CONST:
|
||||||
|
if co_consts is not None:
|
||||||
|
argval = co_consts[arg]
|
||||||
|
return argval
|
||||||
|
|
||||||
|
def _get_const_info(op, arg, co_consts):
|
||||||
"""Helper to get optional details about const references
|
"""Helper to get optional details about const references
|
||||||
|
|
||||||
Returns the dereferenced constant and its repr if the constant
|
Returns the dereferenced constant and its repr if the value
|
||||||
list is defined.
|
can be calculated.
|
||||||
Otherwise returns the sentinel value dis.UNKNOWN for the value
|
Otherwise returns the sentinel value dis.UNKNOWN for the value
|
||||||
and an empty string for its repr.
|
and an empty string for its repr.
|
||||||
"""
|
"""
|
||||||
if const_list is not None:
|
argval = _get_const_value(op, arg, co_consts)
|
||||||
argval = const_list[const_index]
|
argrepr = repr(argval) if argval is not UNKNOWN else ''
|
||||||
return argval, repr(argval)
|
return argval, argrepr
|
||||||
else:
|
|
||||||
return UNKNOWN, ''
|
|
||||||
|
|
||||||
def _get_name_info(name_index, get_name, **extrainfo):
|
def _get_name_info(name_index, get_name, **extrainfo):
|
||||||
"""Helper to get optional details about named references
|
"""Helper to get optional details about named references
|
||||||
|
@ -371,14 +385,14 @@ def parse_exception_table(code):
|
||||||
return entries
|
return entries
|
||||||
|
|
||||||
def _get_instructions_bytes(code, varname_from_oparg=None,
|
def _get_instructions_bytes(code, varname_from_oparg=None,
|
||||||
names=None, constants=None,
|
names=None, co_consts=None,
|
||||||
linestarts=None, line_offset=0,
|
linestarts=None, line_offset=0,
|
||||||
exception_entries=(), co_positions=None):
|
exception_entries=(), co_positions=None):
|
||||||
"""Iterate over the instructions in a bytecode string.
|
"""Iterate over the instructions in a bytecode string.
|
||||||
|
|
||||||
Generates a sequence of Instruction namedtuples giving the details of each
|
Generates a sequence of Instruction namedtuples giving the details of each
|
||||||
opcode. Additional information about the code's runtime environment
|
opcode. Additional information about the code's runtime environment
|
||||||
(e.g. variable names, constants) can be specified using optional
|
(e.g. variable names, co_consts) can be specified using optional
|
||||||
arguments.
|
arguments.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -408,7 +422,7 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
|
||||||
# raw name index for LOAD_GLOBAL, LOAD_CONST, etc.
|
# raw name index for LOAD_GLOBAL, LOAD_CONST, etc.
|
||||||
argval = arg
|
argval = arg
|
||||||
if op in hasconst:
|
if op in hasconst:
|
||||||
argval, argrepr = _get_const_info(arg, constants)
|
argval, argrepr = _get_const_info(op, arg, co_consts)
|
||||||
elif op in hasname:
|
elif op in hasname:
|
||||||
argval, argrepr = _get_name_info(arg, get_name)
|
argval, argrepr = _get_name_info(arg, get_name)
|
||||||
elif op in hasjabs:
|
elif op in hasjabs:
|
||||||
|
@ -457,7 +471,7 @@ def _disassemble_recursive(co, *, file=None, depth=None):
|
||||||
_disassemble_recursive(x, file=file, depth=depth)
|
_disassemble_recursive(x, file=file, depth=depth)
|
||||||
|
|
||||||
def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None,
|
def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None,
|
||||||
names=None, constants=None, linestarts=None,
|
names=None, co_consts=None, linestarts=None,
|
||||||
*, file=None, line_offset=0, exception_entries=(),
|
*, file=None, line_offset=0, exception_entries=(),
|
||||||
co_positions=None):
|
co_positions=None):
|
||||||
# Omit the line number column entirely if we have no line number info
|
# Omit the line number column entirely if we have no line number info
|
||||||
|
@ -476,7 +490,7 @@ def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None,
|
||||||
else:
|
else:
|
||||||
offset_width = 4
|
offset_width = 4
|
||||||
for instr in _get_instructions_bytes(code, varname_from_oparg, names,
|
for instr in _get_instructions_bytes(code, varname_from_oparg, names,
|
||||||
constants, linestarts,
|
co_consts, linestarts,
|
||||||
line_offset=line_offset, exception_entries=exception_entries,
|
line_offset=line_offset, exception_entries=exception_entries,
|
||||||
co_positions=co_positions):
|
co_positions=co_positions):
|
||||||
new_source_line = (show_lineno and
|
new_source_line = (show_lineno and
|
||||||
|
@ -557,11 +571,13 @@ def _find_imports(co):
|
||||||
opargs = [(op, arg) for _, op, arg in _unpack_opargs(co.co_code)
|
opargs = [(op, arg) for _, op, arg in _unpack_opargs(co.co_code)
|
||||||
if op != EXTENDED_ARG]
|
if op != EXTENDED_ARG]
|
||||||
for i, (op, oparg) in enumerate(opargs):
|
for i, (op, oparg) in enumerate(opargs):
|
||||||
if (op == IMPORT_NAME and i >= 2
|
if op == IMPORT_NAME and i >= 2:
|
||||||
and opargs[i-1][0] == opargs[i-2][0] == LOAD_CONST):
|
from_op = opargs[i-1]
|
||||||
level = consts[opargs[i-2][1]]
|
level_op = opargs[i-2]
|
||||||
fromlist = consts[opargs[i-1][1]]
|
if (from_op[0] in hasconst and level_op[0] in hasconst):
|
||||||
yield (names[oparg], level, fromlist)
|
level = _get_const_value(level_op[0], level_op[1], consts)
|
||||||
|
fromlist = _get_const_value(from_op[0], from_op[1], consts)
|
||||||
|
yield (names[oparg], level, fromlist)
|
||||||
|
|
||||||
def _find_store_names(co):
|
def _find_store_names(co):
|
||||||
"""Find names of variables which are written in the code
|
"""Find names of variables which are written in the code
|
||||||
|
@ -635,7 +651,7 @@ class Bytecode:
|
||||||
with io.StringIO() as output:
|
with io.StringIO() as output:
|
||||||
_disassemble_bytes(co.co_code,
|
_disassemble_bytes(co.co_code,
|
||||||
varname_from_oparg=co._varname_from_oparg,
|
varname_from_oparg=co._varname_from_oparg,
|
||||||
names=co.co_names, constants=co.co_consts,
|
names=co.co_names, co_consts=co.co_consts,
|
||||||
linestarts=self._linestarts,
|
linestarts=self._linestarts,
|
||||||
line_offset=self._line_offset,
|
line_offset=self._line_offset,
|
||||||
file=output,
|
file=output,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue