Skip to content

Commit 40d2ac9

Browse files
authored
bpo-45152: refactor the dis module to make handling of hasconst opcodes more generic (GH-28258)
1 parent 1afc7b3 commit 40d2ac9

File tree

1 file changed

+35
-19
lines changed

1 file changed

+35
-19
lines changed

Lib/dis.py

+35-19
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
MAKE_FUNCTION = opmap['MAKE_FUNCTION']
2727
MAKE_FUNCTION_FLAGS = ('defaults', 'kwdefaults', 'annotations', 'closure')
2828

29+
LOAD_CONST = opmap['LOAD_CONST']
2930

3031
def _try_compile(source, name):
3132
"""Attempts to compile the given source, first as an expression and
@@ -317,19 +318,32 @@ def get_instructions(x, *, first_line=None):
317318
co.co_names, co.co_consts,
318319
linestarts, line_offset, co_positions=co.co_positions())
319320

320-
def _get_const_info(const_index, const_list):
321+
def _get_const_value(op, arg, co_consts):
322+
"""Helper to get the value of the const in a hasconst op.
323+
324+
Returns the dereferenced constant if this is possible.
325+
Otherwise (if it is a LOAD_CONST and co_consts is not
326+
provided) returns the dis.UNKNOWN sentinel.
327+
"""
328+
assert op in hasconst
329+
330+
argval = UNKNOWN
331+
if op == LOAD_CONST:
332+
if co_consts is not None:
333+
argval = co_consts[arg]
334+
return argval
335+
336+
def _get_const_info(op, arg, co_consts):
321337
"""Helper to get optional details about const references
322338
323-
Returns the dereferenced constant and its repr if the constant
324-
list is defined.
339+
Returns the dereferenced constant and its repr if the value
340+
can be calculated.
325341
Otherwise returns the sentinel value dis.UNKNOWN for the value
326342
and an empty string for its repr.
327343
"""
328-
if const_list is not None:
329-
argval = const_list[const_index]
330-
return argval, repr(argval)
331-
else:
332-
return UNKNOWN, ''
344+
argval = _get_const_value(op, arg, co_consts)
345+
argrepr = repr(argval) if argval is not UNKNOWN else ''
346+
return argval, argrepr
333347

334348
def _get_name_info(name_index, get_name, **extrainfo):
335349
"""Helper to get optional details about named references
@@ -371,14 +385,14 @@ def parse_exception_table(code):
371385
return entries
372386

373387
def _get_instructions_bytes(code, varname_from_oparg=None,
374-
names=None, constants=None,
388+
names=None, co_consts=None,
375389
linestarts=None, line_offset=0,
376390
exception_entries=(), co_positions=None):
377391
"""Iterate over the instructions in a bytecode string.
378392
379393
Generates a sequence of Instruction namedtuples giving the details of each
380394
opcode. Additional information about the code's runtime environment
381-
(e.g. variable names, constants) can be specified using optional
395+
(e.g. variable names, co_consts) can be specified using optional
382396
arguments.
383397
384398
"""
@@ -408,7 +422,7 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
408422
# raw name index for LOAD_GLOBAL, LOAD_CONST, etc.
409423
argval = arg
410424
if op in hasconst:
411-
argval, argrepr = _get_const_info(arg, constants)
425+
argval, argrepr = _get_const_info(op, arg, co_consts)
412426
elif op in hasname:
413427
argval, argrepr = _get_name_info(arg, get_name)
414428
elif op in hasjabs:
@@ -457,7 +471,7 @@ def _disassemble_recursive(co, *, file=None, depth=None):
457471
_disassemble_recursive(x, file=file, depth=depth)
458472

459473
def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None,
460-
names=None, constants=None, linestarts=None,
474+
names=None, co_consts=None, linestarts=None,
461475
*, file=None, line_offset=0, exception_entries=(),
462476
co_positions=None):
463477
# 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,
476490
else:
477491
offset_width = 4
478492
for instr in _get_instructions_bytes(code, varname_from_oparg, names,
479-
constants, linestarts,
493+
co_consts, linestarts,
480494
line_offset=line_offset, exception_entries=exception_entries,
481495
co_positions=co_positions):
482496
new_source_line = (show_lineno and
@@ -557,11 +571,13 @@ def _find_imports(co):
557571
opargs = [(op, arg) for _, op, arg in _unpack_opargs(co.co_code)
558572
if op != EXTENDED_ARG]
559573
for i, (op, oparg) in enumerate(opargs):
560-
if (op == IMPORT_NAME and i >= 2
561-
and opargs[i-1][0] == opargs[i-2][0] == LOAD_CONST):
562-
level = consts[opargs[i-2][1]]
563-
fromlist = consts[opargs[i-1][1]]
564-
yield (names[oparg], level, fromlist)
574+
if op == IMPORT_NAME and i >= 2:
575+
from_op = opargs[i-1]
576+
level_op = opargs[i-2]
577+
if (from_op[0] in hasconst and level_op[0] in hasconst):
578+
level = _get_const_value(level_op[0], level_op[1], consts)
579+
fromlist = _get_const_value(from_op[0], from_op[1], consts)
580+
yield (names[oparg], level, fromlist)
565581

566582
def _find_store_names(co):
567583
"""Find names of variables which are written in the code
@@ -635,7 +651,7 @@ def dis(self):
635651
with io.StringIO() as output:
636652
_disassemble_bytes(co.co_code,
637653
varname_from_oparg=co._varname_from_oparg,
638-
names=co.co_names, constants=co.co_consts,
654+
names=co.co_names, co_consts=co.co_consts,
639655
linestarts=self._linestarts,
640656
line_offset=self._line_offset,
641657
file=output,

0 commit comments

Comments
 (0)