Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 02d9f5e

Browse files
Issue #26881: The modulefinder module now supports extended opcode arguments.
1 parent c7cc985 commit 02d9f5e

4 files changed

Lines changed: 61 additions & 56 deletions

File tree

Lib/dis.py

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -275,31 +275,17 @@ def _get_instructions_bytes(code, varnames=None, names=None, constants=None,
275275
276276
"""
277277
labels = findlabels(code)
278-
extended_arg = 0
279278
starts_line = None
280279
free = None
281-
# enumerate() is not an option, since we sometimes process
282-
# multiple elements on a single pass through the loop
283-
n = len(code)
284-
i = 0
285-
while i < n:
286-
op = code[i]
287-
offset = i
280+
for offset, op, arg in _unpack_opargs(code):
288281
if linestarts is not None:
289-
starts_line = linestarts.get(i, None)
282+
starts_line = linestarts.get(offset, None)
290283
if starts_line is not None:
291284
starts_line += line_offset
292-
is_jump_target = i in labels
293-
i = i+1
294-
arg = None
285+
is_jump_target = offset in labels
295286
argval = None
296287
argrepr = ''
297-
if op >= HAVE_ARGUMENT:
298-
arg = code[i] + code[i+1]*256 + extended_arg
299-
extended_arg = 0
300-
i = i+2
301-
if op == EXTENDED_ARG:
302-
extended_arg = arg*65536
288+
if arg is not None:
303289
# Set argval to the dereferenced value of the argument when
304290
# availabe, and argrepr to the string representation of argval.
305291
# _disassemble_bytes needs the string repr of the
@@ -310,7 +296,7 @@ def _get_instructions_bytes(code, varnames=None, names=None, constants=None,
310296
elif op in hasname:
311297
argval, argrepr = _get_name_info(arg, names)
312298
elif op in hasjrel:
313-
argval = i + arg
299+
argval = offset + 3 + arg
314300
argrepr = "to " + repr(argval)
315301
elif op in haslocal:
316302
argval, argrepr = _get_name_info(arg, varnames)
@@ -320,7 +306,7 @@ def _get_instructions_bytes(code, varnames=None, names=None, constants=None,
320306
elif op in hasfree:
321307
argval, argrepr = _get_name_info(arg, cells)
322308
elif op in hasnargs:
323-
argrepr = "%d positional, %d keyword pair" % (code[i-2], code[i-1])
309+
argrepr = "%d positional, %d keyword pair" % (arg%256, arg//256)
324310
yield Instruction(opname[op], op,
325311
arg, argval, argrepr,
326312
offset, starts_line, is_jump_target)
@@ -356,26 +342,37 @@ def _disassemble_str(source, *, file=None):
356342

357343
disco = disassemble # XXX For backwards compatibility
358344

359-
def findlabels(code):
360-
"""Detect all offsets in a byte code which are jump targets.
361-
362-
Return the list of offsets.
363-
364-
"""
365-
labels = []
345+
def _unpack_opargs(code):
366346
# enumerate() is not an option, since we sometimes process
367347
# multiple elements on a single pass through the loop
348+
extended_arg = 0
368349
n = len(code)
369350
i = 0
370351
while i < n:
371352
op = code[i]
353+
offset = i
372354
i = i+1
355+
arg = None
373356
if op >= HAVE_ARGUMENT:
374-
arg = code[i] + code[i+1]*256
357+
arg = code[i] + code[i+1]*256 + extended_arg
358+
extended_arg = 0
375359
i = i+2
360+
if op == EXTENDED_ARG:
361+
extended_arg = arg*65536
362+
yield (offset, op, arg)
363+
364+
def findlabels(code):
365+
"""Detect all offsets in a byte code which are jump targets.
366+
367+
Return the list of offsets.
368+
369+
"""
370+
labels = []
371+
for offset, op, arg in _unpack_opargs(code):
372+
if arg is not None:
376373
label = -1
377374
if op in hasjrel:
378-
label = i+arg
375+
label = offset + 3 + arg
379376
elif op in hasjabs:
380377
label = arg
381378
if label >= 0:

Lib/modulefinder.py

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,12 @@
1313
warnings.simplefilter('ignore', PendingDeprecationWarning)
1414
import imp
1515

16-
# XXX Clean up once str8's cstor matches bytes.
17-
LOAD_CONST = bytes([dis.opname.index('LOAD_CONST')])
18-
IMPORT_NAME = bytes([dis.opname.index('IMPORT_NAME')])
19-
STORE_NAME = bytes([dis.opname.index('STORE_NAME')])
20-
STORE_GLOBAL = bytes([dis.opname.index('STORE_GLOBAL')])
21-
STORE_OPS = [STORE_NAME, STORE_GLOBAL]
22-
HAVE_ARGUMENT = bytes([dis.HAVE_ARGUMENT])
16+
LOAD_CONST = dis.opmap['LOAD_CONST']
17+
IMPORT_NAME = dis.opmap['IMPORT_NAME']
18+
STORE_NAME = dis.opmap['STORE_NAME']
19+
STORE_GLOBAL = dis.opmap['STORE_GLOBAL']
20+
STORE_OPS = STORE_NAME, STORE_GLOBAL
21+
EXTENDED_ARG = dis.EXTENDED_ARG
2322

2423
# Modulefinder does a good job at simulating Python's, but it can not
2524
# handle __path__ modifications packages make at runtime. Therefore there
@@ -337,38 +336,30 @@ def _safe_import_hook(self, name, caller, fromlist, level=-1):
337336
fullname = name + "." + sub
338337
self._add_badmodule(fullname, caller)
339338

340-
def scan_opcodes_25(self, co,
341-
unpack = struct.unpack):
339+
def scan_opcodes(self, co):
342340
# Scan the code, and yield 'interesting' opcode combinations
343-
# Python 2.5 version (has absolute and relative imports)
344341
code = co.co_code
345342
names = co.co_names
346343
consts = co.co_consts
347-
LOAD_LOAD_AND_IMPORT = LOAD_CONST + LOAD_CONST + IMPORT_NAME
348-
while code:
349-
c = bytes([code[0]])
350-
if c in STORE_OPS:
351-
oparg, = unpack('<H', code[1:3])
344+
opargs = [(op, arg) for _, op, arg in dis._unpack_opargs(code)
345+
if op != EXTENDED_ARG]
346+
for i, (op, oparg) in enumerate(opargs):
347+
if op in STORE_OPS:
352348
yield "store", (names[oparg],)
353-
code = code[3:]
354349
continue
355-
if code[:9:3] == LOAD_LOAD_AND_IMPORT:
356-
oparg_1, oparg_2, oparg_3 = unpack('<xHxHxH', code[:9])
357-
level = consts[oparg_1]
350+
if (op == IMPORT_NAME and i >= 2
351+
and opargs[i-1][0] == opargs[i-2][0] == LOAD_CONST):
352+
level = consts[opargs[i-2][1]]
353+
fromlist = consts[opargs[i-1][1]]
358354
if level == 0: # absolute import
359-
yield "absolute_import", (consts[oparg_2], names[oparg_3])
355+
yield "absolute_import", (fromlist, names[oparg])
360356
else: # relative import
361-
yield "relative_import", (level, consts[oparg_2], names[oparg_3])
362-
code = code[9:]
357+
yield "relative_import", (level, fromlist, names[oparg])
363358
continue
364-
if c >= HAVE_ARGUMENT:
365-
code = code[3:]
366-
else:
367-
code = code[1:]
368359

369360
def scan_code(self, co, m):
370361
code = co.co_code
371-
scanner = self.scan_opcodes_25
362+
scanner = self.scan_opcodes
372363
for what, args in scanner(co):
373364
if what == "store":
374365
name, = args

Lib/test/test_modulefinder.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,19 @@ def test_replace_paths(self):
319319
expected = "co_filename %r changed to %r" % (old_path, new_path)
320320
self.assertIn(expected, output)
321321

322+
def test_extended_opargs(self):
323+
extended_opargs_test = [
324+
"a",
325+
["a", "b"],
326+
[], [],
327+
"""\
328+
a.py
329+
%r
330+
import b
331+
b.py
332+
""" % list(range(2**16))] # 2**16 constants
333+
self._do_test(extended_opargs_test)
334+
322335

323336
if __name__ == "__main__":
324337
unittest.main()

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ Core and Builtins
116116
Library
117117
-------
118118

119+
- Issue #26881: The modulefinder module now supports extended opcode arguments.
120+
119121
- Issue #23815: Fixed crashes related to directly created instances of types in
120122
_tkinter and curses.panel modules.
121123

@@ -125,6 +127,8 @@ Library
125127
- Issue #26873: xmlrpc now raises ResponseError on unsupported type tags
126128
instead of silently return incorrect result.
127129

130+
- Issue #26881: modulefinder now works with bytecode with extended args.
131+
128132
- Issue #26711: Fixed the comparison of plistlib.Data with other types.
129133

130134
- Issue #24114: Fix an uninitialized variable in `ctypes.util`.

0 commit comments

Comments
 (0)