| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -284,31 +284,17 @@ def _get_instructions_bytes(code, varnames=None, names=None, constants=None, | |||
| 284 | 284 | ||
| 285 | 285 | """ | |
| 286 | 286 | labels = findlabels(code) | |
| 287 | - extended_arg = 0 | ||
| 288 | 287 | starts_line = None | |
| 289 | 288 | 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): | ||
| 297 | 290 | if linestarts is not None: | |
| 298 | - starts_line = linestarts.get(i, None) | ||
| 291 | + starts_line = linestarts.get(offset, None) | ||
| 299 | 292 | if starts_line is not None: | |
| 300 | 293 | 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 | ||
| 304 | 295 | argval = None | |
| 305 | 296 | 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: | ||
| 312 | 298 | # Set argval to the dereferenced value of the argument when | |
| 313 | 299 | # availabe, and argrepr to the string representation of argval. | |
| 314 | 300 | # _disassemble_bytes needs the string repr of the | |
@@ -319,7 +305,7 @@ def _get_instructions_bytes(code, varnames=None, names=None, constants=None, | |||
| 319 | 305 | elif op in hasname: | |
| 320 | 306 | argval, argrepr = _get_name_info(arg, names) | |
| 321 | 307 | elif op in hasjrel: | |
| 322 | - argval = i + arg | ||
| 308 | + argval = offset + 3 + arg | ||
| 323 | 309 | argrepr = "to " + repr(argval) | |
| 324 | 310 | elif op in haslocal: | |
| 325 | 311 | argval, argrepr = _get_name_info(arg, varnames) | |
@@ -329,7 +315,7 @@ def _get_instructions_bytes(code, varnames=None, names=None, constants=None, | |||
| 329 | 315 | elif op in hasfree: | |
| 330 | 316 | argval, argrepr = _get_name_info(arg, cells) | |
| 331 | 317 | 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) | ||
| 333 | 319 | yield Instruction(opname[op], op, | |
| 334 | 320 | arg, argval, argrepr, | |
| 335 | 321 | offset, starts_line, is_jump_target) | |
@@ -365,26 +351,37 @@ def _disassemble_str(source, *, file=None): | |||
| 365 | 351 | ||
| 366 | 352 | disco = disassemble # XXX For backwards compatibility | |
| 367 | 353 | ||
| 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): | ||
| 375 | 355 | # enumerate() is not an option, since we sometimes process | |
| 376 | 356 | # multiple elements on a single pass through the loop | |
| 357 | + extended_arg = 0 | ||
| 377 | 358 | n = len(code) | |
| 378 | 359 | i = 0 | |
| 379 | 360 | while i < n: | |
| 380 | 361 | op = code[i] | |
| 362 | + offset = i | ||
| 381 | 363 | i = i+1 | |
| 364 | + arg = None | ||
| 382 | 365 | 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 | ||
| 384 | 368 | 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: | ||
| 385 | 382 | label = -1 | |
| 386 | 383 | if op in hasjrel: | |
| 387 | - label = i+arg | ||
| 384 | + label = offset + 3 + arg | ||
| 388 | 385 | elif op in hasjabs: | |
| 389 | 386 | label = arg | |
| 390 | 387 | if label >= 0: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -13,13 +13,12 @@ | |||
| 13 | 13 | warnings.simplefilter('ignore', DeprecationWarning) | |
| 14 | 14 | import imp | |
| 15 | 15 | ||
| 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'] | ||
| 21 | 20 | STORE_OPS = STORE_NAME, STORE_GLOBAL | |
| 22 | - HAVE_ARGUMENT = bytes([dis.HAVE_ARGUMENT]) | ||
| 21 | + EXTENDED_ARG = dis.EXTENDED_ARG | ||
| 23 | 22 | ||
| 24 | 23 | # Modulefinder does a good job at simulating Python's, but it can not | |
| 25 | 24 | # handle __path__ modifications packages make at runtime. Therefore there | |
@@ -337,38 +336,30 @@ def _safe_import_hook(self, name, caller, fromlist, level=-1): | |||
| 337 | 336 | fullname = name + "." + sub | |
| 338 | 337 | self._add_badmodule(fullname, caller) | |
| 339 | 338 | ||
| 340 | - def scan_opcodes_25(self, co, | ||
| 341 | - unpack = struct.unpack): | ||
| 339 | + def scan_opcodes(self, co): | ||
| 342 | 340 | # Scan the code, and yield 'interesting' opcode combinations | |
| 343 | - # Python 2.5 version (has absolute and relative imports) | ||
| 344 | 341 | code = co.co_code | |
| 345 | 342 | names = co.co_names | |
| 346 | 343 | 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: | ||
| 352 | 348 | yield "store", (names[oparg],) | |
| 353 | - code = code[3:] | ||
| 354 | 349 | 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]] | ||
| 358 | 354 | if level == 0: # absolute import | |
| 359 | - yield "absolute_import", (consts[oparg_2], names[oparg_3]) | ||
| 355 | + yield "absolute_import", (fromlist, names[oparg]) | ||
| 360 | 356 | 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]) | ||
| 363 | 358 | continue | |
| 364 | - if c >= HAVE_ARGUMENT: | ||
| 365 | - code = code[3:] | ||
| 366 | - else: | ||
| 367 | - code = code[1:] | ||
| 368 | 359 | ||
| 369 | 360 | def scan_code(self, co, m): | |
| 370 | 361 | code = co.co_code | |
| 371 | - scanner = self.scan_opcodes_25 | ||
| 362 | + scanner = self.scan_opcodes | ||
| 372 | 363 | for what, args in scanner(co): | |
| 373 | 364 | if what == "store": | |
| 374 | 365 | name, = args | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -319,6 +319,19 @@ def test_replace_paths(self): | |||
| 319 | 319 | expected = "co_filename %r changed to %r" % (old_path, new_path) | |
| 320 | 320 | self.assertIn(expected, output) | |
| 321 | 321 | ||
| 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 | + | ||
| 322 | 335 | ||
| 323 | 336 | if __name__ == "__main__": | |
| 324 | 337 | unittest.main() | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -268,6 +268,8 @@ Core and Builtins | |||
| 268 | 268 | Library | |
| 269 | 269 | ------- | |
| 270 | 270 | ||
| 271 | + - Issue #26881: The modulefinder module now supports extended opcode arguments. | ||
| 272 | + | ||
| 271 | 273 | - Issue #23815: Fixed crashes related to directly created instances of types in | |
| 272 | 274 | _tkinter and curses.panel modules. | |
| 273 | 275 | ||
@@ -277,6 +279,8 @@ Library | |||
| 277 | 279 | - Issue #26873: xmlrpc now raises ResponseError on unsupported type tags | |
| 278 | 280 | instead of silently return incorrect result. | |
| 279 | 281 | ||
| 282 | + - Issue #26881: modulefinder now works with bytecode with extended args. | ||
| 283 | + | ||
| 280 | 284 | - Issue #26915: The __contains__ methods in the collections ABCs now check | |
| 281 | 285 | for identity before checking equality. This better matches the behavior | |
| 282 | 286 | of the concrete classes, allows sensible handling of NaNs, and makes it | |
| Back | FazBrowse Home | New Git URL |
0 commit comments