Repository URL to install this package:
|
Version:
2022.2.8 ▾
|
"""Opcode definitions."""
# We define all-uppercase classes, to match their opcode names:
# pylint: disable=invalid-name
HAS_CONST = 1 # references the constant table
HAS_NAME = 2 # references the name table
HAS_JREL = 4 # relative jump
HAS_JABS = 8 # absolute jump
HAS_JUNKNOWN = 16 # jumps to unknown location
HAS_LOCAL = 32 # references the varnames table
HAS_FREE = 64 # references "free variable" cells
HAS_NARGS = 128 # stores number of args + kwargs
HAS_ARGUMENT = 256 # all opcodes >= 90
NO_NEXT = 512 # doesn't execute the following opcode
STORE_JUMP = 1024 # only stores a jump, doesn't actually execute it
PUSHES_BLOCK = 2048 # starts a block (while, try, finally, with, etc.)
POPS_BLOCK = 4096 # ends a block
class Opcode:
"""An opcode without arguments."""
__slots__ = ("line", "index", "prev", "next", "target", "block_target",
"code", "annotation", "folded")
FLAGS = 0
def __init__(self, index, line):
self.index = index
self.line = line
self.target = None
self.code = None # If we have a CodeType or OrderedCode parent
self.annotation = None
self.folded = None # elided by constant folding
def at_line(self, line):
"""Return a new opcode similar to this one but with a different line."""
# Ignore the optional slots (prev, next, block_target).
op = Opcode(self.index, line)
op.target = self.target
op.code = self.code
return op
def basic_str(self):
"""Helper function for the various __str__ formats."""
folded = "<<<<" if self.folded else ""
return "%d: %d: %s %s" % (
self.line, self.index, self.__class__.__name__, folded)
def __str__(self):
if self.annotation:
return "%s # type: %s" % (self.basic_str(), self.annotation)
else:
return self.basic_str()
def __repr__(self):
return self.__class__.__name__
@property
def name(self):
return self.__class__.__name__
@classmethod
def has_const(cls):
return bool(cls.FLAGS & HAS_CONST)
@classmethod
def has_name(cls):
return bool(cls.FLAGS & HAS_NAME)
@classmethod
def has_jrel(cls):
return bool(cls.FLAGS & HAS_JREL)
@classmethod
def has_jabs(cls):
return bool(cls.FLAGS & HAS_JABS)
@classmethod
def has_junknown(cls):
return bool(cls.FLAGS & HAS_JUNKNOWN)
@classmethod
def has_jump(cls):
return bool(cls.FLAGS & (HAS_JREL | HAS_JABS | HAS_JUNKNOWN))
@classmethod
def has_local(cls):
return bool(cls.FLAGS & HAS_LOCAL)
@classmethod
def has_free(cls):
return bool(cls.FLAGS & HAS_FREE)
@classmethod
def has_nargs(cls):
return bool(cls.FLAGS & HAS_NARGS)
@classmethod
def has_argument(cls):
return bool(cls.FLAGS & HAS_ARGUMENT)
@classmethod
def no_next(cls):
return bool(cls.FLAGS & NO_NEXT)
@classmethod
def carry_on_to_next(cls):
return not cls.FLAGS & NO_NEXT
@classmethod
def store_jump(cls):
return bool(cls.FLAGS & STORE_JUMP)
@classmethod
def does_jump(cls):
return cls.has_jump() and not cls.store_jump()
@classmethod
def pushes_block(cls):
return bool(cls.FLAGS & PUSHES_BLOCK)
@classmethod
def pops_block(cls):
return bool(cls.FLAGS & POPS_BLOCK)
@classmethod
def has_arg(cls):
return False
class OpcodeWithArg(Opcode):
"""An opcode with one argument."""
__slots__ = ("arg", "pretty_arg")
def __init__(self, index, line, arg, pretty_arg):
super().__init__(index, line)
self.arg = arg
self.pretty_arg = pretty_arg
def __str__(self):
out = "%s %s" % (self.basic_str(), self.pretty_arg)
if self.annotation:
return "%s # type: %s" % (out, self.annotation)
else:
return out
@classmethod
def has_arg(cls):
return True
class LOAD_FOLDED_CONST(OpcodeWithArg): # A fake opcode used internally
FLAGS = HAS_ARGUMENT
__slots__ = ()
def __str__(self):
return self.basic_str() + " " + str(self.arg.value)
class POP_TOP(Opcode):
__slots__ = ()
class ROT_TWO(Opcode):
__slots__ = ()
class ROT_THREE(Opcode):
__slots__ = ()
class DUP_TOP(Opcode):
__slots__ = ()
class ROT_FOUR(Opcode):
__slots__ = ()
class DUP_TOP_TWO(Opcode):
__slots__ = ()
class NOP(Opcode):
__slots__ = ()
class UNARY_POSITIVE(Opcode):
__slots__ = ()
class UNARY_NEGATIVE(Opcode):
__slots__ = ()
class UNARY_NOT(Opcode):
__slots__ = ()
class UNARY_INVERT(Opcode):
__slots__ = ()
class BINARY_MATRIX_MULTIPLY(Opcode):
__slots__ = ()
class INPLACE_MATRIX_MULTIPLY(Opcode):
__slots__ = ()
class BINARY_POWER(Opcode):
__slots__ = ()
class BINARY_MULTIPLY(Opcode):
__slots__ = ()
class BINARY_MODULO(Opcode):
__slots__ = ()
class BINARY_ADD(Opcode):
__slots__ = ()
class BINARY_SUBTRACT(Opcode):
__slots__ = ()
class BINARY_SUBSCR(Opcode):
__slots__ = ()
class BINARY_FLOOR_DIVIDE(Opcode):
__slots__ = ()
class BINARY_TRUE_DIVIDE(Opcode):
__slots__ = ()
class INPLACE_FLOOR_DIVIDE(Opcode):
__slots__ = ()
class INPLACE_TRUE_DIVIDE(Opcode):
__slots__ = ()
class GET_AITER(Opcode):
__slots__ = ()
class GET_ANEXT(Opcode):
__slots__ = ()
class BEFORE_ASYNC_WITH(Opcode):
__slots__ = ()
class BEGIN_FINALLY(Opcode):
__slots__ = ()
class END_ASYNC_FOR(Opcode):
# Even though dis documentation says that END_ASYNC_FOR may reraise an
# exception, we do not include NO_NEXT in the flags because doing so would
# cause the return statement for an async method to be skipped, leading to
# an incorrect return type.
# See tests/test_stdlib2:StdlibTestsFeatures.test_async_iter and
# tests/test_coroutine:GeneratorFeatureTest.test_async_for_pyi for tests
# that fail if we add NO_NEXT.
FLAGS = HAS_JUNKNOWN
__slots__ = ()
class INPLACE_ADD(Opcode):
__slots__ = ()
class INPLACE_SUBTRACT(Opcode):
__slots__ = ()
class INPLACE_MULTIPLY(Opcode):
__slots__ = ()
class INPLACE_MODULO(Opcode):
__slots__ = ()
class STORE_SUBSCR(Opcode):
__slots__ = ()
class DELETE_SUBSCR(Opcode):
__slots__ = ()
class BINARY_LSHIFT(Opcode):
__slots__ = ()
class BINARY_RSHIFT(Opcode):
__slots__ = ()
class BINARY_AND(Opcode):
__slots__ = ()
class BINARY_XOR(Opcode):
__slots__ = ()
class BINARY_OR(Opcode):
__slots__ = ()
class INPLACE_POWER(Opcode):
__slots__ = ()
class GET_ITER(Opcode):
__slots__ = ()
class GET_YIELD_FROM_ITER(Opcode):
__slots__ = ()
class PRINT_EXPR(Opcode):
__slots__ = ()
class LOAD_BUILD_CLASS(Opcode):
__slots__ = ()
class YIELD_FROM(Opcode):
FLAGS = HAS_JUNKNOWN
__slots__ = ()
class GET_AWAITABLE(Opcode):
__slots__ = ()
class INPLACE_LSHIFT(Opcode):
__slots__ = ()
class INPLACE_RSHIFT(Opcode):
__slots__ = ()
class INPLACE_AND(Opcode):
__slots__ = ()
class INPLACE_XOR(Opcode):
__slots__ = ()
class INPLACE_OR(Opcode):
__slots__ = ()
class BREAK_LOOP(Opcode):
FLAGS = HAS_JUNKNOWN | NO_NEXT
__slots__ = ()
class WITH_CLEANUP_START(Opcode):
FLAGS = HAS_JUNKNOWN # might call __exit__
__slots__ = ()
class WITH_CLEANUP_FINISH(Opcode):
__slots__ = ()
class RETURN_VALUE(Opcode):
FLAGS = HAS_JUNKNOWN | NO_NEXT
__slots__ = ()
class IMPORT_STAR(Opcode):
__slots__ = ()
class SETUP_ANNOTATIONS(Opcode):
__slots__ = ()
class YIELD_VALUE(Opcode):
FLAGS = HAS_JUNKNOWN
__slots__ = ()
class POP_BLOCK(Opcode):
FLAGS = POPS_BLOCK
__slots__ = ()
class END_FINALLY(Opcode):
FLAGS = HAS_JUNKNOWN # might re-raise an exception
__slots__ = ()
class POP_EXCEPT(Opcode):
__slots__ = ()
class STORE_NAME(OpcodeWithArg): # Indexes into name list
FLAGS = HAS_NAME|HAS_ARGUMENT
__slots__ = ()
class DELETE_NAME(OpcodeWithArg): # Indexes into name list
FLAGS = HAS_NAME|HAS_ARGUMENT
__slots__ = ()
class UNPACK_SEQUENCE(OpcodeWithArg): # Arg: Number of tuple items
FLAGS = HAS_ARGUMENT
__slots__ = ()
class FOR_ITER(OpcodeWithArg):
FLAGS = HAS_JREL|HAS_ARGUMENT
__slots__ = ()
class LIST_APPEND(OpcodeWithArg):
FLAGS = HAS_ARGUMENT
__slots__ = ()
class UNPACK_EX(OpcodeWithArg):
FLAGS = HAS_ARGUMENT
__slots__ = ()
class STORE_ATTR(OpcodeWithArg): # Indexes into name list
FLAGS = HAS_NAME|HAS_ARGUMENT
__slots__ = ()
class DELETE_ATTR(OpcodeWithArg): # Indexes into name list
FLAGS = HAS_NAME|HAS_ARGUMENT
__slots__ = ()
class STORE_GLOBAL(OpcodeWithArg): # Indexes into name list
FLAGS = HAS_NAME|HAS_ARGUMENT
__slots__ = ()
class DELETE_GLOBAL(OpcodeWithArg): # Indexes into name list
FLAGS = HAS_NAME|HAS_ARGUMENT
__slots__ = ()
class LOAD_CONST(OpcodeWithArg): # Arg: Index in const list
FLAGS = HAS_ARGUMENT|HAS_CONST
__slots__ = ()
class LOAD_NAME(OpcodeWithArg): # Arg: Index in name list
FLAGS = HAS_NAME|HAS_ARGUMENT
__slots__ = ()
class BUILD_TUPLE(OpcodeWithArg): # Arg: Number of tuple items
FLAGS = HAS_ARGUMENT
__slots__ = ()
class BUILD_LIST(OpcodeWithArg): # Arg: Number of list items
FLAGS = HAS_ARGUMENT
__slots__ = ()
class BUILD_SET(OpcodeWithArg): # Arg: Number of set items
FLAGS = HAS_ARGUMENT
__slots__ = ()
class BUILD_MAP(OpcodeWithArg): # Arg: Number of dict entries (up to 255)
FLAGS = HAS_ARGUMENT
__slots__ = ()
class LOAD_ATTR(OpcodeWithArg): # Arg: Index in name list
FLAGS = HAS_NAME|HAS_ARGUMENT
__slots__ = ()
class COMPARE_OP(OpcodeWithArg): # Arg: Comparison operator
FLAGS = HAS_ARGUMENT
__slots__ = ()
class IMPORT_NAME(OpcodeWithArg): # Arg: Index in name list
FLAGS = HAS_NAME|HAS_ARGUMENT|HAS_JUNKNOWN
__slots__ = ()
class IMPORT_FROM(OpcodeWithArg): # Arg: Index in name list
FLAGS = HAS_NAME|HAS_ARGUMENT
__slots__ = ()
class JUMP_FORWARD(OpcodeWithArg):
FLAGS = HAS_JREL|HAS_ARGUMENT|NO_NEXT
__slots__ = ()
class JUMP_IF_FALSE_OR_POP(OpcodeWithArg):
FLAGS = HAS_JABS|HAS_ARGUMENT
__slots__ = ()
class JUMP_IF_TRUE_OR_POP(OpcodeWithArg):
FLAGS = HAS_JABS|HAS_ARGUMENT
__slots__ = ()
class JUMP_ABSOLUTE(OpcodeWithArg):
FLAGS = HAS_JABS|HAS_ARGUMENT|NO_NEXT
__slots__ = ()
class POP_JUMP_IF_FALSE(OpcodeWithArg):
FLAGS = HAS_JABS|HAS_ARGUMENT
__slots__ = ()
class POP_JUMP_IF_TRUE(OpcodeWithArg):
FLAGS = HAS_JABS|HAS_ARGUMENT
__slots__ = ()
class LOAD_GLOBAL(OpcodeWithArg): # Indexes into name list
FLAGS = HAS_NAME|HAS_ARGUMENT
__slots__ = ()
class CONTINUE_LOOP(OpcodeWithArg): # Acts as jump
FLAGS = HAS_JABS|HAS_ARGUMENT|NO_NEXT
__slots__ = ()
class SETUP_LOOP(OpcodeWithArg):
FLAGS = HAS_JREL|HAS_ARGUMENT|STORE_JUMP|PUSHES_BLOCK
__slots__ = ()
class SETUP_EXCEPT(OpcodeWithArg):
FLAGS = HAS_JREL|HAS_ARGUMENT|STORE_JUMP|PUSHES_BLOCK
__slots__ = ()
class SETUP_FINALLY(OpcodeWithArg):
FLAGS = HAS_JREL|HAS_ARGUMENT|STORE_JUMP|PUSHES_BLOCK
__slots__ = ()
class LOAD_FAST(OpcodeWithArg): # Loads local variable number
FLAGS = HAS_LOCAL|HAS_ARGUMENT
__slots__ = ()
class STORE_FAST(OpcodeWithArg): # Stores local variable number
FLAGS = HAS_LOCAL|HAS_ARGUMENT
__slots__ = ()
class DELETE_FAST(OpcodeWithArg): # Deletes local variable number
FLAGS = HAS_LOCAL|HAS_ARGUMENT
__slots__ = ()
class STORE_ANNOTATION(OpcodeWithArg):
FLAGS = HAS_NAME|HAS_ARGUMENT
__slots__ = ()
class RAISE_VARARGS(OpcodeWithArg): # Arg: Number of raise args (1, 2, or 3)
FLAGS = HAS_ARGUMENT|HAS_JUNKNOWN|NO_NEXT
__slots__ = ()
class CALL_FUNCTION(OpcodeWithArg): # Arg: #args + (#kwargs << 8)
FLAGS = HAS_NARGS|HAS_ARGUMENT|HAS_JUNKNOWN
__slots__ = ()
class MAKE_FUNCTION(OpcodeWithArg): # Arg: Number of args with default values
FLAGS = HAS_ARGUMENT
__slots__ = ()
class BUILD_SLICE(OpcodeWithArg): # Arg: Number of items
FLAGS = HAS_ARGUMENT
__slots__ = ()
class MAKE_CLOSURE(OpcodeWithArg):
FLAGS = HAS_ARGUMENT
__slots__ = ()
class LOAD_CLOSURE(OpcodeWithArg):
FLAGS = HAS_FREE|HAS_ARGUMENT
__slots__ = ()
class LOAD_DEREF(OpcodeWithArg):
FLAGS = HAS_FREE|HAS_ARGUMENT
__slots__ = ()
class STORE_DEREF(OpcodeWithArg):
FLAGS = HAS_FREE|HAS_ARGUMENT
__slots__ = ()
class DELETE_DEREF(OpcodeWithArg):
FLAGS = HAS_FREE|HAS_ARGUMENT
__slots__ = ()
class CALL_FUNCTION_VAR(OpcodeWithArg): # Arg: #args + (#kwargs << 8)
FLAGS = HAS_NARGS|HAS_ARGUMENT|HAS_JUNKNOWN
__slots__ = ()
class CALL_FUNCTION_KW(OpcodeWithArg): # Arg: #args + (#kwargs << 8)
FLAGS = HAS_NARGS|HAS_ARGUMENT|HAS_JUNKNOWN
__slots__ = ()
class CALL_FUNCTION_VAR_KW(OpcodeWithArg): # Arg: #args + (#kwargs << 8)
FLAGS = HAS_NARGS|HAS_ARGUMENT|HAS_JUNKNOWN
__slots__ = ()
class CALL_FUNCTION_EX(OpcodeWithArg): # Arg: flags
FLAGS = HAS_ARGUMENT
__slots__ = ()
class SETUP_WITH(OpcodeWithArg):
FLAGS = HAS_JREL|HAS_ARGUMENT|STORE_JUMP|PUSHES_BLOCK
__slots__ = ()
class EXTENDED_ARG(OpcodeWithArg):
FLAGS = HAS_ARGUMENT
__slots__ = ()
class SET_ADD(OpcodeWithArg):
FLAGS = HAS_ARGUMENT
__slots__ = ()
class MAP_ADD(OpcodeWithArg):
FLAGS = HAS_ARGUMENT
__slots__ = ()
class LOAD_CLASSDEREF(OpcodeWithArg):
FLAGS = HAS_FREE|HAS_ARGUMENT
__slots__ = ()
class BUILD_LIST_UNPACK(OpcodeWithArg): # Arg: Number of items
FLAGS = HAS_ARGUMENT
__slots__ = ()
class BUILD_MAP_UNPACK(OpcodeWithArg): # Arg: Number of items
FLAGS = HAS_ARGUMENT
__slots__ = ()
class BUILD_MAP_UNPACK_WITH_CALL(OpcodeWithArg): # Arg: Number of items
FLAGS = HAS_ARGUMENT
__slots__ = ()
class BUILD_TUPLE_UNPACK(OpcodeWithArg): # Arg: Number of items
FLAGS = HAS_ARGUMENT
__slots__ = ()
class BUILD_SET_UNPACK(OpcodeWithArg): # Arg: Number of items
FLAGS = HAS_ARGUMENT
__slots__ = ()
class SETUP_ASYNC_WITH(OpcodeWithArg):
FLAGS = HAS_JREL|HAS_ARGUMENT|STORE_JUMP|PUSHES_BLOCK
__slots__ = ()
class FORMAT_VALUE(OpcodeWithArg): # Arg: Flags
FLAGS = HAS_ARGUMENT
__slots__ = ()
class BUILD_CONST_KEY_MAP(OpcodeWithArg): # Arg: Number of items
FLAGS = HAS_ARGUMENT
__slots__ = ()
class BUILD_STRING(OpcodeWithArg): # Arg: Number of items
FLAGS = HAS_ARGUMENT
__slots__ = ()
class BUILD_TUPLE_UNPACK_WITH_CALL(OpcodeWithArg): # Arg: Number of items
FLAGS = HAS_ARGUMENT
__slots__ = ()
class LOAD_METHOD(OpcodeWithArg): # Arg: Index in name list
FLAGS = HAS_NAME|HAS_ARGUMENT
__slots__ = ()
class CALL_METHOD(OpcodeWithArg): # Arg: #args
FLAGS = HAS_NARGS|HAS_ARGUMENT|HAS_JUNKNOWN
__slots__ = ()
class CALL_FINALLY(OpcodeWithArg): # Arg: Jump offset to finally block
FLAGS = HAS_JREL | HAS_ARGUMENT
__slots__ = ()
class POP_FINALLY(OpcodeWithArg):
# might re-raise an exception or jump to a finally
FLAGS = HAS_ARGUMENT | HAS_JUNKNOWN
__slots__ = ()
class RERAISE(Opcode):
__slots__ = ()
class WITH_EXCEPT_START(Opcode):
__slots__ = ()
class LOAD_ASSERTION_ERROR(Opcode):
__slots__ = ()
class LIST_TO_TUPLE(Opcode):
__slots__ = ()
class IS_OP(OpcodeWithArg):
FLAGS = HAS_ARGUMENT
__slots__ = ()
class CONTAINS_OP(OpcodeWithArg):
FLAGS = HAS_ARGUMENT
__slots__ = ()
class JUMP_IF_NOT_EXC_MATCH(OpcodeWithArg):
FLAGS = HAS_ARGUMENT | HAS_JABS
__slots__ = ()
class LIST_EXTEND(OpcodeWithArg):
FLAGS = HAS_ARGUMENT
__slots__ = ()
class SET_UPDATE(OpcodeWithArg):
FLAGS = HAS_ARGUMENT
__slots__ = ()
class DICT_MERGE(OpcodeWithArg):
FLAGS = HAS_ARGUMENT
__slots__ = ()
class DICT_UPDATE(OpcodeWithArg):
FLAGS = HAS_ARGUMENT
__slots__ = ()
class GET_LEN(Opcode):
__slots__ = ()
class MATCH_MAPPING(Opcode):
__slots__ = ()
class MATCH_SEQUENCE(Opcode):
__slots__ = ()
class MATCH_KEYS(Opcode):
__slots__ = ()
class COPY_DICT_WITHOUT_KEYS(Opcode):
__slots__ = ()
class ROT_N(OpcodeWithArg):
FLAGS = HAS_ARGUMENT
__slots__ = ()
class GEN_START(OpcodeWithArg):
FLAGS = HAS_ARGUMENT
__slots__ = ()
class MATCH_CLASS(OpcodeWithArg):
FLAGS = HAS_ARGUMENT
__slots__ = ()
def _overlay_mapping(mapping, new_entries):
ret = mapping.copy()
ret.update(new_entries)
return dict((k, v) for k, v in ret.items() if v is not None)
python_3_7_mapping = {
1: POP_TOP,
2: ROT_TWO,
3: ROT_THREE,
4: DUP_TOP,
5: DUP_TOP_TWO,
9: NOP,
10: UNARY_POSITIVE,
11: UNARY_NEGATIVE,
12: UNARY_NOT,
15: UNARY_INVERT,
16: BINARY_MATRIX_MULTIPLY,
17: INPLACE_MATRIX_MULTIPLY,
19: BINARY_POWER,
20: BINARY_MULTIPLY,
22: BINARY_MODULO,
23: BINARY_ADD,
24: BINARY_SUBTRACT,
25: BINARY_SUBSCR,
26: BINARY_FLOOR_DIVIDE,
27: BINARY_TRUE_DIVIDE,
28: INPLACE_FLOOR_DIVIDE,
29: INPLACE_TRUE_DIVIDE,
50: GET_AITER,
51: GET_ANEXT,
52: BEFORE_ASYNC_WITH,
55: INPLACE_ADD,
56: INPLACE_SUBTRACT,
57: INPLACE_MULTIPLY,
59: INPLACE_MODULO,
60: STORE_SUBSCR,
61: DELETE_SUBSCR,
62: BINARY_LSHIFT,
63: BINARY_RSHIFT,
64: BINARY_AND,
65: BINARY_XOR,
66: BINARY_OR,
67: INPLACE_POWER,
68: GET_ITER,
69: GET_YIELD_FROM_ITER,
70: PRINT_EXPR,
71: LOAD_BUILD_CLASS,
72: YIELD_FROM,
73: GET_AWAITABLE,
75: INPLACE_LSHIFT,
76: INPLACE_RSHIFT,
77: INPLACE_AND,
78: INPLACE_XOR,
79: INPLACE_OR,
80: BREAK_LOOP,
81: WITH_CLEANUP_START,
82: WITH_CLEANUP_FINISH,
83: RETURN_VALUE,
84: IMPORT_STAR,
85: SETUP_ANNOTATIONS,
86: YIELD_VALUE,
87: POP_BLOCK,
88: END_FINALLY,
89: POP_EXCEPT,
90: STORE_NAME,
91: DELETE_NAME,
92: UNPACK_SEQUENCE,
93: FOR_ITER,
94: UNPACK_EX,
95: STORE_ATTR,
96: DELETE_ATTR,
97: STORE_GLOBAL,
98: DELETE_GLOBAL,
100: LOAD_CONST,
101: LOAD_NAME,
102: BUILD_TUPLE,
103: BUILD_LIST,
104: BUILD_SET,
105: BUILD_MAP,
106: LOAD_ATTR,
107: COMPARE_OP,
108: IMPORT_NAME,
109: IMPORT_FROM,
110: JUMP_FORWARD,
111: JUMP_IF_FALSE_OR_POP,
112: JUMP_IF_TRUE_OR_POP,
113: JUMP_ABSOLUTE,
114: POP_JUMP_IF_FALSE,
115: POP_JUMP_IF_TRUE,
116: LOAD_GLOBAL,
119: CONTINUE_LOOP,
120: SETUP_LOOP,
121: SETUP_EXCEPT,
122: SETUP_FINALLY,
124: LOAD_FAST,
125: STORE_FAST,
126: DELETE_FAST,
130: RAISE_VARARGS,
131: CALL_FUNCTION,
132: MAKE_FUNCTION,
133: BUILD_SLICE,
134: MAKE_CLOSURE,
135: LOAD_CLOSURE,
136: LOAD_DEREF,
137: STORE_DEREF,
138: DELETE_DEREF,
141: CALL_FUNCTION_KW,
142: CALL_FUNCTION_EX,
143: SETUP_WITH,
144: EXTENDED_ARG,
145: LIST_APPEND,
146: SET_ADD,
147: MAP_ADD,
148: LOAD_CLASSDEREF,
149: BUILD_LIST_UNPACK,
150: BUILD_MAP_UNPACK,
151: BUILD_MAP_UNPACK_WITH_CALL,
152: BUILD_TUPLE_UNPACK,
153: BUILD_SET_UNPACK,
154: SETUP_ASYNC_WITH,
155: FORMAT_VALUE,
156: BUILD_CONST_KEY_MAP,
157: BUILD_STRING,
158: BUILD_TUPLE_UNPACK_WITH_CALL,
160: LOAD_METHOD,
161: CALL_METHOD,
}
python_3_8_mapping = _overlay_mapping(python_3_7_mapping, {
6: ROT_FOUR, # ROT_FOUR returns under a different, cooler id!
53: BEGIN_FINALLY,
54: END_ASYNC_FOR,
80: None, # BREAK_LOOP was removed in 3.8
119: None, # CONTINUE_LOOP was removed in 3.8
120: None, # SETUP_LOOP was removed in 3.8
121: None, # SETUP_EXCEPT was removed in 3.8
162: CALL_FINALLY,
163: POP_FINALLY,
})
python_3_9_mapping = _overlay_mapping(python_3_8_mapping, {
48: RERAISE,
49: WITH_EXCEPT_START,
53: None, # was BEGIN_FINALLY in 3.8
74: LOAD_ASSERTION_ERROR,
81: None, # was WITH_CLEANUP_START in 3.8
82: LIST_TO_TUPLE, # was WITH_CLEANUP_FINISH in 3.8
88: None, # was END_FINALLY in 3.8
117: IS_OP,
118: CONTAINS_OP,
121: JUMP_IF_NOT_EXC_MATCH,
149: None, # was BUILD_LIST_UNPACK in 3.8
150: None, # was BUILD_MAP_UNPACK in 3.8
151: None, # was BUILD_MAP_UNPACK_WITH_CALL in 3.8
152: None, # was BUILD_TUPLE_UNPACK in 3.8
153: None, # was BUILD_SET_UNPACK in 3.8
158: None, # was BUILD_TUPLE_UNPACK_WITH_CALL in 3.8
162: LIST_EXTEND, # was CALL_FINALLY in 3.8
163: SET_UPDATE, # was POP_FINALLY in 3.8
164: DICT_MERGE,
165: DICT_UPDATE,
})
python_3_10_mapping = _overlay_mapping(python_3_9_mapping, {
30: GET_LEN,
31: MATCH_MAPPING,
32: MATCH_SEQUENCE,
33: MATCH_KEYS,
34: COPY_DICT_WITHOUT_KEYS,
99: ROT_N,
129: GEN_START,
152: MATCH_CLASS,
})
class _LineNumberTableParser:
"""State machine for decoding a Python line number array."""
def __init__(self, lnotab, firstlineno):
assert not len(lnotab) & 1 # lnotab always has an even number of elements
self.lnotab = lnotab
self.lineno = firstlineno
self.next_addr = self.lnotab[0] if self.lnotab else 0
self.pos = 0
def get(self, i):
"""Get the line number for the instruction at the given position.
This does NOT allow random access. Call with incremental numbers.
Args:
i: The byte position in the bytecode. i needs to stay constant or increase
between calls.
Returns:
The line number corresponding to the position at i.
"""
while i >= self.next_addr and self.pos < len(self.lnotab):
line_diff = self.lnotab[self.pos + 1]
# The Python docs have more details on this weird bit twiddling.
# https://github.com/python/cpython/blob/master/Objects/lnotab_notes.txt
# https://github.com/python/cpython/commit/f3914eb16d28ad9eb20fe5133d9aa83658bcc27f
if line_diff >= 0x80:
line_diff -= 0x100
self.lineno += line_diff
self.pos += 2
if self.pos < len(self.lnotab):
self.next_addr += self.lnotab[self.pos]
return self.lineno
def _prettyprint_arg(cls, oparg, co_consts, co_names,
co_varnames, cellvars_freevars):
"""Prettyprint `oparg`."""
if cls.has_jrel():
return oparg
elif co_consts and cls.has_const():
return repr(co_consts[oparg])
elif co_names and cls.has_name():
return co_names[oparg]
elif co_varnames and cls.has_local():
return co_varnames[oparg]
elif cellvars_freevars and cls.has_free():
return cellvars_freevars[oparg]
else:
return oparg
def _wordcode_reader(data, mapping):
"""Reads binary data from pyc files as wordcode.
Works with Python3.6 and above.
Arguments:
data: The block of binary pyc code
mapping: {opcode : class}
Yields:
(start position, end position, opcode class, oparg)
"""
assert isinstance(data, bytes)
extended_arg = 0
start = 0
for pos in range(0, len(data), 2):
opcode = data[pos]
cls = mapping[opcode]
if cls is EXTENDED_ARG:
oparg = data[pos+1] | extended_arg
extended_arg = oparg << 8
elif cls.FLAGS & HAS_ARGUMENT:
oparg = data[pos+1] | extended_arg
extended_arg = 0
else:
oparg = None
extended_arg = 0
# Don't yield EXTENDED_ARG; it is part of the next opcode.
if cls is not EXTENDED_ARG:
yield (start, pos + 2, cls, oparg)
start = pos + 2
def _dis(data, mapping,
co_varnames=None, co_names=None, co_consts=None, co_cellvars=None,
co_freevars=None, co_lnotab=None, co_firstlineno=None):
"""Disassemble a string into a list of Opcode instances."""
code = []
if co_lnotab:
lp = _LineNumberTableParser(co_lnotab, co_firstlineno)
else:
lp = None
offset_to_index = {}
if co_cellvars is not None and co_freevars is not None:
cellvars_freevars = co_cellvars + co_freevars
else:
cellvars_freevars = None
for pos, end_pos, cls, oparg in _wordcode_reader(data, mapping):
index = len(code)
offset_to_index[pos] = index
if lp:
line = lp.get(pos)
else:
# single line programs don't have co_lnotab
line = co_firstlineno
if oparg is not None:
if cls.has_jrel():
oparg += end_pos
pretty = _prettyprint_arg(cls, oparg, co_consts, co_names, co_varnames,
cellvars_freevars)
code.append(cls(index, line, oparg, pretty))
else:
code.append(cls(index, line))
# Map the target of jump instructions to the opcode they jump to, and fill
# in "next" and "prev" pointers
for i, op in enumerate(code):
if op.FLAGS & (HAS_JREL | HAS_JABS):
op.arg = op.pretty_arg = offset_to_index[op.arg]
op.target = code[op.arg]
op.prev = code[i - 1] if i > 0 else None
op.next = code[i + 1] if i < len(code) - 1 else None
return code
def dis(data, python_version, *args, **kwargs):
"""Set up version-specific arguments and call _dis()."""
major, minor = python_version[0], python_version[1]
assert major == 3
mapping = {
(3, 7): python_3_7_mapping,
(3, 8): python_3_8_mapping,
(3, 9): python_3_9_mapping,
(3, 10): python_3_10_mapping,
}[(major, minor)]
return _dis(data, mapping, *args, **kwargs)
def dis_code(code):
return dis(data=code.co_code,
python_version=code.python_version,
co_varnames=code.co_varnames,
co_names=code.co_names,
co_consts=code.co_consts,
co_cellvars=code.co_cellvars,
co_freevars=code.co_freevars,
co_lnotab=code.co_lnotab,
co_firstlineno=code.co_firstlineno)