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

Skip to content

Commit 3e99fde

Browse files
Issue #26881: The modulefinder module now supports extended opcode arguments.
2 parents ce41287 + 02d9f5e commit 3e99fde

4 files changed

Lines changed: 60 additions & 55 deletions

File tree

Lib/dis.py

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -284,31 +284,17 @@ def _get_instructions_bytes(code, varnames=None, names=None, constants=None,
284284
285285
"""
286286
labels = findlabels(code)
287-
extended_arg = 0
288287
starts_line = None
289288
free = None
290-
# enumerate() is not an option, since we sometimes process
291-
# multiple elements on a single pass through the loop
292-
n = len(code)
293-
i = 0
294-
while i < n:
295-
op = code[i]
296-
offset = i
289+
for offset, op, arg in _unpack_opargs(code):
297290
if linestarts is not None:
298-
starts_line = linestarts.get(i, None)
291+
starts_line = linestarts.get(offset, None)
299292
if starts_line is not None:
300293
starts_line += line_offset
301-
is_jump_target = i in labels
302-
i = i+1
303-
arg = None
294+
is_jump_target = offset in labels
304295
argval = None
305296
argrepr = ''
306-
if op >= HAVE_ARGUMENT:
307-
arg = code[i] + code[i+1]*256 + extended_arg
308-
extended_arg = 0
309-
i = i+2
310-
if op == EXTENDED_ARG:
311-
extended_arg = arg*65536
297+
if arg is not None:
312298
# Set argval to the dereferenced value of the argument when
313299
# availabe, and argrepr to the string representation of argval.
314300
# _disassemble_bytes needs the string repr of the
@@ -319,7 +305,7 @@ def _get_instructions_bytes(code, varnames=None, names=None, constants=None,
319305
elif op in hasname:
320306
argval, argrepr = _get_name_info(arg, names)
321307
elif op in hasjrel:
322-
argval = i + arg
308+
argval = offset + 3 + arg
323309
argrepr = "to " + repr(argval)
324310
elif op in haslocal:
325311
argval, argrepr = _get_name_info(arg, varnames)
@@ -329,7 +315,7 @@ def _get_instructions_bytes(code, varnames=None, names=None, constants=None,
329315
elif op in hasfree:
330316
argval, argrepr = _get_name_info(arg, cells)
331317
elif op in hasnargs:
332-
argrepr = "%d positional, %d keyword pair" % (code[i-2], code[i-1])
318+
argrepr = "%d positional, %d keyword pair" % (arg%256, arg//256)
333319
yield Instruction(opname[op], op,
334320
arg, argval, argrepr,
335321
offset, starts_line, is_jump_target)
@@ -365,26 +351,37 @@ def _disassemble_str(source, *, file=None):
365351

366352
disco = disassemble # XXX For backwards compatibility
367353

368-
def findlabels(code):
369-
"""Detect all offsets in a byte code which are jump targets.
370-
371-
Return the list of offsets.
372-
373-
"""
374-
labels = []
354+
def _unpack_opargs(code):
375355
# enumerate() is not an option, since we sometimes process
376356
# multiple elements on a single pass through the loop
357+
extended_arg = 0
377358
n = len(code)
378359
i = 0
379360
while i < n:
380361
op = code[i]
362+
offset = i
381363
i = i+1
364+
arg = None
382365
if op >= HAVE_ARGUMENT:
383-
arg = code[i] + code[i+1]*256
366+
arg = code[i] + code[i+1]*256 + extended_arg
367+
extended_arg = 0
384368
i = i+2
369+
if op == EXTENDED_ARG:
370+
extended_arg = arg*65536
371+
yield (offset, op, arg)
372+
373+
def findlabels(code):
374+
"""Detect all offsets in a byte code which are jump targets.
375+
376+
Return the list of offsets.
377+
378+
"""
379+
labels = []
380+
for offset, op, arg in _unpack_opargs(code):
381+
if arg is not None:
385382
label = -1
386383
if op in hasjrel:
387-
label = i+arg
384+
label = offset + 3 + arg
388385
elif op in hasjabs:
389386
label = arg
390387
if label >= 0:

Lib/modulefinder.py

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

16-
# XXX Clean up once str8's cstor matches bytes.
17-
LOAD_CONST = bytes([dis.opmap['LOAD_CONST']])
18-
IMPORT_NAME = bytes([dis.opmap['IMPORT_NAME']])
19-
STORE_NAME = bytes([dis.opmap['STORE_NAME']])
20-
STORE_GLOBAL = bytes([dis.opmap['STORE_GLOBAL']])
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']
2120
STORE_OPS = STORE_NAME, STORE_GLOBAL
22-
HAVE_ARGUMENT = bytes([dis.HAVE_ARGUMENT])
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
@@ -268,6 +268,8 @@ Core and Builtins
268268
Library
269269
-------
270270

271+
- Issue #26881: The modulefinder module now supports extended opcode arguments.
272+
271273
- Issue #23815: Fixed crashes related to directly created instances of types in
272274
_tkinter and curses.panel modules.
273275

@@ -277,6 +279,8 @@ Library
277279
- Issue #26873: xmlrpc now raises ResponseError on unsupported type tags
278280
instead of silently return incorrect result.
279281

282+
- Issue #26881: modulefinder now works with bytecode with extended args.
283+
280284
- Issue #26915: The __contains__ methods in the collections ABCs now check
281285
for identity before checking equality. This better matches the behavior
282286
of the concrete classes, allows sensible handling of NaNs, and makes it

0 commit comments

Comments
 (0)