Compare commits

...

1 Commits

Author SHA1 Message Date
Shahar Naveh
3a6fda4daf Update opcode from 3.13.7 (#6156)
* Update `opcode` from 3.13.7

* Base `_opcode`

* Add `test__opcode.py` from 3.13.7

* Impl `has_*` methods

* Add more methods

* Update `dis.py` from 3.13.7

* Update `support/bytecode_helper.py` from 3.13.7

* correct is_valid

* Patch failing tests

* Unpatch `support/__init__.py`

* clippy

* Make comments to doc

* impl `_varname_from_oparg` for code

* Unmark passing tests

* Revert changes to `dis`

* Mark failing tests
2025-10-05 11:14:33 +09:00
10 changed files with 911 additions and 444 deletions

343
Lib/_opcode_metadata.py vendored Normal file
View File

@@ -0,0 +1,343 @@
# This file is generated by Tools/cases_generator/py_metadata_generator.py
# from:
# Python/bytecodes.c
# Do not edit!
_specializations = {
"RESUME": [
"RESUME_CHECK",
],
"TO_BOOL": [
"TO_BOOL_ALWAYS_TRUE",
"TO_BOOL_BOOL",
"TO_BOOL_INT",
"TO_BOOL_LIST",
"TO_BOOL_NONE",
"TO_BOOL_STR",
],
"BINARY_OP": [
"BINARY_OP_MULTIPLY_INT",
"BINARY_OP_ADD_INT",
"BINARY_OP_SUBTRACT_INT",
"BINARY_OP_MULTIPLY_FLOAT",
"BINARY_OP_ADD_FLOAT",
"BINARY_OP_SUBTRACT_FLOAT",
"BINARY_OP_ADD_UNICODE",
"BINARY_OP_INPLACE_ADD_UNICODE",
],
"BINARY_SUBSCR": [
"BINARY_SUBSCR_DICT",
"BINARY_SUBSCR_GETITEM",
"BINARY_SUBSCR_LIST_INT",
"BINARY_SUBSCR_STR_INT",
"BINARY_SUBSCR_TUPLE_INT",
],
"STORE_SUBSCR": [
"STORE_SUBSCR_DICT",
"STORE_SUBSCR_LIST_INT",
],
"SEND": [
"SEND_GEN",
],
"UNPACK_SEQUENCE": [
"UNPACK_SEQUENCE_TWO_TUPLE",
"UNPACK_SEQUENCE_TUPLE",
"UNPACK_SEQUENCE_LIST",
],
"STORE_ATTR": [
"STORE_ATTR_INSTANCE_VALUE",
"STORE_ATTR_SLOT",
"STORE_ATTR_WITH_HINT",
],
"LOAD_GLOBAL": [
"LOAD_GLOBAL_MODULE",
"LOAD_GLOBAL_BUILTIN",
],
"LOAD_SUPER_ATTR": [
"LOAD_SUPER_ATTR_ATTR",
"LOAD_SUPER_ATTR_METHOD",
],
"LOAD_ATTR": [
"LOAD_ATTR_INSTANCE_VALUE",
"LOAD_ATTR_MODULE",
"LOAD_ATTR_WITH_HINT",
"LOAD_ATTR_SLOT",
"LOAD_ATTR_CLASS",
"LOAD_ATTR_PROPERTY",
"LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN",
"LOAD_ATTR_METHOD_WITH_VALUES",
"LOAD_ATTR_METHOD_NO_DICT",
"LOAD_ATTR_METHOD_LAZY_DICT",
"LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES",
"LOAD_ATTR_NONDESCRIPTOR_NO_DICT",
],
"COMPARE_OP": [
"COMPARE_OP_FLOAT",
"COMPARE_OP_INT",
"COMPARE_OP_STR",
],
"CONTAINS_OP": [
"CONTAINS_OP_SET",
"CONTAINS_OP_DICT",
],
"FOR_ITER": [
"FOR_ITER_LIST",
"FOR_ITER_TUPLE",
"FOR_ITER_RANGE",
"FOR_ITER_GEN",
],
"CALL": [
"CALL_BOUND_METHOD_EXACT_ARGS",
"CALL_PY_EXACT_ARGS",
"CALL_TYPE_1",
"CALL_STR_1",
"CALL_TUPLE_1",
"CALL_BUILTIN_CLASS",
"CALL_BUILTIN_O",
"CALL_BUILTIN_FAST",
"CALL_BUILTIN_FAST_WITH_KEYWORDS",
"CALL_LEN",
"CALL_ISINSTANCE",
"CALL_LIST_APPEND",
"CALL_METHOD_DESCRIPTOR_O",
"CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS",
"CALL_METHOD_DESCRIPTOR_NOARGS",
"CALL_METHOD_DESCRIPTOR_FAST",
"CALL_ALLOC_AND_ENTER_INIT",
"CALL_PY_GENERAL",
"CALL_BOUND_METHOD_GENERAL",
"CALL_NON_PY_GENERAL",
],
}
_specialized_opmap = {
'BINARY_OP_ADD_FLOAT': 150,
'BINARY_OP_ADD_INT': 151,
'BINARY_OP_ADD_UNICODE': 152,
'BINARY_OP_INPLACE_ADD_UNICODE': 3,
'BINARY_OP_MULTIPLY_FLOAT': 153,
'BINARY_OP_MULTIPLY_INT': 154,
'BINARY_OP_SUBTRACT_FLOAT': 155,
'BINARY_OP_SUBTRACT_INT': 156,
'BINARY_SUBSCR_DICT': 157,
'BINARY_SUBSCR_GETITEM': 158,
'BINARY_SUBSCR_LIST_INT': 159,
'BINARY_SUBSCR_STR_INT': 160,
'BINARY_SUBSCR_TUPLE_INT': 161,
'CALL_ALLOC_AND_ENTER_INIT': 162,
'CALL_BOUND_METHOD_EXACT_ARGS': 163,
'CALL_BOUND_METHOD_GENERAL': 164,
'CALL_BUILTIN_CLASS': 165,
'CALL_BUILTIN_FAST': 166,
'CALL_BUILTIN_FAST_WITH_KEYWORDS': 167,
'CALL_BUILTIN_O': 168,
'CALL_ISINSTANCE': 169,
'CALL_LEN': 170,
'CALL_LIST_APPEND': 171,
'CALL_METHOD_DESCRIPTOR_FAST': 172,
'CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS': 173,
'CALL_METHOD_DESCRIPTOR_NOARGS': 174,
'CALL_METHOD_DESCRIPTOR_O': 175,
'CALL_NON_PY_GENERAL': 176,
'CALL_PY_EXACT_ARGS': 177,
'CALL_PY_GENERAL': 178,
'CALL_STR_1': 179,
'CALL_TUPLE_1': 180,
'CALL_TYPE_1': 181,
'COMPARE_OP_FLOAT': 182,
'COMPARE_OP_INT': 183,
'COMPARE_OP_STR': 184,
'CONTAINS_OP_DICT': 185,
'CONTAINS_OP_SET': 186,
'FOR_ITER_GEN': 187,
'FOR_ITER_LIST': 188,
'FOR_ITER_RANGE': 189,
'FOR_ITER_TUPLE': 190,
'LOAD_ATTR_CLASS': 191,
'LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN': 192,
'LOAD_ATTR_INSTANCE_VALUE': 193,
'LOAD_ATTR_METHOD_LAZY_DICT': 194,
'LOAD_ATTR_METHOD_NO_DICT': 195,
'LOAD_ATTR_METHOD_WITH_VALUES': 196,
'LOAD_ATTR_MODULE': 197,
'LOAD_ATTR_NONDESCRIPTOR_NO_DICT': 198,
'LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES': 199,
'LOAD_ATTR_PROPERTY': 200,
'LOAD_ATTR_SLOT': 201,
'LOAD_ATTR_WITH_HINT': 202,
'LOAD_GLOBAL_BUILTIN': 203,
'LOAD_GLOBAL_MODULE': 204,
'LOAD_SUPER_ATTR_ATTR': 205,
'LOAD_SUPER_ATTR_METHOD': 206,
'RESUME_CHECK': 207,
'SEND_GEN': 208,
'STORE_ATTR_INSTANCE_VALUE': 209,
'STORE_ATTR_SLOT': 210,
'STORE_ATTR_WITH_HINT': 211,
'STORE_SUBSCR_DICT': 212,
'STORE_SUBSCR_LIST_INT': 213,
'TO_BOOL_ALWAYS_TRUE': 214,
'TO_BOOL_BOOL': 215,
'TO_BOOL_INT': 216,
'TO_BOOL_LIST': 217,
'TO_BOOL_NONE': 218,
'TO_BOOL_STR': 219,
'UNPACK_SEQUENCE_LIST': 220,
'UNPACK_SEQUENCE_TUPLE': 221,
'UNPACK_SEQUENCE_TWO_TUPLE': 222,
}
opmap = {
'CACHE': 0,
'RESERVED': 17,
'RESUME': 149,
'INSTRUMENTED_LINE': 254,
'BEFORE_ASYNC_WITH': 1,
'BEFORE_WITH': 2,
'BINARY_SLICE': 4,
'BINARY_SUBSCR': 5,
'CHECK_EG_MATCH': 6,
'CHECK_EXC_MATCH': 7,
'CLEANUP_THROW': 8,
'DELETE_SUBSCR': 9,
'END_ASYNC_FOR': 10,
'END_FOR': 11,
'END_SEND': 12,
'EXIT_INIT_CHECK': 13,
'FORMAT_SIMPLE': 14,
'FORMAT_WITH_SPEC': 15,
'GET_AITER': 16,
'GET_ANEXT': 18,
'GET_ITER': 19,
'GET_LEN': 20,
'GET_YIELD_FROM_ITER': 21,
'INTERPRETER_EXIT': 22,
'LOAD_ASSERTION_ERROR': 23,
'LOAD_BUILD_CLASS': 24,
'LOAD_LOCALS': 25,
'MAKE_FUNCTION': 26,
'MATCH_KEYS': 27,
'MATCH_MAPPING': 28,
'MATCH_SEQUENCE': 29,
'NOP': 30,
'POP_EXCEPT': 31,
'POP_TOP': 32,
'PUSH_EXC_INFO': 33,
'PUSH_NULL': 34,
'RETURN_GENERATOR': 35,
'RETURN_VALUE': 36,
'SETUP_ANNOTATIONS': 37,
'STORE_SLICE': 38,
'STORE_SUBSCR': 39,
'TO_BOOL': 40,
'UNARY_INVERT': 41,
'UNARY_NEGATIVE': 42,
'UNARY_NOT': 43,
'WITH_EXCEPT_START': 44,
'BINARY_OP': 45,
'BUILD_CONST_KEY_MAP': 46,
'BUILD_LIST': 47,
'BUILD_MAP': 48,
'BUILD_SET': 49,
'BUILD_SLICE': 50,
'BUILD_STRING': 51,
'BUILD_TUPLE': 52,
'CALL': 53,
'CALL_FUNCTION_EX': 54,
'CALL_INTRINSIC_1': 55,
'CALL_INTRINSIC_2': 56,
'CALL_KW': 57,
'COMPARE_OP': 58,
'CONTAINS_OP': 59,
'CONVERT_VALUE': 60,
'COPY': 61,
'COPY_FREE_VARS': 62,
'DELETE_ATTR': 63,
'DELETE_DEREF': 64,
'DELETE_FAST': 65,
'DELETE_GLOBAL': 66,
'DELETE_NAME': 67,
'DICT_MERGE': 68,
'DICT_UPDATE': 69,
'ENTER_EXECUTOR': 70,
'EXTENDED_ARG': 71,
'FOR_ITER': 72,
'GET_AWAITABLE': 73,
'IMPORT_FROM': 74,
'IMPORT_NAME': 75,
'IS_OP': 76,
'JUMP_BACKWARD': 77,
'JUMP_BACKWARD_NO_INTERRUPT': 78,
'JUMP_FORWARD': 79,
'LIST_APPEND': 80,
'LIST_EXTEND': 81,
'LOAD_ATTR': 82,
'LOAD_CONST': 83,
'LOAD_DEREF': 84,
'LOAD_FAST': 85,
'LOAD_FAST_AND_CLEAR': 86,
'LOAD_FAST_CHECK': 87,
'LOAD_FAST_LOAD_FAST': 88,
'LOAD_FROM_DICT_OR_DEREF': 89,
'LOAD_FROM_DICT_OR_GLOBALS': 90,
'LOAD_GLOBAL': 91,
'LOAD_NAME': 92,
'LOAD_SUPER_ATTR': 93,
'MAKE_CELL': 94,
'MAP_ADD': 95,
'MATCH_CLASS': 96,
'POP_JUMP_IF_FALSE': 97,
'POP_JUMP_IF_NONE': 98,
'POP_JUMP_IF_NOT_NONE': 99,
'POP_JUMP_IF_TRUE': 100,
'RAISE_VARARGS': 101,
'RERAISE': 102,
'RETURN_CONST': 103,
'SEND': 104,
'SET_ADD': 105,
'SET_FUNCTION_ATTRIBUTE': 106,
'SET_UPDATE': 107,
'STORE_ATTR': 108,
'STORE_DEREF': 109,
'STORE_FAST': 110,
'STORE_FAST_LOAD_FAST': 111,
'STORE_FAST_STORE_FAST': 112,
'STORE_GLOBAL': 113,
'STORE_NAME': 114,
'SWAP': 115,
'UNPACK_EX': 116,
'UNPACK_SEQUENCE': 117,
'YIELD_VALUE': 118,
'INSTRUMENTED_RESUME': 236,
'INSTRUMENTED_END_FOR': 237,
'INSTRUMENTED_END_SEND': 238,
'INSTRUMENTED_RETURN_VALUE': 239,
'INSTRUMENTED_RETURN_CONST': 240,
'INSTRUMENTED_YIELD_VALUE': 241,
'INSTRUMENTED_LOAD_SUPER_ATTR': 242,
'INSTRUMENTED_FOR_ITER': 243,
'INSTRUMENTED_CALL': 244,
'INSTRUMENTED_CALL_KW': 245,
'INSTRUMENTED_CALL_FUNCTION_EX': 246,
'INSTRUMENTED_INSTRUCTION': 247,
'INSTRUMENTED_JUMP_FORWARD': 248,
'INSTRUMENTED_JUMP_BACKWARD': 249,
'INSTRUMENTED_POP_JUMP_IF_TRUE': 250,
'INSTRUMENTED_POP_JUMP_IF_FALSE': 251,
'INSTRUMENTED_POP_JUMP_IF_NONE': 252,
'INSTRUMENTED_POP_JUMP_IF_NOT_NONE': 253,
'JUMP': 256,
'JUMP_NO_INTERRUPT': 257,
'LOAD_CLOSURE': 258,
'LOAD_METHOD': 259,
'LOAD_SUPER_METHOD': 260,
'LOAD_ZERO_SUPER_ATTR': 261,
'LOAD_ZERO_SUPER_METHOD': 262,
'POP_BLOCK': 263,
'SETUP_CLEANUP': 264,
'SETUP_FINALLY': 265,
'SETUP_WITH': 266,
'STORE_FAST_MAYBE_NULL': 267,
}
HAVE_ARGUMENT = 44
MIN_INSTRUMENTED_OPCODE = 236

449
Lib/opcode.py vendored
View File

@@ -4,404 +4,47 @@ opcode module - potentially shared between dis and other modules which
operate on bytecodes (e.g. peephole optimizers).
"""
__all__ = ["cmp_op", "hasarg", "hasconst", "hasname", "hasjrel", "hasjabs",
"haslocal", "hascompare", "hasfree", "hasexc", "opname", "opmap",
"HAVE_ARGUMENT", "EXTENDED_ARG"]
# It's a chicken-and-egg I'm afraid:
# We're imported before _opcode's made.
# With exception unheeded
# (stack_effect is not needed)
# Both our chickens and eggs are allayed.
# --Larry Hastings, 2013/11/23
__all__ = ["cmp_op", "stack_effect", "hascompare", "opname", "opmap",
"HAVE_ARGUMENT", "EXTENDED_ARG", "hasarg", "hasconst", "hasname",
"hasjump", "hasjrel", "hasjabs", "hasfree", "haslocal", "hasexc"]
try:
from _opcode import stack_effect
__all__.append('stack_effect')
except ImportError:
pass
import _opcode
from _opcode import stack_effect
cmp_op = ('<', '<=', '==', '!=', '>', '>=')
from _opcode_metadata import (_specializations, _specialized_opmap, opmap,
HAVE_ARGUMENT, MIN_INSTRUMENTED_OPCODE)
EXTENDED_ARG = opmap['EXTENDED_ARG']
hasarg = []
hasconst = []
hasname = []
hasjrel = []
hasjabs = []
haslocal = []
hascompare = []
hasfree = []
hasexc = []
def is_pseudo(op):
return op >= MIN_PSEUDO_OPCODE and op <= MAX_PSEUDO_OPCODE
oplists = [hasarg, hasconst, hasname, hasjrel, hasjabs,
haslocal, hascompare, hasfree, hasexc]
opmap = {}
## pseudo opcodes (used in the compiler) mapped to the values
## they can become in the actual code.
_pseudo_ops = {}
def def_op(name, op):
opmap[name] = op
def name_op(name, op):
def_op(name, op)
hasname.append(op)
def jrel_op(name, op):
def_op(name, op)
hasjrel.append(op)
def jabs_op(name, op):
def_op(name, op)
hasjabs.append(op)
def pseudo_op(name, op, real_ops):
def_op(name, op)
_pseudo_ops[name] = real_ops
# add the pseudo opcode to the lists its targets are in
for oplist in oplists:
res = [opmap[rop] in oplist for rop in real_ops]
if any(res):
assert all(res)
oplist.append(op)
# Instruction opcodes for compiled code
# Blank lines correspond to available opcodes
def_op('CACHE', 0)
def_op('POP_TOP', 1)
def_op('PUSH_NULL', 2)
def_op('NOP', 9)
def_op('UNARY_POSITIVE', 10)
def_op('UNARY_NEGATIVE', 11)
def_op('UNARY_NOT', 12)
def_op('UNARY_INVERT', 15)
def_op('BINARY_SUBSCR', 25)
def_op('BINARY_SLICE', 26)
def_op('STORE_SLICE', 27)
def_op('GET_LEN', 30)
def_op('MATCH_MAPPING', 31)
def_op('MATCH_SEQUENCE', 32)
def_op('MATCH_KEYS', 33)
def_op('PUSH_EXC_INFO', 35)
def_op('CHECK_EXC_MATCH', 36)
def_op('CHECK_EG_MATCH', 37)
def_op('WITH_EXCEPT_START', 49)
def_op('GET_AITER', 50)
def_op('GET_ANEXT', 51)
def_op('BEFORE_ASYNC_WITH', 52)
def_op('BEFORE_WITH', 53)
def_op('END_ASYNC_FOR', 54)
def_op('CLEANUP_THROW', 55)
def_op('STORE_SUBSCR', 60)
def_op('DELETE_SUBSCR', 61)
# TODO: RUSTPYTHON
# Delete below def_op after updating coroutines.py
def_op('YIELD_FROM', 72)
def_op('GET_ITER', 68)
def_op('GET_YIELD_FROM_ITER', 69)
def_op('PRINT_EXPR', 70)
def_op('LOAD_BUILD_CLASS', 71)
def_op('LOAD_ASSERTION_ERROR', 74)
def_op('RETURN_GENERATOR', 75)
def_op('LIST_TO_TUPLE', 82)
def_op('RETURN_VALUE', 83)
def_op('IMPORT_STAR', 84)
def_op('SETUP_ANNOTATIONS', 85)
def_op('ASYNC_GEN_WRAP', 87)
def_op('PREP_RERAISE_STAR', 88)
def_op('POP_EXCEPT', 89)
HAVE_ARGUMENT = 90 # real opcodes from here have an argument:
name_op('STORE_NAME', 90) # Index in name list
name_op('DELETE_NAME', 91) # ""
def_op('UNPACK_SEQUENCE', 92) # Number of tuple items
jrel_op('FOR_ITER', 93)
def_op('UNPACK_EX', 94)
name_op('STORE_ATTR', 95) # Index in name list
name_op('DELETE_ATTR', 96) # ""
name_op('STORE_GLOBAL', 97) # ""
name_op('DELETE_GLOBAL', 98) # ""
def_op('SWAP', 99)
def_op('LOAD_CONST', 100) # Index in const list
hasconst.append(100)
name_op('LOAD_NAME', 101) # Index in name list
def_op('BUILD_TUPLE', 102) # Number of tuple items
def_op('BUILD_LIST', 103) # Number of list items
def_op('BUILD_SET', 104) # Number of set items
def_op('BUILD_MAP', 105) # Number of dict entries
name_op('LOAD_ATTR', 106) # Index in name list
def_op('COMPARE_OP', 107) # Comparison operator
hascompare.append(107)
name_op('IMPORT_NAME', 108) # Index in name list
name_op('IMPORT_FROM', 109) # Index in name list
jrel_op('JUMP_FORWARD', 110) # Number of words to skip
jrel_op('JUMP_IF_FALSE_OR_POP', 111) # Number of words to skip
jrel_op('JUMP_IF_TRUE_OR_POP', 112) # ""
jrel_op('POP_JUMP_IF_FALSE', 114)
jrel_op('POP_JUMP_IF_TRUE', 115)
name_op('LOAD_GLOBAL', 116) # Index in name list
def_op('IS_OP', 117)
def_op('CONTAINS_OP', 118)
def_op('RERAISE', 119)
def_op('COPY', 120)
def_op('BINARY_OP', 122)
jrel_op('SEND', 123) # Number of bytes to skip
def_op('LOAD_FAST', 124) # Local variable number, no null check
haslocal.append(124)
def_op('STORE_FAST', 125) # Local variable number
haslocal.append(125)
def_op('DELETE_FAST', 126) # Local variable number
haslocal.append(126)
def_op('LOAD_FAST_CHECK', 127) # Local variable number
haslocal.append(127)
jrel_op('POP_JUMP_IF_NOT_NONE', 128)
jrel_op('POP_JUMP_IF_NONE', 129)
def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3)
def_op('GET_AWAITABLE', 131)
def_op('MAKE_FUNCTION', 132) # Flags
def_op('BUILD_SLICE', 133) # Number of items
jrel_op('JUMP_BACKWARD_NO_INTERRUPT', 134) # Number of words to skip (backwards)
def_op('MAKE_CELL', 135)
hasfree.append(135)
def_op('LOAD_CLOSURE', 136)
hasfree.append(136)
def_op('LOAD_DEREF', 137)
hasfree.append(137)
def_op('STORE_DEREF', 138)
hasfree.append(138)
def_op('DELETE_DEREF', 139)
hasfree.append(139)
jrel_op('JUMP_BACKWARD', 140) # Number of words to skip (backwards)
def_op('CALL_FUNCTION_EX', 142) # Flags
def_op('EXTENDED_ARG', 144)
EXTENDED_ARG = 144
def_op('LIST_APPEND', 145)
def_op('SET_ADD', 146)
def_op('MAP_ADD', 147)
def_op('LOAD_CLASSDEREF', 148)
hasfree.append(148)
def_op('COPY_FREE_VARS', 149)
def_op('YIELD_VALUE', 150)
def_op('RESUME', 151) # This must be kept in sync with deepfreeze.py
def_op('MATCH_CLASS', 152)
def_op('FORMAT_VALUE', 155)
def_op('BUILD_CONST_KEY_MAP', 156)
def_op('BUILD_STRING', 157)
def_op('LIST_EXTEND', 162)
def_op('SET_UPDATE', 163)
def_op('DICT_MERGE', 164)
def_op('DICT_UPDATE', 165)
def_op('CALL', 171)
def_op('KW_NAMES', 172)
hasconst.append(172)
hasarg.extend([op for op in opmap.values() if op >= HAVE_ARGUMENT])
MIN_PSEUDO_OPCODE = 256
pseudo_op('SETUP_FINALLY', 256, ['NOP'])
hasexc.append(256)
pseudo_op('SETUP_CLEANUP', 257, ['NOP'])
hasexc.append(257)
pseudo_op('SETUP_WITH', 258, ['NOP'])
hasexc.append(258)
pseudo_op('POP_BLOCK', 259, ['NOP'])
pseudo_op('JUMP', 260, ['JUMP_FORWARD', 'JUMP_BACKWARD'])
pseudo_op('JUMP_NO_INTERRUPT', 261, ['JUMP_FORWARD', 'JUMP_BACKWARD_NO_INTERRUPT'])
pseudo_op('LOAD_METHOD', 262, ['LOAD_ATTR'])
MAX_PSEUDO_OPCODE = MIN_PSEUDO_OPCODE + len(_pseudo_ops) - 1
del def_op, name_op, jrel_op, jabs_op, pseudo_op
opname = ['<%r>' % (op,) for op in range(MAX_PSEUDO_OPCODE + 1)]
opname = ['<%r>' % (op,) for op in range(max(opmap.values()) + 1)]
for op, i in opmap.items():
opname[i] = op
cmp_op = ('<', '<=', '==', '!=', '>', '>=')
_nb_ops = [
("NB_ADD", "+"),
("NB_AND", "&"),
("NB_FLOOR_DIVIDE", "//"),
("NB_LSHIFT", "<<"),
("NB_MATRIX_MULTIPLY", "@"),
("NB_MULTIPLY", "*"),
("NB_REMAINDER", "%"),
("NB_OR", "|"),
("NB_POWER", "**"),
("NB_RSHIFT", ">>"),
("NB_SUBTRACT", "-"),
("NB_TRUE_DIVIDE", "/"),
("NB_XOR", "^"),
("NB_INPLACE_ADD", "+="),
("NB_INPLACE_AND", "&="),
("NB_INPLACE_FLOOR_DIVIDE", "//="),
("NB_INPLACE_LSHIFT", "<<="),
("NB_INPLACE_MATRIX_MULTIPLY", "@="),
("NB_INPLACE_MULTIPLY", "*="),
("NB_INPLACE_REMAINDER", "%="),
("NB_INPLACE_OR", "|="),
("NB_INPLACE_POWER", "**="),
("NB_INPLACE_RSHIFT", ">>="),
("NB_INPLACE_SUBTRACT", "-="),
("NB_INPLACE_TRUE_DIVIDE", "/="),
("NB_INPLACE_XOR", "^="),
]
# These lists are documented as part of the dis module's API
hasarg = [op for op in opmap.values() if _opcode.has_arg(op)]
hasconst = [op for op in opmap.values() if _opcode.has_const(op)]
hasname = [op for op in opmap.values() if _opcode.has_name(op)]
hasjump = [op for op in opmap.values() if _opcode.has_jump(op)]
hasjrel = hasjump # for backward compatibility
hasjabs = []
hasfree = [op for op in opmap.values() if _opcode.has_free(op)]
haslocal = [op for op in opmap.values() if _opcode.has_local(op)]
hasexc = [op for op in opmap.values() if _opcode.has_exc(op)]
_specializations = {
"BINARY_OP": [
"BINARY_OP_ADAPTIVE",
"BINARY_OP_ADD_FLOAT",
"BINARY_OP_ADD_INT",
"BINARY_OP_ADD_UNICODE",
"BINARY_OP_INPLACE_ADD_UNICODE",
"BINARY_OP_MULTIPLY_FLOAT",
"BINARY_OP_MULTIPLY_INT",
"BINARY_OP_SUBTRACT_FLOAT",
"BINARY_OP_SUBTRACT_INT",
],
"BINARY_SUBSCR": [
"BINARY_SUBSCR_ADAPTIVE",
"BINARY_SUBSCR_DICT",
"BINARY_SUBSCR_GETITEM",
"BINARY_SUBSCR_LIST_INT",
"BINARY_SUBSCR_TUPLE_INT",
],
"CALL": [
"CALL_ADAPTIVE",
"CALL_PY_EXACT_ARGS",
"CALL_PY_WITH_DEFAULTS",
"CALL_BOUND_METHOD_EXACT_ARGS",
"CALL_BUILTIN_CLASS",
"CALL_BUILTIN_FAST_WITH_KEYWORDS",
"CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS",
"CALL_NO_KW_BUILTIN_FAST",
"CALL_NO_KW_BUILTIN_O",
"CALL_NO_KW_ISINSTANCE",
"CALL_NO_KW_LEN",
"CALL_NO_KW_LIST_APPEND",
"CALL_NO_KW_METHOD_DESCRIPTOR_FAST",
"CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS",
"CALL_NO_KW_METHOD_DESCRIPTOR_O",
"CALL_NO_KW_STR_1",
"CALL_NO_KW_TUPLE_1",
"CALL_NO_KW_TYPE_1",
],
"COMPARE_OP": [
"COMPARE_OP_ADAPTIVE",
"COMPARE_OP_FLOAT_JUMP",
"COMPARE_OP_INT_JUMP",
"COMPARE_OP_STR_JUMP",
],
"EXTENDED_ARG": [
"EXTENDED_ARG_QUICK",
],
"FOR_ITER": [
"FOR_ITER_ADAPTIVE",
"FOR_ITER_LIST",
"FOR_ITER_RANGE",
],
"JUMP_BACKWARD": [
"JUMP_BACKWARD_QUICK",
],
"LOAD_ATTR": [
"LOAD_ATTR_ADAPTIVE",
# These potentially push [NULL, bound method] onto the stack.
"LOAD_ATTR_CLASS",
"LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN",
"LOAD_ATTR_INSTANCE_VALUE",
"LOAD_ATTR_MODULE",
"LOAD_ATTR_PROPERTY",
"LOAD_ATTR_SLOT",
"LOAD_ATTR_WITH_HINT",
# These will always push [unbound method, self] onto the stack.
"LOAD_ATTR_METHOD_LAZY_DICT",
"LOAD_ATTR_METHOD_NO_DICT",
"LOAD_ATTR_METHOD_WITH_DICT",
"LOAD_ATTR_METHOD_WITH_VALUES",
],
"LOAD_CONST": [
"LOAD_CONST__LOAD_FAST",
],
"LOAD_FAST": [
"LOAD_FAST__LOAD_CONST",
"LOAD_FAST__LOAD_FAST",
],
"LOAD_GLOBAL": [
"LOAD_GLOBAL_ADAPTIVE",
"LOAD_GLOBAL_BUILTIN",
"LOAD_GLOBAL_MODULE",
],
"RESUME": [
"RESUME_QUICK",
],
"STORE_ATTR": [
"STORE_ATTR_ADAPTIVE",
"STORE_ATTR_INSTANCE_VALUE",
"STORE_ATTR_SLOT",
"STORE_ATTR_WITH_HINT",
],
"STORE_FAST": [
"STORE_FAST__LOAD_FAST",
"STORE_FAST__STORE_FAST",
],
"STORE_SUBSCR": [
"STORE_SUBSCR_ADAPTIVE",
"STORE_SUBSCR_DICT",
"STORE_SUBSCR_LIST_INT",
],
"UNPACK_SEQUENCE": [
"UNPACK_SEQUENCE_ADAPTIVE",
"UNPACK_SEQUENCE_LIST",
"UNPACK_SEQUENCE_TUPLE",
"UNPACK_SEQUENCE_TWO_TUPLE",
],
}
_specialized_instructions = [
opcode for family in _specializations.values() for opcode in family
]
_specialization_stats = [
"success",
"failure",
"hit",
"deferred",
"miss",
"deopt",
]
_intrinsic_1_descs = _opcode.get_intrinsic1_descs()
_intrinsic_2_descs = _opcode.get_intrinsic2_descs()
_nb_ops = _opcode.get_nb_ops()
hascompare = [opmap["COMPARE_OP"]]
_cache_format = {
"LOAD_GLOBAL": {
"counter": 1,
"index": 1,
"module_keys_version": 2,
"module_keys_version": 1,
"builtin_keys_version": 1,
},
"BINARY_OP": {
@@ -412,16 +55,19 @@ _cache_format = {
},
"COMPARE_OP": {
"counter": 1,
"mask": 1,
},
"CONTAINS_OP": {
"counter": 1,
},
"BINARY_SUBSCR": {
"counter": 1,
"type_version": 2,
"func_version": 1,
},
"FOR_ITER": {
"counter": 1,
},
"LOAD_SUPER_ATTR": {
"counter": 1,
},
"LOAD_ATTR": {
"counter": 1,
"version": 2,
@@ -436,13 +82,34 @@ _cache_format = {
"CALL": {
"counter": 1,
"func_version": 2,
"min_args": 1,
},
"STORE_SUBSCR": {
"counter": 1,
},
"SEND": {
"counter": 1,
},
"JUMP_BACKWARD": {
"counter": 1,
},
"TO_BOOL": {
"counter": 1,
"version": 2,
},
"POP_JUMP_IF_TRUE": {
"counter": 1,
},
"POP_JUMP_IF_FALSE": {
"counter": 1,
},
"POP_JUMP_IF_NONE": {
"counter": 1,
},
"POP_JUMP_IF_NOT_NONE": {
"counter": 1,
},
}
_inline_cache_entries = [
sum(_cache_format.get(opname[opcode], {}).values()) for opcode in range(256)
]
_inline_cache_entries = {
name : sum(value.values()) for (name, value) in _cache_format.items()
}

View File

@@ -7,7 +7,7 @@ import contextlib
import dataclasses
import functools
import logging
# import _opcode # TODO: RUSTPYTHON
import _opcode
import os
import re
import stat

View File

@@ -3,10 +3,26 @@
import unittest
import dis
import io
from _testinternalcapi import compiler_codegen, optimize_cfg, assemble_code_object
import opcode
try:
import _testinternalcapi
except ImportError:
_testinternalcapi = None
_UNSPECIFIED = object()
def instructions_with_positions(instrs, co_positions):
# Return (instr, positions) pairs from the instrs list and co_positions
# iterator. The latter contains items for cache lines and the former
# doesn't, so those need to be skipped.
co_positions = co_positions or iter(())
for instr in instrs:
yield instr, next(co_positions, ())
for _, size, _ in (instr.cache_info or ()):
for i in range(size):
next(co_positions, ())
class BytecodeTestCase(unittest.TestCase):
"""Custom assertion methods for inspecting bytecode."""
@@ -53,16 +69,14 @@ class CompilationStepTestCase(unittest.TestCase):
class Label:
pass
def assertInstructionsMatch(self, actual_, expected_):
# get two lists where each entry is a label or
# an instruction tuple. Normalize the labels to the
# instruction count of the target, and compare the lists.
def assertInstructionsMatch(self, actual_seq, expected):
# get an InstructionSequence and an expected list, where each
# entry is a label or an instruction tuple. Construct an expcted
# instruction sequence and compare with the one given.
self.assertIsInstance(actual_, list)
self.assertIsInstance(expected_, list)
actual = self.normalize_insts(actual_)
expected = self.normalize_insts(expected_)
self.assertIsInstance(expected, list)
actual = actual_seq.get_instructions()
expected = self.seq_from_insts(expected).get_instructions()
self.assertEqual(len(actual), len(expected))
# compare instructions
@@ -72,10 +86,8 @@ class CompilationStepTestCase(unittest.TestCase):
continue
self.assertIsInstance(exp, tuple)
self.assertIsInstance(act, tuple)
# crop comparison to the provided expected values
if len(act) > len(exp):
act = act[:len(exp)]
self.assertEqual(exp, act)
idx = max([p[0] for p in enumerate(exp) if p[1] != -1])
self.assertEqual(exp[:idx], act[:idx])
def resolveAndRemoveLabels(self, insts):
idx = 0
@@ -90,54 +102,57 @@ class CompilationStepTestCase(unittest.TestCase):
return res
def normalize_insts(self, insts):
""" Map labels to instruction index.
Map opcodes to opnames.
"""
insts = self.resolveAndRemoveLabels(insts)
res = []
def seq_from_insts(self, insts):
labels = {item for item in insts if isinstance(item, self.Label)}
for i, lbl in enumerate(labels):
lbl.value = i
seq = _testinternalcapi.new_instruction_sequence()
for item in insts:
assert isinstance(item, tuple)
opcode, oparg, *loc = item
opcode = dis.opmap.get(opcode, opcode)
if isinstance(oparg, self.Label):
arg = oparg.value
if isinstance(item, self.Label):
seq.use_label(item.value)
else:
arg = oparg if opcode in self.HAS_ARG else None
opcode = dis.opname[opcode]
res.append((opcode, arg, *loc))
return res
op = item[0]
if isinstance(op, str):
op = opcode.opmap[op]
arg, *loc = item[1:]
if isinstance(arg, self.Label):
arg = arg.value
loc = loc + [-1] * (4 - len(loc))
seq.addop(op, arg or 0, *loc)
return seq
def complete_insts_info(self, insts):
# fill in omitted fields in location, and oparg 0 for ops with no arg.
res = []
for item in insts:
assert isinstance(item, tuple)
inst = list(item)
opcode = dis.opmap[inst[0]]
oparg = inst[1]
loc = inst[2:] + [-1] * (6 - len(inst))
res.append((opcode, oparg, *loc))
return res
def check_instructions(self, insts):
for inst in insts:
if isinstance(inst, self.Label):
continue
op, arg, *loc = inst
if isinstance(op, str):
op = opcode.opmap[op]
self.assertEqual(op in opcode.hasarg,
arg is not None,
f"{opcode.opname[op]=} {arg=}")
self.assertTrue(all(isinstance(l, int) for l in loc))
@unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi")
class CodegenTestCase(CompilationStepTestCase):
def generate_code(self, ast):
insts, _ = compiler_codegen(ast, "my_file.py", 0)
insts, _ = _testinternalcapi.compiler_codegen(ast, "my_file.py", 0)
return insts
@unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi")
class CfgOptimizationTestCase(CompilationStepTestCase):
def get_optimized(self, insts, consts, nlocals=0):
insts = self.normalize_insts(insts)
insts = self.complete_insts_info(insts)
insts = optimize_cfg(insts, consts, nlocals)
def get_optimized(self, seq, consts, nlocals=0):
insts = _testinternalcapi.optimize_cfg(seq, consts, nlocals)
return insts, consts
@unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi")
class AssemblerTestCase(CompilationStepTestCase):
def get_code_object(self, filename, insts, metadata):
co = assemble_code_object(filename, insts, metadata)
co = _testinternalcapi.assemble_code_object(filename, insts, metadata)
return co

143
Lib/test/test__opcode.py vendored Normal file
View File

@@ -0,0 +1,143 @@
import dis
from test.support.import_helper import import_module
import unittest
import opcode
_opcode = import_module("_opcode")
from _opcode import stack_effect
class OpListTests(unittest.TestCase):
def check_bool_function_result(self, func, ops, expected):
for op in ops:
if isinstance(op, str):
op = dis.opmap[op]
with self.subTest(opcode=op, func=func):
self.assertIsInstance(func(op), bool)
self.assertEqual(func(op), expected)
def test_invalid_opcodes(self):
invalid = [-100, -1, 255, 512, 513, 1000]
self.check_bool_function_result(_opcode.is_valid, invalid, False)
self.check_bool_function_result(_opcode.has_arg, invalid, False)
self.check_bool_function_result(_opcode.has_const, invalid, False)
self.check_bool_function_result(_opcode.has_name, invalid, False)
self.check_bool_function_result(_opcode.has_jump, invalid, False)
self.check_bool_function_result(_opcode.has_free, invalid, False)
self.check_bool_function_result(_opcode.has_local, invalid, False)
self.check_bool_function_result(_opcode.has_exc, invalid, False)
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'dis' has no attribute 'opmap'
def test_is_valid(self):
names = [
'CACHE',
'POP_TOP',
'IMPORT_NAME',
'JUMP',
'INSTRUMENTED_RETURN_VALUE',
]
opcodes = [dis.opmap[opname] for opname in names]
self.check_bool_function_result(_opcode.is_valid, opcodes, True)
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'dis' has no attribute 'hasarg'
def test_oplists(self):
def check_function(self, func, expected):
for op in [-10, 520]:
with self.subTest(opcode=op, func=func):
res = func(op)
self.assertIsInstance(res, bool)
self.assertEqual(res, op in expected)
check_function(self, _opcode.has_arg, dis.hasarg)
check_function(self, _opcode.has_const, dis.hasconst)
check_function(self, _opcode.has_name, dis.hasname)
check_function(self, _opcode.has_jump, dis.hasjump)
check_function(self, _opcode.has_free, dis.hasfree)
check_function(self, _opcode.has_local, dis.haslocal)
check_function(self, _opcode.has_exc, dis.hasexc)
class StackEffectTests(unittest.TestCase):
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_stack_effect(self):
self.assertEqual(stack_effect(dis.opmap['POP_TOP']), -1)
self.assertEqual(stack_effect(dis.opmap['BUILD_SLICE'], 0), -1)
self.assertEqual(stack_effect(dis.opmap['BUILD_SLICE'], 1), -1)
self.assertEqual(stack_effect(dis.opmap['BUILD_SLICE'], 3), -2)
self.assertRaises(ValueError, stack_effect, 30000)
# All defined opcodes
has_arg = dis.hasarg
for name, code in filter(lambda item: item[0] not in dis.deoptmap, dis.opmap.items()):
if code >= opcode.MIN_INSTRUMENTED_OPCODE:
continue
with self.subTest(opname=name):
stack_effect(code)
stack_effect(code, 0)
# All not defined opcodes
for code in set(range(256)) - set(dis.opmap.values()):
with self.subTest(opcode=code):
self.assertRaises(ValueError, stack_effect, code)
self.assertRaises(ValueError, stack_effect, code, 0)
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_stack_effect_jump(self):
FOR_ITER = dis.opmap['FOR_ITER']
self.assertEqual(stack_effect(FOR_ITER, 0), 1)
self.assertEqual(stack_effect(FOR_ITER, 0, jump=True), 1)
self.assertEqual(stack_effect(FOR_ITER, 0, jump=False), 1)
JUMP_FORWARD = dis.opmap['JUMP_FORWARD']
self.assertEqual(stack_effect(JUMP_FORWARD, 0), 0)
self.assertEqual(stack_effect(JUMP_FORWARD, 0, jump=True), 0)
self.assertEqual(stack_effect(JUMP_FORWARD, 0, jump=False), 0)
# All defined opcodes
has_arg = dis.hasarg
has_exc = dis.hasexc
has_jump = dis.hasjabs + dis.hasjrel
for name, code in filter(lambda item: item[0] not in dis.deoptmap, dis.opmap.items()):
if code >= opcode.MIN_INSTRUMENTED_OPCODE:
continue
with self.subTest(opname=name):
if code not in has_arg:
common = stack_effect(code)
jump = stack_effect(code, jump=True)
nojump = stack_effect(code, jump=False)
else:
common = stack_effect(code, 0)
jump = stack_effect(code, 0, jump=True)
nojump = stack_effect(code, 0, jump=False)
if code in has_jump or code in has_exc:
self.assertEqual(common, max(jump, nojump))
else:
self.assertEqual(jump, common)
self.assertEqual(nojump, common)
class SpecializationStatsTests(unittest.TestCase):
def test_specialization_stats(self):
stat_names = ["success", "failure", "hit", "deferred", "miss", "deopt"]
specialized_opcodes = [
op.lower()
for op in opcode._specializations
if opcode._inline_cache_entries.get(op, 0)
]
self.assertIn('load_attr', specialized_opcodes)
self.assertIn('binary_subscr', specialized_opcodes)
stats = _opcode.get_specialization_stats()
if stats is not None:
self.assertIsInstance(stats, dict)
self.assertCountEqual(stats.keys(), specialized_opcodes)
self.assertCountEqual(
stats['load_attr'].keys(),
stat_names + ['failure_kinds'])
for sn in stat_names:
self.assertIsInstance(stats['load_attr'][sn], int)
self.assertIsInstance(
stats['load_attr']['failure_kinds'],
tuple)
for v in stats['load_attr']['failure_kinds']:
self.assertIsInstance(v, int)
if __name__ == "__main__":
unittest.main()

View File

@@ -198,27 +198,27 @@ pub struct CodeObject<C: Constant = ConstantData> {
pub instructions: Box<[CodeUnit]>,
pub locations: Box<[SourceLocation]>,
pub flags: CodeFlags,
/// Number of positional-only arguments
pub posonlyarg_count: u32,
// Number of positional-only arguments
pub arg_count: u32,
pub kwonlyarg_count: u32,
pub source_path: C::Name,
pub first_line_number: Option<OneIndexed>,
pub max_stackdepth: u32,
/// Name of the object that created this code object
pub obj_name: C::Name,
// Name of the object that created this code object
/// Qualified name of the object (like CPython's co_qualname)
pub qualname: C::Name,
// Qualified name of the object (like CPython's co_qualname)
pub cell2arg: Option<Box<[i32]>>,
pub constants: Box<[C]>,
pub names: Box<[C::Name]>,
pub varnames: Box<[C::Name]>,
pub cellvars: Box<[C::Name]>,
pub freevars: Box<[C::Name]>,
/// Line number table (CPython 3.11+ format)
pub linetable: Box<[u8]>,
// Line number table (CPython 3.11+ format)
/// Exception handling table
pub exceptiontable: Box<[u8]>,
// Exception handling table
}
bitflags! {
@@ -294,6 +294,12 @@ impl OpArg {
}
}
impl From<u32> for OpArg {
fn from(raw: u32) -> Self {
Self(raw)
}
}
#[derive(Default, Copy, Clone)]
#[repr(transparent)]
pub struct OpArgState {

View File

@@ -22,7 +22,7 @@ tkinter = ["dep:tk-sys", "dep:tcl-sys"]
[dependencies]
# rustpython crates
rustpython-derive = { workspace = true }
rustpython-vm = { workspace = true, default-features = false }
rustpython-vm = { workspace = true, default-features = false, features = ["compiler"]}
rustpython-common = { workspace = true }
ahash = { workspace = true }

View File

@@ -38,6 +38,7 @@ mod locale;
mod math;
#[cfg(unix)]
mod mmap;
mod opcode;
mod pyexpat;
mod pystruct;
mod random;
@@ -135,6 +136,7 @@ pub fn get_module_inits() -> impl Iterator<Item = (Cow<'static, str>, StdlibInit
"_json" => json::make_module,
"math" => math::make_module,
"pyexpat" => pyexpat::make_module,
"_opcode" => opcode::make_module,
"_random" => random::make_module,
"_statistics" => statistics::make_module,
"_struct" => pystruct::make_module,

282
stdlib/src/opcode.rs Normal file
View File

@@ -0,0 +1,282 @@
pub(crate) use opcode::make_module;
#[pymodule]
mod opcode {
use crate::vm::{
AsObject, PyObjectRef, PyResult, VirtualMachine,
builtins::{PyBool, PyInt, PyIntRef, PyNone},
bytecode::Instruction,
match_class,
};
use std::ops::Deref;
struct Opcode(Instruction);
impl Deref for Opcode {
type Target = Instruction;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Opcode {
// https://github.com/python/cpython/blob/bcee1c322115c581da27600f2ae55e5439c027eb/Include/opcode_ids.h#L238
const HAVE_ARGUMENT: i32 = 44;
pub fn try_from_pyint(raw: PyIntRef, vm: &VirtualMachine) -> PyResult<Self> {
let instruction = raw
.try_to_primitive::<u8>(vm)
.and_then(|v| {
Instruction::try_from(v).map_err(|_| {
vm.new_exception_empty(vm.ctx.exceptions.value_error.to_owned())
})
})
.map_err(|_| vm.new_value_error("invalid opcode or oparg"))?;
Ok(Self(instruction))
}
/// https://github.com/python/cpython/blob/bcee1c322115c581da27600f2ae55e5439c027eb/Include/internal/pycore_opcode_metadata.h#L914-L916
#[must_use]
pub const fn is_valid(opcode: i32) -> bool {
opcode >= 0 && opcode < 268 && opcode != 255
}
// All `has_*` methods below mimics
// https://github.com/python/cpython/blob/bcee1c322115c581da27600f2ae55e5439c027eb/Include/internal/pycore_opcode_metadata.h#L966-L1190
#[must_use]
pub const fn has_arg(opcode: i32) -> bool {
Self::is_valid(opcode) && opcode > Self::HAVE_ARGUMENT
}
#[must_use]
pub const fn has_const(opcode: i32) -> bool {
Self::is_valid(opcode) && matches!(opcode, 83 | 103 | 240)
}
#[must_use]
pub const fn has_name(opcode: i32) -> bool {
Self::is_valid(opcode)
&& matches!(
opcode,
63 | 66
| 67
| 74
| 75
| 82
| 90
| 91
| 92
| 93
| 108
| 113
| 114
| 259
| 260
| 261
| 262
)
}
#[must_use]
pub const fn has_jump(opcode: i32) -> bool {
Self::is_valid(opcode)
&& matches!(
opcode,
72 | 77 | 78 | 79 | 97 | 98 | 99 | 100 | 104 | 256 | 257
)
}
#[must_use]
pub const fn has_free(opcode: i32) -> bool {
Self::is_valid(opcode) && matches!(opcode, 64 | 84 | 89 | 94 | 109)
}
#[must_use]
pub const fn has_local(opcode: i32) -> bool {
Self::is_valid(opcode)
&& matches!(opcode, 65 | 85 | 86 | 87 | 88 | 110 | 111 | 112 | 258 | 267)
}
#[must_use]
pub const fn has_exc(opcode: i32) -> bool {
Self::is_valid(opcode) && matches!(opcode, 264..=266)
}
}
#[pyattr]
const ENABLE_SPECIALIZATION: i8 = 1;
#[derive(FromArgs)]
struct StackEffectArgs {
#[pyarg(positional)]
opcode: PyIntRef,
#[pyarg(positional, optional)]
oparg: Option<PyObjectRef>,
#[pyarg(named, optional)]
jump: Option<PyObjectRef>,
}
#[pyfunction]
fn stack_effect(args: StackEffectArgs, vm: &VirtualMachine) -> PyResult<i32> {
let oparg = args
.oparg
.map(|v| {
if !v.fast_isinstance(vm.ctx.types.int_type) {
return Err(vm.new_type_error(format!(
"'{}' object cannot be interpreted as an integer",
v.class().name()
)));
}
v.downcast_ref::<PyInt>()
.ok_or_else(|| vm.new_type_error(""))?
.try_to_primitive::<u32>(vm)
})
.unwrap_or(Ok(0))?;
let jump = args
.jump
.map(|v| {
match_class!(match v {
b @ PyBool => Ok(b.is(&vm.ctx.true_value)),
_n @ PyNone => Ok(false),
_ => {
Err(vm.new_value_error("stack_effect: jump must be False, True or None"))
}
})
})
.unwrap_or(Ok(false))?;
let opcode = Opcode::try_from_pyint(args.opcode, vm)?;
Ok(opcode.stack_effect(oparg.into(), jump))
}
#[pyfunction]
fn is_valid(opcode: i32) -> bool {
Opcode::is_valid(opcode)
}
#[pyfunction]
fn has_arg(opcode: i32) -> bool {
Opcode::has_arg(opcode)
}
#[pyfunction]
fn has_const(opcode: i32) -> bool {
Opcode::has_const(opcode)
}
#[pyfunction]
fn has_name(opcode: i32) -> bool {
Opcode::has_name(opcode)
}
#[pyfunction]
fn has_jump(opcode: i32) -> bool {
Opcode::has_jump(opcode)
}
#[pyfunction]
fn has_free(opcode: i32) -> bool {
Opcode::has_free(opcode)
}
#[pyfunction]
fn has_local(opcode: i32) -> bool {
Opcode::has_local(opcode)
}
#[pyfunction]
fn has_exc(opcode: i32) -> bool {
Opcode::has_exc(opcode)
}
#[pyfunction]
fn get_intrinsic1_descs(vm: &VirtualMachine) -> Vec<PyObjectRef> {
[
"INTRINSIC_1_INVALID",
"INTRINSIC_PRINT",
"INTRINSIC_IMPORT_STAR",
"INTRINSIC_STOPITERATION_ERROR",
"INTRINSIC_ASYNC_GEN_WRAP",
"INTRINSIC_UNARY_POSITIVE",
"INTRINSIC_LIST_TO_TUPLE",
"INTRINSIC_TYPEVAR",
"INTRINSIC_PARAMSPEC",
"INTRINSIC_TYPEVARTUPLE",
"INTRINSIC_SUBSCRIPT_GENERIC",
"INTRINSIC_TYPEALIAS",
]
.into_iter()
.map(|x| vm.ctx.new_str(x).into())
.collect()
}
#[pyfunction]
fn get_intrinsic2_descs(vm: &VirtualMachine) -> Vec<PyObjectRef> {
[
"INTRINSIC_2_INVALID",
"INTRINSIC_PREP_RERAISE_STAR",
"INTRINSIC_TYPEVAR_WITH_BOUND",
"INTRINSIC_TYPEVAR_WITH_CONSTRAINTS",
"INTRINSIC_SET_FUNCTION_TYPE_PARAMS",
"INTRINSIC_SET_TYPEPARAM_DEFAULT",
]
.into_iter()
.map(|x| vm.ctx.new_str(x).into())
.collect()
}
#[pyfunction]
fn get_nb_ops(vm: &VirtualMachine) -> Vec<PyObjectRef> {
[
("NB_ADD", "+"),
("NB_AND", "&"),
("NB_FLOOR_DIVIDE", "//"),
("NB_LSHIFT", "<<"),
("NB_MATRIX_MULTIPLY", "@"),
("NB_MULTIPLY", "*"),
("NB_REMAINDER", "%"),
("NB_OR", "|"),
("NB_POWER", "**"),
("NB_RSHIFT", ">>"),
("NB_SUBTRACT", "-"),
("NB_TRUE_DIVIDE", "/"),
("NB_XOR", "^"),
("NB_INPLACE_ADD", "+="),
("NB_INPLACE_AND", "&="),
("NB_INPLACE_FLOOR_DIVIDE", "//="),
("NB_INPLACE_LSHIFT", "<<="),
("NB_INPLACE_MATRIX_MULTIPLY", "@="),
("NB_INPLACE_MULTIPLY", "*="),
("NB_INPLACE_REMAINDER", "%="),
("NB_INPLACE_OR", "|="),
("NB_INPLACE_POWER", "**="),
("NB_INPLACE_RSHIFT", ">>="),
("NB_INPLACE_SUBTRACT", "-="),
("NB_INPLACE_TRUE_DIVIDE", "/="),
("NB_INPLACE_XOR", "^="),
]
.into_iter()
.map(|(a, b)| {
vm.ctx
.new_tuple(vec![vm.ctx.new_str(a).into(), vm.ctx.new_str(b).into()])
.into()
})
.collect()
}
#[pyfunction]
fn get_executor(_code: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
// TODO
Ok(vm.ctx.none())
}
#[pyfunction]
fn get_specialization_stats(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.none()
}
}

View File

@@ -837,6 +837,15 @@ impl PyCode {
},
})
}
#[pymethod]
fn _varname_from_oparg(&self, opcode: i32, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
let idx_err = |vm: &VirtualMachine| vm.new_index_error("tuple index out of range");
let idx = usize::try_from(opcode).map_err(|_| idx_err(vm))?;
let name = self.code.varnames.get(idx).ok_or_else(|| idx_err(vm))?;
Ok(name.to_object())
}
}
impl fmt::Display for PyCode {