mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 10:44:36 +08:00
Align compiler and VM bytecode contracts with CPython 3.15 (#9105)
* Align compiler and VM with final CPython 3.15 bytecode Reconstruct the compiler-only baseline from validated487ae47 on the current main and frozen mapping prerequisitec78d811. Preserve the full source delta while dropping obsolete prerequisite ancestry; detailed old-to-new provenance is retained in the recovery artifact. Assisted-by: OpenAI Codex: model identifier unavailable * Fix final script validation, exception cleanup and private cache contracts Replay reviewed compiler CI repairs2191a9d,1f1fe04,a2dacafon the frozen current prerequisite. Intermediate compile/diagnostic corrections are combined where they address the same failure. Assisted-by: OpenAI Codex: model identifier unavailable * Align canonical compiler fixtures and generator frame state Replay reviewed compiler CI repairs476bb74on the frozen current prerequisite. Intermediate compile/diagnostic corrections are combined where they address the same failure. Assisted-by: OpenAI Codex: model identifier unavailable * Preserve code-name owners and final warning and constant-fold contracts Replay reviewed compiler CI repairs6482adfon the frozen current prerequisite. Intermediate compile/diagnostic corrections are combined where they address the same failure. Assisted-by: OpenAI Codex: model identifier unavailable * Skip unavailable monitoring C API tests before tool registration Replay reviewed compiler CI repairs246c309on the frozen current prerequisite. Intermediate compile/diagnostic corrections are combined where they address the same failure. Assisted-by: OpenAI Codex: model identifier unavailable * Assert exact final constant-list iterable folding Replay reviewed compiler CI repairs7ecedcfon the frozen current prerequisite. Intermediate compile/diagnostic corrections are combined where they address the same failure. Assisted-by: OpenAI Codex: model identifier unavailable * Align local monitoring events and exception lifecycle state with final 3.15 Replay reviewed compiler CI repairsec206b1,191fe58on the frozen current prerequisite. Intermediate compile/diagnostic corrections are combined where they address the same failure. Assisted-by: OpenAI Codex: model identifier unavailable * Align 3.15 code objects and codegen with CPython - Store co_consts, co_names, co_localsplusnames, co_localspluskinds, co_linetable, co_exceptiontable and co_name as objects on PyCode; build co_code and the varnames/cellvars/freevars tuples lazily in _co_cached, with co_code deoptimized through deopt_code. - Move the compiler constant cache (_PyCode_ConstantKey, const_cache_insert) to builtins/code/const_cache.rs. - Write every code field with w_object in marshal and pass the read field objects to MarshalBag::make_code through CodeFields. - Emit RETURN_GENERATOR/POP_TOP in enter_scope and place SETUP_CLEANUP after them; drop the generator part of insert_prefix_instructions. - Add PyCode::first_traceable (_co_firsttraceable) for _PyFrame_IsIncomplete and sys.monitoring. - Follow the CPython DISABLE branches in sys.monitoring, the check_matched signature in warnings, the 3.15 % format error messages for str and bytes, code_returns_only_none, and the FOR_ITER stack analysis. - Compute IMPORT_NAME flags with in_exception_handler. - Replace the linetable/exceptiontable byte tests in codegen with dis(show_positions=True) insta snapshots in _opcode. - Remove the opcode metadata verification tooling, byte-exact scripts, private cache tests and redundant snippets. - Restore test_code.py and test_monitoring.py to v3.15.0 plus RUSTPYTHON markers. Assisted-by: Claude Code:claude-opus-5-5 * Remove generator snippet checks covered by Lib/test Drop extra_tests/snippets/generator_iterator_prefix.py. Its loop, delegation, async, f_back and never-awaited checks are covered by test_generators, test_coroutines, test_asyncgen, test_yield_from and test_monitoring. Move the remaining checks into existing files: genexpr prefix tracing and monitoring into stdlib_sys.py, and the caller frame seen from the eager iterator into stdlib_sys_getframe.py. Assisted-by: Claude Code:claude-opus-5-5 * Emit the generator prefix for top-level await modules Module code compiled with PyCF_ALLOW_TOP_LEVEL_AWAIT and marked ste_coroutine now starts with RETURN_GENERATOR; POP_TOP. The prefix and RESUME emission move into emit_scope_prefix, shared by enter_scope and the module entry points, as in codegen_enter_scope. Fixes test_builtin.test_compile_top_level_await. Assisted-by: Claude Code:claude-opus-5-5 * Allow the c_long cast in _opcode.stack_effect c_long is i32 on Windows, where clippy reports the cast to i32 as unnecessary. Assisted-by: Claude Code:claude-opus-5-5 * Update malachite crates to 0.14.0 pymath requires malachite-bigint ^0, so the example projects, which have no lock file, resolve it to 0.14.0 while the workspace pinned 0.13.0, and fail to build. Assisted-by: Claude Code:claude-opus-5-5 --------- Co-authored-by: dot <dot@youknowone.org>
This commit is contained in:
1 parent
b429155ff6
commit
2a09ee02bd
102 files changed
+8290
-3628
No files matched your search
@@ -12,6 +12,7 @@ pyast
|
||||
pyattr
|
||||
pyclass
|
||||
pyclassmethod
|
||||
pycode
|
||||
pyexception
|
||||
pyfunction
|
||||
pygetset
|
||||
|
||||
@@ -790,9 +790,7 @@ jobs:
|
||||
with:
|
||||
repository: python/cpython
|
||||
path: cpython
|
||||
# RustPython still uses the 3.14 bytecode format; this source is only
|
||||
# for opcode metadata, independent of the 3.15 runtime target.
|
||||
ref: "v3.14.7"
|
||||
ref: "v3.15.0"
|
||||
persist-credentials: false
|
||||
|
||||
- name: prek run (manual stage)
|
||||
|
||||
+381
-366
@@ -1,371 +1,386 @@
|
||||
# This file is generated by tools/opcode_metadata/generate_py_opcode_metadata.py
|
||||
# for RustPython bytecode format (CPython 3.14 compatible opcode numbers).
|
||||
# for RustPython bytecode format.
|
||||
# Do not edit!
|
||||
_specializations = frozendict(
|
||||
RESUME=(
|
||||
"RESUME_CHECK",
|
||||
"RESUME_CHECK_JIT",
|
||||
),
|
||||
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_SUBSCR_LIST_INT",
|
||||
"BINARY_OP_SUBSCR_LIST_SLICE",
|
||||
"BINARY_OP_SUBSCR_TUPLE_INT",
|
||||
"BINARY_OP_SUBSCR_STR_INT",
|
||||
"BINARY_OP_SUBSCR_USTR_INT",
|
||||
"BINARY_OP_SUBSCR_DICT",
|
||||
"BINARY_OP_SUBSCR_GETITEM",
|
||||
"BINARY_OP_INPLACE_ADD_UNICODE",
|
||||
"BINARY_OP_EXTEND",
|
||||
),
|
||||
STORE_SUBSCR=(
|
||||
"STORE_SUBSCR_DICT",
|
||||
"STORE_SUBSCR_LIST_INT",
|
||||
),
|
||||
SEND=(
|
||||
"SEND_GEN",
|
||||
"SEND_VIRTUAL",
|
||||
"SEND_ASYNC_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_CLASS_WITH_METACLASS_CHECK",
|
||||
"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",
|
||||
),
|
||||
JUMP_BACKWARD=(
|
||||
"JUMP_BACKWARD_NO_JIT",
|
||||
"JUMP_BACKWARD_JIT",
|
||||
),
|
||||
GET_ITER=(
|
||||
"GET_ITER_SELF",
|
||||
"GET_ITER_VIRTUAL",
|
||||
),
|
||||
FOR_ITER=(
|
||||
"FOR_ITER_LIST",
|
||||
"FOR_ITER_TUPLE",
|
||||
"FOR_ITER_RANGE",
|
||||
"FOR_ITER_GEN",
|
||||
"FOR_ITER_VIRTUAL",
|
||||
),
|
||||
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",
|
||||
),
|
||||
CALL_KW=(
|
||||
"CALL_KW_BOUND_METHOD",
|
||||
"CALL_KW_PY",
|
||||
"CALL_KW_NON_PY",
|
||||
),
|
||||
CALL_FUNCTION_EX=(
|
||||
"CALL_EX_PY",
|
||||
"CALL_EX_NON_PY_GENERAL",
|
||||
),
|
||||
)
|
||||
|
||||
_specializations = {
|
||||
"RESUME": [
|
||||
"RESUME_CHECK",
|
||||
],
|
||||
"LOAD_CONST": [
|
||||
"LOAD_CONST_MORTAL",
|
||||
"LOAD_CONST_IMMORTAL",
|
||||
],
|
||||
"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_SUBSCR_LIST_INT",
|
||||
"BINARY_OP_SUBSCR_LIST_SLICE",
|
||||
"BINARY_OP_SUBSCR_TUPLE_INT",
|
||||
"BINARY_OP_SUBSCR_STR_INT",
|
||||
"BINARY_OP_SUBSCR_DICT",
|
||||
"BINARY_OP_SUBSCR_GETITEM",
|
||||
"BINARY_OP_EXTEND",
|
||||
"BINARY_OP_INPLACE_ADD_UNICODE",
|
||||
],
|
||||
"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_CLASS_WITH_METACLASS_CHECK",
|
||||
"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",
|
||||
],
|
||||
"JUMP_BACKWARD": [
|
||||
"JUMP_BACKWARD_NO_JIT",
|
||||
"JUMP_BACKWARD_JIT",
|
||||
],
|
||||
"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",
|
||||
],
|
||||
"CALL_KW": [
|
||||
"CALL_KW_BOUND_METHOD",
|
||||
"CALL_KW_PY",
|
||||
"CALL_KW_NON_PY",
|
||||
],
|
||||
}
|
||||
_specialized_opmap = frozendict(
|
||||
BINARY_OP_ADD_FLOAT=129,
|
||||
BINARY_OP_ADD_INT=130,
|
||||
BINARY_OP_ADD_UNICODE=131,
|
||||
BINARY_OP_EXTEND=132,
|
||||
BINARY_OP_INPLACE_ADD_UNICODE=3,
|
||||
BINARY_OP_MULTIPLY_FLOAT=133,
|
||||
BINARY_OP_MULTIPLY_INT=134,
|
||||
BINARY_OP_SUBSCR_DICT=135,
|
||||
BINARY_OP_SUBSCR_GETITEM=136,
|
||||
BINARY_OP_SUBSCR_LIST_INT=137,
|
||||
BINARY_OP_SUBSCR_LIST_SLICE=138,
|
||||
BINARY_OP_SUBSCR_STR_INT=139,
|
||||
BINARY_OP_SUBSCR_TUPLE_INT=140,
|
||||
BINARY_OP_SUBSCR_USTR_INT=141,
|
||||
BINARY_OP_SUBTRACT_FLOAT=142,
|
||||
BINARY_OP_SUBTRACT_INT=143,
|
||||
CALL_ALLOC_AND_ENTER_INIT=144,
|
||||
CALL_BOUND_METHOD_EXACT_ARGS=145,
|
||||
CALL_BOUND_METHOD_GENERAL=146,
|
||||
CALL_BUILTIN_CLASS=147,
|
||||
CALL_BUILTIN_FAST=148,
|
||||
CALL_BUILTIN_FAST_WITH_KEYWORDS=149,
|
||||
CALL_BUILTIN_O=150,
|
||||
CALL_EX_NON_PY_GENERAL=151,
|
||||
CALL_EX_PY=152,
|
||||
CALL_ISINSTANCE=153,
|
||||
CALL_KW_BOUND_METHOD=154,
|
||||
CALL_KW_NON_PY=155,
|
||||
CALL_KW_PY=156,
|
||||
CALL_LEN=157,
|
||||
CALL_LIST_APPEND=158,
|
||||
CALL_METHOD_DESCRIPTOR_FAST=159,
|
||||
CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS=160,
|
||||
CALL_METHOD_DESCRIPTOR_NOARGS=161,
|
||||
CALL_METHOD_DESCRIPTOR_O=162,
|
||||
CALL_NON_PY_GENERAL=163,
|
||||
CALL_PY_EXACT_ARGS=164,
|
||||
CALL_PY_GENERAL=165,
|
||||
CALL_STR_1=166,
|
||||
CALL_TUPLE_1=167,
|
||||
CALL_TYPE_1=168,
|
||||
COMPARE_OP_FLOAT=169,
|
||||
COMPARE_OP_INT=170,
|
||||
COMPARE_OP_STR=171,
|
||||
CONTAINS_OP_DICT=172,
|
||||
CONTAINS_OP_SET=173,
|
||||
FOR_ITER_GEN=174,
|
||||
FOR_ITER_LIST=175,
|
||||
FOR_ITER_RANGE=176,
|
||||
FOR_ITER_TUPLE=177,
|
||||
FOR_ITER_VIRTUAL=178,
|
||||
GET_ITER_SELF=179,
|
||||
GET_ITER_VIRTUAL=180,
|
||||
JUMP_BACKWARD_JIT=181,
|
||||
JUMP_BACKWARD_NO_JIT=182,
|
||||
LOAD_ATTR_CLASS=183,
|
||||
LOAD_ATTR_CLASS_WITH_METACLASS_CHECK=184,
|
||||
LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN=185,
|
||||
LOAD_ATTR_INSTANCE_VALUE=186,
|
||||
LOAD_ATTR_METHOD_LAZY_DICT=187,
|
||||
LOAD_ATTR_METHOD_NO_DICT=188,
|
||||
LOAD_ATTR_METHOD_WITH_VALUES=189,
|
||||
LOAD_ATTR_MODULE=190,
|
||||
LOAD_ATTR_NONDESCRIPTOR_NO_DICT=191,
|
||||
LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES=192,
|
||||
LOAD_ATTR_PROPERTY=193,
|
||||
LOAD_ATTR_SLOT=194,
|
||||
LOAD_ATTR_WITH_HINT=195,
|
||||
LOAD_GLOBAL_BUILTIN=196,
|
||||
LOAD_GLOBAL_MODULE=197,
|
||||
LOAD_SUPER_ATTR_ATTR=198,
|
||||
LOAD_SUPER_ATTR_METHOD=199,
|
||||
RESUME_CHECK=200,
|
||||
RESUME_CHECK_JIT=201,
|
||||
SEND_ASYNC_GEN=202,
|
||||
SEND_GEN=203,
|
||||
SEND_VIRTUAL=204,
|
||||
STORE_ATTR_INSTANCE_VALUE=205,
|
||||
STORE_ATTR_SLOT=206,
|
||||
STORE_ATTR_WITH_HINT=207,
|
||||
STORE_SUBSCR_DICT=208,
|
||||
STORE_SUBSCR_LIST_INT=209,
|
||||
TO_BOOL_ALWAYS_TRUE=210,
|
||||
TO_BOOL_BOOL=211,
|
||||
TO_BOOL_INT=212,
|
||||
TO_BOOL_LIST=213,
|
||||
TO_BOOL_NONE=214,
|
||||
TO_BOOL_STR=215,
|
||||
UNPACK_SEQUENCE_LIST=216,
|
||||
UNPACK_SEQUENCE_TUPLE=217,
|
||||
UNPACK_SEQUENCE_TWO_TUPLE=218,
|
||||
)
|
||||
|
||||
_specialized_opmap = {
|
||||
'BINARY_OP_ADD_FLOAT': 129,
|
||||
'BINARY_OP_ADD_INT': 130,
|
||||
'BINARY_OP_ADD_UNICODE': 131,
|
||||
'BINARY_OP_EXTEND': 132,
|
||||
'BINARY_OP_INPLACE_ADD_UNICODE': 3,
|
||||
'BINARY_OP_MULTIPLY_FLOAT': 133,
|
||||
'BINARY_OP_MULTIPLY_INT': 134,
|
||||
'BINARY_OP_SUBSCR_DICT': 135,
|
||||
'BINARY_OP_SUBSCR_GETITEM': 136,
|
||||
'BINARY_OP_SUBSCR_LIST_INT': 137,
|
||||
'BINARY_OP_SUBSCR_LIST_SLICE': 138,
|
||||
'BINARY_OP_SUBSCR_STR_INT': 139,
|
||||
'BINARY_OP_SUBSCR_TUPLE_INT': 140,
|
||||
'BINARY_OP_SUBTRACT_FLOAT': 141,
|
||||
'BINARY_OP_SUBTRACT_INT': 142,
|
||||
'CALL_ALLOC_AND_ENTER_INIT': 143,
|
||||
'CALL_BOUND_METHOD_EXACT_ARGS': 144,
|
||||
'CALL_BOUND_METHOD_GENERAL': 145,
|
||||
'CALL_BUILTIN_CLASS': 146,
|
||||
'CALL_BUILTIN_FAST': 147,
|
||||
'CALL_BUILTIN_FAST_WITH_KEYWORDS': 148,
|
||||
'CALL_BUILTIN_O': 149,
|
||||
'CALL_ISINSTANCE': 150,
|
||||
'CALL_KW_BOUND_METHOD': 151,
|
||||
'CALL_KW_NON_PY': 152,
|
||||
'CALL_KW_PY': 153,
|
||||
'CALL_LEN': 154,
|
||||
'CALL_LIST_APPEND': 155,
|
||||
'CALL_METHOD_DESCRIPTOR_FAST': 156,
|
||||
'CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS': 157,
|
||||
'CALL_METHOD_DESCRIPTOR_NOARGS': 158,
|
||||
'CALL_METHOD_DESCRIPTOR_O': 159,
|
||||
'CALL_NON_PY_GENERAL': 160,
|
||||
'CALL_PY_EXACT_ARGS': 161,
|
||||
'CALL_PY_GENERAL': 162,
|
||||
'CALL_STR_1': 163,
|
||||
'CALL_TUPLE_1': 164,
|
||||
'CALL_TYPE_1': 165,
|
||||
'COMPARE_OP_FLOAT': 166,
|
||||
'COMPARE_OP_INT': 167,
|
||||
'COMPARE_OP_STR': 168,
|
||||
'CONTAINS_OP_DICT': 169,
|
||||
'CONTAINS_OP_SET': 170,
|
||||
'FOR_ITER_GEN': 171,
|
||||
'FOR_ITER_LIST': 172,
|
||||
'FOR_ITER_RANGE': 173,
|
||||
'FOR_ITER_TUPLE': 174,
|
||||
'JUMP_BACKWARD_JIT': 175,
|
||||
'JUMP_BACKWARD_NO_JIT': 176,
|
||||
'LOAD_ATTR_CLASS': 177,
|
||||
'LOAD_ATTR_CLASS_WITH_METACLASS_CHECK': 178,
|
||||
'LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN': 179,
|
||||
'LOAD_ATTR_INSTANCE_VALUE': 180,
|
||||
'LOAD_ATTR_METHOD_LAZY_DICT': 181,
|
||||
'LOAD_ATTR_METHOD_NO_DICT': 182,
|
||||
'LOAD_ATTR_METHOD_WITH_VALUES': 183,
|
||||
'LOAD_ATTR_MODULE': 184,
|
||||
'LOAD_ATTR_NONDESCRIPTOR_NO_DICT': 185,
|
||||
'LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES': 186,
|
||||
'LOAD_ATTR_PROPERTY': 187,
|
||||
'LOAD_ATTR_SLOT': 188,
|
||||
'LOAD_ATTR_WITH_HINT': 189,
|
||||
'LOAD_CONST_IMMORTAL': 190,
|
||||
'LOAD_CONST_MORTAL': 191,
|
||||
'LOAD_GLOBAL_BUILTIN': 192,
|
||||
'LOAD_GLOBAL_MODULE': 193,
|
||||
'LOAD_SUPER_ATTR_ATTR': 194,
|
||||
'LOAD_SUPER_ATTR_METHOD': 195,
|
||||
'RESUME_CHECK': 196,
|
||||
'SEND_GEN': 197,
|
||||
'STORE_ATTR_INSTANCE_VALUE': 198,
|
||||
'STORE_ATTR_SLOT': 199,
|
||||
'STORE_ATTR_WITH_HINT': 200,
|
||||
'STORE_SUBSCR_DICT': 201,
|
||||
'STORE_SUBSCR_LIST_INT': 202,
|
||||
'TO_BOOL_ALWAYS_TRUE': 203,
|
||||
'TO_BOOL_BOOL': 204,
|
||||
'TO_BOOL_INT': 205,
|
||||
'TO_BOOL_LIST': 206,
|
||||
'TO_BOOL_NONE': 207,
|
||||
'TO_BOOL_STR': 208,
|
||||
'UNPACK_SEQUENCE_LIST': 209,
|
||||
'UNPACK_SEQUENCE_TUPLE': 210,
|
||||
'UNPACK_SEQUENCE_TWO_TUPLE': 211,
|
||||
}
|
||||
opmap = frozendict(
|
||||
CACHE=0,
|
||||
RESERVED=17,
|
||||
RESUME=128,
|
||||
INSTRUMENTED_LINE=253,
|
||||
ENTER_EXECUTOR=254,
|
||||
TRACE_RECORD=255,
|
||||
BINARY_SLICE=1,
|
||||
BUILD_TEMPLATE=2,
|
||||
CALL_FUNCTION_EX=4,
|
||||
CHECK_EG_MATCH=5,
|
||||
CHECK_EXC_MATCH=6,
|
||||
CLEANUP_THROW=7,
|
||||
DELETE_SUBSCR=8,
|
||||
END_FOR=9,
|
||||
END_SEND=10,
|
||||
EXIT_INIT_CHECK=11,
|
||||
FORMAT_SIMPLE=12,
|
||||
FORMAT_WITH_SPEC=13,
|
||||
GET_AITER=14,
|
||||
GET_ANEXT=15,
|
||||
GET_LEN=16,
|
||||
INTERPRETER_EXIT=18,
|
||||
LOAD_BUILD_CLASS=19,
|
||||
LOAD_LOCALS=20,
|
||||
MAKE_FUNCTION=21,
|
||||
MATCH_KEYS=22,
|
||||
MATCH_MAPPING=23,
|
||||
MATCH_SEQUENCE=24,
|
||||
NOP=25,
|
||||
NOT_TAKEN=26,
|
||||
POP_EXCEPT=27,
|
||||
POP_ITER=28,
|
||||
POP_TOP=29,
|
||||
PUSH_EXC_INFO=30,
|
||||
PUSH_NULL=31,
|
||||
RETURN_GENERATOR=32,
|
||||
RETURN_VALUE=33,
|
||||
SETUP_ANNOTATIONS=34,
|
||||
STORE_SLICE=35,
|
||||
STORE_SUBSCR=36,
|
||||
TO_BOOL=37,
|
||||
UNARY_INVERT=38,
|
||||
UNARY_NEGATIVE=39,
|
||||
UNARY_NOT=40,
|
||||
WITH_EXCEPT_START=41,
|
||||
BINARY_OP=42,
|
||||
BUILD_INTERPOLATION=43,
|
||||
BUILD_LIST=44,
|
||||
BUILD_MAP=45,
|
||||
BUILD_SET=46,
|
||||
BUILD_SLICE=47,
|
||||
BUILD_STRING=48,
|
||||
BUILD_TUPLE=49,
|
||||
CALL=50,
|
||||
CALL_INTRINSIC_1=51,
|
||||
CALL_INTRINSIC_2=52,
|
||||
CALL_KW=53,
|
||||
COMPARE_OP=54,
|
||||
CONTAINS_OP=55,
|
||||
CONVERT_VALUE=56,
|
||||
COPY=57,
|
||||
COPY_FREE_VARS=58,
|
||||
DELETE_ATTR=59,
|
||||
DELETE_DEREF=60,
|
||||
DELETE_FAST=61,
|
||||
DELETE_GLOBAL=62,
|
||||
DELETE_NAME=63,
|
||||
DICT_MERGE=64,
|
||||
DICT_UPDATE=65,
|
||||
END_ASYNC_FOR=66,
|
||||
EXTENDED_ARG=67,
|
||||
FOR_ITER=68,
|
||||
GET_AWAITABLE=69,
|
||||
GET_ITER=70,
|
||||
IMPORT_FROM=71,
|
||||
IMPORT_NAME=72,
|
||||
IS_OP=73,
|
||||
JUMP_BACKWARD=74,
|
||||
JUMP_BACKWARD_NO_INTERRUPT=75,
|
||||
JUMP_FORWARD=76,
|
||||
LIST_APPEND=77,
|
||||
LIST_EXTEND=78,
|
||||
LOAD_ATTR=79,
|
||||
LOAD_COMMON_CONSTANT=80,
|
||||
LOAD_CONST=81,
|
||||
LOAD_DEREF=82,
|
||||
LOAD_FAST=83,
|
||||
LOAD_FAST_AND_CLEAR=84,
|
||||
LOAD_FAST_BORROW=85,
|
||||
LOAD_FAST_BORROW_LOAD_FAST_BORROW=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_SMALL_INT=93,
|
||||
LOAD_SPECIAL=94,
|
||||
LOAD_SUPER_ATTR=95,
|
||||
MAKE_CELL=96,
|
||||
MAP_ADD=97,
|
||||
MATCH_CLASS=98,
|
||||
POP_JUMP_IF_FALSE=99,
|
||||
POP_JUMP_IF_NONE=100,
|
||||
POP_JUMP_IF_NOT_NONE=101,
|
||||
POP_JUMP_IF_TRUE=102,
|
||||
RAISE_VARARGS=103,
|
||||
RERAISE=104,
|
||||
SEND=105,
|
||||
SET_ADD=106,
|
||||
SET_FUNCTION_ATTRIBUTE=107,
|
||||
SET_UPDATE=108,
|
||||
STORE_ATTR=109,
|
||||
STORE_DEREF=110,
|
||||
STORE_FAST=111,
|
||||
STORE_FAST_LOAD_FAST=112,
|
||||
STORE_FAST_STORE_FAST=113,
|
||||
STORE_GLOBAL=114,
|
||||
STORE_NAME=115,
|
||||
SWAP=116,
|
||||
UNPACK_EX=117,
|
||||
UNPACK_SEQUENCE=118,
|
||||
YIELD_VALUE=119,
|
||||
INSTRUMENTED_END_FOR=233,
|
||||
INSTRUMENTED_POP_ITER=234,
|
||||
INSTRUMENTED_END_SEND=235,
|
||||
INSTRUMENTED_FOR_ITER=236,
|
||||
INSTRUMENTED_INSTRUCTION=237,
|
||||
INSTRUMENTED_JUMP_FORWARD=238,
|
||||
INSTRUMENTED_NOT_TAKEN=239,
|
||||
INSTRUMENTED_POP_JUMP_IF_TRUE=240,
|
||||
INSTRUMENTED_POP_JUMP_IF_FALSE=241,
|
||||
INSTRUMENTED_POP_JUMP_IF_NONE=242,
|
||||
INSTRUMENTED_POP_JUMP_IF_NOT_NONE=243,
|
||||
INSTRUMENTED_RESUME=244,
|
||||
INSTRUMENTED_RETURN_VALUE=245,
|
||||
INSTRUMENTED_YIELD_VALUE=246,
|
||||
INSTRUMENTED_END_ASYNC_FOR=247,
|
||||
INSTRUMENTED_LOAD_SUPER_ATTR=248,
|
||||
INSTRUMENTED_CALL=249,
|
||||
INSTRUMENTED_CALL_KW=250,
|
||||
INSTRUMENTED_CALL_FUNCTION_EX=251,
|
||||
INSTRUMENTED_JUMP_BACKWARD=252,
|
||||
ANNOTATIONS_PLACEHOLDER=256,
|
||||
JUMP=257,
|
||||
JUMP_IF_FALSE=258,
|
||||
JUMP_IF_TRUE=259,
|
||||
JUMP_NO_INTERRUPT=260,
|
||||
LOAD_CLOSURE=261,
|
||||
POP_BLOCK=262,
|
||||
SETUP_CLEANUP=263,
|
||||
SETUP_FINALLY=264,
|
||||
SETUP_WITH=265,
|
||||
STORE_FAST_MAYBE_NULL=266,
|
||||
)
|
||||
|
||||
opmap = {
|
||||
'CACHE': 0,
|
||||
'RESERVED': 17,
|
||||
'RESUME': 128,
|
||||
'INSTRUMENTED_LINE': 254,
|
||||
'ENTER_EXECUTOR': 255,
|
||||
'BINARY_SLICE': 1,
|
||||
'BUILD_TEMPLATE': 2,
|
||||
'CALL_FUNCTION_EX': 4,
|
||||
'CHECK_EG_MATCH': 5,
|
||||
'CHECK_EXC_MATCH': 6,
|
||||
'CLEANUP_THROW': 7,
|
||||
'DELETE_SUBSCR': 8,
|
||||
'END_FOR': 9,
|
||||
'END_SEND': 10,
|
||||
'EXIT_INIT_CHECK': 11,
|
||||
'FORMAT_SIMPLE': 12,
|
||||
'FORMAT_WITH_SPEC': 13,
|
||||
'GET_AITER': 14,
|
||||
'GET_ANEXT': 15,
|
||||
'GET_ITER': 16,
|
||||
'GET_LEN': 18,
|
||||
'GET_YIELD_FROM_ITER': 19,
|
||||
'INTERPRETER_EXIT': 20,
|
||||
'LOAD_BUILD_CLASS': 21,
|
||||
'LOAD_LOCALS': 22,
|
||||
'MAKE_FUNCTION': 23,
|
||||
'MATCH_KEYS': 24,
|
||||
'MATCH_MAPPING': 25,
|
||||
'MATCH_SEQUENCE': 26,
|
||||
'NOP': 27,
|
||||
'NOT_TAKEN': 28,
|
||||
'POP_EXCEPT': 29,
|
||||
'POP_ITER': 30,
|
||||
'POP_TOP': 31,
|
||||
'PUSH_EXC_INFO': 32,
|
||||
'PUSH_NULL': 33,
|
||||
'RETURN_GENERATOR': 34,
|
||||
'RETURN_VALUE': 35,
|
||||
'SETUP_ANNOTATIONS': 36,
|
||||
'STORE_SLICE': 37,
|
||||
'STORE_SUBSCR': 38,
|
||||
'TO_BOOL': 39,
|
||||
'UNARY_INVERT': 40,
|
||||
'UNARY_NEGATIVE': 41,
|
||||
'UNARY_NOT': 42,
|
||||
'WITH_EXCEPT_START': 43,
|
||||
'BINARY_OP': 44,
|
||||
'BUILD_INTERPOLATION': 45,
|
||||
'BUILD_LIST': 46,
|
||||
'BUILD_MAP': 47,
|
||||
'BUILD_SET': 48,
|
||||
'BUILD_SLICE': 49,
|
||||
'BUILD_STRING': 50,
|
||||
'BUILD_TUPLE': 51,
|
||||
'CALL': 52,
|
||||
'CALL_INTRINSIC_1': 53,
|
||||
'CALL_INTRINSIC_2': 54,
|
||||
'CALL_KW': 55,
|
||||
'COMPARE_OP': 56,
|
||||
'CONTAINS_OP': 57,
|
||||
'CONVERT_VALUE': 58,
|
||||
'COPY': 59,
|
||||
'COPY_FREE_VARS': 60,
|
||||
'DELETE_ATTR': 61,
|
||||
'DELETE_DEREF': 62,
|
||||
'DELETE_FAST': 63,
|
||||
'DELETE_GLOBAL': 64,
|
||||
'DELETE_NAME': 65,
|
||||
'DICT_MERGE': 66,
|
||||
'DICT_UPDATE': 67,
|
||||
'END_ASYNC_FOR': 68,
|
||||
'EXTENDED_ARG': 69,
|
||||
'FOR_ITER': 70,
|
||||
'GET_AWAITABLE': 71,
|
||||
'IMPORT_FROM': 72,
|
||||
'IMPORT_NAME': 73,
|
||||
'IS_OP': 74,
|
||||
'JUMP_BACKWARD': 75,
|
||||
'JUMP_BACKWARD_NO_INTERRUPT': 76,
|
||||
'JUMP_FORWARD': 77,
|
||||
'LIST_APPEND': 78,
|
||||
'LIST_EXTEND': 79,
|
||||
'LOAD_ATTR': 80,
|
||||
'LOAD_COMMON_CONSTANT': 81,
|
||||
'LOAD_CONST': 82,
|
||||
'LOAD_DEREF': 83,
|
||||
'LOAD_FAST': 84,
|
||||
'LOAD_FAST_AND_CLEAR': 85,
|
||||
'LOAD_FAST_BORROW': 86,
|
||||
'LOAD_FAST_BORROW_LOAD_FAST_BORROW': 87,
|
||||
'LOAD_FAST_CHECK': 88,
|
||||
'LOAD_FAST_LOAD_FAST': 89,
|
||||
'LOAD_FROM_DICT_OR_DEREF': 90,
|
||||
'LOAD_FROM_DICT_OR_GLOBALS': 91,
|
||||
'LOAD_GLOBAL': 92,
|
||||
'LOAD_NAME': 93,
|
||||
'LOAD_SMALL_INT': 94,
|
||||
'LOAD_SPECIAL': 95,
|
||||
'LOAD_SUPER_ATTR': 96,
|
||||
'MAKE_CELL': 97,
|
||||
'MAP_ADD': 98,
|
||||
'MATCH_CLASS': 99,
|
||||
'POP_JUMP_IF_FALSE': 100,
|
||||
'POP_JUMP_IF_NONE': 101,
|
||||
'POP_JUMP_IF_NOT_NONE': 102,
|
||||
'POP_JUMP_IF_TRUE': 103,
|
||||
'RAISE_VARARGS': 104,
|
||||
'RERAISE': 105,
|
||||
'SEND': 106,
|
||||
'SET_ADD': 107,
|
||||
'SET_FUNCTION_ATTRIBUTE': 108,
|
||||
'SET_UPDATE': 109,
|
||||
'STORE_ATTR': 110,
|
||||
'STORE_DEREF': 111,
|
||||
'STORE_FAST': 112,
|
||||
'STORE_FAST_LOAD_FAST': 113,
|
||||
'STORE_FAST_STORE_FAST': 114,
|
||||
'STORE_GLOBAL': 115,
|
||||
'STORE_NAME': 116,
|
||||
'SWAP': 117,
|
||||
'UNPACK_EX': 118,
|
||||
'UNPACK_SEQUENCE': 119,
|
||||
'YIELD_VALUE': 120,
|
||||
'INSTRUMENTED_END_FOR': 234,
|
||||
'INSTRUMENTED_POP_ITER': 235,
|
||||
'INSTRUMENTED_END_SEND': 236,
|
||||
'INSTRUMENTED_FOR_ITER': 237,
|
||||
'INSTRUMENTED_INSTRUCTION': 238,
|
||||
'INSTRUMENTED_JUMP_FORWARD': 239,
|
||||
'INSTRUMENTED_NOT_TAKEN': 240,
|
||||
'INSTRUMENTED_POP_JUMP_IF_TRUE': 241,
|
||||
'INSTRUMENTED_POP_JUMP_IF_FALSE': 242,
|
||||
'INSTRUMENTED_POP_JUMP_IF_NONE': 243,
|
||||
'INSTRUMENTED_POP_JUMP_IF_NOT_NONE': 244,
|
||||
'INSTRUMENTED_RESUME': 245,
|
||||
'INSTRUMENTED_RETURN_VALUE': 246,
|
||||
'INSTRUMENTED_YIELD_VALUE': 247,
|
||||
'INSTRUMENTED_END_ASYNC_FOR': 248,
|
||||
'INSTRUMENTED_LOAD_SUPER_ATTR': 249,
|
||||
'INSTRUMENTED_CALL': 250,
|
||||
'INSTRUMENTED_CALL_KW': 251,
|
||||
'INSTRUMENTED_CALL_FUNCTION_EX': 252,
|
||||
'INSTRUMENTED_JUMP_BACKWARD': 253,
|
||||
'ANNOTATIONS_PLACEHOLDER': 256,
|
||||
'JUMP': 257,
|
||||
'JUMP_IF_FALSE': 258,
|
||||
'JUMP_IF_TRUE': 259,
|
||||
'JUMP_NO_INTERRUPT': 260,
|
||||
'LOAD_CLOSURE': 261,
|
||||
'POP_BLOCK': 262,
|
||||
'SETUP_CLEANUP': 263,
|
||||
'SETUP_FINALLY': 264,
|
||||
'SETUP_WITH': 265,
|
||||
'STORE_FAST_MAYBE_NULL': 266,
|
||||
}
|
||||
|
||||
HAVE_ARGUMENT = 43
|
||||
MIN_INSTRUMENTED_OPCODE = 234
|
||||
HAVE_ARGUMENT = 41
|
||||
MIN_INSTRUMENTED_OPCODE = 233
|
||||
Vendored
+83
-24
@@ -369,9 +369,15 @@ def _setoption(arg):
|
||||
if message or module:
|
||||
import re
|
||||
if message:
|
||||
message = re.escape(message)
|
||||
if len(message) >= 2 and message[0] == message[-1] == '/':
|
||||
message = message[1:-1]
|
||||
else:
|
||||
message = re.escape(message)
|
||||
if module:
|
||||
module = re.escape(module) + r'\z'
|
||||
if len(module) >= 2 and module[0] == module[-1] == '/':
|
||||
module = module[1:-1]
|
||||
else:
|
||||
module = re.escape(module) + r'\z'
|
||||
if lineno:
|
||||
try:
|
||||
lineno = int(lineno)
|
||||
@@ -381,7 +387,23 @@ def _setoption(arg):
|
||||
raise _wm._OptionError("invalid lineno %r" % (lineno,)) from None
|
||||
else:
|
||||
lineno = 0
|
||||
_wm.filterwarnings(action, message, category, module, lineno)
|
||||
try:
|
||||
_wm.filterwarnings(action, message, category, module, lineno)
|
||||
except re.PatternError if message or module else ():
|
||||
if message:
|
||||
try:
|
||||
re.compile(message)
|
||||
except re.PatternError:
|
||||
raise _wm._OptionError(f"invalid regular expression for "
|
||||
f"message: {message!r}") from None
|
||||
if module:
|
||||
try:
|
||||
re.compile(module)
|
||||
except re.PatternError:
|
||||
raise _wm._OptionError(f"invalid regular expression for "
|
||||
f"module: {module!r}") from None
|
||||
# Should never happen.
|
||||
raise
|
||||
|
||||
|
||||
# Helper for _setoption()
|
||||
@@ -449,9 +471,12 @@ def warn(message, category=None, stacklevel=1, source=None,
|
||||
# Check category argument
|
||||
if category is None:
|
||||
category = UserWarning
|
||||
if not (isinstance(category, type) and issubclass(category, Warning)):
|
||||
raise TypeError("category must be a Warning subclass, "
|
||||
"not '{:s}'".format(type(category).__name__))
|
||||
elif not isinstance(category, type):
|
||||
raise TypeError(f"category must be a Warning subclass, not "
|
||||
f"'{type(category).__name__}'")
|
||||
elif not issubclass(category, Warning):
|
||||
raise TypeError(f"category must be a Warning subclass, not "
|
||||
f"class '{category.__name__}'")
|
||||
if not isinstance(skip_file_prefixes, tuple):
|
||||
# The C version demands a tuple for implementation performance.
|
||||
raise TypeError('skip_file_prefixes must be a tuple of strs.')
|
||||
@@ -495,14 +520,43 @@ def warn(message, category=None, stacklevel=1, source=None,
|
||||
)
|
||||
|
||||
|
||||
def _match_filename(pattern, filename, *, MS_WINDOWS=(sys.platform == 'win32')):
|
||||
if not filename:
|
||||
return pattern.match('<unknown>') is not None
|
||||
if filename[0] == '<' and filename[-1] == '>':
|
||||
return pattern.match(filename) is not None
|
||||
|
||||
is_py = (filename[-3:].lower() == '.py'
|
||||
if MS_WINDOWS else
|
||||
filename.endswith('.py'))
|
||||
if is_py:
|
||||
filename = filename[:-3]
|
||||
if pattern.match(filename): # for backward compatibility
|
||||
return True
|
||||
if MS_WINDOWS:
|
||||
if not is_py and filename[-4:].lower() == '.pyw':
|
||||
filename = filename[:-4]
|
||||
is_py = True
|
||||
if is_py and filename[-9:].lower() in (r'\__init__', '/__init__'):
|
||||
filename = filename[:-9]
|
||||
filename = filename.replace('\\', '/')
|
||||
else:
|
||||
if is_py and filename.endswith('/__init__'):
|
||||
filename = filename[:-9]
|
||||
filename = filename.replace('/', '.')
|
||||
i = 0
|
||||
while True:
|
||||
if pattern.match(filename, i):
|
||||
return True
|
||||
i = filename.find('.', i) + 1
|
||||
if not i:
|
||||
return False
|
||||
|
||||
|
||||
def warn_explicit(message, category, filename, lineno,
|
||||
module=None, registry=None, module_globals=None,
|
||||
source=None):
|
||||
lineno = int(lineno)
|
||||
if module is None:
|
||||
module = filename or "<unknown>"
|
||||
if module[-3:].lower() == ".py":
|
||||
module = module[:-3] # XXX What about leading pathname?
|
||||
if isinstance(message, Warning):
|
||||
text = str(message)
|
||||
category = message.__class__
|
||||
@@ -524,9 +578,11 @@ def warn_explicit(message, category, filename, lineno,
|
||||
action, msg, cat, mod, ln = item
|
||||
if ((msg is None or msg.match(text)) and
|
||||
issubclass(category, cat) and
|
||||
(mod is None or mod.match(module)) and
|
||||
(ln == 0 or lineno == ln)):
|
||||
break
|
||||
(ln == 0 or lineno == ln) and
|
||||
(mod is None or (_match_filename(mod, filename)
|
||||
if module is None else
|
||||
mod.match(module)))):
|
||||
break
|
||||
else:
|
||||
action = _wm.defaultaction
|
||||
# Early exit actions
|
||||
@@ -564,17 +620,18 @@ def warn_explicit(message, category, filename, lineno,
|
||||
linecache.getlines(filename, module_globals)
|
||||
|
||||
# Print message and context
|
||||
msg = _wm.WarningMessage(message, category, filename, lineno, source=source)
|
||||
msg = _wm.WarningMessage(message, category, filename, lineno,
|
||||
module=module, source=source)
|
||||
_wm._showwarnmsg(msg)
|
||||
|
||||
|
||||
class WarningMessage(object):
|
||||
|
||||
_WARNING_DETAILS = ("message", "category", "filename", "lineno", "file",
|
||||
"line", "source")
|
||||
"line", "source", "module")
|
||||
|
||||
def __init__(self, message, category, filename, lineno, file=None,
|
||||
line=None, source=None):
|
||||
line=None, source=None, module=None):
|
||||
self.message = message
|
||||
self.category = category
|
||||
self.filename = filename
|
||||
@@ -582,12 +639,14 @@ class WarningMessage(object):
|
||||
self.file = file
|
||||
self.line = line
|
||||
self.source = source
|
||||
self.module = module
|
||||
self._category_name = category.__name__ if category else None
|
||||
|
||||
def __str__(self):
|
||||
return ("{message : %r, category : %r, filename : %r, lineno : %s, "
|
||||
"line : %r}" % (self.message, self._category_name,
|
||||
self.filename, self.lineno, self.line))
|
||||
return ("{message : %r, category : %r, module : %r, "
|
||||
"filename : %r, lineno : %s, line : %r}" % (
|
||||
self.message, self._category_name, self.module,
|
||||
self.filename, self.lineno, self.line))
|
||||
|
||||
def __repr__(self):
|
||||
return f'<{type(self).__qualname__} {self}>'
|
||||
@@ -647,8 +706,8 @@ class catch_warnings(object):
|
||||
context = None
|
||||
self._filters = self._module.filters
|
||||
self._module.filters = self._filters[:]
|
||||
self._showwarning = self._module.showwarning
|
||||
self._showwarnmsg_impl = self._module._showwarnmsg_impl
|
||||
self._showwarning = self._module.showwarning
|
||||
self._module._filters_mutated_lock_held()
|
||||
if self._record:
|
||||
if _use_context:
|
||||
@@ -656,9 +715,9 @@ class catch_warnings(object):
|
||||
else:
|
||||
log = []
|
||||
self._module._showwarnmsg_impl = log.append
|
||||
# Reset showwarning() to the default implementation to make sure
|
||||
# that _showwarnmsg() calls _showwarnmsg_impl()
|
||||
self._module.showwarning = self._module._showwarning_orig
|
||||
# Reset showwarning() to the default implementation to make sure
|
||||
# that _showwarnmsg() calls _showwarnmsg_impl()
|
||||
self._module.showwarning = self._module._showwarning_orig
|
||||
else:
|
||||
log = None
|
||||
if self._filter is not None:
|
||||
@@ -673,8 +732,8 @@ class catch_warnings(object):
|
||||
self._module._warnings_context.set(self._saved_context)
|
||||
else:
|
||||
self._module.filters = self._filters
|
||||
self._module.showwarning = self._showwarning
|
||||
self._module._showwarnmsg_impl = self._showwarnmsg_impl
|
||||
self._module.showwarning = self._showwarning
|
||||
self._module._filters_mutated_lock_held()
|
||||
|
||||
|
||||
|
||||
Vendored
+21
-6
@@ -35,6 +35,7 @@ SET_FUNCTION_ATTRIBUTE = opmap['SET_FUNCTION_ATTRIBUTE']
|
||||
FUNCTION_ATTR_FLAGS = ('defaults', 'kwdefaults', 'annotations', 'closure', 'annotate')
|
||||
|
||||
ENTER_EXECUTOR = opmap['ENTER_EXECUTOR']
|
||||
IMPORT_NAME = opmap['IMPORT_NAME']
|
||||
LOAD_GLOBAL = opmap['LOAD_GLOBAL']
|
||||
LOAD_SMALL_INT = opmap['LOAD_SMALL_INT']
|
||||
BINARY_OP = opmap['BINARY_OP']
|
||||
@@ -530,7 +531,6 @@ class Formatter:
|
||||
fields.append(instr.opname.ljust(_OPNAME_WIDTH))
|
||||
# Column: Opcode argument
|
||||
if instr.arg is not None:
|
||||
arg = repr(instr.arg)
|
||||
# If opname is longer than _OPNAME_WIDTH, we allow it to overflow into
|
||||
# the space reserved for oparg. This results in fewer misaligned opargs
|
||||
# in the disassembly output.
|
||||
@@ -601,6 +601,12 @@ class ArgResolver:
|
||||
argval, argrepr = _get_name_info(arg//4, get_name)
|
||||
if (arg & 1) and argrepr:
|
||||
argrepr = f"{argrepr} + NULL|self"
|
||||
elif deop == IMPORT_NAME:
|
||||
argval, argrepr = _get_name_info(arg//4, get_name)
|
||||
if (arg & 1) and argrepr:
|
||||
argrepr = f"{argrepr} + lazy"
|
||||
elif (arg & 2) and argrepr:
|
||||
argrepr = f"{argrepr} + eager"
|
||||
else:
|
||||
argval, argrepr = _get_name_info(arg, get_name)
|
||||
elif deop in hasjump or deop in hasexc:
|
||||
@@ -637,6 +643,7 @@ class ArgResolver:
|
||||
argrepr = _intrinsic_2_descs[arg]
|
||||
elif deop == LOAD_COMMON_CONSTANT:
|
||||
obj = _common_constants[arg]
|
||||
argval = obj
|
||||
if isinstance(obj, type):
|
||||
argrepr = obj.__name__
|
||||
else:
|
||||
@@ -686,10 +693,15 @@ def _get_const_value(op, arg, co_consts):
|
||||
Otherwise (if it is a LOAD_CONST and co_consts is not
|
||||
provided) returns the dis.UNKNOWN sentinel.
|
||||
"""
|
||||
assert op in hasconst or op == LOAD_SMALL_INT
|
||||
assert op in hasconst or op == LOAD_SMALL_INT or op == LOAD_COMMON_CONSTANT
|
||||
|
||||
if op == LOAD_SMALL_INT:
|
||||
return arg
|
||||
if op == LOAD_COMMON_CONSTANT:
|
||||
# Opargs 0-6 are callables; 7-11 are literal values.
|
||||
if 7 <= arg <= 11:
|
||||
return _common_constants[arg]
|
||||
return UNKNOWN
|
||||
argval = UNKNOWN
|
||||
if co_consts is not None:
|
||||
argval = co_consts[arg]
|
||||
@@ -1009,11 +1021,14 @@ def _find_imports(co):
|
||||
if op == IMPORT_NAME and i >= 2:
|
||||
from_op = opargs[i-1]
|
||||
level_op = opargs[i-2]
|
||||
if (from_op[0] in hasconst and
|
||||
(level_op[0] in hasconst or level_op[0] == LOAD_SMALL_INT)):
|
||||
if ((from_op[0] in hasconst or from_op[0] == LOAD_COMMON_CONSTANT) and
|
||||
(level_op[0] in hasconst or level_op[0] == LOAD_SMALL_INT or
|
||||
level_op[0] == LOAD_COMMON_CONSTANT)):
|
||||
level = _get_const_value(level_op[0], level_op[1], consts)
|
||||
fromlist = _get_const_value(from_op[0], from_op[1], consts)
|
||||
yield (names[oparg], level, fromlist)
|
||||
# IMPORT_NAME encodes lazy/eager flags in bits 0-1,
|
||||
# name index in bits 2+.
|
||||
yield (names[oparg >> 2], level, fromlist)
|
||||
|
||||
def _find_store_names(co):
|
||||
"""Find names of variables which are written in the code
|
||||
@@ -1131,7 +1146,7 @@ class Bytecode:
|
||||
def main(args=None):
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(color=True)
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-C', '--show-caches', action='store_true',
|
||||
help='show inline caches')
|
||||
parser.add_argument('-O', '--show-offsets', action='store_true',
|
||||
|
||||
Vendored
+87
-74
@@ -40,83 +40,96 @@ _intrinsic_1_descs = _opcode.get_intrinsic1_descs()
|
||||
_intrinsic_2_descs = _opcode.get_intrinsic2_descs()
|
||||
_special_method_names = _opcode.get_special_method_names()
|
||||
_common_constants = [builtins.AssertionError, builtins.NotImplementedError,
|
||||
builtins.tuple, builtins.all, builtins.any]
|
||||
builtins.tuple, builtins.all, builtins.any, builtins.list,
|
||||
builtins.set,
|
||||
# Append-only — must match CONSTANT_* in
|
||||
# Include/internal/pycore_opcode_utils.h.
|
||||
None, "", True, False, -1]
|
||||
_nb_ops = _opcode.get_nb_ops()
|
||||
|
||||
hascompare = [opmap["COMPARE_OP"]]
|
||||
|
||||
_cache_format = {
|
||||
"LOAD_GLOBAL": {
|
||||
"counter": 1,
|
||||
"index": 1,
|
||||
"module_keys_version": 1,
|
||||
"builtin_keys_version": 1,
|
||||
},
|
||||
"BINARY_OP": {
|
||||
"counter": 1,
|
||||
"descr": 4,
|
||||
},
|
||||
"UNPACK_SEQUENCE": {
|
||||
"counter": 1,
|
||||
},
|
||||
"COMPARE_OP": {
|
||||
"counter": 1,
|
||||
},
|
||||
"CONTAINS_OP": {
|
||||
"counter": 1,
|
||||
},
|
||||
"FOR_ITER": {
|
||||
"counter": 1,
|
||||
},
|
||||
"LOAD_SUPER_ATTR": {
|
||||
"counter": 1,
|
||||
},
|
||||
"LOAD_ATTR": {
|
||||
"counter": 1,
|
||||
"version": 2,
|
||||
"keys_version": 2,
|
||||
"descr": 4,
|
||||
},
|
||||
"STORE_ATTR": {
|
||||
"counter": 1,
|
||||
"version": 2,
|
||||
"index": 1,
|
||||
},
|
||||
"CALL": {
|
||||
"counter": 1,
|
||||
"func_version": 2,
|
||||
},
|
||||
"CALL_KW": {
|
||||
"counter": 1,
|
||||
"func_version": 2,
|
||||
},
|
||||
"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,
|
||||
},
|
||||
}
|
||||
_cache_format = frozendict(
|
||||
LOAD_GLOBAL=frozendict(
|
||||
counter=1,
|
||||
index=1,
|
||||
module_keys_version=1,
|
||||
builtin_keys_version=1,
|
||||
),
|
||||
BINARY_OP=frozendict(
|
||||
counter=1,
|
||||
descr=4,
|
||||
),
|
||||
UNPACK_SEQUENCE=frozendict(
|
||||
counter=1,
|
||||
),
|
||||
COMPARE_OP=frozendict(
|
||||
counter=1,
|
||||
),
|
||||
CONTAINS_OP=frozendict(
|
||||
counter=1,
|
||||
),
|
||||
FOR_ITER=frozendict(
|
||||
counter=1,
|
||||
),
|
||||
GET_ITER=frozendict(
|
||||
counter=1,
|
||||
),
|
||||
LOAD_SUPER_ATTR=frozendict(
|
||||
counter=1,
|
||||
),
|
||||
LOAD_ATTR=frozendict(
|
||||
counter=1,
|
||||
version=2,
|
||||
keys_version=2,
|
||||
descr=4,
|
||||
),
|
||||
STORE_ATTR=frozendict(
|
||||
counter=1,
|
||||
version=2,
|
||||
index=1,
|
||||
),
|
||||
CALL=frozendict(
|
||||
counter=1,
|
||||
func_version=2,
|
||||
),
|
||||
CALL_KW=frozendict(
|
||||
counter=1,
|
||||
func_version=2,
|
||||
),
|
||||
CALL_FUNCTION_EX=frozendict(
|
||||
counter=1,
|
||||
),
|
||||
STORE_SUBSCR=frozendict(
|
||||
counter=1,
|
||||
),
|
||||
SEND=frozendict(
|
||||
counter=1,
|
||||
),
|
||||
JUMP_BACKWARD=frozendict(
|
||||
counter=1,
|
||||
),
|
||||
TO_BOOL=frozendict(
|
||||
counter=1,
|
||||
version=2,
|
||||
),
|
||||
POP_JUMP_IF_TRUE=frozendict(
|
||||
counter=1,
|
||||
),
|
||||
POP_JUMP_IF_FALSE=frozendict(
|
||||
counter=1,
|
||||
),
|
||||
POP_JUMP_IF_NONE=frozendict(
|
||||
counter=1,
|
||||
),
|
||||
POP_JUMP_IF_NOT_NONE=frozendict(
|
||||
counter=1,
|
||||
),
|
||||
RESUME=frozendict(
|
||||
counter=1,
|
||||
),
|
||||
)
|
||||
|
||||
_inline_cache_entries = {
|
||||
_inline_cache_entries = frozendict({
|
||||
name : sum(value.values()) for (name, value) in _cache_format.items()
|
||||
}
|
||||
})
|
||||
Vendored
+18
-13
@@ -87,7 +87,7 @@ cellvars: ()
|
||||
freevars: ()
|
||||
nlocals: 0
|
||||
flags: 67108867
|
||||
consts: ("'doc string'", 'None')
|
||||
consts: ("'doc string'",)
|
||||
|
||||
>>> def keywordonly_args(a,b,*,k1):
|
||||
... return a,b,k1
|
||||
@@ -161,7 +161,7 @@ cellvars: ()
|
||||
freevars: ()
|
||||
nlocals: 3
|
||||
flags: 67108995
|
||||
consts: ("'This is a docstring from async function'", 'None')
|
||||
consts: ("'This is a docstring from async function'",)
|
||||
|
||||
>>> def no_docstring(x, y, z):
|
||||
... return x + "hello" + y + z + "world"
|
||||
@@ -423,14 +423,6 @@ class CodeTest(unittest.TestCase):
|
||||
new_code = code = func.__code__.replace(co_linetable=b'')
|
||||
self.assertEqual(list(new_code.co_lines()), [])
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; co_lnotab intentionally not implemented (deprecated since 3.12)
|
||||
def test_co_lnotab_is_deprecated(self): # TODO: remove in 3.14
|
||||
def func():
|
||||
pass
|
||||
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
func.__code__.co_lnotab
|
||||
|
||||
@unittest.skipIf(_testinternalcapi is None, '_testinternalcapi is missing')
|
||||
def test_returns_only_none(self):
|
||||
value = True
|
||||
@@ -540,7 +532,7 @@ class CodeTest(unittest.TestCase):
|
||||
],
|
||||
[
|
||||
("PUSH_EXC_INFO", None),
|
||||
("LOAD_CONST", None), # artificial 'None'
|
||||
("LOAD_COMMON_CONSTANT", None), # artificial 'None'
|
||||
("STORE_NAME", "e"), # XX: we know the location for this
|
||||
("DELETE_NAME", "e"),
|
||||
("RERAISE", 1),
|
||||
@@ -1165,6 +1157,18 @@ class CodeTest(unittest.TestCase):
|
||||
with self.assertRaises(Exception):
|
||||
_testinternalcapi.verify_stateless_code(func)
|
||||
|
||||
def test_code_richcompare_raise_exception(self):
|
||||
class BadStr(str):
|
||||
def __eq__(self, _):
|
||||
raise RuntimeError("Poison!")
|
||||
|
||||
__hash__ = str.__hash__
|
||||
|
||||
c1 = compile("pass", "test", "exec")
|
||||
c2 = c1.replace(co_name=BadStr("poison"))
|
||||
c3 = compile("pass", "poison", "exec")
|
||||
with self.assertRaises(RuntimeError):
|
||||
c2 == c3
|
||||
|
||||
def isinterned(s):
|
||||
return s is sys.intern(('_' + s + '_')[1:-1])
|
||||
@@ -1438,6 +1442,7 @@ class CodeLocationTest(unittest.TestCase):
|
||||
co_code=bytes(
|
||||
[
|
||||
dis.opmap["RESUME"], 0,
|
||||
dis.opmap["CACHE"], 0,
|
||||
dis.opmap["LOAD_COMMON_CONSTANT"], 0,
|
||||
dis.opmap["RAISE_VARARGS"], 1,
|
||||
]
|
||||
@@ -1446,7 +1451,7 @@ class CodeLocationTest(unittest.TestCase):
|
||||
[
|
||||
(1 << 7)
|
||||
| (PY_CODE_LOCATION_INFO_NO_COLUMNS << 3)
|
||||
| (3 - 1),
|
||||
| (4 - 1),
|
||||
0,
|
||||
]
|
||||
),
|
||||
@@ -1454,7 +1459,7 @@ class CodeLocationTest(unittest.TestCase):
|
||||
self.assertRaises(AssertionError, f)
|
||||
self.assertEqual(
|
||||
list(f.__code__.co_positions()),
|
||||
3 * [(42, 42, None, None)],
|
||||
4 * [(42, 42, None, None)],
|
||||
)
|
||||
|
||||
@cpython_only
|
||||
|
||||
Vendored
+51
-10
@@ -724,8 +724,7 @@ class TestSpecifics(unittest.TestCase):
|
||||
|
||||
@support.cpython_only
|
||||
@unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI")
|
||||
@support.run_with_limited_c_stack(
|
||||
100_000 if sys.platform == "android" else 500_000)
|
||||
@support.skip_if_huge_c_stack(100_000 if sys.platform == "android" else 500_000)
|
||||
@support.skip_emscripten_stack_overflow()
|
||||
def test_compiler_recursion_limit(self):
|
||||
# Compiler frames are small
|
||||
@@ -841,8 +840,10 @@ class TestSpecifics(unittest.TestCase):
|
||||
else:
|
||||
return "unused"
|
||||
|
||||
self.assertEqual(f.__code__.co_consts,
|
||||
(f.__doc__, "used"))
|
||||
if f.__doc__ is None:
|
||||
self.assertEqual(f.__code__.co_consts, (True, "used"))
|
||||
else:
|
||||
self.assertEqual(f.__code__.co_consts, (f.__doc__, "used"))
|
||||
|
||||
@support.cpython_only
|
||||
def test_remove_unused_consts_no_docstring(self):
|
||||
@@ -887,7 +888,11 @@ class TestSpecifics(unittest.TestCase):
|
||||
def f1():
|
||||
"docstring"
|
||||
return 42
|
||||
self.assertEqual(f1.__code__.co_consts, (f1.__doc__,))
|
||||
|
||||
if f1.__doc__ is None:
|
||||
self.assertEqual(f1.__code__.co_consts, (42,))
|
||||
else:
|
||||
self.assertEqual(f1.__code__.co_consts, (f1.__doc__,))
|
||||
|
||||
# This is a regression test for a CPython specific peephole optimizer
|
||||
# implementation bug present in a few releases. It's assertion verifies
|
||||
@@ -1294,7 +1299,7 @@ class TestSpecifics(unittest.TestCase):
|
||||
x
|
||||
in
|
||||
y)
|
||||
genexp_lines = [0, 4, 2, 0, 4]
|
||||
genexp_lines = [4, 0, 4, 2, 0, 4]
|
||||
|
||||
genexp_code = return_genexp.__code__.co_consts[0]
|
||||
code_lines = self.get_code_lines(genexp_code)
|
||||
@@ -1629,6 +1634,17 @@ class TestSpecifics(unittest.TestCase):
|
||||
def f():
|
||||
a if (1 if b else c) else d
|
||||
|
||||
def test_lineno_propagation_empty_blocks(self):
|
||||
# Smoke test. See gh-138714.
|
||||
def f():
|
||||
while name:
|
||||
try:
|
||||
break
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
1 if 1 else 1
|
||||
|
||||
def test_global_declaration_in_except_used_in_else(self):
|
||||
# See gh-111123
|
||||
code = textwrap.dedent("""\
|
||||
@@ -1733,6 +1749,30 @@ class TestSpecifics(unittest.TestCase):
|
||||
self.assertEqual(wm.category, SyntaxWarning)
|
||||
self.assertIn("\"is\" with 'int' literal", str(wm.message))
|
||||
|
||||
def test_filter_syntax_warnings_by_module(self):
|
||||
filename = support.findfile('test_import/data/syntax_warnings.py')
|
||||
with open(filename, 'rb') as f:
|
||||
source = f.read()
|
||||
module_re = r'test\.test_import\.data\.syntax_warnings\z'
|
||||
with warnings.catch_warnings(record=True) as wlog:
|
||||
warnings.simplefilter('error')
|
||||
warnings.filterwarnings('always', module=module_re)
|
||||
compile(source, filename, 'exec')
|
||||
self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10, 13, 14, 21])
|
||||
for wm in wlog:
|
||||
self.assertEqual(wm.filename, filename)
|
||||
self.assertIs(wm.category, SyntaxWarning)
|
||||
|
||||
with warnings.catch_warnings(record=True) as wlog:
|
||||
warnings.simplefilter('error')
|
||||
warnings.filterwarnings('always', module=r'package\.module\z')
|
||||
warnings.filterwarnings('error', module=module_re)
|
||||
compile(source, filename, 'exec', module='package.module')
|
||||
self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10, 13, 14, 21])
|
||||
for wm in wlog:
|
||||
self.assertEqual(wm.filename, filename)
|
||||
self.assertIs(wm.category, SyntaxWarning)
|
||||
|
||||
@support.subTests('src', [
|
||||
textwrap.dedent("""
|
||||
def f():
|
||||
@@ -2419,8 +2459,8 @@ class TestSourcePositions(unittest.TestCase):
|
||||
for i, pos in enumerate(positions):
|
||||
with self.subTest(i=i, pos=pos):
|
||||
start_line, end_line, start_col, end_col = pos
|
||||
if i == 0 and start_col == end_col == 0:
|
||||
# ignore the RESUME in the beginning
|
||||
if i <= 1:
|
||||
# ignore the RESUME and CACHE in the beginning
|
||||
continue
|
||||
self.assertEqual(start_line, 1)
|
||||
self.assertEqual(end_line, 1)
|
||||
@@ -2446,12 +2486,13 @@ class TestSourcePositions(unittest.TestCase):
|
||||
start_line, end_line, _, _ = instr.positions
|
||||
self.assertEqual(start_line, end_line)
|
||||
|
||||
# Expect four `LOAD_CONST None` instructions:
|
||||
# Expect four `None`-loading instructions:
|
||||
# three for the no-exception __exit__ call, and one for the return.
|
||||
# They should all have the locations of the context manager ('xyz').
|
||||
|
||||
load_none = [instr for instr in dis.get_instructions(f) if
|
||||
instr.opname == 'LOAD_CONST' and instr.argval is None]
|
||||
instr.opname in ('LOAD_CONST', 'LOAD_COMMON_CONSTANT')
|
||||
and instr.argval is None]
|
||||
return_value = [instr for instr in dis.get_instructions(f) if
|
||||
instr.opname == 'RETURN_VALUE']
|
||||
|
||||
|
||||
Vendored
+350
-310
File diff suppressed because it is too large.
Load diff
Vendored
+1
-1
@@ -626,7 +626,7 @@ class FormatTest(unittest.TestCase):
|
||||
with self.assertRaisesRegex(ValueError, error_msg):
|
||||
f"{'x':zs}" # can't apply to string
|
||||
|
||||
error_msg = re.escape("unsupported format character 'z'")
|
||||
error_msg = re.escape("unsupported format %z at position 0")
|
||||
with self.assertRaisesRegex(ValueError, error_msg):
|
||||
"%z.1f" % 0 # not allowed in old style string interpolation
|
||||
with self.assertRaisesRegex(ValueError, error_msg):
|
||||
|
||||
Vendored
+22
-24
@@ -300,7 +300,7 @@ class GeneratorTest(unittest.TestCase):
|
||||
def __iter__(self):
|
||||
return Iterator()
|
||||
|
||||
self.assertEqual([1, 2], list(i for i in C()))
|
||||
self.assertEqual([1,2], list(i for i in C()))
|
||||
|
||||
def test_close_clears_frame(self):
|
||||
# gh-142766: Test that closing a generator clears its frame
|
||||
@@ -357,26 +357,25 @@ class ModifyUnderlyingIterableTest(unittest.TestCase):
|
||||
yield x
|
||||
return gen(range(10))
|
||||
|
||||
def process_tests(self, get_generator, is_expr):
|
||||
err_iterator = "'.*' object is not an iterator"
|
||||
err_iterable = "'.*' object is not iterable"
|
||||
for obj in self.iterables:
|
||||
g_obj = get_generator(obj)
|
||||
with self.subTest(g_obj=g_obj, obj=obj):
|
||||
if is_expr:
|
||||
self.assertRaisesRegex(TypeError, err_iterator, list, g_obj)
|
||||
else:
|
||||
def process_tests(self, get_generator, changes_iterable):
|
||||
if changes_iterable:
|
||||
for obj in self.iterables:
|
||||
g_obj = get_generator(obj)
|
||||
with self.subTest(g_obj=g_obj, obj=obj):
|
||||
self.assertListEqual(list(g_obj), list(obj))
|
||||
|
||||
g_iter = get_generator(iter(obj))
|
||||
with self.subTest(g_iter=g_iter, obj=obj):
|
||||
self.assertListEqual(list(g_iter), list(obj))
|
||||
g_iter = get_generator(iter(obj))
|
||||
with self.subTest(g_iter=g_iter, obj=obj):
|
||||
self.assertListEqual(list(g_iter), list(obj))
|
||||
|
||||
err_regex = "'.*' object is not iterable"
|
||||
for obj in self.non_iterables:
|
||||
g_obj = get_generator(obj)
|
||||
with self.subTest(g_obj=g_obj):
|
||||
err = err_iterator if is_expr else err_iterable
|
||||
self.assertRaisesRegex(TypeError, err, list, g_obj)
|
||||
if changes_iterable:
|
||||
self.assertRaisesRegex(TypeError, err_regex, list, g_obj)
|
||||
else:
|
||||
next(g_obj)
|
||||
|
||||
def test_modify_f_locals(self):
|
||||
def modify_f_locals(g, local, obj):
|
||||
@@ -389,22 +388,22 @@ class ModifyUnderlyingIterableTest(unittest.TestCase):
|
||||
def get_generator_genfunc(obj):
|
||||
return modify_f_locals(self.genfunc(), 'it', obj)
|
||||
|
||||
self.process_tests(get_generator_genexpr, True)
|
||||
self.process_tests(get_generator_genfunc, False)
|
||||
self.process_tests(get_generator_genexpr, False)
|
||||
self.process_tests(get_generator_genfunc, True)
|
||||
|
||||
def test_new_gen_from_gi_code(self):
|
||||
def new_gen_from_gi_code(g, obj):
|
||||
generator_func = types.FunctionType(g.gi_code, {})
|
||||
return generator_func(obj)
|
||||
|
||||
def get_generator_genexpr(obj):
|
||||
return new_gen_from_gi_code(self.genexpr(), obj)
|
||||
for obj in self.non_iterables:
|
||||
with self.assertRaises(TypeError):
|
||||
new_gen_from_gi_code(self.genexpr(), obj)
|
||||
|
||||
def get_generator_genfunc(obj):
|
||||
return new_gen_from_gi_code(self.genfunc(), obj)
|
||||
|
||||
self.process_tests(get_generator_genexpr, True)
|
||||
self.process_tests(get_generator_genfunc, False)
|
||||
self.process_tests(get_generator_genfunc, True)
|
||||
|
||||
|
||||
class ExceptionTest(unittest.TestCase):
|
||||
@@ -1367,7 +1366,7 @@ From the Iterators list, about the types of these things.
|
||||
>>> type(i)
|
||||
<class 'generator'>
|
||||
>>> [s for s in dir(i) if not s.startswith('_')]
|
||||
['close', 'gi_code', 'gi_frame', 'gi_running', 'gi_suspended', 'gi_yieldfrom', 'send', 'throw']
|
||||
['close', 'gi_code', 'gi_frame', 'gi_running', 'gi_state', 'gi_suspended', 'gi_yieldfrom', 'send', 'throw']
|
||||
>>> from test.support import HAVE_DOCSTRINGS
|
||||
>>> print(i.__next__.__doc__ if HAVE_DOCSTRINGS else 'Implement next(self).')
|
||||
Implement next(self).
|
||||
@@ -2878,8 +2877,7 @@ __test__ = {"tut": tutorial_tests,
|
||||
}
|
||||
|
||||
def load_tests(loader, tests, pattern):
|
||||
from test.support.rustpython import DocTestChecker # TODO: RUSTPYTHON
|
||||
tests.addTest(doctest.DocTestSuite(checker=DocTestChecker())) # TODO: RUSTPYTHON
|
||||
tests.addTest(doctest.DocTestSuite())
|
||||
return tests
|
||||
|
||||
|
||||
|
||||
Vendored
+442
-115
@@ -12,16 +12,7 @@ import types
|
||||
import unittest
|
||||
|
||||
import test.support
|
||||
from test.support import requires_specialization_ft, script_helper
|
||||
|
||||
try:
|
||||
import _testcapi
|
||||
except ImportError:
|
||||
_testcapi = None
|
||||
try:
|
||||
import _testinternalcapi
|
||||
except ImportError:
|
||||
_testinternalcapi = None
|
||||
from test.support import import_helper, requires_specialization, script_helper
|
||||
|
||||
PAIR = (0,1)
|
||||
|
||||
@@ -202,13 +193,10 @@ INSTRUMENTED_EVENTS = [
|
||||
(E.BRANCH, "branch"),
|
||||
]
|
||||
|
||||
EXCEPT_EVENTS = [
|
||||
SIMPLE_EVENTS = INSTRUMENTED_EVENTS + [
|
||||
(E.RAISE, "raise"),
|
||||
(E.PY_UNWIND, "unwind"),
|
||||
(E.EXCEPTION_HANDLED, "exception_handled"),
|
||||
]
|
||||
|
||||
SIMPLE_EVENTS = INSTRUMENTED_EVENTS + EXCEPT_EVENTS + [
|
||||
(E.PY_UNWIND, "unwind"),
|
||||
(E.C_RAISE, "c_raise"),
|
||||
(E.C_RETURN, "c_return"),
|
||||
]
|
||||
@@ -744,18 +732,6 @@ class TestDisable(MonitoringTestBase, unittest.TestCase):
|
||||
sys.monitoring.register_callback(TEST_TOOL, event, None)
|
||||
|
||||
|
||||
def test_disable_illegal_events(self):
|
||||
for event, name in EXCEPT_EVENTS:
|
||||
try:
|
||||
counter = CounterWithDisable()
|
||||
counter.disable = True
|
||||
sys.monitoring.register_callback(TEST_TOOL, event, counter)
|
||||
sys.monitoring.set_events(TEST_TOOL, event)
|
||||
with self.assertRaises(ValueError):
|
||||
self.raise_handle_reraise()
|
||||
finally:
|
||||
sys.monitoring.set_events(TEST_TOOL, 0)
|
||||
sys.monitoring.register_callback(TEST_TOOL, event, None)
|
||||
|
||||
|
||||
class ExceptionRecorder:
|
||||
@@ -879,7 +855,6 @@ class ExceptionMonitoringTest(CheckEvents):
|
||||
|
||||
self.check_events(func1, [("raise", KeyError)])
|
||||
|
||||
@unittest.skipUnless(_testinternalcapi, "requires _testinternalcapi")
|
||||
def test_implicit_stop_iteration(self):
|
||||
"""Generators are documented as raising a StopIteration
|
||||
when they terminate.
|
||||
@@ -889,6 +864,8 @@ class ExceptionMonitoringTest(CheckEvents):
|
||||
This test checks that both paths record an equivalent event.
|
||||
"""
|
||||
|
||||
_testinternalcapi = import_helper.import_module("_testinternalcapi")
|
||||
|
||||
def gen():
|
||||
yield 1
|
||||
return 2
|
||||
@@ -1054,9 +1031,10 @@ class ExceptionMonitoringTest(CheckEvents):
|
||||
)
|
||||
self.assertEqual(events[0], ("throw", IndexError))
|
||||
|
||||
@unittest.skipUnless(_testinternalcapi, "requires _testinternalcapi")
|
||||
@requires_specialization_ft
|
||||
@requires_specialization
|
||||
def test_no_unwind_for_shim_frame(self):
|
||||
_testinternalcapi = import_helper.import_module("_testinternalcapi")
|
||||
|
||||
class ValueErrorRaiser:
|
||||
def __init__(self):
|
||||
raise ValueError()
|
||||
@@ -1222,19 +1200,20 @@ class TestLineAndInstructionEvents(CheckEvents):
|
||||
line3 = 3
|
||||
|
||||
self.check_events(func1, recorders = LINE_AND_INSTRUCTION_RECORDERS, expected = [
|
||||
('line', 'get_events', 10),
|
||||
('line', 'func1', 1),
|
||||
('instruction', 'func1', 2),
|
||||
('instruction', 'func1', 4),
|
||||
('line', 'func1', 2),
|
||||
('instruction', 'func1', 6),
|
||||
('instruction', 'func1', 8),
|
||||
('line', 'func1', 3),
|
||||
('instruction', 'func1', 10),
|
||||
('instruction', 'func1', 12),
|
||||
('instruction', 'func1', 14),
|
||||
('instruction', 'func1', 16),
|
||||
('line', 'get_events', 11)])
|
||||
("line", "get_events", 10),
|
||||
("line", "func1", 1),
|
||||
("instruction", "func1", 4),
|
||||
("instruction", "func1", 6),
|
||||
("line", "func1", 2),
|
||||
("instruction", "func1", 8),
|
||||
("instruction", "func1", 10),
|
||||
("line", "func1", 3),
|
||||
("instruction", "func1", 12),
|
||||
("instruction", "func1", 14),
|
||||
("instruction", "func1", 16),
|
||||
("instruction", "func1", 18),
|
||||
("line", "get_events", 11),
|
||||
])
|
||||
|
||||
def test_c_call(self):
|
||||
|
||||
@@ -1244,22 +1223,23 @@ class TestLineAndInstructionEvents(CheckEvents):
|
||||
line3 = 3
|
||||
|
||||
self.check_events(func2, recorders = LINE_AND_INSTRUCTION_RECORDERS, expected = [
|
||||
('line', 'get_events', 10),
|
||||
('line', 'func2', 1),
|
||||
('instruction', 'func2', 2),
|
||||
('instruction', 'func2', 4),
|
||||
('line', 'func2', 2),
|
||||
('instruction', 'func2', 6),
|
||||
('instruction', 'func2', 8),
|
||||
('instruction', 'func2', 28),
|
||||
('instruction', 'func2', 30),
|
||||
('instruction', 'func2', 38),
|
||||
('line', 'func2', 3),
|
||||
('instruction', 'func2', 40),
|
||||
('instruction', 'func2', 42),
|
||||
('instruction', 'func2', 44),
|
||||
('instruction', 'func2', 46),
|
||||
('line', 'get_events', 11)])
|
||||
("line", "get_events", 10),
|
||||
("line", "func2", 1),
|
||||
("instruction", "func2", 4),
|
||||
("instruction", "func2", 6),
|
||||
("line", "func2", 2),
|
||||
("instruction", "func2", 8),
|
||||
("instruction", "func2", 10),
|
||||
("instruction", "func2", 30),
|
||||
("instruction", "func2", 32),
|
||||
("instruction", "func2", 40),
|
||||
("line", "func2", 3),
|
||||
("instruction", "func2", 42),
|
||||
("instruction", "func2", 44),
|
||||
("instruction", "func2", 46),
|
||||
("instruction", "func2", 48),
|
||||
("line", "get_events", 11),
|
||||
])
|
||||
|
||||
def test_try_except(self):
|
||||
|
||||
@@ -1272,28 +1252,29 @@ class TestLineAndInstructionEvents(CheckEvents):
|
||||
line = 6
|
||||
|
||||
self.check_events(func3, recorders = LINE_AND_INSTRUCTION_RECORDERS, expected = [
|
||||
('line', 'get_events', 10),
|
||||
('line', 'func3', 1),
|
||||
('instruction', 'func3', 2),
|
||||
('line', 'func3', 2),
|
||||
('instruction', 'func3', 4),
|
||||
('instruction', 'func3', 6),
|
||||
('line', 'func3', 3),
|
||||
('instruction', 'func3', 8),
|
||||
('instruction', 'func3', 18),
|
||||
('instruction', 'func3', 20),
|
||||
('line', 'func3', 4),
|
||||
('instruction', 'func3', 22),
|
||||
('line', 'func3', 5),
|
||||
('instruction', 'func3', 24),
|
||||
('instruction', 'func3', 26),
|
||||
('instruction', 'func3', 28),
|
||||
('line', 'func3', 6),
|
||||
('instruction', 'func3', 30),
|
||||
('instruction', 'func3', 32),
|
||||
('instruction', 'func3', 34),
|
||||
('instruction', 'func3', 36),
|
||||
('line', 'get_events', 11)])
|
||||
("line", "get_events", 10),
|
||||
("line", "func3", 1),
|
||||
("instruction", "func3", 4),
|
||||
("line", "func3", 2),
|
||||
("instruction", "func3", 6),
|
||||
("instruction", "func3", 8),
|
||||
("line", "func3", 3),
|
||||
("instruction", "func3", 10),
|
||||
("instruction", "func3", 20),
|
||||
("instruction", "func3", 22),
|
||||
("line", "func3", 4),
|
||||
("instruction", "func3", 24),
|
||||
("line", "func3", 5),
|
||||
("instruction", "func3", 26),
|
||||
("instruction", "func3", 28),
|
||||
("instruction", "func3", 30),
|
||||
("line", "func3", 6),
|
||||
("instruction", "func3", 32),
|
||||
("instruction", "func3", 34),
|
||||
("instruction", "func3", 36),
|
||||
("instruction", "func3", 38),
|
||||
("line", "get_events", 11),
|
||||
])
|
||||
|
||||
def test_with_restart(self):
|
||||
def func1():
|
||||
@@ -1304,16 +1285,16 @@ class TestLineAndInstructionEvents(CheckEvents):
|
||||
self.check_events(func1, recorders = LINE_AND_INSTRUCTION_RECORDERS, expected = [
|
||||
('line', 'get_events', 10),
|
||||
('line', 'func1', 1),
|
||||
('instruction', 'func1', 2),
|
||||
('instruction', 'func1', 4),
|
||||
('line', 'func1', 2),
|
||||
('instruction', 'func1', 6),
|
||||
('line', 'func1', 2),
|
||||
('instruction', 'func1', 8),
|
||||
('line', 'func1', 3),
|
||||
('instruction', 'func1', 10),
|
||||
('line', 'func1', 3),
|
||||
('instruction', 'func1', 12),
|
||||
('instruction', 'func1', 14),
|
||||
('instruction', 'func1', 16),
|
||||
('instruction', 'func1', 18),
|
||||
('line', 'get_events', 11)])
|
||||
|
||||
sys.monitoring.restart_events()
|
||||
@@ -1321,16 +1302,16 @@ class TestLineAndInstructionEvents(CheckEvents):
|
||||
self.check_events(func1, recorders = LINE_AND_INSTRUCTION_RECORDERS, expected = [
|
||||
('line', 'get_events', 10),
|
||||
('line', 'func1', 1),
|
||||
('instruction', 'func1', 2),
|
||||
('instruction', 'func1', 4),
|
||||
('line', 'func1', 2),
|
||||
('instruction', 'func1', 6),
|
||||
('line', 'func1', 2),
|
||||
('instruction', 'func1', 8),
|
||||
('line', 'func1', 3),
|
||||
('instruction', 'func1', 10),
|
||||
('line', 'func1', 3),
|
||||
('instruction', 'func1', 12),
|
||||
('instruction', 'func1', 14),
|
||||
('instruction', 'func1', 16),
|
||||
('instruction', 'func1', 18),
|
||||
('line', 'get_events', 11)])
|
||||
|
||||
def test_turn_off_only_instruction(self):
|
||||
@@ -1378,10 +1359,10 @@ class TestInstallIncrementally(MonitoringTestBase, unittest.TestCase):
|
||||
line1 = 1
|
||||
|
||||
MUST_INCLUDE_LI = [
|
||||
('instruction', 'func1', 2),
|
||||
('line', 'func1', 2),
|
||||
('instruction', 'func1', 4),
|
||||
('instruction', 'func1', 6)]
|
||||
('line', 'func1', 2),
|
||||
('instruction', 'func1', 6),
|
||||
('instruction', 'func1', 8)]
|
||||
|
||||
def test_line_then_instruction(self):
|
||||
recorders = [ LineRecorder, InstructionRecorder ]
|
||||
@@ -1398,11 +1379,11 @@ class TestInstallIncrementally(MonitoringTestBase, unittest.TestCase):
|
||||
len(())
|
||||
|
||||
MUST_INCLUDE_CI = [
|
||||
('instruction', 'func2', 2),
|
||||
('instruction', 'func2', 4),
|
||||
('call', 'func2', sys.monitoring.MISSING),
|
||||
('call', 'len', ()),
|
||||
('instruction', 'func2', 12),
|
||||
('instruction', 'func2', 14)]
|
||||
('instruction', 'func2', 14),
|
||||
('instruction', 'func2', 16)]
|
||||
|
||||
|
||||
|
||||
@@ -1486,8 +1467,334 @@ class TestLocalEvents(MonitoringTestBase, unittest.TestCase):
|
||||
('line', 'func3', 6)])
|
||||
|
||||
def test_set_non_local_event(self):
|
||||
# C_RETURN/C_RAISE are ancillary (derived) events — not settable as local
|
||||
with self.assertRaises(ValueError):
|
||||
sys.monitoring.set_local_events(TEST_TOOL, just_call.__code__, E.RAISE)
|
||||
sys.monitoring.set_local_events(TEST_TOOL, just_call.__code__, E.C_RETURN)
|
||||
|
||||
def test_local_reraise(self):
|
||||
"""RERAISE fires as a local event only for the instrumented code object."""
|
||||
|
||||
def foo():
|
||||
try:
|
||||
raise RuntimeError("test")
|
||||
except RuntimeError:
|
||||
raise
|
||||
|
||||
def bar():
|
||||
try:
|
||||
raise RuntimeError("test")
|
||||
except RuntimeError:
|
||||
raise
|
||||
|
||||
events = set()
|
||||
|
||||
def callback(code, offset, exc):
|
||||
events.add(code.co_name)
|
||||
|
||||
try:
|
||||
sys.monitoring.register_callback(TEST_TOOL, E.RERAISE, callback)
|
||||
sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, E.RERAISE)
|
||||
try:
|
||||
foo()
|
||||
except RuntimeError:
|
||||
pass
|
||||
try:
|
||||
bar() # should NOT trigger the callback
|
||||
except RuntimeError:
|
||||
pass
|
||||
self.assertEqual(events, {'foo'})
|
||||
finally:
|
||||
sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, 0)
|
||||
sys.monitoring.register_callback(TEST_TOOL, E.RERAISE, None)
|
||||
|
||||
def test_local_reraise_disable(self):
|
||||
"""Returning DISABLE from a RERAISE callback disables it for that code object."""
|
||||
|
||||
call_count = 0
|
||||
|
||||
def foo():
|
||||
try:
|
||||
raise RuntimeError("test")
|
||||
except RuntimeError:
|
||||
raise
|
||||
|
||||
def callback(code, offset, exc):
|
||||
nonlocal call_count
|
||||
call_count += 1
|
||||
return sys.monitoring.DISABLE
|
||||
|
||||
try:
|
||||
sys.monitoring.register_callback(TEST_TOOL, E.RERAISE, callback)
|
||||
sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, E.RERAISE)
|
||||
try:
|
||||
foo()
|
||||
except RuntimeError:
|
||||
pass
|
||||
self.assertEqual(call_count, 1)
|
||||
try:
|
||||
foo()
|
||||
except RuntimeError:
|
||||
pass
|
||||
self.assertEqual(call_count, 1) # not fired again — disabled
|
||||
finally:
|
||||
sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, 0)
|
||||
sys.monitoring.register_callback(TEST_TOOL, E.RERAISE, None)
|
||||
|
||||
def test_local_py_throw(self):
|
||||
"""PY_THROW fires as a local event only for the instrumented code object."""
|
||||
|
||||
def gen_foo():
|
||||
yield 1
|
||||
yield 2
|
||||
|
||||
def gen_bar():
|
||||
yield 1
|
||||
yield 2
|
||||
|
||||
events = []
|
||||
|
||||
def callback(code, offset, exc):
|
||||
events.append(code.co_name)
|
||||
|
||||
try:
|
||||
sys.monitoring.register_callback(TEST_TOOL, E.PY_THROW, callback)
|
||||
sys.monitoring.set_local_events(TEST_TOOL, gen_foo.__code__, E.PY_THROW)
|
||||
|
||||
g = gen_foo()
|
||||
next(g)
|
||||
try:
|
||||
g.throw(RuntimeError("test"))
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
h = gen_bar()
|
||||
next(h)
|
||||
try:
|
||||
h.throw(RuntimeError("test")) # should NOT trigger the callback
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
self.assertEqual(events, ['gen_foo'])
|
||||
finally:
|
||||
sys.monitoring.set_local_events(TEST_TOOL, gen_foo.__code__, 0)
|
||||
sys.monitoring.register_callback(TEST_TOOL, E.PY_THROW, None)
|
||||
|
||||
def test_local_py_throw_disable(self):
|
||||
"""Returning DISABLE from a PY_THROW callback disables it for that code object."""
|
||||
|
||||
call_count = 0
|
||||
|
||||
def gen_foo():
|
||||
yield 1
|
||||
yield 2
|
||||
|
||||
def callback(code, offset, exc):
|
||||
nonlocal call_count
|
||||
call_count += 1
|
||||
return sys.monitoring.DISABLE
|
||||
|
||||
try:
|
||||
sys.monitoring.register_callback(TEST_TOOL, E.PY_THROW, callback)
|
||||
sys.monitoring.set_local_events(TEST_TOOL, gen_foo.__code__, E.PY_THROW)
|
||||
|
||||
g = gen_foo()
|
||||
next(g)
|
||||
try:
|
||||
g.throw(RuntimeError("test"))
|
||||
except RuntimeError:
|
||||
pass
|
||||
self.assertEqual(call_count, 1)
|
||||
|
||||
g2 = gen_foo()
|
||||
next(g2)
|
||||
try:
|
||||
g2.throw(RuntimeError("test"))
|
||||
except RuntimeError:
|
||||
pass
|
||||
self.assertEqual(call_count, 1) # not fired again — disabled
|
||||
finally:
|
||||
sys.monitoring.set_local_events(TEST_TOOL, gen_foo.__code__, 0)
|
||||
sys.monitoring.register_callback(TEST_TOOL, E.PY_THROW, None)
|
||||
|
||||
def test_local_raise(self):
|
||||
"""RAISE fires as a local event only for the instrumented code object."""
|
||||
|
||||
def foo():
|
||||
try:
|
||||
raise RuntimeError("test")
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
def bar():
|
||||
try:
|
||||
raise RuntimeError("test")
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
events = []
|
||||
|
||||
def callback(code, offset, exc):
|
||||
events.append(code.co_name)
|
||||
|
||||
try:
|
||||
sys.monitoring.register_callback(TEST_TOOL, E.RAISE, callback)
|
||||
sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, E.RAISE)
|
||||
foo()
|
||||
bar() # should NOT trigger the callback
|
||||
self.assertEqual(events, ['foo'])
|
||||
finally:
|
||||
sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, 0)
|
||||
sys.monitoring.register_callback(TEST_TOOL, E.RAISE, None)
|
||||
|
||||
def test_local_raise_disable(self):
|
||||
"""Returning DISABLE from a RAISE callback disables it for that code object."""
|
||||
|
||||
call_count = 0
|
||||
|
||||
def foo():
|
||||
try:
|
||||
raise RuntimeError("test")
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
def callback(code, offset, exc):
|
||||
nonlocal call_count
|
||||
call_count += 1
|
||||
return sys.monitoring.DISABLE
|
||||
|
||||
try:
|
||||
sys.monitoring.register_callback(TEST_TOOL, E.RAISE, callback)
|
||||
sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, E.RAISE)
|
||||
foo()
|
||||
self.assertEqual(call_count, 1)
|
||||
foo()
|
||||
self.assertEqual(call_count, 1) # not fired again — disabled
|
||||
finally:
|
||||
sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, 0)
|
||||
sys.monitoring.register_callback(TEST_TOOL, E.RAISE, None)
|
||||
|
||||
def test_local_exception_handled(self):
|
||||
"""EXCEPTION_HANDLED fires as a local event only for the instrumented code object."""
|
||||
|
||||
def foo():
|
||||
try:
|
||||
raise RuntimeError("test")
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
def bar():
|
||||
try:
|
||||
raise RuntimeError("test")
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
events = []
|
||||
|
||||
def callback(code, offset, exc):
|
||||
events.append(code.co_name)
|
||||
|
||||
try:
|
||||
sys.monitoring.register_callback(TEST_TOOL, E.EXCEPTION_HANDLED, callback)
|
||||
sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, E.EXCEPTION_HANDLED)
|
||||
foo()
|
||||
bar() # should NOT trigger the callback
|
||||
self.assertEqual(events, ['foo'])
|
||||
finally:
|
||||
sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, 0)
|
||||
sys.monitoring.register_callback(TEST_TOOL, E.EXCEPTION_HANDLED, None)
|
||||
|
||||
def test_local_exception_handled_disable(self):
|
||||
"""Returning DISABLE from an EXCEPTION_HANDLED callback disables it for that code object."""
|
||||
|
||||
call_count = 0
|
||||
|
||||
def foo():
|
||||
try:
|
||||
raise RuntimeError("test")
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
def callback(code, offset, exc):
|
||||
nonlocal call_count
|
||||
call_count += 1
|
||||
return sys.monitoring.DISABLE
|
||||
|
||||
try:
|
||||
sys.monitoring.register_callback(TEST_TOOL, E.EXCEPTION_HANDLED, callback)
|
||||
sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, E.EXCEPTION_HANDLED)
|
||||
foo()
|
||||
self.assertEqual(call_count, 1)
|
||||
foo()
|
||||
self.assertEqual(call_count, 1) # not fired again — disabled
|
||||
finally:
|
||||
sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, 0)
|
||||
sys.monitoring.register_callback(TEST_TOOL, E.EXCEPTION_HANDLED, None)
|
||||
|
||||
def test_local_py_unwind(self):
|
||||
"""PY_UNWIND fires as a local event only for the instrumented code object."""
|
||||
|
||||
def foo():
|
||||
raise RuntimeError("test")
|
||||
|
||||
def bar():
|
||||
raise RuntimeError("test")
|
||||
|
||||
events = []
|
||||
|
||||
def callback(code, offset, exc):
|
||||
events.append(code.co_name)
|
||||
|
||||
try:
|
||||
sys.monitoring.register_callback(TEST_TOOL, E.PY_UNWIND, callback)
|
||||
sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, E.PY_UNWIND)
|
||||
|
||||
try:
|
||||
foo()
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
bar() # should NOT trigger the callback
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
self.assertEqual(events, ['foo'])
|
||||
finally:
|
||||
sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, 0)
|
||||
sys.monitoring.register_callback(TEST_TOOL, E.PY_UNWIND, None)
|
||||
|
||||
def test_local_py_unwind_disable(self):
|
||||
"""Returning DISABLE from a PY_UNWIND callback disables it for that code object."""
|
||||
|
||||
call_count = 0
|
||||
|
||||
def foo():
|
||||
raise RuntimeError("test")
|
||||
|
||||
def callback(code, offset, exc):
|
||||
nonlocal call_count
|
||||
call_count += 1
|
||||
return sys.monitoring.DISABLE
|
||||
|
||||
try:
|
||||
sys.monitoring.register_callback(TEST_TOOL, E.PY_UNWIND, callback)
|
||||
sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, E.PY_UNWIND)
|
||||
|
||||
try:
|
||||
foo()
|
||||
except RuntimeError:
|
||||
pass
|
||||
self.assertEqual(call_count, 1) # fired once
|
||||
|
||||
try:
|
||||
foo()
|
||||
except RuntimeError:
|
||||
pass
|
||||
self.assertEqual(call_count, 1) # not fired again — disabled by DISABLE return
|
||||
|
||||
finally:
|
||||
sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, 0)
|
||||
sys.monitoring.register_callback(TEST_TOOL, E.PY_UNWIND, None)
|
||||
|
||||
def line_from_offset(code, offset):
|
||||
for start, end, line in code.co_lines():
|
||||
@@ -1617,11 +1924,11 @@ class TestBranchAndJumpEvents(CheckEvents):
|
||||
('branch right', 'whilefunc', 1, 3)])
|
||||
|
||||
self.check_events(func, recorders = BRANCH_OFFSET_RECORDERS, expected = [
|
||||
('branch left', 'func', 28, 32),
|
||||
('branch right', 'func', 44, 58),
|
||||
('branch left', 'func', 28, 32),
|
||||
('branch left', 'func', 44, 50),
|
||||
('branch right', 'func', 28, 70)])
|
||||
('branch left', 'func', 32, 36),
|
||||
('branch right', 'func', 48, 62),
|
||||
('branch left', 'func', 32, 36),
|
||||
('branch left', 'func', 48, 54),
|
||||
('branch right', 'func', 32, 74)])
|
||||
|
||||
def test_except_star(self):
|
||||
|
||||
@@ -1648,8 +1955,8 @@ class TestBranchAndJumpEvents(CheckEvents):
|
||||
('branch', 'func', 4, 4),
|
||||
('line', 'func', 5),
|
||||
('line', 'meth', 1),
|
||||
('jump', 'func', 5, '[offset=120]'),
|
||||
('branch', 'func', '[offset=124]', '[offset=130]'),
|
||||
('jump', 'func', 5, '[offset=122]'),
|
||||
('branch', 'func', '[offset=126]', '[offset=132]'),
|
||||
('line', 'get_events', 11)])
|
||||
|
||||
self.check_events(func, recorders = FLOW_AND_LINE_RECORDERS, expected = [
|
||||
@@ -1663,8 +1970,8 @@ class TestBranchAndJumpEvents(CheckEvents):
|
||||
('line', 'func', 5),
|
||||
('line', 'meth', 1),
|
||||
('return', 'meth', None),
|
||||
('jump', 'func', 5, '[offset=120]'),
|
||||
('branch', 'func', '[offset=124]', '[offset=130]'),
|
||||
('jump', 'func', 5, '[offset=122]'),
|
||||
('branch', 'func', '[offset=126]', '[offset=132]'),
|
||||
('return', 'func', None),
|
||||
('line', 'get_events', 11)])
|
||||
|
||||
@@ -1676,8 +1983,8 @@ class TestBranchAndJumpEvents(CheckEvents):
|
||||
n += 1
|
||||
return None
|
||||
|
||||
in_loop = ('branch left', 'foo', 10, 16)
|
||||
exit_loop = ('branch right', 'foo', 10, 40)
|
||||
in_loop = ('branch left', 'foo', 12, 18)
|
||||
exit_loop = ('branch right', 'foo', 12, 42)
|
||||
self.check_events(foo, recorders = BRANCH_OFFSET_RECORDERS, expected = [
|
||||
in_loop,
|
||||
in_loop,
|
||||
@@ -2150,8 +2457,8 @@ class TestRegressions(MonitoringTestBase, unittest.TestCase):
|
||||
finally:
|
||||
sys.monitoring.set_events(TEST_TOOL, 0)
|
||||
|
||||
@unittest.skipUnless(_testinternalcapi, "requires _testinternalcapi")
|
||||
def test_108390(self):
|
||||
_testinternalcapi = import_helper.import_module("_testinternalcapi")
|
||||
|
||||
class Foo:
|
||||
def __init__(self, set_event):
|
||||
@@ -2209,6 +2516,21 @@ class TestRegressions(MonitoringTestBase, unittest.TestCase):
|
||||
sys.monitoring.restart_events()
|
||||
sys.monitoring.set_events(0, 0)
|
||||
|
||||
def test_134879(self):
|
||||
# gh-134789
|
||||
# Specialized FOR_ITER not incrementing index
|
||||
def foo():
|
||||
t = 0
|
||||
for i in [1,2,3,4]:
|
||||
t += i
|
||||
self.assertEqual(t, 10)
|
||||
|
||||
sys.monitoring.use_tool_id(0, "test")
|
||||
self.addCleanup(sys.monitoring.free_tool_id, 0)
|
||||
sys.monitoring.set_local_events(0, foo.__code__, E.BRANCH_LEFT | E.BRANCH_RIGHT)
|
||||
foo()
|
||||
sys.monitoring.set_local_events(0, foo.__code__, 0)
|
||||
|
||||
|
||||
class TestOptimizer(MonitoringTestBase, unittest.TestCase):
|
||||
|
||||
@@ -2227,8 +2549,9 @@ class TestOptimizer(MonitoringTestBase, unittest.TestCase):
|
||||
|
||||
class TestTier2Optimizer(CheckEvents):
|
||||
|
||||
@unittest.skipUnless(_testinternalcapi, "requires _testinternalcapi")
|
||||
def test_monitoring_already_opimized_loop(self):
|
||||
_testinternalcapi = import_helper.import_module("_testinternalcapi")
|
||||
|
||||
def test_func(recorder):
|
||||
set_events = sys.monitoring.set_events
|
||||
line = E.LINE
|
||||
@@ -2262,23 +2585,25 @@ class TestMonitoringAtShutdown(unittest.TestCase):
|
||||
script_helper.run_test_script(script)
|
||||
|
||||
|
||||
@unittest.skipUnless(_testcapi, "requires _testcapi")
|
||||
@unittest.skip("TODO: RUSTPYTHON; no _testcapi, and setUp leaves the tool ids in use when its import fails")
|
||||
class TestCApiEventGeneration(MonitoringTestBase, unittest.TestCase):
|
||||
|
||||
class Scope:
|
||||
def __init__(self, *args):
|
||||
self._testcapi = import_helper.import_module("_testcapi")
|
||||
self.args = args
|
||||
|
||||
def __enter__(self):
|
||||
_testcapi.monitoring_enter_scope(*self.args)
|
||||
self._testcapi.monitoring_enter_scope(*self.args)
|
||||
|
||||
def __exit__(self, *args):
|
||||
_testcapi.monitoring_exit_scope()
|
||||
self._testcapi.monitoring_exit_scope()
|
||||
|
||||
def setUp(self):
|
||||
super(TestCApiEventGeneration, self).setUp()
|
||||
|
||||
capi = _testcapi
|
||||
self._testcapi = import_helper.import_module("_testcapi")
|
||||
capi = self._testcapi
|
||||
|
||||
self.codelike = capi.CodeLike(2)
|
||||
|
||||
@@ -2347,7 +2672,7 @@ class TestCApiEventGeneration(MonitoringTestBase, unittest.TestCase):
|
||||
def test_fire_event(self):
|
||||
for expected, event, function, *args in self.cases:
|
||||
offset = 0
|
||||
self.codelike = _testcapi.CodeLike(1)
|
||||
self.codelike = self._testcapi.CodeLike(1)
|
||||
with self.subTest(function.__name__):
|
||||
args_ = (self.codelike, offset) + tuple(args)
|
||||
self.check_event_count(event, function, args_, expected)
|
||||
@@ -2358,7 +2683,7 @@ class TestCApiEventGeneration(MonitoringTestBase, unittest.TestCase):
|
||||
continue
|
||||
assert args and isinstance(args[-1], BaseException)
|
||||
offset = 0
|
||||
self.codelike = _testcapi.CodeLike(1)
|
||||
self.codelike = self._testcapi.CodeLike(1)
|
||||
with self.subTest(function.__name__):
|
||||
args_ = (self.codelike, offset) + tuple(args[:-1]) + (None,)
|
||||
evt = int(math.log2(event))
|
||||
@@ -2368,7 +2693,7 @@ class TestCApiEventGeneration(MonitoringTestBase, unittest.TestCase):
|
||||
def test_fire_event_failing_callback(self):
|
||||
for expected, event, function, *args in self.cases:
|
||||
offset = 0
|
||||
self.codelike = _testcapi.CodeLike(1)
|
||||
self.codelike = self._testcapi.CodeLike(1)
|
||||
with self.subTest(function.__name__):
|
||||
args_ = (self.codelike, offset) + tuple(args)
|
||||
exc = OSError(42)
|
||||
@@ -2418,12 +2743,14 @@ class TestCApiEventGeneration(MonitoringTestBase, unittest.TestCase):
|
||||
def test_disable_event(self):
|
||||
for expected, event, function, *args in self.cases:
|
||||
offset = 0
|
||||
self.codelike = _testcapi.CodeLike(2)
|
||||
self.codelike = self._testcapi.CodeLike(2)
|
||||
with self.subTest(function.__name__):
|
||||
args_ = (self.codelike, 0) + tuple(args)
|
||||
self.check_disable(event, function, args_, expected)
|
||||
|
||||
def test_enter_scope_two_events(self):
|
||||
_testcapi = self._testcapi
|
||||
|
||||
try:
|
||||
yield_counter = CounterWithDisable()
|
||||
unwind_counter = CounterWithDisable()
|
||||
|
||||
Vendored
+313
-61
@@ -13,7 +13,7 @@ except ImportError:
|
||||
|
||||
from test import support
|
||||
from test.support.bytecode_helper import (
|
||||
BytecodeTestCase, CfgOptimizationTestCase, CompilationStepTestCase)
|
||||
BytecodeTestCase, CfgOptimizationTestCase)
|
||||
|
||||
|
||||
def compile_pattern_with_fast_locals(pattern):
|
||||
@@ -106,7 +106,7 @@ class TestTranforms(BytecodeTestCase):
|
||||
self.check_lnotab(code)
|
||||
|
||||
def test_global_as_constant(self):
|
||||
# LOAD_GLOBAL None/True/False --> LOAD_CONST None/True/False
|
||||
# LOAD_GLOBAL None/True/False --> LOAD_COMMON_CONSTANT None/True/False
|
||||
def f():
|
||||
x = None
|
||||
x = None
|
||||
@@ -121,7 +121,7 @@ class TestTranforms(BytecodeTestCase):
|
||||
for func, elem in ((f, None), (g, True), (h, False)):
|
||||
with self.subTest(func=func):
|
||||
self.assertNotInBytecode(func, 'LOAD_GLOBAL')
|
||||
self.assertInBytecode(func, 'LOAD_CONST', elem)
|
||||
self.assertInBytecode(func, 'LOAD_COMMON_CONSTANT', elem)
|
||||
self.check_lnotab(func)
|
||||
|
||||
def f():
|
||||
@@ -129,7 +129,7 @@ class TestTranforms(BytecodeTestCase):
|
||||
return None
|
||||
|
||||
self.assertNotInBytecode(f, 'LOAD_GLOBAL')
|
||||
self.assertInBytecode(f, 'LOAD_CONST', None)
|
||||
self.assertInBytecode(f, 'LOAD_COMMON_CONSTANT', None)
|
||||
self.check_lnotab(f)
|
||||
|
||||
def test_while_one(self):
|
||||
@@ -146,13 +146,14 @@ class TestTranforms(BytecodeTestCase):
|
||||
|
||||
def test_pack_unpack(self):
|
||||
for line, elem in (
|
||||
('a, = a,', 'LOAD_CONST',),
|
||||
('a, = a,', None),
|
||||
('a, b = a, b', 'SWAP',),
|
||||
('a, b, c = a, b, c', 'SWAP',),
|
||||
):
|
||||
with self.subTest(line=line):
|
||||
code = compile(line,'','single')
|
||||
self.assertInBytecode(code, elem)
|
||||
if elem is not None:
|
||||
self.assertInBytecode(code, elem)
|
||||
self.assertNotInBytecode(code, 'BUILD_TUPLE')
|
||||
self.assertNotInBytecode(code, 'UNPACK_SEQUENCE')
|
||||
self.check_lnotab(code)
|
||||
@@ -174,10 +175,10 @@ class TestTranforms(BytecodeTestCase):
|
||||
# Long tuples should be folded too.
|
||||
code = compile(repr(tuple(range(10000))),'','single')
|
||||
self.assertNotInBytecode(code, 'BUILD_TUPLE')
|
||||
# One LOAD_CONST for the tuple, one for the None return value
|
||||
# One LOAD_CONST for the tuple; None return value uses LOAD_COMMON_CONSTANT
|
||||
load_consts = [instr for instr in dis.get_instructions(code)
|
||||
if instr.opname == 'LOAD_CONST']
|
||||
self.assertEqual(len(load_consts), 2)
|
||||
self.assertEqual(len(load_consts), 1)
|
||||
self.check_lnotab(code)
|
||||
|
||||
# Bug 1053819: Tuple of constants misidentified when presented with:
|
||||
@@ -283,11 +284,11 @@ class TestTranforms(BytecodeTestCase):
|
||||
('-0.0', 'UNARY_NEGATIVE', None, True, 'LOAD_CONST', -0.0),
|
||||
('-(1.0-1.0)', 'UNARY_NEGATIVE', None, True, 'LOAD_CONST', -0.0),
|
||||
('-0.5', 'UNARY_NEGATIVE', None, True, 'LOAD_CONST', -0.5),
|
||||
('---1', 'UNARY_NEGATIVE', None, True, 'LOAD_CONST', -1),
|
||||
('---1', 'UNARY_NEGATIVE', None, True, 'LOAD_COMMON_CONSTANT', -1),
|
||||
('---""', 'UNARY_NEGATIVE', None, False, None, None),
|
||||
('~~~1', 'UNARY_INVERT', None, True, 'LOAD_CONST', -2),
|
||||
('~~~""', 'UNARY_INVERT', None, False, None, None),
|
||||
('not not True', 'UNARY_NOT', None, True, 'LOAD_CONST', True),
|
||||
('not not True', 'UNARY_NOT', None, True, 'LOAD_COMMON_CONSTANT', True),
|
||||
('not not x', 'UNARY_NOT', None, True, 'LOAD_NAME', 'x'), # this should be optimized regardless of constant or not
|
||||
('+++1', 'CALL_INTRINSIC_1', intrinsic_positive, True, 'LOAD_SMALL_INT', 1),
|
||||
('---x', 'UNARY_NEGATIVE', None, False, None, None),
|
||||
@@ -326,7 +327,7 @@ class TestTranforms(BytecodeTestCase):
|
||||
('1 + 2', 'NB_ADD', True, 'LOAD_SMALL_INT', 3),
|
||||
('1 + 2 + 3', 'NB_ADD', True, 'LOAD_SMALL_INT', 6),
|
||||
('1 + ""', 'NB_ADD', False, None, None),
|
||||
('1 - 2', 'NB_SUBTRACT', True, 'LOAD_CONST', -1),
|
||||
('1 - 2', 'NB_SUBTRACT', True, 'LOAD_COMMON_CONSTANT', -1),
|
||||
('1 - 2 - 3', 'NB_SUBTRACT', True, 'LOAD_CONST', -4),
|
||||
('1 - ""', 'NB_SUBTRACT', False, None, None),
|
||||
('2 * 2', 'NB_MULTIPLY', True, 'LOAD_SMALL_INT', 4),
|
||||
@@ -734,23 +735,27 @@ class TestTranforms(BytecodeTestCase):
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
'not all arguments converted during string formatting'):
|
||||
eval("'%s' % (x, y)", {'x': 1, 'y': 2})
|
||||
with self.assertRaisesRegex(ValueError, 'incomplete format'):
|
||||
with self.assertRaisesRegex(ValueError, 'stray % at position 2'):
|
||||
eval("'%s%' % (x,)", {'x': 1234})
|
||||
with self.assertRaisesRegex(ValueError, 'incomplete format'):
|
||||
with self.assertRaisesRegex(ValueError, 'stray % at position 4'):
|
||||
eval("'%s%%%' % (x,)", {'x': 1234})
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
'not enough arguments for format string'):
|
||||
eval("'%s%z' % (x,)", {'x': 1234})
|
||||
with self.assertRaisesRegex(ValueError, 'unsupported format character'):
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
'unsupported format %z at position 2'):
|
||||
eval("'%s%z' % (x, 5)", {'x': 1234})
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
'format argument 1: %d requires a real number, not str'):
|
||||
eval("'%d' % (x,)", {'x': '1234'})
|
||||
with self.assertRaisesRegex(TypeError, 'an integer is required, not float'):
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
'format argument 1: %x requires an integer, not float'):
|
||||
eval("'%x' % (x,)", {'x': 1234.56})
|
||||
with self.assertRaisesRegex(TypeError, 'an integer is required, not str'):
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
'format argument 1: %x requires an integer, not str'):
|
||||
eval("'%x' % (x,)", {'x': '1234'})
|
||||
with self.assertRaisesRegex(TypeError, 'must be real number, not str'):
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
'format argument 1: %f requires a real number, not str'):
|
||||
eval("'%f' % (x,)", {'x': '1234'})
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
'not enough arguments for format string'):
|
||||
@@ -1118,6 +1123,13 @@ class TestMarkingVariablesAsUnKnown(BytecodeTestCase):
|
||||
self.assertInBytecode(f, "LOAD_FAST_BORROW")
|
||||
self.assertNotInBytecode(f, "LOAD_FAST_CHECK")
|
||||
|
||||
def test_import_from_doesnt_clobber_load_fast_borrow(self):
|
||||
def f(self):
|
||||
if x: pass
|
||||
self.x
|
||||
from shutil import ExecError
|
||||
print(ExecError)
|
||||
self.assertInBytecode(f, "LOAD_FAST_BORROW", "self")
|
||||
|
||||
class DirectCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
|
||||
@@ -1507,7 +1519,7 @@ class DirectCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
('LOAD_SMALL_INT', 1, 0),
|
||||
('LOAD_SMALL_INT', 2, 0),
|
||||
('BUILD_LIST', 2, 0),
|
||||
('GET_ITER', None, 0),
|
||||
('GET_ITER', 0, 0),
|
||||
start := self.Label(),
|
||||
('FOR_ITER', end := self.Label(), 0),
|
||||
('STORE_FAST', 0, 0),
|
||||
@@ -1520,7 +1532,7 @@ class DirectCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
]
|
||||
after = [
|
||||
('LOAD_CONST', 1, 0),
|
||||
('GET_ITER', None, 0),
|
||||
('GET_ITER', 0, 0),
|
||||
start := self.Label(),
|
||||
('FOR_ITER', end := self.Label(), 0),
|
||||
('STORE_FAST', 0, 0),
|
||||
@@ -1528,7 +1540,7 @@ class DirectCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
end,
|
||||
('END_FOR', None, 0),
|
||||
('POP_ITER', None, 0),
|
||||
('LOAD_CONST', 0, 0),
|
||||
('LOAD_COMMON_CONSTANT', 7, 0),
|
||||
('RETURN_VALUE', None, 0),
|
||||
]
|
||||
self.cfg_optimization_test(before, after, consts=[None], expected_consts=[None, (1, 2)])
|
||||
@@ -1538,7 +1550,7 @@ class DirectCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
('LOAD_SMALL_INT', 1, 0),
|
||||
('LOAD_NAME', 0, 0),
|
||||
('BUILD_LIST', 2, 0),
|
||||
('GET_ITER', None, 0),
|
||||
('GET_ITER', 0, 0),
|
||||
start := self.Label(),
|
||||
('FOR_ITER', end := self.Label(), 0),
|
||||
('STORE_FAST', 0, 0),
|
||||
@@ -1553,7 +1565,7 @@ class DirectCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
('LOAD_SMALL_INT', 1, 0),
|
||||
('LOAD_NAME', 0, 0),
|
||||
('BUILD_TUPLE', 2, 0),
|
||||
('GET_ITER', None, 0),
|
||||
('GET_ITER', 0, 0),
|
||||
start := self.Label(),
|
||||
('FOR_ITER', end := self.Label(), 0),
|
||||
('STORE_FAST', 0, 0),
|
||||
@@ -1561,7 +1573,7 @@ class DirectCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
end,
|
||||
('END_FOR', None, 0),
|
||||
('POP_ITER', None, 0),
|
||||
('LOAD_CONST', 0, 0),
|
||||
('LOAD_COMMON_CONSTANT', 7, 0),
|
||||
('RETURN_VALUE', None, 0),
|
||||
]
|
||||
self.cfg_optimization_test(before, after, consts=[None], expected_consts=[None])
|
||||
@@ -1572,7 +1584,7 @@ class DirectCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
('LOAD_SMALL_INT', 1, 0),
|
||||
('LOAD_SMALL_INT', 2, 0),
|
||||
('BUILD_SET', 2, 0),
|
||||
('GET_ITER', None, 0),
|
||||
('GET_ITER', 0, 0),
|
||||
start := self.Label(),
|
||||
('FOR_ITER', end := self.Label(), 0),
|
||||
('STORE_FAST', 0, 0),
|
||||
@@ -1585,7 +1597,7 @@ class DirectCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
]
|
||||
after = [
|
||||
('LOAD_CONST', 1, 0),
|
||||
('GET_ITER', None, 0),
|
||||
('GET_ITER', 0, 0),
|
||||
start := self.Label(),
|
||||
('FOR_ITER', end := self.Label(), 0),
|
||||
('STORE_FAST', 0, 0),
|
||||
@@ -1593,7 +1605,7 @@ class DirectCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
end,
|
||||
('END_FOR', None, 0),
|
||||
('POP_ITER', None, 0),
|
||||
('LOAD_CONST', 0, 0),
|
||||
('LOAD_COMMON_CONSTANT', 7, 0),
|
||||
('RETURN_VALUE', None, 0),
|
||||
]
|
||||
self.cfg_optimization_test(before, after, consts=[None], expected_consts=[None, frozenset({1, 2})])
|
||||
@@ -1604,7 +1616,7 @@ class DirectCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
('LOAD_SMALL_INT', 1, 0),
|
||||
('LOAD_NAME', 0, 0),
|
||||
('BUILD_SET', 2, 0),
|
||||
('GET_ITER', None, 0),
|
||||
('GET_ITER', 0, 0),
|
||||
start := self.Label(),
|
||||
('FOR_ITER', end := self.Label(), 0),
|
||||
('STORE_FAST', 0, 0),
|
||||
@@ -1615,7 +1627,22 @@ class DirectCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
('LOAD_CONST', 0, 0),
|
||||
('RETURN_VALUE', None, 0),
|
||||
]
|
||||
self.cfg_optimization_test(same, same, consts=[None], expected_consts=[None])
|
||||
expected = [
|
||||
('LOAD_SMALL_INT', 1, 0),
|
||||
('LOAD_NAME', 0, 0),
|
||||
('BUILD_SET', 2, 0),
|
||||
('GET_ITER', 0, 0),
|
||||
start := self.Label(),
|
||||
('FOR_ITER', end := self.Label(), 0),
|
||||
('STORE_FAST', 0, 0),
|
||||
('JUMP', start, 0),
|
||||
end,
|
||||
('END_FOR', None, 0),
|
||||
('POP_ITER', None, 0),
|
||||
('LOAD_COMMON_CONSTANT', 7, 0),
|
||||
('RETURN_VALUE', None, 0),
|
||||
]
|
||||
self.cfg_optimization_test(same, expected, consts=[None], expected_consts=[None])
|
||||
|
||||
def test_optimize_literal_list_contains(self):
|
||||
# x in [1, 2] ==> x in (1, 2)
|
||||
@@ -1634,7 +1661,7 @@ class DirectCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
('LOAD_CONST', 1, 0),
|
||||
('CONTAINS_OP', 0, 0),
|
||||
('POP_TOP', None, 0),
|
||||
('LOAD_CONST', 0, 0),
|
||||
('LOAD_COMMON_CONSTANT', 7, 0),
|
||||
('RETURN_VALUE', None, 0),
|
||||
]
|
||||
self.cfg_optimization_test(before, after, consts=[None], expected_consts=[None, (1, 2)])
|
||||
@@ -1657,7 +1684,7 @@ class DirectCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
('BUILD_TUPLE', 2, 0),
|
||||
('CONTAINS_OP', 0, 0),
|
||||
('POP_TOP', None, 0),
|
||||
('LOAD_CONST', 0, 0),
|
||||
('LOAD_COMMON_CONSTANT', 7, 0),
|
||||
('RETURN_VALUE', None, 0),
|
||||
]
|
||||
self.cfg_optimization_test(before, after, consts=[None], expected_consts=[None])
|
||||
@@ -1679,7 +1706,7 @@ class DirectCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
('LOAD_CONST', 1, 0),
|
||||
('CONTAINS_OP', 0, 0),
|
||||
('POP_TOP', None, 0),
|
||||
('LOAD_CONST', 0, 0),
|
||||
('LOAD_COMMON_CONSTANT', 7, 0),
|
||||
('RETURN_VALUE', None, 0),
|
||||
]
|
||||
self.cfg_optimization_test(before, after, consts=[None], expected_consts=[None, frozenset({1, 2})])
|
||||
@@ -1696,7 +1723,17 @@ class DirectCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
('LOAD_CONST', 0, 0),
|
||||
('RETURN_VALUE', None, 0),
|
||||
]
|
||||
self.cfg_optimization_test(same, same, consts=[None], expected_consts=[None])
|
||||
expected = [
|
||||
('LOAD_NAME', 0, 0),
|
||||
('LOAD_SMALL_INT', 1, 0),
|
||||
('LOAD_NAME', 1, 0),
|
||||
('BUILD_SET', 2, 0),
|
||||
('CONTAINS_OP', 0, 0),
|
||||
('POP_TOP', None, 0),
|
||||
('LOAD_COMMON_CONSTANT', 7, 0),
|
||||
('RETURN_VALUE', None, 0),
|
||||
]
|
||||
self.cfg_optimization_test(same, expected, consts=[None], expected_consts=[None])
|
||||
|
||||
def test_optimize_unary_not(self):
|
||||
# test folding
|
||||
@@ -1707,10 +1744,10 @@ class DirectCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
('RETURN_VALUE', None, 0),
|
||||
]
|
||||
after = [
|
||||
('LOAD_CONST', 1, 0),
|
||||
('LOAD_COMMON_CONSTANT', 10, 0),
|
||||
('RETURN_VALUE', None, 0),
|
||||
]
|
||||
self.cfg_optimization_test(before, after, consts=[], expected_consts=[True, False])
|
||||
self.cfg_optimization_test(before, after, consts=[], expected_consts=[True])
|
||||
|
||||
# test cancel out
|
||||
before = [
|
||||
@@ -1758,7 +1795,7 @@ class DirectCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
('RETURN_VALUE', None, 0),
|
||||
]
|
||||
after = [
|
||||
('LOAD_CONST', 0, 0),
|
||||
('LOAD_COMMON_CONSTANT', 9, 0),
|
||||
('RETURN_VALUE', None, 0),
|
||||
]
|
||||
self.cfg_optimization_test(before, after, consts=[], expected_consts=[True])
|
||||
@@ -1774,10 +1811,10 @@ class DirectCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
('RETURN_VALUE', None, 0),
|
||||
]
|
||||
after = [
|
||||
('LOAD_CONST', 1, 0),
|
||||
('LOAD_COMMON_CONSTANT', 10, 0),
|
||||
('RETURN_VALUE', None, 0),
|
||||
]
|
||||
self.cfg_optimization_test(before, after, consts=[], expected_consts=[True, False])
|
||||
self.cfg_optimization_test(before, after, consts=[], expected_consts=[True])
|
||||
|
||||
# test cancel out & eliminate to bool (to bool stays as we are not iterating to a fixed point)
|
||||
before = [
|
||||
@@ -2393,7 +2430,7 @@ class DirectCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
("LOAD_FAST", 1, 4),
|
||||
("LIST_EXTEND", 1, 5),
|
||||
("CALL_INTRINSIC_1", INTRINSIC_LIST_TO_TUPLE, 6),
|
||||
("GET_ITER", None, 7),
|
||||
("GET_ITER", 0, 7),
|
||||
top := self.Label(),
|
||||
("FOR_ITER", end := self.Label(), 8),
|
||||
("STORE_FAST", 2, 9),
|
||||
@@ -2411,7 +2448,7 @@ class DirectCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
("LOAD_FAST_BORROW", 1, 4),
|
||||
("LIST_EXTEND", 1, 5),
|
||||
("NOP", None, 6), # ("CALL_INTRINSIC_1", INTRINSIC_LIST_TO_TUPLE, 6),
|
||||
("GET_ITER", None, 7),
|
||||
("GET_ITER", 0, 7),
|
||||
top := self.Label(),
|
||||
("FOR_ITER", end := self.Label(), 8),
|
||||
("STORE_FAST", 2, 9),
|
||||
@@ -2419,7 +2456,7 @@ class DirectCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
end,
|
||||
("END_FOR", None, 11),
|
||||
("POP_TOP", None, 12),
|
||||
("LOAD_CONST", 0, 13),
|
||||
("LOAD_COMMON_CONSTANT", 7, 13),
|
||||
("RETURN_VALUE", None, 14),
|
||||
]
|
||||
self.cfg_optimization_test(insts, expected_insts, consts=[None])
|
||||
@@ -2433,6 +2470,168 @@ class DirectCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
self.assertEqual(b, [3, 2, 1, 0])
|
||||
self.assertEqual(items, [])
|
||||
|
||||
def test_fold_constant_big_list_for_iter(self):
|
||||
# for x in [c1, c2, ..., cN] (N > 30) should fold to LOAD_CONST tuple
|
||||
consts = 35
|
||||
before = (
|
||||
[("BUILD_LIST", 0, 1)] +
|
||||
[("LOAD_CONST", 0, 2), ("LIST_APPEND", 1, 3)] * consts +
|
||||
[("GET_ITER", 0, 4),
|
||||
top := self.Label(),
|
||||
("FOR_ITER", end := self.Label(), 5),
|
||||
("STORE_FAST", 0, 6),
|
||||
("JUMP", top, 7),
|
||||
end,
|
||||
("END_FOR", None, 8),
|
||||
("POP_ITER", None, 9),
|
||||
("LOAD_CONST", 0, 10),
|
||||
("RETURN_VALUE", None, 11)]
|
||||
)
|
||||
after = [
|
||||
("LOAD_CONST", 1, 3),
|
||||
("GET_ITER", 0, 4),
|
||||
top := self.Label(),
|
||||
("FOR_ITER", end := self.Label(), 5),
|
||||
("STORE_FAST", 0, 6),
|
||||
("JUMP", top, 7),
|
||||
end,
|
||||
("END_FOR", None, 8),
|
||||
("POP_ITER", None, 9),
|
||||
("LOAD_CONST", 0, 10),
|
||||
("RETURN_VALUE", None, 11),
|
||||
]
|
||||
result_const = tuple(["test"] * consts)
|
||||
self.cfg_optimization_test(before, after, consts=["test"],
|
||||
expected_consts=["test", result_const])
|
||||
|
||||
def test_fold_constant_big_set_for_iter(self):
|
||||
# for x in {c1, c2, ..., cN} (N > 30) should fold to LOAD_CONST frozenset
|
||||
before = [
|
||||
("BUILD_SET", 0, 1),
|
||||
("LOAD_SMALL_INT", 1, 2), ("SET_ADD", 1, 3),
|
||||
("LOAD_SMALL_INT", 2, 4), ("SET_ADD", 1, 5),
|
||||
("LOAD_SMALL_INT", 3, 6), ("SET_ADD", 1, 7),
|
||||
("GET_ITER", 0, 8),
|
||||
top := self.Label(),
|
||||
("FOR_ITER", end := self.Label(), 9),
|
||||
("STORE_FAST", 0, 10),
|
||||
("JUMP", top, 11),
|
||||
end,
|
||||
("END_FOR", None, 12),
|
||||
("POP_ITER", None, 13),
|
||||
("LOAD_CONST", 0, 14),
|
||||
("RETURN_VALUE", None, 15),
|
||||
]
|
||||
after = [
|
||||
("LOAD_CONST", 1, 7),
|
||||
("GET_ITER", 0, 8),
|
||||
top := self.Label(),
|
||||
("FOR_ITER", end := self.Label(), 9),
|
||||
("STORE_FAST", 0, 10),
|
||||
("JUMP", top, 11),
|
||||
end,
|
||||
("END_FOR", None, 12),
|
||||
("POP_ITER", None, 13),
|
||||
("LOAD_CONST", 0, 14),
|
||||
("RETURN_VALUE", None, 15),
|
||||
]
|
||||
self.cfg_optimization_test(before, after, consts=["test"],
|
||||
expected_consts=["test", frozenset({1, 2, 3})])
|
||||
|
||||
def test_fold_constant_list_to_tuple_for_iter(self):
|
||||
INTRINSIC_LIST_TO_TUPLE = 6
|
||||
before = [
|
||||
("BUILD_LIST", 0, 1),
|
||||
("LOAD_SMALL_INT", 1, 2), ("LIST_APPEND", 1, 3),
|
||||
("LOAD_SMALL_INT", 2, 4), ("LIST_APPEND", 1, 5),
|
||||
("LOAD_SMALL_INT", 3, 6), ("LIST_APPEND", 1, 7),
|
||||
("CALL_INTRINSIC_1", INTRINSIC_LIST_TO_TUPLE, 8),
|
||||
("GET_ITER", 0, 9),
|
||||
top := self.Label(),
|
||||
("FOR_ITER", end := self.Label(), 10),
|
||||
("STORE_FAST", 0, 11),
|
||||
("JUMP", top, 12),
|
||||
end,
|
||||
("END_FOR", None, 13),
|
||||
("POP_ITER", None, 14),
|
||||
("LOAD_CONST", 0, 15),
|
||||
("RETURN_VALUE", None, 16),
|
||||
]
|
||||
after = [
|
||||
("LOAD_CONST", 1, 8),
|
||||
("GET_ITER", 0, 9),
|
||||
top := self.Label(),
|
||||
("FOR_ITER", end := self.Label(), 10),
|
||||
("STORE_FAST", 0, 11),
|
||||
("JUMP", top, 12),
|
||||
end,
|
||||
("END_FOR", None, 13),
|
||||
("POP_ITER", None, 14),
|
||||
("LOAD_CONST", 0, 15),
|
||||
("RETURN_VALUE", None, 16),
|
||||
]
|
||||
self.cfg_optimization_test(before, after, consts=["test"],
|
||||
expected_consts=["test", (1, 2, 3)])
|
||||
|
||||
def test_fold_constant_big_list_contains_op(self):
|
||||
# x in [c1, c2, ..., cN] (N > 30) should fold to LOAD_CONST tuple
|
||||
before = [
|
||||
("LOAD_FAST", 0, 1),
|
||||
("BUILD_LIST", 0, 2),
|
||||
("LOAD_SMALL_INT", 1, 3), ("LIST_APPEND", 1, 4),
|
||||
("LOAD_SMALL_INT", 2, 5), ("LIST_APPEND", 1, 6),
|
||||
("LOAD_SMALL_INT", 3, 7), ("LIST_APPEND", 1, 8),
|
||||
("CONTAINS_OP", 0, 9),
|
||||
("RETURN_VALUE", None, 10),
|
||||
]
|
||||
after = [
|
||||
("LOAD_FAST_BORROW", 0, 1),
|
||||
("LOAD_CONST", 1, 8),
|
||||
("CONTAINS_OP", 0, 9),
|
||||
("RETURN_VALUE", None, 10),
|
||||
]
|
||||
self.cfg_optimization_test(before, after, consts=[None],
|
||||
expected_consts=[None, (1, 2, 3)])
|
||||
|
||||
def test_fold_constant_big_set_contains_op(self):
|
||||
# x in {c1, c2, ..., cN} (N > 30) should fold to LOAD_CONST frozenset
|
||||
before = [
|
||||
("LOAD_FAST", 0, 1),
|
||||
("BUILD_SET", 0, 2),
|
||||
("LOAD_SMALL_INT", 1, 3), ("SET_ADD", 1, 4),
|
||||
("LOAD_SMALL_INT", 2, 5), ("SET_ADD", 1, 6),
|
||||
("LOAD_SMALL_INT", 3, 7), ("SET_ADD", 1, 8),
|
||||
("CONTAINS_OP", 0, 9),
|
||||
("RETURN_VALUE", None, 10),
|
||||
]
|
||||
after = [
|
||||
("LOAD_FAST_BORROW", 0, 1),
|
||||
("LOAD_CONST", 1, 8),
|
||||
("CONTAINS_OP", 0, 9),
|
||||
("RETURN_VALUE", None, 10),
|
||||
]
|
||||
self.cfg_optimization_test(before, after, consts=[None],
|
||||
expected_consts=[None, frozenset({1, 2, 3})])
|
||||
|
||||
def test_no_fold_big_list_for_iter_with_non_const(self):
|
||||
same = [
|
||||
("BUILD_LIST", 0, 1),
|
||||
("LOAD_SMALL_INT", 1, 2), ("LIST_APPEND", 1, 3),
|
||||
("LOAD_FAST_BORROW", 0, 4), ("LIST_APPEND", 1, 5),
|
||||
("LOAD_SMALL_INT", 3, 6), ("LIST_APPEND", 1, 7),
|
||||
("GET_ITER", 0, 8),
|
||||
top := self.Label(),
|
||||
("FOR_ITER", end := self.Label(), 9),
|
||||
("STORE_FAST", 1, 10),
|
||||
("JUMP", top, 11),
|
||||
end,
|
||||
("END_FOR", None, 12),
|
||||
("POP_ITER", None, 13),
|
||||
("LOAD_CONST", 0, 14),
|
||||
("RETURN_VALUE", None, 15),
|
||||
]
|
||||
self.cfg_optimization_test(same, same, consts=["test"])
|
||||
|
||||
|
||||
class OptimizeLoadFastTestCase(DirectCfgOptimizerTests):
|
||||
def make_bb(self, insts):
|
||||
@@ -2443,7 +2642,7 @@ class OptimizeLoadFastTestCase(DirectCfgOptimizerTests):
|
||||
maxconst = max(maxconst, arg)
|
||||
consts = [None for _ in range(maxconst + 1)]
|
||||
return insts + [
|
||||
("LOAD_CONST", 0, last_loc + 1),
|
||||
("LOAD_COMMON_CONSTANT", 7, last_loc + 1),
|
||||
("RETURN_VALUE", None, last_loc + 2),
|
||||
], consts
|
||||
|
||||
@@ -2474,7 +2673,7 @@ class OptimizeLoadFastTestCase(DirectCfgOptimizerTests):
|
||||
]
|
||||
expected = [
|
||||
("LOAD_FAST_BORROW", 0, 1),
|
||||
("LOAD_CONST", 1, 2),
|
||||
("LOAD_COMMON_CONSTANT", 7, 2),
|
||||
("SWAP", 2, 3),
|
||||
("POP_TOP", None, 4),
|
||||
]
|
||||
@@ -2512,7 +2711,13 @@ class OptimizeLoadFastTestCase(DirectCfgOptimizerTests):
|
||||
("STORE_FAST", 0, 3),
|
||||
("POP_TOP", None, 4),
|
||||
]
|
||||
self.check(insts, insts)
|
||||
expected = [
|
||||
("LOAD_FAST", 0, 1),
|
||||
("LOAD_COMMON_CONSTANT", 7, 2),
|
||||
("STORE_FAST", 0, 3),
|
||||
("POP_TOP", None, 4),
|
||||
]
|
||||
self.check(insts, expected)
|
||||
|
||||
insts = [
|
||||
("LOAD_FAST", 0, 1),
|
||||
@@ -2521,7 +2726,14 @@ class OptimizeLoadFastTestCase(DirectCfgOptimizerTests):
|
||||
("STORE_FAST_STORE_FAST", ((0 << 4) | 1), 4),
|
||||
("POP_TOP", None, 5),
|
||||
]
|
||||
self.check(insts, insts)
|
||||
expected = [
|
||||
("LOAD_FAST", 0, 1),
|
||||
("LOAD_COMMON_CONSTANT", 7, 2),
|
||||
("LOAD_COMMON_CONSTANT", 7, 3),
|
||||
("STORE_FAST_STORE_FAST", ((0 << 4) | 1), 4),
|
||||
("POP_TOP", None, 5),
|
||||
]
|
||||
self.check(insts, expected)
|
||||
|
||||
insts = [
|
||||
("LOAD_FAST", 0, 1),
|
||||
@@ -2542,7 +2754,12 @@ class OptimizeLoadFastTestCase(DirectCfgOptimizerTests):
|
||||
("LOAD_CONST", 0, 3),
|
||||
("STORE_FAST_STORE_FAST", ((0 << 4) | 1), 4),
|
||||
]
|
||||
self.check(insts, insts)
|
||||
expected = [
|
||||
("LOAD_FAST", 0, 1),
|
||||
("LOAD_COMMON_CONSTANT", 7, 3),
|
||||
("STORE_FAST_STORE_FAST", ((0 << 4) | 1), 4),
|
||||
]
|
||||
self.check(insts, expected)
|
||||
|
||||
def test_consume_no_inputs(self):
|
||||
insts = [
|
||||
@@ -2587,7 +2804,19 @@ class OptimizeLoadFastTestCase(DirectCfgOptimizerTests):
|
||||
("LOAD_CONST", 0, 7),
|
||||
("RETURN_VALUE", None, 8),
|
||||
]
|
||||
self.cfg_optimization_test(insts, insts, consts=[None])
|
||||
expected = [
|
||||
("LOAD_FAST", 0, 1),
|
||||
top := self.Label(),
|
||||
("FOR_ITER", end := self.Label(), 2),
|
||||
("STORE_FAST", 2, 3),
|
||||
("JUMP", top, 4),
|
||||
end,
|
||||
("END_FOR", None, 5),
|
||||
("POP_TOP", None, 6),
|
||||
("LOAD_COMMON_CONSTANT", 7, 7),
|
||||
("RETURN_VALUE", None, 8),
|
||||
]
|
||||
self.cfg_optimization_test(insts, expected, consts=[None])
|
||||
|
||||
def test_load_attr(self):
|
||||
insts = [
|
||||
@@ -2656,10 +2885,10 @@ class OptimizeLoadFastTestCase(DirectCfgOptimizerTests):
|
||||
("LOAD_FAST", 0, 1),
|
||||
("LOAD_FAST_BORROW", 1, 2),
|
||||
("SEND", end := self.Label(), 3),
|
||||
("LOAD_CONST", 0, 4),
|
||||
("LOAD_COMMON_CONSTANT", 7, 4),
|
||||
("RETURN_VALUE", None, 5),
|
||||
end,
|
||||
("LOAD_CONST", 0, 6),
|
||||
("LOAD_COMMON_CONSTANT", 7, 6),
|
||||
("RETURN_VALUE", None, 7)
|
||||
]
|
||||
self.cfg_optimization_test(insts, expected, consts=[None])
|
||||
@@ -2697,7 +2926,15 @@ class OptimizeLoadFastTestCase(DirectCfgOptimizerTests):
|
||||
("LOAD_CONST", 0, 5),
|
||||
("RETURN_VALUE", None, 6)
|
||||
]
|
||||
self.cfg_optimization_test(insts, insts, consts=[None])
|
||||
expected = [
|
||||
("LOAD_COMMON_CONSTANT", 7, 1),
|
||||
("LOAD_FAST", 0, 2),
|
||||
("SET_FUNCTION_ATTRIBUTE", 2, 3),
|
||||
("STORE_FAST", 1, 4),
|
||||
("LOAD_COMMON_CONSTANT", 7, 5),
|
||||
("RETURN_VALUE", None, 6)
|
||||
]
|
||||
self.cfg_optimization_test(insts, expected, consts=[None])
|
||||
|
||||
insts = [
|
||||
("LOAD_CONST", 0, 1),
|
||||
@@ -2706,7 +2943,7 @@ class OptimizeLoadFastTestCase(DirectCfgOptimizerTests):
|
||||
("RETURN_VALUE", None, 4)
|
||||
]
|
||||
expected = [
|
||||
("LOAD_CONST", 0, 1),
|
||||
("LOAD_COMMON_CONSTANT", 7, 1),
|
||||
("LOAD_FAST_BORROW", 0, 2),
|
||||
("SET_FUNCTION_ATTRIBUTE", 2, 3),
|
||||
("RETURN_VALUE", None, 4)
|
||||
@@ -2714,22 +2951,37 @@ class OptimizeLoadFastTestCase(DirectCfgOptimizerTests):
|
||||
self.cfg_optimization_test(insts, expected, consts=[None])
|
||||
|
||||
def test_get_yield_from_iter(self):
|
||||
# GET_YIELD_FROM_ITER may leave its operand on the stack
|
||||
insts = [
|
||||
("LOAD_FAST", 0, 1),
|
||||
("GET_YIELD_FROM_ITER", None, 2),
|
||||
("LOAD_CONST", 0, 3),
|
||||
("GET_ITER", 1, 2),
|
||||
("PUSH_NULL", None, 3),
|
||||
("LOAD_CONST", 0, 4),
|
||||
send := self.Label(),
|
||||
("SEND", end := self.Label(), 5),
|
||||
("YIELD_VALUE", 1, 6),
|
||||
("RESUME", 2, 7),
|
||||
("JUMP", send, 8),
|
||||
("SEND", end := self.Label(), 6),
|
||||
("YIELD_VALUE", 1, 7),
|
||||
("RESUME", 2, 8),
|
||||
("JUMP", send, 9),
|
||||
end,
|
||||
("END_SEND", None, 9),
|
||||
("LOAD_CONST", 0, 10),
|
||||
("RETURN_VALUE", None, 11),
|
||||
("END_SEND", None, 10),
|
||||
("LOAD_CONST", 0, 11),
|
||||
("RETURN_VALUE", None, 12),
|
||||
]
|
||||
self.cfg_optimization_test(insts, insts, consts=[None])
|
||||
expected = [
|
||||
("LOAD_FAST", 0, 1),
|
||||
("GET_ITER", 1, 2),
|
||||
("PUSH_NULL", None, 3),
|
||||
("LOAD_COMMON_CONSTANT", 7, 4),
|
||||
send := self.Label(),
|
||||
("SEND", end := self.Label(), 6),
|
||||
("YIELD_VALUE", 1, 7),
|
||||
("RESUME", 2, 8),
|
||||
("JUMP", send, 9),
|
||||
end,
|
||||
("END_SEND", None, 10),
|
||||
("LOAD_COMMON_CONSTANT", 7, 11),
|
||||
("RETURN_VALUE", None, 12),
|
||||
]
|
||||
self.cfg_optimization_test(insts, expected, consts=[None])
|
||||
|
||||
def test_push_exc_info(self):
|
||||
insts = [
|
||||
|
||||
Vendored
+21
-10
@@ -250,10 +250,23 @@ class FilterTests(BaseTest):
|
||||
self.module.warn_explicit('msg', UserWarning, 'filename', 42,
|
||||
module='package.module')
|
||||
self.assertEqual(len(w), 1)
|
||||
self.module.warn_explicit('msg', UserWarning, '/path/to/package/module', 42)
|
||||
self.assertEqual(len(w), 2)
|
||||
self.module.warn_explicit('msg', UserWarning, '/path/to/package/module.py', 42)
|
||||
self.assertEqual(len(w), 3)
|
||||
self.module.warn_explicit('msg', UserWarning, '/path/to/package/module/__init__.py', 42)
|
||||
self.assertEqual(len(w), 4)
|
||||
with self.assertRaises(UserWarning):
|
||||
self.module.warn_explicit('msg', UserWarning, '/path/to/package/module', 42)
|
||||
with self.assertRaises(UserWarning):
|
||||
self.module.warn_explicit('msg', UserWarning, '/path/to/package/module.py', 42)
|
||||
self.module.warn_explicit('msg', UserWarning, '/path/to/package/module/__init__', 42)
|
||||
if MS_WINDOWS:
|
||||
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.PY', 42)
|
||||
self.assertEqual(len(w), 5)
|
||||
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module\__INIT__.PY', 42)
|
||||
self.assertEqual(len(w), 6)
|
||||
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.PYW', 42)
|
||||
self.assertEqual(len(w), 7)
|
||||
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module\__INIT__.PYW', 42)
|
||||
self.assertEqual(len(w), 8)
|
||||
|
||||
with self.module.catch_warnings(record=True) as w:
|
||||
self.module.simplefilter('error')
|
||||
@@ -277,9 +290,8 @@ class FilterTests(BaseTest):
|
||||
with self.assertRaises(UserWarning):
|
||||
self.module.warn_explicit('msg', UserWarning, '/PATH/TO/PACKAGE/MODULE', 42)
|
||||
if MS_WINDOWS:
|
||||
if self.module is py_warnings:
|
||||
self.module.warn_explicit('msg', UserWarning, r'/path/to/package/module.PY', 42)
|
||||
self.assertEqual(len(w), 3)
|
||||
self.module.warn_explicit('msg', UserWarning, r'/path/to/package/module.PY', 42)
|
||||
self.assertEqual(len(w), 3)
|
||||
with self.assertRaises(UserWarning):
|
||||
self.module.warn_explicit('msg', UserWarning, r'/path/to/package/module/__init__.py', 42)
|
||||
with self.assertRaises(UserWarning):
|
||||
@@ -303,9 +315,8 @@ class FilterTests(BaseTest):
|
||||
self.assertEqual(len(w), 1)
|
||||
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.py', 42)
|
||||
self.assertEqual(len(w), 2)
|
||||
if self.module is py_warnings:
|
||||
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.PY', 42)
|
||||
self.assertEqual(len(w), 3)
|
||||
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.PY', 42)
|
||||
self.assertEqual(len(w), 3)
|
||||
with self.assertRaises(UserWarning):
|
||||
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.pyw', 42)
|
||||
with self.assertRaises(UserWarning):
|
||||
@@ -400,7 +411,7 @@ class FilterTests(BaseTest):
|
||||
|
||||
def test_mutate_filter_list(self):
|
||||
class X:
|
||||
def match(self, a):
|
||||
def match(self, a, start=0):
|
||||
L[:] = []
|
||||
|
||||
L = [("default",X(),UserWarning,X(),0) for i in range(2)]
|
||||
|
||||
+539
-1469
File diff suppressed because it is too large.
Load diff
+275
-242
@@ -455,7 +455,11 @@ impl InstructionInfo {
|
||||
|
||||
/// flowgraph.c loads_const
|
||||
const fn loads_const(&self) -> bool {
|
||||
self.instr.has_const() || matches!(self.instr.real_opcode(), Some(Opcode::LoadSmallInt))
|
||||
self.instr.has_const()
|
||||
|| matches!(
|
||||
self.instr.real_opcode(),
|
||||
Some(Opcode::LoadSmallInt | Opcode::LoadCommonConstant)
|
||||
)
|
||||
}
|
||||
|
||||
/// flowgraph.c STORES_TO
|
||||
@@ -478,6 +482,26 @@ impl InstructionInfo {
|
||||
false
|
||||
}
|
||||
|
||||
/// flowgraph.c maybe_instr_make_load_common_const
|
||||
fn maybe_instr_make_load_common_const(&mut self, constant: &ConstantData) -> bool {
|
||||
use oparg::CommonConstant;
|
||||
let idx = match constant {
|
||||
ConstantData::None => CommonConstant::None,
|
||||
ConstantData::Boolean { value: true } => CommonConstant::True,
|
||||
ConstantData::Boolean { value: false } => CommonConstant::False,
|
||||
ConstantData::Str { value } if value.is_empty() => CommonConstant::EmptyStr,
|
||||
ConstantData::Integer { value } if value.to_i32() == Some(-1) => {
|
||||
CommonConstant::MinusOne
|
||||
}
|
||||
_ => return false,
|
||||
};
|
||||
self.instr_set_op1(
|
||||
Opcode::LoadCommonConstant.into(),
|
||||
OpArg::new(u32::from(idx)),
|
||||
);
|
||||
true
|
||||
}
|
||||
|
||||
/// flowgraph.c make_super_instruction
|
||||
fn make_super_instruction(inst1: &mut Self, inst2: &mut Self, super_op: AnyInstruction) {
|
||||
let line1 = inst1.instruction_lineno();
|
||||
@@ -1084,7 +1108,7 @@ fn resolve_unconditional_jumps(instr_sequence: &mut InstructionSequence) {
|
||||
/// assemble.c resolve_jump_offsets
|
||||
fn resolve_jump_offsets(instr_sequence: &mut InstructionSequence) {
|
||||
// The offset (in code units) of END_SEND from SEND in the yield-from sequence.
|
||||
const END_SEND_OFFSET: i32 = 5;
|
||||
const END_SEND_OFFSET: i32 = 6;
|
||||
for i in 0..instr_sequence.instr_used {
|
||||
let instr = &mut instr_sequence.instrs[i];
|
||||
let opcode = instr.info.instr.expect_real();
|
||||
@@ -2227,14 +2251,17 @@ impl Blocks {
|
||||
AnyInstruction::Real(Instruction::CallIntrinsic1 { func }) => {
|
||||
match func.get(inst.arg) {
|
||||
IntrinsicFunction1::ListToTuple => {
|
||||
if matches!(nextop, Some(Instruction::GetIter)) {
|
||||
fold_constant_sequence_into_load_const(
|
||||
metadata,
|
||||
&mut self[block_idx],
|
||||
i,
|
||||
)?;
|
||||
if matches!(
|
||||
self[block_idx].instructions[i].instr.real(),
|
||||
Some(Instruction::CallIntrinsic1 { .. })
|
||||
) && matches!(nextop, Some(Instruction::GetIter { .. }))
|
||||
{
|
||||
self[block_idx].instructions[i].set_to_nop();
|
||||
} else {
|
||||
fold_constant_intrinsic_list_to_tuple(
|
||||
metadata,
|
||||
&mut self[block_idx],
|
||||
i,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
IntrinsicFunction1::UnaryPositive => {
|
||||
@@ -2243,6 +2270,16 @@ impl Blocks {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
AnyInstruction::Real(
|
||||
Instruction::ListAppend { .. } | Instruction::SetAdd { .. },
|
||||
) if u32::from(inst.arg) == 1
|
||||
&& matches!(
|
||||
nextop,
|
||||
Some(Instruction::GetIter { .. } | Instruction::ContainsOp { .. })
|
||||
) =>
|
||||
{
|
||||
fold_constant_sequence_into_load_const(metadata, &mut self[block_idx], i)?;
|
||||
}
|
||||
AnyInstruction::Real(Instruction::BinaryOp { .. }) => {
|
||||
fold_const_binop(metadata, &mut self[block_idx], i)?;
|
||||
}
|
||||
@@ -2410,7 +2447,7 @@ impl Blocks {
|
||||
Instruction::FormatSimple
|
||||
| Instruction::GetAnext
|
||||
| Instruction::GetLen
|
||||
| Instruction::GetYieldFromIter
|
||||
| Instruction::GetIter { .. }
|
||||
| Instruction::ImportFrom { .. }
|
||||
| Instruction::MatchKeys
|
||||
| Instruction::MatchMapping
|
||||
@@ -2420,10 +2457,8 @@ impl Blocks {
|
||||
let effect = instr.stack_effect_info(arg_u32);
|
||||
let net_pushed = effect.pushed() as isize - effect.popped() as isize;
|
||||
debug_assert!(net_pushed >= 0);
|
||||
// CPython optimize_load_fast() shadows the outer
|
||||
// instruction index in this produced-value loop.
|
||||
for produced in 0..net_pushed {
|
||||
push_ref(&mut refs, produced, NOT_LOCAL)?;
|
||||
for _ in 0..net_pushed {
|
||||
push_ref(&mut refs, i as isize, NOT_LOCAL)?;
|
||||
}
|
||||
}
|
||||
AnyInstruction::Real(
|
||||
@@ -2447,10 +2482,19 @@ impl Blocks {
|
||||
Instruction::EndSend | Instruction::SetFunctionAttribute { .. },
|
||||
) => {
|
||||
let effect = instr.stack_effect_info(arg_u32);
|
||||
debug_assert_eq!(effect.popped(), 2);
|
||||
debug_assert_eq!(
|
||||
effect.popped(),
|
||||
if matches!(instr.real(), Some(Instruction::EndSend)) {
|
||||
3
|
||||
} else {
|
||||
2
|
||||
}
|
||||
);
|
||||
debug_assert_eq!(effect.pushed(), 1);
|
||||
let tos = ref_stack_pop(&mut refs);
|
||||
let _ = ref_stack_pop(&mut refs);
|
||||
for _ in 1..effect.popped() {
|
||||
let _ = ref_stack_pop(&mut refs);
|
||||
}
|
||||
push_ref(&mut refs, tos.instr, tos.local)?;
|
||||
}
|
||||
AnyInstruction::Real(Instruction::CheckExcMatch) => {
|
||||
@@ -2634,8 +2678,14 @@ impl Blocks {
|
||||
|
||||
let opcode = instr_info.instr.real_opcode();
|
||||
let is_redundant_pair = matches!(opcode, Some(Opcode::PopTop))
|
||||
&& (matches!(prev_opcode, Some(Opcode::LoadConst | Opcode::LoadSmallInt))
|
||||
|| (prev_oparg == 1 && matches!(prev_opcode, Some(Opcode::Copy))));
|
||||
&& (matches!(
|
||||
prev_opcode,
|
||||
Some(
|
||||
Opcode::LoadConst
|
||||
| Opcode::LoadSmallInt
|
||||
| Opcode::LoadCommonConstant
|
||||
)
|
||||
) || (prev_oparg == 1 && matches!(prev_opcode, Some(Opcode::Copy))));
|
||||
|
||||
if is_redundant_pair {
|
||||
let (prev_block, prev_instr_idx) =
|
||||
@@ -3452,6 +3502,8 @@ impl Blocks {
|
||||
debug_assert!(dest <= instr_count);
|
||||
let num_removed = instr_count - dest;
|
||||
self[block_idx].instruction_used = dest;
|
||||
// Later instructions reuse these slots and must not inherit a removed handler.
|
||||
self[block_idx].instructions[dest..instr_count].fill(InstructionInfo::empty());
|
||||
num_removed
|
||||
}
|
||||
|
||||
@@ -3985,6 +4037,7 @@ impl CodeInfo {
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert SETUP_CLEANUP just after the initial RETURN_GENERATOR; POP_TOP.
|
||||
pub(crate) fn insert_start_setup_cleanup(
|
||||
&mut self,
|
||||
handler_block: BlockIdx,
|
||||
@@ -3994,9 +4047,20 @@ impl CodeInfo {
|
||||
&mut self.instr_sequence,
|
||||
handler_block,
|
||||
)?;
|
||||
let seq = &self.instr_sequence;
|
||||
let opcode_at = |i: usize| seq.instrs[i].info.instr.real();
|
||||
let mut resume = 0;
|
||||
while !matches!(opcode_at(resume), Some(Instruction::ReturnGenerator)) {
|
||||
resume += 1;
|
||||
debug_assert!(resume < seq.instr_used);
|
||||
}
|
||||
resume += 1;
|
||||
debug_assert!(matches!(opcode_at(resume), Some(Instruction::PopTop)));
|
||||
resume += 1;
|
||||
debug_assert!(resume < seq.instr_used);
|
||||
instruction_sequence_insert_instruction(
|
||||
&mut self.instr_sequence,
|
||||
0,
|
||||
resume,
|
||||
InstructionInfo {
|
||||
instr: PseudoOpcode::SetupCleanup.into(),
|
||||
arg: instruction_sequence_label_oparg(handler_label),
|
||||
@@ -4009,6 +4073,27 @@ impl CodeInfo {
|
||||
)
|
||||
}
|
||||
|
||||
/// Insert `RESUME`, `LOAD_FAST 0` and the outermost `GET_ITER` (or
|
||||
/// `GET_AITER`) before RETURN_GENERATOR.
|
||||
pub(crate) fn insert_genexpr_prefix(
|
||||
&mut self,
|
||||
mut iterator: InstructionInfo,
|
||||
) -> crate::InternalResult<()> {
|
||||
let mut resume = InstructionInfo::empty();
|
||||
resume.instr_set_op1(
|
||||
Opcode::Resume.into(),
|
||||
OpArg::new(oparg::ResumeLocation::AtGenExprStart.into()),
|
||||
);
|
||||
resume.lineno_override = Some(NO_LOCATION_OVERRIDE);
|
||||
let mut load = iterator;
|
||||
load.instr_set_op1(Opcode::LoadFast.into(), OpArg::new(0));
|
||||
iterator.arg = OpArg::new(0);
|
||||
for (i, info) in [resume, load, iterator].into_iter().enumerate() {
|
||||
instruction_sequence_insert_instruction(&mut self.instr_sequence, i, info)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn push_unmapped_instr_sequence_label(&mut self) -> crate::InternalResult<()> {
|
||||
instruction_sequence_label_map_push_unmapped_label(
|
||||
&mut self.instr_sequence_label_map,
|
||||
@@ -4114,7 +4199,7 @@ fn optimized_cfg_to_instruction_sequence(
|
||||
blocks.convert_pseudo_conditional_jumps()?;
|
||||
let max_stackdepth = blocks.calculate_stackdepth()?;
|
||||
debug_assert!(!is_generator(flags) || max_stackdepth != 0);
|
||||
let nlocalsplus = prepare_localsplus(metadata, blocks, flags)?;
|
||||
let nlocalsplus = prepare_localsplus(metadata, blocks)?;
|
||||
// Pseudo ops are lowered after stackdepth and
|
||||
// localsplus preparation, before normalize_jumps.
|
||||
convert_pseudo_ops(blocks)?;
|
||||
@@ -4336,7 +4421,6 @@ fn insert_prefix_instructions(
|
||||
blocks: &mut Blocks,
|
||||
cellfixedoffsets: &[i32],
|
||||
nfreevars: usize,
|
||||
flags: CodeFlags,
|
||||
) -> crate::InternalResult<()> {
|
||||
debug_assert!(!blocks.is_empty());
|
||||
let entry = &mut blocks[0];
|
||||
@@ -4344,37 +4428,6 @@ fn insert_prefix_instructions(
|
||||
let firstlineno = metadata.firstlineno;
|
||||
debug_assert!(firstlineno.get() > 0);
|
||||
|
||||
if is_generator(flags) {
|
||||
let location = SourceLocation {
|
||||
line: firstlineno,
|
||||
character_offset: OneIndexed::MIN,
|
||||
};
|
||||
entry.basicblock_insert_instruction(
|
||||
0,
|
||||
InstructionInfo {
|
||||
instr: Instruction::ReturnGenerator.into(),
|
||||
arg: OpArg::new(0),
|
||||
target: BlockIdx::NULL,
|
||||
location,
|
||||
end_location: location,
|
||||
except_handler: None,
|
||||
lineno_override: Some(LINE_ONLY_LOCATION_OVERRIDE),
|
||||
},
|
||||
)?;
|
||||
entry.basicblock_insert_instruction(
|
||||
1,
|
||||
InstructionInfo {
|
||||
instr: Instruction::PopTop.into(),
|
||||
arg: OpArg::new(0),
|
||||
target: BlockIdx::NULL,
|
||||
location,
|
||||
end_location: location,
|
||||
except_handler: None,
|
||||
lineno_override: Some(LINE_ONLY_LOCATION_OVERRIDE),
|
||||
},
|
||||
)?;
|
||||
}
|
||||
|
||||
if ncellvars > 0 {
|
||||
let nvars = metadata.varnames.len() + ncellvars;
|
||||
let mut sorted = Vec::new();
|
||||
@@ -4428,7 +4481,6 @@ fn insert_prefix_instructions(
|
||||
fn prepare_localsplus(
|
||||
metadata: &CodeUnitMetadata,
|
||||
blocks: &mut Blocks,
|
||||
flags: CodeFlags,
|
||||
) -> crate::InternalResult<usize> {
|
||||
let nlocals = metadata.varnames.len();
|
||||
let ncellvars = metadata.cellvars.len();
|
||||
@@ -4443,7 +4495,7 @@ fn prepare_localsplus(
|
||||
let mut cellfixedoffsets = build_cellfixedoffsets(metadata)?;
|
||||
|
||||
// This must be called before fix_cell_offsets().
|
||||
insert_prefix_instructions(metadata, blocks, &cellfixedoffsets, nfreevars, flags)?;
|
||||
insert_prefix_instructions(metadata, blocks, &cellfixedoffsets, nfreevars)?;
|
||||
|
||||
let numdropped = fix_cell_offsets(metadata, blocks, &mut cellfixedoffsets);
|
||||
nlocalsplus -= numdropped;
|
||||
@@ -4517,6 +4569,13 @@ fn load_const_truthiness(
|
||||
Some(constant.truthiness())
|
||||
}
|
||||
Instruction::LoadSmallInt { i } => Some(i.get(arg) != 0),
|
||||
Instruction::LoadCommonConstant { idx } => {
|
||||
use oparg::CommonConstant;
|
||||
Some(!matches!(
|
||||
idx.get(arg),
|
||||
CommonConstant::None | CommonConstant::False | CommonConstant::EmptyStr
|
||||
))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -4534,7 +4593,9 @@ fn instr_make_load_const(
|
||||
instr: &mut InstructionInfo,
|
||||
constant: ConstantData,
|
||||
) -> crate::InternalResult<()> {
|
||||
if instr.maybe_instr_make_load_smallint(&constant) {
|
||||
if instr.maybe_instr_make_load_smallint(&constant)
|
||||
|| instr.maybe_instr_make_load_common_const(&constant)
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -4627,6 +4688,21 @@ fn fold_const_binop(
|
||||
/// flowgraph.c get_const_value
|
||||
fn get_const_value(metadata: &CodeUnitMetadata, info: &InstructionInfo) -> Option<ConstantData> {
|
||||
match info.instr.real_opcode() {
|
||||
Some(Opcode::LoadCommonConstant) => {
|
||||
use oparg::CommonConstant;
|
||||
match CommonConstant::try_from(u32::from(info.arg)).ok()? {
|
||||
CommonConstant::None => Some(ConstantData::None),
|
||||
CommonConstant::EmptyStr => Some(ConstantData::Str {
|
||||
value: Wtf8Buf::new(),
|
||||
}),
|
||||
CommonConstant::True => Some(ConstantData::Boolean { value: true }),
|
||||
CommonConstant::False => Some(ConstantData::Boolean { value: false }),
|
||||
CommonConstant::MinusOne => Some(ConstantData::Integer {
|
||||
value: BigInt::from(-1),
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
Some(Opcode::LoadSmallInt) => {
|
||||
let v = u32::from(info.arg) as i32;
|
||||
Some(ConstantData::Integer {
|
||||
@@ -5358,20 +5434,24 @@ fn fold_tuple_of_constants(
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn fold_constant_intrinsic_list_to_tuple(
|
||||
fn fold_constant_sequence_into_load_const(
|
||||
metadata: &mut CodeUnitMetadata,
|
||||
block: &mut Block,
|
||||
i: usize,
|
||||
) -> crate::InternalResult<bool> {
|
||||
let Some(Instruction::CallIntrinsic1 { func }) = block.instructions[i].instr.real() else {
|
||||
return Ok(false);
|
||||
let (intrinsic, is_set) = match block.instructions[i].instr.real() {
|
||||
Some(Instruction::CallIntrinsic1 { func })
|
||||
if func.get(block.instructions[i].arg) == IntrinsicFunction1::ListToTuple =>
|
||||
{
|
||||
(true, false)
|
||||
}
|
||||
Some(Instruction::ListAppend { .. }) => (false, false),
|
||||
Some(Instruction::SetAdd { .. }) => (false, true),
|
||||
_ => return Ok(false),
|
||||
};
|
||||
if func.get(block.instructions[i].arg) != IntrinsicFunction1::ListToTuple {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let mut consts_found = 0usize;
|
||||
let mut expect_append = true;
|
||||
let mut expect_append = intrinsic;
|
||||
let mut pos = i;
|
||||
while let Some(prev) = pos.checked_sub(1) {
|
||||
pos = prev;
|
||||
@@ -5380,9 +5460,12 @@ fn fold_constant_intrinsic_list_to_tuple(
|
||||
continue;
|
||||
}
|
||||
|
||||
if matches!(instr.instr.real(), Some(Instruction::BuildList { .. }))
|
||||
&& u32::from(instr.arg) == 0
|
||||
{
|
||||
let is_start = if is_set {
|
||||
matches!(instr.instr.real(), Some(Instruction::BuildSet { .. }))
|
||||
} else {
|
||||
matches!(instr.instr.real(), Some(Instruction::BuildList { .. }))
|
||||
};
|
||||
if is_start && u32::from(instr.arg) == 0 {
|
||||
if !expect_append {
|
||||
return Ok(false);
|
||||
}
|
||||
@@ -5391,7 +5474,8 @@ fn fold_constant_intrinsic_list_to_tuple(
|
||||
elements
|
||||
.try_reserve_exact(consts_found)
|
||||
.map_err(|_| InternalError::MalformedControlFlowGraph)?;
|
||||
for idx in (pos..i).rev() {
|
||||
let end = if intrinsic { i } else { i + 1 };
|
||||
for idx in (pos..end).rev() {
|
||||
if matches!(block.instructions[idx].instr.real(), Some(Instruction::Nop)) {
|
||||
continue;
|
||||
}
|
||||
@@ -5408,15 +5492,22 @@ fn fold_constant_intrinsic_list_to_tuple(
|
||||
instr_make_load_const(
|
||||
metadata,
|
||||
&mut block.instructions[i],
|
||||
ConstantData::Tuple { elements },
|
||||
if is_set {
|
||||
ConstantData::Frozenset { elements }
|
||||
} else {
|
||||
ConstantData::Tuple { elements }
|
||||
},
|
||||
)?;
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
if expect_append {
|
||||
if !matches!(instr.instr.real(), Some(Instruction::ListAppend { .. }))
|
||||
|| u32::from(instr.arg) != 1
|
||||
{
|
||||
let is_append = if is_set {
|
||||
matches!(instr.instr.real(), Some(Instruction::SetAdd { .. }))
|
||||
} else {
|
||||
matches!(instr.instr.real(), Some(Instruction::ListAppend { .. }))
|
||||
};
|
||||
if !is_append || u32::from(instr.arg) != 1 {
|
||||
return Ok(false);
|
||||
}
|
||||
} else {
|
||||
@@ -5449,7 +5540,7 @@ fn optimize_lists_and_sets(
|
||||
|
||||
let contains_or_iter = matches!(
|
||||
nextop,
|
||||
Some(Instruction::GetIter | Instruction::ContainsOp { .. })
|
||||
Some(Instruction::GetIter { .. } | Instruction::ContainsOp { .. })
|
||||
);
|
||||
let seq_size = u32::from(block.instructions[i].arg) as usize;
|
||||
if seq_size > STACK_USE_GUIDELINE || (seq_size < MIN_CONST_SEQUENCE_SIZE && !contains_or_iter) {
|
||||
@@ -5539,84 +5630,88 @@ fn basicblock_optimize_load_const(
|
||||
metadata: &mut CodeUnitMetadata,
|
||||
block: &mut Block,
|
||||
) -> crate::InternalResult<()> {
|
||||
let mut i = 0;
|
||||
let mut effective_opcode = Instruction::Nop.into();
|
||||
let mut effective_oparg = OpArg::new(0);
|
||||
while i < block.instruction_used {
|
||||
if matches!(
|
||||
block.instructions[i].instr.real(),
|
||||
Some(Instruction::LoadConst { .. })
|
||||
) && let Some(constant) = get_const_value(metadata, &block.instructions[i])
|
||||
{
|
||||
block.instructions[i].maybe_instr_make_load_smallint(&constant);
|
||||
}
|
||||
|
||||
let curr = block.instructions[i];
|
||||
let curr_arg = curr.arg;
|
||||
|
||||
let is_copy_of_load_const = matches!(
|
||||
(effective_opcode, curr.instr.real()),
|
||||
(AnyInstruction::Real(Instruction::LoadConst { .. }), Some(Instruction::Copy { i }))
|
||||
if i.get(curr_arg) == 1
|
||||
);
|
||||
if !is_copy_of_load_const {
|
||||
effective_opcode = curr.instr;
|
||||
effective_oparg = curr_arg;
|
||||
}
|
||||
debug_assert!(!effective_opcode.is_assembler());
|
||||
let Some(const_instr @ (Instruction::LoadConst { .. } | Instruction::LoadSmallInt { .. })) =
|
||||
effective_opcode.real()
|
||||
else {
|
||||
i += 1;
|
||||
continue;
|
||||
};
|
||||
let const_arg = effective_oparg;
|
||||
|
||||
if i + 1 >= block.instruction_used {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
let next = block.instructions[i + 1];
|
||||
let next_arg = next.arg;
|
||||
|
||||
if let Some(is_true) = load_const_truthiness(const_instr, const_arg, metadata) {
|
||||
let const_jump = match (next.instr.real_opcode(), next.instr.pseudo_opcode()) {
|
||||
(_, Some(PseudoOpcode::JumpIfTrue)) => Some((true, false)),
|
||||
(_, Some(PseudoOpcode::JumpIfFalse)) => Some((false, false)),
|
||||
(Some(Opcode::PopJumpIfTrue), _) => Some((true, true)),
|
||||
(Some(Opcode::PopJumpIfFalse), _) => Some((false, true)),
|
||||
_ => None,
|
||||
};
|
||||
if let Some((jump_if_true, pops_condition)) = const_jump {
|
||||
if pops_condition {
|
||||
block.instructions[i].set_to_nop();
|
||||
}
|
||||
if is_true == jump_if_true {
|
||||
block.instructions[i + 1].instr = PseudoOpcode::Jump.into();
|
||||
} else {
|
||||
block.instructions[i + 1].set_to_nop();
|
||||
}
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// The remaining combinations require both instructions to be real.
|
||||
let Some(next_instr) = next.instr.real() else {
|
||||
i += 1;
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Instruction::LoadConst { consti } = const_instr {
|
||||
let constant = &metadata.consts[consti.get(const_arg).as_usize()];
|
||||
if matches!(constant, ConstantData::None)
|
||||
&& let Instruction::IsOp { invert } = next_instr
|
||||
for i in 0..block.instruction_used {
|
||||
'instruction: {
|
||||
if matches!(
|
||||
block.instructions[i].instr.real(),
|
||||
Some(Instruction::LoadConst { .. })
|
||||
) && let Some(constant) = get_const_value(metadata, &block.instructions[i])
|
||||
{
|
||||
block.instructions[i].maybe_instr_make_load_smallint(&constant);
|
||||
}
|
||||
|
||||
let curr = block.instructions[i];
|
||||
let curr_arg = curr.arg;
|
||||
|
||||
let is_copy_of_load_const = matches!(
|
||||
(effective_opcode, curr.instr.real()),
|
||||
(AnyInstruction::Real(Instruction::LoadConst { .. }), Some(Instruction::Copy { i }))
|
||||
if i.get(curr_arg) == 1
|
||||
);
|
||||
if !is_copy_of_load_const {
|
||||
effective_opcode = curr.instr;
|
||||
effective_oparg = curr_arg;
|
||||
}
|
||||
debug_assert!(!effective_opcode.is_assembler());
|
||||
let Some(
|
||||
const_instr @ (Instruction::LoadConst { .. }
|
||||
| Instruction::LoadSmallInt { .. }
|
||||
| Instruction::LoadCommonConstant { .. }),
|
||||
) = effective_opcode.real()
|
||||
else {
|
||||
break 'instruction;
|
||||
};
|
||||
let const_arg = effective_oparg;
|
||||
|
||||
if i + 1 >= block.instruction_used {
|
||||
break 'instruction;
|
||||
}
|
||||
|
||||
let next = block.instructions[i + 1];
|
||||
let next_arg = next.arg;
|
||||
|
||||
if let Some(is_true) = load_const_truthiness(const_instr, const_arg, metadata) {
|
||||
let const_jump = match (next.instr.real_opcode(), next.instr.pseudo_opcode()) {
|
||||
(_, Some(PseudoOpcode::JumpIfTrue)) => Some((true, false)),
|
||||
(_, Some(PseudoOpcode::JumpIfFalse)) => Some((false, false)),
|
||||
(Some(Opcode::PopJumpIfTrue), _) => Some((true, true)),
|
||||
(Some(Opcode::PopJumpIfFalse), _) => Some((false, true)),
|
||||
_ => None,
|
||||
};
|
||||
if let Some((jump_if_true, pops_condition)) = const_jump {
|
||||
if pops_condition {
|
||||
block.instructions[i].set_to_nop();
|
||||
}
|
||||
if is_true == jump_if_true {
|
||||
block.instructions[i + 1].instr = PseudoOpcode::Jump.into();
|
||||
} else {
|
||||
block.instructions[i + 1].set_to_nop();
|
||||
}
|
||||
break 'instruction;
|
||||
}
|
||||
}
|
||||
|
||||
// The remaining combinations require both instructions to be real.
|
||||
let Some(next_instr) = next.instr.real() else {
|
||||
break 'instruction;
|
||||
};
|
||||
|
||||
let is_none = match const_instr {
|
||||
Instruction::LoadConst { consti } => matches!(
|
||||
&metadata.consts[consti.get(const_arg).as_usize()],
|
||||
ConstantData::None
|
||||
),
|
||||
Instruction::LoadCommonConstant { idx } => {
|
||||
idx.get(const_arg) == oparg::CommonConstant::None
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
if is_none && let Instruction::IsOp { invert } = next_instr {
|
||||
let mut jump_idx = i + 2;
|
||||
if jump_idx >= block.instruction_used {
|
||||
i += 1;
|
||||
continue;
|
||||
break 'instruction;
|
||||
}
|
||||
|
||||
if matches!(
|
||||
@@ -5626,14 +5721,12 @@ fn basicblock_optimize_load_const(
|
||||
block.instructions[jump_idx].set_to_nop();
|
||||
jump_idx += 1;
|
||||
if jump_idx >= block.instruction_used {
|
||||
i += 1;
|
||||
continue;
|
||||
break 'instruction;
|
||||
}
|
||||
}
|
||||
|
||||
let Some(jump_instr) = block.instructions[jump_idx].instr.real() else {
|
||||
i += 1;
|
||||
continue;
|
||||
break 'instruction;
|
||||
};
|
||||
|
||||
let mut invert = matches!(
|
||||
@@ -5646,8 +5739,7 @@ fn basicblock_optimize_load_const(
|
||||
}
|
||||
Instruction::PopJumpIfTrue { .. } => {}
|
||||
_ => {
|
||||
i += 1;
|
||||
continue;
|
||||
break 'instruction;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5659,27 +5751,32 @@ fn basicblock_optimize_load_const(
|
||||
Opcode::PopJumpIfNone
|
||||
}
|
||||
.into();
|
||||
i += 1;
|
||||
continue;
|
||||
break 'instruction;
|
||||
}
|
||||
|
||||
if matches!(
|
||||
const_instr,
|
||||
Instruction::LoadConst { .. }
|
||||
| Instruction::LoadSmallInt { .. }
|
||||
| Instruction::LoadCommonConstant { .. }
|
||||
) && matches!(next_instr, Instruction::ToBool)
|
||||
&& let Some(value) = load_const_truthiness(const_instr, const_arg, metadata)
|
||||
{
|
||||
let const_idx = add_const(metadata, ConstantData::Boolean { value })?;
|
||||
block.instructions[i].set_to_nop();
|
||||
|
||||
block.instructions[i + 1]
|
||||
.instr_set_op1(Opcode::LoadConst.into(), OpArg::new(const_idx as u32));
|
||||
break 'instruction;
|
||||
}
|
||||
}
|
||||
|
||||
if matches!(
|
||||
const_instr,
|
||||
Instruction::LoadConst { .. } | Instruction::LoadSmallInt { .. }
|
||||
) && matches!(next_instr, Instruction::ToBool)
|
||||
&& let Some(value) = load_const_truthiness(const_instr, const_arg, metadata)
|
||||
block.instructions[i].instr.real_opcode(),
|
||||
Some(Opcode::LoadConst)
|
||||
) && let Some(constant) = get_const_value(metadata, &block.instructions[i])
|
||||
{
|
||||
let const_idx = add_const(metadata, ConstantData::Boolean { value })?;
|
||||
block.instructions[i].set_to_nop();
|
||||
|
||||
block.instructions[i + 1]
|
||||
.instr_set_op1(Opcode::LoadConst.into(), OpArg::new(const_idx as u32));
|
||||
i += 1;
|
||||
continue;
|
||||
block.instructions[i].maybe_instr_make_load_common_const(&constant);
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -5837,7 +5934,7 @@ impl CodeInfo {
|
||||
));
|
||||
|
||||
let _max_stackdepth = self.blocks.calculate_stackdepth()?;
|
||||
let _nlocalsplus = prepare_localsplus(&self.metadata, &mut self.blocks, self.flags)?;
|
||||
let _nlocalsplus = prepare_localsplus(&self.metadata, &mut self.blocks)?;
|
||||
convert_pseudo_ops(&mut self.blocks)?;
|
||||
trace.push((
|
||||
"after_convert_pseudo_ops".to_owned(),
|
||||
@@ -6968,7 +7065,9 @@ pub(crate) fn label_exception_targets(blocks: &mut Blocks) -> crate::InternalRes
|
||||
} else if let Some(Instruction::Resume { context: _ }) = instr.real() {
|
||||
blocks[bi].instructions[i].except_handler = handler;
|
||||
let resume_arg = u32::from(arg);
|
||||
if resume_arg != u32::from(oparg::ResumeLocation::AtFuncStart) {
|
||||
if resume_arg != u32::from(oparg::ResumeLocation::AtFuncStart)
|
||||
&& resume_arg != u32::from(oparg::ResumeLocation::AtGenExprStart)
|
||||
{
|
||||
debug_assert!(last_yield_except_depth >= 0);
|
||||
if last_yield_except_depth == 1 {
|
||||
blocks[bi].instructions[i].arg =
|
||||
@@ -7726,7 +7825,7 @@ mod tests {
|
||||
|
||||
// CPython `basicblock_optimize_load_const()` keeps the previous
|
||||
// LOAD_CONST as the effective opcode for a following `COPY 1`, so the
|
||||
// COPY is NOPed and TO_BOOL becomes LOAD_CONST True.
|
||||
// COPY is NOPed and TO_BOOL becomes LOAD_COMMON_CONSTANT True.
|
||||
assert!(matches!(
|
||||
code.blocks[0].instructions[0].instr.real(),
|
||||
Some(Instruction::LoadConst { .. })
|
||||
@@ -7738,11 +7837,11 @@ mod tests {
|
||||
let load_bool = &code.blocks[0].instructions[2];
|
||||
assert!(matches!(
|
||||
load_bool.instr.real(),
|
||||
Some(Instruction::LoadConst { .. })
|
||||
Some(Instruction::LoadCommonConstant { .. })
|
||||
));
|
||||
assert_eq!(
|
||||
code.metadata.consts[u32::from(load_bool.arg) as usize],
|
||||
ConstantData::Boolean { value: true }
|
||||
u32::from(load_bool.arg),
|
||||
u32::from(oparg::CommonConstant::True)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7793,7 +7892,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn optimize_load_fast_records_no_input_opcode_ref_at_cpython_produced_index() {
|
||||
fn optimize_load_fast_records_no_input_opcode_ref_at_cpython_instruction_index() {
|
||||
let mut block = Block::default();
|
||||
test_block_push(&mut block, test_instr(Opcode::LoadFast.into(), 10));
|
||||
test_block_push(&mut block, test_instr(Instruction::GetLen, 10));
|
||||
@@ -7807,77 +7906,11 @@ mod tests {
|
||||
.optimize_load_fast()
|
||||
.expect("optimize_load_fast succeeds");
|
||||
|
||||
// CPython `optimize_load_fast()` shadows the outer instruction index in
|
||||
// the produced-value loop for GET_LEN, so the produced ref is recorded
|
||||
// with index 0 here. The original LOAD_FAST is therefore not considered
|
||||
// the consumed producer.
|
||||
// CPython records GET_LEN's result at instruction index 1. The
|
||||
// SWAP/POP_TOP consumes the original local reference, so it can borrow.
|
||||
assert!(matches!(
|
||||
code.blocks[0].instructions[0].instr.real(),
|
||||
Some(Instruction::LoadFast { .. })
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn constant_sequence_loads_use_cpython_opcode_has_const_metadata() {
|
||||
let mut metadata = CodeUnitMetadata {
|
||||
name: "<module>".to_owned(),
|
||||
qualname: Some("<module>".to_owned()),
|
||||
consts: Default::default(),
|
||||
names: IndexSet::default(),
|
||||
varnames: IndexSet::default(),
|
||||
cellvars: IndexSet::default(),
|
||||
freevars: IndexSet::default(),
|
||||
fast_hidden: IndexMap::default(),
|
||||
fast_hidden_final: IndexSet::default(),
|
||||
argcount: 0,
|
||||
posonlyargcount: 0,
|
||||
kwonlyargcount: 0,
|
||||
firstlineno: OneIndexed::MIN,
|
||||
};
|
||||
let (left, _) = metadata
|
||||
.consts
|
||||
.insert_full(ConstantData::Str { value: "a".into() });
|
||||
let (right, _) = metadata
|
||||
.consts
|
||||
.insert_full(ConstantData::Str { value: "b".into() });
|
||||
|
||||
let mut immortal = test_instr(Instruction::Nop, 90);
|
||||
immortal.instr = Opcode::LoadConstImmortal.into();
|
||||
immortal.arg = OpArg::new(left as u32);
|
||||
let mut mortal = test_instr(Instruction::Nop, 90);
|
||||
mortal.instr = Opcode::LoadConstMortal.into();
|
||||
mortal.arg = OpArg::new(right as u32);
|
||||
let mut build = test_instr(Opcode::BuildTuple.into(), 90);
|
||||
build.arg = OpArg::new(2);
|
||||
let mut block = Block::default();
|
||||
for info in [immortal, mortal, build] {
|
||||
test_block_push(&mut block, info);
|
||||
}
|
||||
|
||||
assert!(
|
||||
fold_tuple_of_constants(&mut metadata, &mut block, 2)
|
||||
.expect("fold_tuple_of_constants succeeds")
|
||||
);
|
||||
|
||||
// CPython `loads_const()` accepts every `OPCODE_HAS_CONST` opcode, not
|
||||
// just canonical LOAD_CONST, so LOAD_CONST_IMMORTAL/MORTAL participate
|
||||
// in constant-sequence folding.
|
||||
assert!(matches!(
|
||||
block.instructions[0].instr.real(),
|
||||
Some(Instruction::Nop)
|
||||
));
|
||||
assert!(matches!(
|
||||
block.instructions[1].instr.real(),
|
||||
Some(Instruction::Nop)
|
||||
));
|
||||
let folded = &block.instructions[2];
|
||||
assert!(matches!(
|
||||
folded.instr.real(),
|
||||
Some(Instruction::LoadConst { .. })
|
||||
));
|
||||
assert!(matches!(
|
||||
&metadata.consts[u32::from(folded.arg) as usize],
|
||||
ConstantData::Tuple { elements } if elements.len() == 2
|
||||
Some(Instruction::LoadFastBorrow { .. })
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ pub enum CFormatErrorType {
|
||||
UnmatchedKeyParentheses,
|
||||
MissingModuloSign,
|
||||
UnsupportedFormatChar(CodePoint),
|
||||
IncompleteFormat,
|
||||
StrayModulo,
|
||||
WidthTooBig,
|
||||
PrecisionTooBig,
|
||||
// Unimplemented,
|
||||
@@ -38,7 +38,7 @@ impl fmt::Display for CFormatError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.typ {
|
||||
CFormatErrorType::UnmatchedKeyParentheses => write!(f, "incomplete format key"),
|
||||
CFormatErrorType::IncompleteFormat => write!(f, "incomplete format"),
|
||||
CFormatErrorType::StrayModulo => write!(f, "stray % at position {}", self.index),
|
||||
CFormatErrorType::UnsupportedFormatChar(c) => write!(
|
||||
f,
|
||||
"unsupported format character '{}' ({:#x}) at index {}",
|
||||
@@ -644,7 +644,7 @@ where
|
||||
{
|
||||
let (index, c) = iter.next().ok_or_else(|| {
|
||||
(
|
||||
CFormatErrorType::IncompleteFormat,
|
||||
CFormatErrorType::StrayModulo,
|
||||
iter.peek().map_or(0, |x| x.0),
|
||||
)
|
||||
})?;
|
||||
@@ -831,9 +831,14 @@ impl<S> CFormatStrOrBytes<S> {
|
||||
}
|
||||
|
||||
let spec =
|
||||
CFormatSpecKeyed::parse(iter, context).map_err(|err| CFormatError {
|
||||
typ: err.0,
|
||||
index: err.1,
|
||||
CFormatSpecKeyed::parse(iter, context).map_err(|(typ, err_index)| {
|
||||
// A stray % is reported at the position of the %.
|
||||
let index = if typ == CFormatErrorType::StrayModulo {
|
||||
index
|
||||
} else {
|
||||
err_index
|
||||
};
|
||||
CFormatError { typ, index }
|
||||
})?;
|
||||
|
||||
parts.push((index, CFormatPart::Spec(spec)));
|
||||
@@ -842,8 +847,8 @@ impl<S> CFormatStrOrBytes<S> {
|
||||
}
|
||||
} else {
|
||||
return Err(CFormatError {
|
||||
typ: CFormatErrorType::IncompleteFormat,
|
||||
index: index + 1,
|
||||
typ: CFormatErrorType::StrayModulo,
|
||||
index,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@@ -991,8 +996,8 @@ mod tests {
|
||||
assert_eq!(
|
||||
"Hello %".parse::<CFormatString>(),
|
||||
Err(CFormatError {
|
||||
typ: CFormatErrorType::IncompleteFormat,
|
||||
index: 7
|
||||
typ: CFormatErrorType::StrayModulo,
|
||||
index: 6
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use bitflags::bitflags;
|
||||
use core::{
|
||||
cell::UnsafeCell,
|
||||
hash, mem,
|
||||
ops::{Deref, DerefMut, Index, IndexMut},
|
||||
ops::{Deref, Index, IndexMut},
|
||||
sync::atomic::{AtomicU8, AtomicU16, AtomicUsize, Ordering},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
@@ -286,12 +286,16 @@ impl PyCodeLocationInfoKind {
|
||||
pub trait Constant: Sized + Clone {
|
||||
type Name: AsRef<str>;
|
||||
|
||||
/// Storage of a code object's constants (`co_consts`).
|
||||
type Tuple: Clone + Deref<Target = [Self]> + FromIterator<Self>;
|
||||
|
||||
/// Transforms the given Constant to a BorrowedConstant
|
||||
fn borrow_constant(&self) -> BorrowedConstant<'_, Self>;
|
||||
}
|
||||
|
||||
impl Constant for ConstantData {
|
||||
type Name = String;
|
||||
type Tuple = Box<[Self]>;
|
||||
|
||||
fn borrow_constant(&self) -> BorrowedConstant<'_, Self> {
|
||||
match self {
|
||||
@@ -373,7 +377,17 @@ impl ConstantBag for BasicBag {
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Constants<C: Constant>(Box<[C]>);
|
||||
pub struct Constants<C: Constant>(C::Tuple);
|
||||
|
||||
impl<C: Constant> Constants<C> {
|
||||
pub const fn new(tuple: C::Tuple) -> Self {
|
||||
Self(tuple)
|
||||
}
|
||||
|
||||
pub const fn as_tuple(&self) -> &C::Tuple {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Constant> Deref for Constants<C> {
|
||||
type Target = [C];
|
||||
@@ -383,12 +397,6 @@ impl<C: Constant> Deref for Constants<C> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Constant> DerefMut for Constants<C> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Constant> Index<oparg::ConstIdx> for Constants<C> {
|
||||
type Output = C;
|
||||
|
||||
@@ -397,12 +405,6 @@ impl<C: Constant> Index<oparg::ConstIdx> for Constants<C> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Constant> IndexMut<oparg::ConstIdx> for Constants<C> {
|
||||
fn index_mut(&mut self, consti: oparg::ConstIdx) -> &mut Self::Output {
|
||||
&mut self.0[consti.as_usize()]
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Constant> FromIterator<C> for Constants<C> {
|
||||
fn from_iter<T: IntoIterator<Item = C>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().collect())
|
||||
@@ -734,6 +736,27 @@ impl Deref for CodeUnits {
|
||||
}
|
||||
|
||||
impl CodeUnits {
|
||||
/// `fixup_getiter` applied to every `GET_ITER` by `_PyCode_Quicken`.
|
||||
///
|
||||
/// The compiler can't know if `types.coroutine()` will be called, so a
|
||||
/// yield-from `GET_ITER` is resolved when the code object is created.
|
||||
pub fn fixup_get_iter(&mut self, flags: CodeFlags) {
|
||||
let mode = if flags.intersects(CodeFlags::COROUTINE | CodeFlags::ITERABLE_COROUTINE) {
|
||||
oparg::GET_ITER_YIELD_FROM_NO_CHECK
|
||||
} else {
|
||||
oparg::GET_ITER_YIELD_FROM_CORO_CHECK
|
||||
};
|
||||
let mut i = 0;
|
||||
let units = self.units.get_mut();
|
||||
while i < units.len() {
|
||||
let unit = &mut units[i];
|
||||
if matches!(unit.op, Instruction::GetIter { .. }) && u8::from(unit.arg) != 0 {
|
||||
unit.arg = (mode as u8).into();
|
||||
}
|
||||
i += 1 + unit.op.cache_entries();
|
||||
}
|
||||
}
|
||||
|
||||
/// Disable adaptive specialization by setting all counters to unreachable.
|
||||
/// Used for CPython-compiled bytecode where specialization may not be safe.
|
||||
pub fn disable_specialization(&self) {
|
||||
@@ -937,11 +960,20 @@ impl CodeUnits {
|
||||
/// (marshal) and `co_code`. Specialized opcodes are mapped back to their
|
||||
/// base variants via `deoptimize()` and all CACHE entries are zeroed.
|
||||
pub fn original_bytes(&self) -> Vec<u8> {
|
||||
self.original_bytes_with(|_, op| op)
|
||||
}
|
||||
|
||||
/// [`Self::original_bytes`], with `resolve` first replacing the opcode at
|
||||
/// an index, e.g. an instrumentation placeholder by its original opcode.
|
||||
pub fn original_bytes_with(
|
||||
&self,
|
||||
resolve: impl Fn(usize, Instruction) -> Instruction,
|
||||
) -> Vec<u8> {
|
||||
let len = self.len();
|
||||
let mut out = Vec::with_capacity(len * 2);
|
||||
let mut i = 0;
|
||||
while i < len {
|
||||
let op = self.read_op(i).deoptimize();
|
||||
let op = resolve(i, self.read_op(i)).deoptimize();
|
||||
let arg = self.read_arg(i);
|
||||
let caches = op.cache_entries();
|
||||
out.push(u8::from(op));
|
||||
|
||||
@@ -333,261 +333,260 @@ define_opcodes!(
|
||||
FormatWithSpec = 13,
|
||||
GetAiter = 14,
|
||||
GetAnext = 15,
|
||||
GetIter = 16,
|
||||
GetLen = 16,
|
||||
Reserved = 17,
|
||||
GetLen = 18,
|
||||
GetYieldFromIter = 19,
|
||||
InterpreterExit = 20,
|
||||
LoadBuildClass = 21,
|
||||
LoadLocals = 22,
|
||||
MakeFunction = 23,
|
||||
MatchKeys = 24,
|
||||
MatchMapping = 25,
|
||||
MatchSequence = 26,
|
||||
Nop = 27,
|
||||
NotTaken = 28,
|
||||
PopExcept = 29,
|
||||
PopIter = 30,
|
||||
PopTop = 31,
|
||||
PushExcInfo = 32,
|
||||
PushNull = 33,
|
||||
ReturnGenerator = 34,
|
||||
ReturnValue = 35,
|
||||
SetupAnnotations = 36,
|
||||
StoreSlice = 37,
|
||||
StoreSubscr = 38,
|
||||
ToBool = 39,
|
||||
UnaryInvert = 40,
|
||||
UnaryNegative = 41,
|
||||
UnaryNot = 42,
|
||||
WithExceptStart = 43,
|
||||
InterpreterExit = 18,
|
||||
LoadBuildClass = 19,
|
||||
LoadLocals = 20,
|
||||
MakeFunction = 21,
|
||||
MatchKeys = 22,
|
||||
MatchMapping = 23,
|
||||
MatchSequence = 24,
|
||||
Nop = 25,
|
||||
NotTaken = 26,
|
||||
PopExcept = 27,
|
||||
PopIter = 28,
|
||||
PopTop = 29,
|
||||
PushExcInfo = 30,
|
||||
PushNull = 31,
|
||||
ReturnGenerator = 32,
|
||||
ReturnValue = 33,
|
||||
SetupAnnotations = 34,
|
||||
StoreSlice = 35,
|
||||
StoreSubscr = 36,
|
||||
ToBool = 37,
|
||||
UnaryInvert = 38,
|
||||
UnaryNegative = 39,
|
||||
UnaryNot = 40,
|
||||
WithExceptStart = 41,
|
||||
BinaryOp {
|
||||
op: Arg<oparg::BinaryOperator>,
|
||||
} = 44,
|
||||
} = 42,
|
||||
BuildInterpolation {
|
||||
format: Arg<u32>,
|
||||
} = 45,
|
||||
} = 43,
|
||||
BuildList {
|
||||
count: Arg<u32>,
|
||||
} = 46,
|
||||
} = 44,
|
||||
BuildMap {
|
||||
count: Arg<u32>,
|
||||
} = 47,
|
||||
} = 45,
|
||||
BuildSet {
|
||||
count: Arg<u32>,
|
||||
} = 48,
|
||||
} = 46,
|
||||
BuildSlice {
|
||||
argc: Arg<oparg::BuildSliceArgCount>,
|
||||
} = 49,
|
||||
} = 47,
|
||||
BuildString {
|
||||
count: Arg<u32>,
|
||||
} = 50,
|
||||
} = 48,
|
||||
BuildTuple {
|
||||
count: Arg<u32>,
|
||||
} = 51,
|
||||
} = 49,
|
||||
Call {
|
||||
argc: Arg<u32>,
|
||||
} = 52,
|
||||
} = 50,
|
||||
CallIntrinsic1 {
|
||||
func: Arg<oparg::IntrinsicFunction1>,
|
||||
} = 53,
|
||||
} = 51,
|
||||
CallIntrinsic2 {
|
||||
func: Arg<oparg::IntrinsicFunction2>,
|
||||
} = 54,
|
||||
} = 52,
|
||||
CallKw {
|
||||
argc: Arg<u32>,
|
||||
} = 55,
|
||||
} = 53,
|
||||
CompareOp {
|
||||
opname: Arg<oparg::ComparisonOperator>,
|
||||
} = 56,
|
||||
} = 54,
|
||||
ContainsOp {
|
||||
invert: Arg<oparg::Invert>,
|
||||
} = 57,
|
||||
} = 55,
|
||||
ConvertValue {
|
||||
oparg: Arg<oparg::ConvertValueOparg>,
|
||||
} = 58,
|
||||
} = 56,
|
||||
Copy {
|
||||
i: Arg<u32>,
|
||||
} = 59,
|
||||
} = 57,
|
||||
CopyFreeVars {
|
||||
n: Arg<u32>,
|
||||
} = 60,
|
||||
} = 58,
|
||||
DeleteAttr {
|
||||
namei: Arg<oparg::NameIdx>,
|
||||
} = 61,
|
||||
} = 59,
|
||||
DeleteDeref {
|
||||
i: Arg<oparg::VarNum>,
|
||||
} = 62,
|
||||
} = 60,
|
||||
DeleteFast {
|
||||
var_num: Arg<oparg::VarNum>,
|
||||
} = 63,
|
||||
} = 61,
|
||||
DeleteGlobal {
|
||||
namei: Arg<oparg::NameIdx>,
|
||||
} = 64,
|
||||
} = 62,
|
||||
DeleteName {
|
||||
namei: Arg<oparg::NameIdx>,
|
||||
} = 65,
|
||||
} = 63,
|
||||
DictMerge {
|
||||
i: Arg<u32>,
|
||||
} = 66,
|
||||
} = 64,
|
||||
DictUpdate {
|
||||
i: Arg<u32>,
|
||||
} = 67,
|
||||
EndAsyncFor = 68,
|
||||
ExtendedArg = 69,
|
||||
} = 65,
|
||||
EndAsyncFor = 66,
|
||||
ExtendedArg = 67,
|
||||
ForIter {
|
||||
delta: Arg<oparg::Label>,
|
||||
} = 70,
|
||||
} = 68,
|
||||
GetAwaitable {
|
||||
r#where: Arg<u32>,
|
||||
} = 71,
|
||||
} = 69,
|
||||
GetIter { mode: Arg<u32> } = 70,
|
||||
ImportFrom {
|
||||
namei: Arg<oparg::NameIdx>,
|
||||
} = 72,
|
||||
} = 71,
|
||||
ImportName {
|
||||
namei: Arg<oparg::NameIdx>,
|
||||
} = 73,
|
||||
} = 72,
|
||||
IsOp {
|
||||
invert: Arg<oparg::Invert>,
|
||||
} = 74,
|
||||
} = 73,
|
||||
JumpBackward {
|
||||
delta: Arg<oparg::Label>,
|
||||
} = 75,
|
||||
} = 74,
|
||||
JumpBackwardNoInterrupt {
|
||||
delta: Arg<oparg::Label>,
|
||||
} = 76,
|
||||
} = 75,
|
||||
JumpForward {
|
||||
delta: Arg<oparg::Label>,
|
||||
} = 77,
|
||||
} = 76,
|
||||
ListAppend {
|
||||
i: Arg<u32>,
|
||||
} = 78,
|
||||
} = 77,
|
||||
ListExtend {
|
||||
i: Arg<u32>,
|
||||
} = 79,
|
||||
} = 78,
|
||||
LoadAttr {
|
||||
namei: Arg<oparg::LoadAttr>,
|
||||
} = 80,
|
||||
} = 79,
|
||||
LoadCommonConstant {
|
||||
idx: Arg<oparg::CommonConstant>,
|
||||
} = 81,
|
||||
} = 80,
|
||||
LoadConst {
|
||||
consti: Arg<oparg::ConstIdx>,
|
||||
} = 82,
|
||||
} = 81,
|
||||
LoadDeref {
|
||||
i: Arg<oparg::VarNum>,
|
||||
} = 83,
|
||||
} = 82,
|
||||
LoadFast {
|
||||
var_num: Arg<oparg::VarNum>,
|
||||
} = 84,
|
||||
} = 83,
|
||||
LoadFastAndClear {
|
||||
var_num: Arg<oparg::VarNum>,
|
||||
} = 85,
|
||||
} = 84,
|
||||
LoadFastBorrow {
|
||||
var_num: Arg<oparg::VarNum>,
|
||||
} = 86,
|
||||
} = 85,
|
||||
LoadFastBorrowLoadFastBorrow {
|
||||
var_nums: Arg<oparg::VarNums>,
|
||||
} = 87,
|
||||
} = 86,
|
||||
LoadFastCheck {
|
||||
var_num: Arg<oparg::VarNum>,
|
||||
} = 88,
|
||||
} = 87,
|
||||
LoadFastLoadFast {
|
||||
var_nums: Arg<oparg::VarNums>,
|
||||
} = 89,
|
||||
} = 88,
|
||||
LoadFromDictOrDeref {
|
||||
i: Arg<oparg::VarNum>,
|
||||
} = 90,
|
||||
} = 89,
|
||||
LoadFromDictOrGlobals {
|
||||
i: Arg<oparg::NameIdx>,
|
||||
} = 91,
|
||||
} = 90,
|
||||
LoadGlobal {
|
||||
namei: Arg<oparg::NameIdx>,
|
||||
} = 92,
|
||||
} = 91,
|
||||
LoadName {
|
||||
namei: Arg<oparg::NameIdx>,
|
||||
} = 93,
|
||||
} = 92,
|
||||
LoadSmallInt {
|
||||
i: Arg<u32>,
|
||||
} = 94,
|
||||
} = 93,
|
||||
LoadSpecial {
|
||||
method: Arg<oparg::SpecialMethod>,
|
||||
} = 95,
|
||||
} = 94,
|
||||
LoadSuperAttr {
|
||||
namei: Arg<oparg::LoadSuperAttr>,
|
||||
} = 96,
|
||||
} = 95,
|
||||
MakeCell {
|
||||
i: Arg<oparg::VarNum>,
|
||||
} = 97,
|
||||
} = 96,
|
||||
MapAdd {
|
||||
i: Arg<u32>,
|
||||
} = 98,
|
||||
} = 97,
|
||||
MatchClass {
|
||||
count: Arg<u32>,
|
||||
} = 99,
|
||||
} = 98,
|
||||
PopJumpIfFalse {
|
||||
delta: Arg<oparg::Label>,
|
||||
} = 100,
|
||||
} = 99,
|
||||
PopJumpIfNone {
|
||||
delta: Arg<oparg::Label>,
|
||||
} = 101,
|
||||
} = 100,
|
||||
PopJumpIfNotNone {
|
||||
delta: Arg<oparg::Label>,
|
||||
} = 102,
|
||||
} = 101,
|
||||
PopJumpIfTrue {
|
||||
delta: Arg<oparg::Label>,
|
||||
} = 103,
|
||||
} = 102,
|
||||
RaiseVarargs {
|
||||
argc: Arg<oparg::RaiseKind>,
|
||||
} = 104,
|
||||
} = 103,
|
||||
Reraise {
|
||||
depth: Arg<u32>,
|
||||
} = 105,
|
||||
} = 104,
|
||||
Send {
|
||||
delta: Arg<oparg::Label>,
|
||||
} = 106,
|
||||
} = 105,
|
||||
SetAdd {
|
||||
i: Arg<u32>,
|
||||
} = 107,
|
||||
} = 106,
|
||||
SetFunctionAttribute {
|
||||
flag: Arg<oparg::MakeFunctionFlag>,
|
||||
} = 108,
|
||||
} = 107,
|
||||
SetUpdate {
|
||||
i: Arg<u32>,
|
||||
} = 109,
|
||||
} = 108,
|
||||
StoreAttr {
|
||||
namei: Arg<oparg::NameIdx>,
|
||||
} = 110,
|
||||
} = 109,
|
||||
StoreDeref {
|
||||
i: Arg<oparg::VarNum>,
|
||||
} = 111,
|
||||
} = 110,
|
||||
StoreFast {
|
||||
var_num: Arg<oparg::VarNum>,
|
||||
} = 112,
|
||||
} = 111,
|
||||
StoreFastLoadFast {
|
||||
var_nums: Arg<oparg::VarNums>,
|
||||
} = 113,
|
||||
} = 112,
|
||||
StoreFastStoreFast {
|
||||
var_nums: Arg<oparg::VarNums>,
|
||||
} = 114,
|
||||
} = 113,
|
||||
StoreGlobal {
|
||||
namei: Arg<oparg::NameIdx>,
|
||||
} = 115,
|
||||
} = 114,
|
||||
StoreName {
|
||||
namei: Arg<oparg::NameIdx>,
|
||||
} = 116,
|
||||
} = 115,
|
||||
Swap {
|
||||
i: Arg<u32>,
|
||||
} = 117,
|
||||
} = 116,
|
||||
UnpackEx {
|
||||
counts: Arg<oparg::UnpackExArgs>,
|
||||
} = 118,
|
||||
} = 117,
|
||||
UnpackSequence {
|
||||
count: Arg<u32>,
|
||||
} = 119,
|
||||
} = 118,
|
||||
YieldValue {
|
||||
arg: Arg<u32>,
|
||||
} = 120,
|
||||
} = 119,
|
||||
Resume {
|
||||
context: Arg<oparg::ResumeContext>,
|
||||
} = 128,
|
||||
@@ -603,99 +602,107 @@ define_opcodes!(
|
||||
BinaryOpSubscrListSlice = 138,
|
||||
BinaryOpSubscrStrInt = 139,
|
||||
BinaryOpSubscrTupleInt = 140,
|
||||
BinaryOpSubtractFloat = 141,
|
||||
BinaryOpSubtractInt = 142,
|
||||
CallAllocAndEnterInit = 143,
|
||||
CallBoundMethodExactArgs = 144,
|
||||
CallBoundMethodGeneral = 145,
|
||||
CallBuiltinClass = 146,
|
||||
CallBuiltinFast = 147,
|
||||
CallBuiltinFastWithKeywords = 148,
|
||||
CallBuiltinO = 149,
|
||||
CallIsinstance = 150,
|
||||
CallKwBoundMethod = 151,
|
||||
CallKwNonPy = 152,
|
||||
CallKwPy = 153,
|
||||
CallLen = 154,
|
||||
CallListAppend = 155,
|
||||
CallMethodDescriptorFast = 156,
|
||||
CallMethodDescriptorFastWithKeywords = 157,
|
||||
CallMethodDescriptorNoargs = 158,
|
||||
CallMethodDescriptorO = 159,
|
||||
CallNonPyGeneral = 160,
|
||||
CallPyExactArgs = 161,
|
||||
CallPyGeneral = 162,
|
||||
CallStr1 = 163,
|
||||
CallTuple1 = 164,
|
||||
CallType1 = 165,
|
||||
CompareOpFloat = 166,
|
||||
CompareOpInt = 167,
|
||||
CompareOpStr = 168,
|
||||
ContainsOpDict = 169,
|
||||
ContainsOpSet = 170,
|
||||
ForIterGen = 171,
|
||||
ForIterList = 172,
|
||||
ForIterRange = 173,
|
||||
ForIterTuple = 174,
|
||||
JumpBackwardJit = 175,
|
||||
JumpBackwardNoJit = 176,
|
||||
LoadAttrClass = 177,
|
||||
LoadAttrClassWithMetaclassCheck = 178,
|
||||
LoadAttrGetattributeOverridden = 179,
|
||||
LoadAttrInstanceValue = 180,
|
||||
LoadAttrMethodLazyDict = 181,
|
||||
LoadAttrMethodNoDict = 182,
|
||||
LoadAttrMethodWithValues = 183,
|
||||
LoadAttrModule = 184,
|
||||
LoadAttrNondescriptorNoDict = 185,
|
||||
LoadAttrNondescriptorWithValues = 186,
|
||||
LoadAttrProperty = 187,
|
||||
LoadAttrSlot = 188,
|
||||
LoadAttrWithHint = 189,
|
||||
LoadConstImmortal = 190,
|
||||
LoadConstMortal = 191,
|
||||
LoadGlobalBuiltin = 192,
|
||||
LoadGlobalModule = 193,
|
||||
LoadSuperAttrAttr = 194,
|
||||
LoadSuperAttrMethod = 195,
|
||||
ResumeCheck = 196,
|
||||
SendGen = 197,
|
||||
StoreAttrInstanceValue = 198,
|
||||
StoreAttrSlot = 199,
|
||||
StoreAttrWithHint = 200,
|
||||
StoreSubscrDict = 201,
|
||||
StoreSubscrListInt = 202,
|
||||
ToBoolAlwaysTrue = 203,
|
||||
ToBoolBool = 204,
|
||||
ToBoolInt = 205,
|
||||
ToBoolList = 206,
|
||||
ToBoolNone = 207,
|
||||
ToBoolStr = 208,
|
||||
UnpackSequenceList = 209,
|
||||
UnpackSequenceTuple = 210,
|
||||
UnpackSequenceTwoTuple = 211,
|
||||
InstrumentedEndFor = 234,
|
||||
InstrumentedPopIter = 235,
|
||||
InstrumentedEndSend = 236,
|
||||
InstrumentedForIter = 237,
|
||||
InstrumentedInstruction = 238,
|
||||
InstrumentedJumpForward = 239,
|
||||
InstrumentedNotTaken = 240,
|
||||
InstrumentedPopJumpIfTrue = 241,
|
||||
InstrumentedPopJumpIfFalse = 242,
|
||||
InstrumentedPopJumpIfNone = 243,
|
||||
InstrumentedPopJumpIfNotNone = 244,
|
||||
InstrumentedResume = 245,
|
||||
InstrumentedReturnValue = 246,
|
||||
InstrumentedYieldValue = 247,
|
||||
InstrumentedEndAsyncFor = 248,
|
||||
InstrumentedLoadSuperAttr = 249,
|
||||
InstrumentedCall = 250,
|
||||
InstrumentedCallKw = 251,
|
||||
InstrumentedCallFunctionEx = 252,
|
||||
InstrumentedJumpBackward = 253,
|
||||
InstrumentedLine = 254,
|
||||
EnterExecutor = 255,
|
||||
BinaryOpSubscrUstrInt = 141,
|
||||
BinaryOpSubtractFloat = 142,
|
||||
BinaryOpSubtractInt = 143,
|
||||
CallAllocAndEnterInit = 144,
|
||||
CallBoundMethodExactArgs = 145,
|
||||
CallBoundMethodGeneral = 146,
|
||||
CallBuiltinClass = 147,
|
||||
CallBuiltinFast = 148,
|
||||
CallBuiltinFastWithKeywords = 149,
|
||||
CallBuiltinO = 150,
|
||||
CallExNonPyGeneral = 151,
|
||||
CallExPy = 152,
|
||||
CallIsinstance = 153,
|
||||
CallKwBoundMethod = 154,
|
||||
CallKwNonPy = 155,
|
||||
CallKwPy = 156,
|
||||
CallLen = 157,
|
||||
CallListAppend = 158,
|
||||
CallMethodDescriptorFast = 159,
|
||||
CallMethodDescriptorFastWithKeywords = 160,
|
||||
CallMethodDescriptorNoargs = 161,
|
||||
CallMethodDescriptorO = 162,
|
||||
CallNonPyGeneral = 163,
|
||||
CallPyExactArgs = 164,
|
||||
CallPyGeneral = 165,
|
||||
CallStr1 = 166,
|
||||
CallTuple1 = 167,
|
||||
CallType1 = 168,
|
||||
CompareOpFloat = 169,
|
||||
CompareOpInt = 170,
|
||||
CompareOpStr = 171,
|
||||
ContainsOpDict = 172,
|
||||
ContainsOpSet = 173,
|
||||
ForIterGen = 174,
|
||||
ForIterList = 175,
|
||||
ForIterRange = 176,
|
||||
ForIterTuple = 177,
|
||||
ForIterVirtual = 178,
|
||||
GetIterSelf = 179,
|
||||
GetIterVirtual = 180,
|
||||
JumpBackwardJit = 181,
|
||||
JumpBackwardNoJit = 182,
|
||||
LoadAttrClass = 183,
|
||||
LoadAttrClassWithMetaclassCheck = 184,
|
||||
LoadAttrGetattributeOverridden = 185,
|
||||
LoadAttrInstanceValue = 186,
|
||||
LoadAttrMethodLazyDict = 187,
|
||||
LoadAttrMethodNoDict = 188,
|
||||
LoadAttrMethodWithValues = 189,
|
||||
LoadAttrModule = 190,
|
||||
LoadAttrNondescriptorNoDict = 191,
|
||||
LoadAttrNondescriptorWithValues = 192,
|
||||
LoadAttrProperty = 193,
|
||||
LoadAttrSlot = 194,
|
||||
LoadAttrWithHint = 195,
|
||||
LoadGlobalBuiltin = 196,
|
||||
LoadGlobalModule = 197,
|
||||
LoadSuperAttrAttr = 198,
|
||||
LoadSuperAttrMethod = 199,
|
||||
ResumeCheck = 200,
|
||||
ResumeCheckJit = 201,
|
||||
SendAsyncGen = 202,
|
||||
SendGen = 203,
|
||||
SendVirtual = 204,
|
||||
StoreAttrInstanceValue = 205,
|
||||
StoreAttrSlot = 206,
|
||||
StoreAttrWithHint = 207,
|
||||
StoreSubscrDict = 208,
|
||||
StoreSubscrListInt = 209,
|
||||
ToBoolAlwaysTrue = 210,
|
||||
ToBoolBool = 211,
|
||||
ToBoolInt = 212,
|
||||
ToBoolList = 213,
|
||||
ToBoolNone = 214,
|
||||
ToBoolStr = 215,
|
||||
UnpackSequenceList = 216,
|
||||
UnpackSequenceTuple = 217,
|
||||
UnpackSequenceTwoTuple = 218,
|
||||
InstrumentedEndFor = 233,
|
||||
InstrumentedPopIter = 234,
|
||||
InstrumentedEndSend = 235,
|
||||
InstrumentedForIter = 236,
|
||||
InstrumentedInstruction = 237,
|
||||
InstrumentedJumpForward = 238,
|
||||
InstrumentedNotTaken = 239,
|
||||
InstrumentedPopJumpIfTrue = 240,
|
||||
InstrumentedPopJumpIfFalse = 241,
|
||||
InstrumentedPopJumpIfNone = 242,
|
||||
InstrumentedPopJumpIfNotNone = 243,
|
||||
InstrumentedResume = 244,
|
||||
InstrumentedReturnValue = 245,
|
||||
InstrumentedYieldValue = 246,
|
||||
InstrumentedEndAsyncFor = 247,
|
||||
InstrumentedLoadSuperAttr = 248,
|
||||
InstrumentedCall = 249,
|
||||
InstrumentedCallKw = 250,
|
||||
InstrumentedCallFunctionEx = 251,
|
||||
InstrumentedJumpBackward = 252,
|
||||
InstrumentedLine = 253,
|
||||
EnterExecutor = 254,
|
||||
TraceRecord = 255,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1427,8 +1434,6 @@ mod tests {
|
||||
#[test]
|
||||
fn const_flags_match_cpython_opcode_metadata() {
|
||||
assert!(Opcode::LoadConst.has_const());
|
||||
assert!(Opcode::LoadConstImmortal.has_const());
|
||||
assert!(Opcode::LoadConstMortal.has_const());
|
||||
assert!(!Opcode::LoadSmallInt.has_const());
|
||||
assert!(!Opcode::Nop.has_const());
|
||||
|
||||
@@ -1438,8 +1443,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn stack_effects_match_cpython_opcode_metadata() {
|
||||
assert_eq!(Opcode::ForIter.stack_effect_info(0).popped(), 1);
|
||||
assert_eq!(Opcode::ForIter.stack_effect_info(0).pushed(), 2);
|
||||
assert_eq!(Opcode::ForIter.stack_effect_info(0).popped(), 2);
|
||||
assert_eq!(Opcode::ForIter.stack_effect_info(0).pushed(), 3);
|
||||
assert_eq!(Opcode::ForIter.stack_effect(0), 1);
|
||||
assert_eq!(Opcode::ForIter.stack_effect_jump(0), 1);
|
||||
|
||||
@@ -1482,8 +1487,7 @@ mod tests {
|
||||
|
||||
pub(super) const fn deopt(op: Opcode) -> Option<Opcode> {
|
||||
Some(match op {
|
||||
Opcode::ResumeCheck => Opcode::Resume,
|
||||
Opcode::LoadConstMortal | Opcode::LoadConstImmortal => Opcode::LoadConst,
|
||||
Opcode::ResumeCheck | Opcode::ResumeCheckJit => Opcode::Resume,
|
||||
Opcode::ToBoolAlwaysTrue
|
||||
| Opcode::ToBoolBool
|
||||
| Opcode::ToBoolInt
|
||||
@@ -1501,12 +1505,13 @@ mod tests {
|
||||
| Opcode::BinaryOpSubscrListSlice
|
||||
| Opcode::BinaryOpSubscrTupleInt
|
||||
| Opcode::BinaryOpSubscrStrInt
|
||||
| Opcode::BinaryOpSubscrUstrInt
|
||||
| Opcode::BinaryOpSubscrDict
|
||||
| Opcode::BinaryOpSubscrGetitem
|
||||
| Opcode::BinaryOpExtend
|
||||
| Opcode::BinaryOpInplaceAddUnicode => Opcode::BinaryOp,
|
||||
Opcode::StoreSubscrDict | Opcode::StoreSubscrListInt => Opcode::StoreSubscr,
|
||||
Opcode::SendGen => Opcode::Send,
|
||||
Opcode::SendGen | Opcode::SendAsyncGen | Opcode::SendVirtual => Opcode::Send,
|
||||
Opcode::UnpackSequenceTwoTuple
|
||||
| Opcode::UnpackSequenceTuple
|
||||
| Opcode::UnpackSequenceList => Opcode::UnpackSequence,
|
||||
@@ -1536,7 +1541,9 @@ mod tests {
|
||||
Opcode::ForIterList
|
||||
| Opcode::ForIterTuple
|
||||
| Opcode::ForIterRange
|
||||
| Opcode::ForIterGen => Opcode::ForIter,
|
||||
| Opcode::ForIterGen
|
||||
| Opcode::ForIterVirtual => Opcode::ForIter,
|
||||
Opcode::GetIterSelf | Opcode::GetIterVirtual => Opcode::GetIter,
|
||||
Opcode::CallBoundMethodExactArgs
|
||||
| Opcode::CallPyExactArgs
|
||||
| Opcode::CallType1
|
||||
@@ -1560,6 +1567,7 @@ mod tests {
|
||||
Opcode::CallKwBoundMethod | Opcode::CallKwPy | Opcode::CallKwNonPy => {
|
||||
Opcode::CallKw
|
||||
}
|
||||
Opcode::CallExPy | Opcode::CallExNonPyGeneral => Opcode::CallFunctionEx,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
@@ -1576,6 +1584,7 @@ mod tests {
|
||||
|
||||
pub(super) const fn cache_entries(op: Opcode) -> usize {
|
||||
match deoptimize(op) {
|
||||
Opcode::Resume | Opcode::GetIter | Opcode::CallFunctionEx => 1,
|
||||
Opcode::StoreSubscr => 1,
|
||||
Opcode::ToBool => 3,
|
||||
Opcode::BinaryOp => 5,
|
||||
|
||||
@@ -342,6 +342,11 @@ oparg_enum!(
|
||||
}
|
||||
);
|
||||
|
||||
/// Values used in the oparg for `GET_ITER`.
|
||||
pub const GET_ITER_YIELD_FROM: u32 = 1;
|
||||
pub const GET_ITER_YIELD_FROM_NO_CHECK: u32 = 2;
|
||||
pub const GET_ITER_YIELD_FROM_CORO_CHECK: u32 = 3;
|
||||
|
||||
pub type NameIdx = u32;
|
||||
|
||||
impl OpArgType for u32 {}
|
||||
@@ -756,6 +761,16 @@ oparg_enum!(
|
||||
BuiltinList = (5, "list"),
|
||||
/// Built-in `set` type
|
||||
BuiltinSet = (6, "set"),
|
||||
/// The None singleton.
|
||||
None = (7, "None"),
|
||||
/// The empty string.
|
||||
EmptyStr = (8, "empty string"),
|
||||
/// The true singleton.
|
||||
True = (9, "True"),
|
||||
/// The false singleton.
|
||||
False = (10, "False"),
|
||||
/// Integer minus one.
|
||||
MinusOne = (11, "-1"),
|
||||
}
|
||||
);
|
||||
|
||||
@@ -906,11 +921,11 @@ newtype_oparg!(
|
||||
);
|
||||
|
||||
impl ResumeContext {
|
||||
/// [CPython `RESUME_OPARG_LOCATION_MASK`](https://github.com/python/cpython/blob/v3.14.3/Include/internal/pycore_opcode_utils.h#L84)
|
||||
pub const LOCATION_MASK: u32 = 0x3;
|
||||
/// [CPython `RESUME_OPARG_LOCATION_MASK`](https://github.com/python/cpython/blob/v3.15.0/Include/internal/pycore_opcode_utils.h#L92)
|
||||
pub const LOCATION_MASK: u32 = 0x7;
|
||||
|
||||
/// [CPython `RESUME_OPARG_DEPTH1_MASK`](https://github.com/python/cpython/blob/v3.14.3/Include/internal/pycore_opcode_utils.h#L85)
|
||||
pub const DEPTH1_MASK: u32 = 0x4;
|
||||
/// [CPython `RESUME_OPARG_DEPTH1_MASK`](https://github.com/python/cpython/blob/v3.15.0/Include/internal/pycore_opcode_utils.h#L93)
|
||||
pub const DEPTH1_MASK: u32 = 0x8;
|
||||
|
||||
#[must_use]
|
||||
pub const fn new(location: ResumeLocation, is_exception_depth1: bool) -> Self {
|
||||
@@ -926,8 +941,8 @@ impl ResumeContext {
|
||||
/// Resume location is determined by [`Self::LOCATION_MASK`].
|
||||
#[must_use]
|
||||
pub fn location(&self) -> ResumeLocation {
|
||||
// SAFETY: The mask should return a value that is in range.
|
||||
unsafe { ResumeLocation::try_from(self.as_u32() & Self::LOCATION_MASK).unwrap_unchecked() }
|
||||
ResumeLocation::try_from(self.as_u32() & Self::LOCATION_MASK)
|
||||
.expect("invalid RESUME location")
|
||||
}
|
||||
|
||||
/// True if the bit at [`Self::DEPTH1_MASK`] is on.
|
||||
@@ -947,6 +962,9 @@ pub enum ResumeLocation {
|
||||
AfterYieldFrom,
|
||||
/// After an `await` expression.
|
||||
AfterAwait,
|
||||
/// Start of a generator expression, before its outermost iterator is
|
||||
/// acquired and the generator is created.
|
||||
AtGenExprStart,
|
||||
}
|
||||
|
||||
impl From<ResumeLocation> for ResumeContext {
|
||||
@@ -964,6 +982,7 @@ impl TryFrom<u32> for ResumeLocation {
|
||||
1 => Self::AfterYield,
|
||||
2 => Self::AfterYieldFrom,
|
||||
3 => Self::AfterAwait,
|
||||
4 => Self::AtGenExprStart,
|
||||
_ => return Err(Self::Error::InvalidBytecode),
|
||||
})
|
||||
}
|
||||
@@ -977,6 +996,7 @@ impl ResumeLocation {
|
||||
Self::AfterYield => 1,
|
||||
Self::AfterYieldFrom => 2,
|
||||
Self::AfterAwait => 3,
|
||||
Self::AtGenExprStart => 4,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+60
-48
@@ -15,15 +15,15 @@ impl super::Opcode {
|
||||
#[inline]
|
||||
pub const fn cache_entries(self) -> usize {
|
||||
const CACHE_ENTRIES: [u8; 256] = [
|
||||
0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 3, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 4, 4, 1, 1, 0, 1, 4, 4, 4, 1, 1,
|
||||
3, 3, 3, 3, 3, 3, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 3, 3, 0, 1, 0, 0,
|
||||
0, 0, 0, 5, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 3, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 3, 1, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3,
|
||||
3, 3, 3, 3, 3, 3, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 4, 4, 1, 1, 1, 1, 1,
|
||||
1, 1, 4, 4, 4, 1, 1, 3, 3, 3, 3, 3, 3, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 3, 3, 1, 1, 0, 0, 0,
|
||||
];
|
||||
|
||||
CACHE_ENTRIES[self.as_numeric() as usize] as usize
|
||||
@@ -176,6 +176,7 @@ impl super::Opcode {
|
||||
Some(super::Opcode::BinaryOp),
|
||||
Some(super::Opcode::BinaryOp),
|
||||
Some(super::Opcode::BinaryOp),
|
||||
Some(super::Opcode::BinaryOp),
|
||||
Some(super::Opcode::Call),
|
||||
Some(super::Opcode::Call),
|
||||
Some(super::Opcode::Call),
|
||||
@@ -183,6 +184,8 @@ impl super::Opcode {
|
||||
Some(super::Opcode::Call),
|
||||
Some(super::Opcode::Call),
|
||||
Some(super::Opcode::Call),
|
||||
Some(super::Opcode::CallFunctionEx),
|
||||
Some(super::Opcode::CallFunctionEx),
|
||||
Some(super::Opcode::Call),
|
||||
Some(super::Opcode::CallKw),
|
||||
Some(super::Opcode::CallKw),
|
||||
@@ -208,6 +211,9 @@ impl super::Opcode {
|
||||
Some(super::Opcode::ForIter),
|
||||
Some(super::Opcode::ForIter),
|
||||
Some(super::Opcode::ForIter),
|
||||
Some(super::Opcode::ForIter),
|
||||
Some(super::Opcode::GetIter),
|
||||
Some(super::Opcode::GetIter),
|
||||
Some(super::Opcode::JumpBackward),
|
||||
Some(super::Opcode::JumpBackward),
|
||||
Some(super::Opcode::LoadAttr),
|
||||
@@ -223,13 +229,14 @@ impl super::Opcode {
|
||||
Some(super::Opcode::LoadAttr),
|
||||
Some(super::Opcode::LoadAttr),
|
||||
Some(super::Opcode::LoadAttr),
|
||||
Some(super::Opcode::LoadConst),
|
||||
Some(super::Opcode::LoadConst),
|
||||
Some(super::Opcode::LoadGlobal),
|
||||
Some(super::Opcode::LoadGlobal),
|
||||
Some(super::Opcode::LoadSuperAttr),
|
||||
Some(super::Opcode::LoadSuperAttr),
|
||||
Some(super::Opcode::Resume),
|
||||
Some(super::Opcode::Resume),
|
||||
Some(super::Opcode::Send),
|
||||
Some(super::Opcode::Send),
|
||||
Some(super::Opcode::Send),
|
||||
Some(super::Opcode::StoreAttr),
|
||||
Some(super::Opcode::StoreAttr),
|
||||
@@ -282,13 +289,6 @@ impl super::Opcode {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
];
|
||||
|
||||
DEOPT[self.as_numeric() as usize]
|
||||
@@ -327,6 +327,7 @@ impl super::Opcode {
|
||||
| Self::ExtendedArg
|
||||
| Self::ForIter
|
||||
| Self::GetAwaitable
|
||||
| Self::GetIter
|
||||
| Self::ImportFrom
|
||||
| Self::ImportName
|
||||
| Self::IsOp
|
||||
@@ -384,7 +385,7 @@ impl super::Opcode {
|
||||
| Self::CallBuiltinFast
|
||||
| Self::CallBuiltinFastWithKeywords
|
||||
| Self::CallBuiltinO
|
||||
| Self::CallIsinstance
|
||||
| Self::CallExPy
|
||||
| Self::CallKwBoundMethod
|
||||
| Self::CallKwNonPy
|
||||
| Self::CallKwPy
|
||||
@@ -408,6 +409,7 @@ impl super::Opcode {
|
||||
| Self::ForIterList
|
||||
| Self::ForIterRange
|
||||
| Self::ForIterTuple
|
||||
| Self::ForIterVirtual
|
||||
| Self::JumpBackwardJit
|
||||
| Self::JumpBackwardNoJit
|
||||
| Self::LoadAttrClass
|
||||
@@ -423,13 +425,14 @@ impl super::Opcode {
|
||||
| Self::LoadAttrProperty
|
||||
| Self::LoadAttrSlot
|
||||
| Self::LoadAttrWithHint
|
||||
| Self::LoadConstImmortal
|
||||
| Self::LoadConstMortal
|
||||
| Self::LoadGlobalBuiltin
|
||||
| Self::LoadGlobalModule
|
||||
| Self::LoadSuperAttrAttr
|
||||
| Self::LoadSuperAttrMethod
|
||||
| Self::ResumeCheckJit
|
||||
| Self::SendAsyncGen
|
||||
| Self::SendGen
|
||||
| Self::SendVirtual
|
||||
| Self::StoreAttrWithHint
|
||||
| Self::UnpackSequenceList
|
||||
| Self::UnpackSequenceTuple
|
||||
@@ -448,16 +451,14 @@ impl super::Opcode {
|
||||
| Self::InstrumentedCallKw
|
||||
| Self::InstrumentedJumpBackward
|
||||
| Self::EnterExecutor
|
||||
| Self::TraceRecord
|
||||
)
|
||||
}
|
||||
|
||||
/// Does this opcode have 'HAS_CONST_FLAG' set.
|
||||
#[must_use]
|
||||
pub const fn has_const(self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::LoadConst | Self::LoadConstImmortal | Self::LoadConstMortal
|
||||
)
|
||||
matches!(self, Self::LoadConst)
|
||||
}
|
||||
|
||||
/// Does this opcode have 'HAS_EVAL_BREAK_FLAG' set.
|
||||
@@ -473,6 +474,7 @@ impl super::Opcode {
|
||||
| Self::CallBuiltinFast
|
||||
| Self::CallBuiltinFastWithKeywords
|
||||
| Self::CallBuiltinO
|
||||
| Self::CallExNonPyGeneral
|
||||
| Self::CallKwNonPy
|
||||
| Self::CallMethodDescriptorFast
|
||||
| Self::CallMethodDescriptorFastWithKeywords
|
||||
@@ -517,8 +519,11 @@ impl super::Opcode {
|
||||
| Self::ForIterList
|
||||
| Self::ForIterRange
|
||||
| Self::ForIterTuple
|
||||
| Self::ForIterVirtual
|
||||
| Self::JumpBackwardJit
|
||||
| Self::JumpBackwardNoJit
|
||||
| Self::SendAsyncGen
|
||||
| Self::SendVirtual
|
||||
| Self::InstrumentedForIter
|
||||
| Self::InstrumentedEndAsyncFor
|
||||
)
|
||||
@@ -609,23 +614,21 @@ impl super::Opcode {
|
||||
Self::Cache => (0, 0),
|
||||
Self::BinarySlice => (1, 3),
|
||||
Self::BuildTemplate => (1, 2),
|
||||
Self::BinaryOpInplaceAddUnicode => (0, 2),
|
||||
Self::BinaryOpInplaceAddUnicode => (1, 2),
|
||||
Self::CallFunctionEx => (1, 4),
|
||||
Self::CheckEgMatch => (2, 2),
|
||||
Self::CheckExcMatch => (2, 2),
|
||||
Self::CleanupThrow => (2, 3),
|
||||
Self::CleanupThrow => (3, 4),
|
||||
Self::DeleteSubscr => (0, 2),
|
||||
Self::EndFor => (0, 1),
|
||||
Self::EndSend => (1, 2),
|
||||
Self::EndSend => (1, 3),
|
||||
Self::ExitInitCheck => (0, 1),
|
||||
Self::FormatSimple => (1, 1),
|
||||
Self::FormatWithSpec => (1, 2),
|
||||
Self::GetAiter => (1, 1),
|
||||
Self::GetAnext => (2, 1),
|
||||
Self::GetIter => (1, 1),
|
||||
Self::Reserved => (0, 0),
|
||||
Self::GetLen => (2, 1),
|
||||
Self::GetYieldFromIter => (1, 1),
|
||||
Self::Reserved => (0, 0),
|
||||
Self::InterpreterExit => (0, 1),
|
||||
Self::LoadBuildClass => (1, 0),
|
||||
Self::LoadLocals => (1, 0),
|
||||
@@ -636,7 +639,7 @@ impl super::Opcode {
|
||||
Self::Nop => (0, 0),
|
||||
Self::NotTaken => (0, 0),
|
||||
Self::PopExcept => (0, 1),
|
||||
Self::PopIter => (0, 1),
|
||||
Self::PopIter => (0, 2),
|
||||
Self::PopTop => (0, 1),
|
||||
Self::PushExcInfo => (2, 1),
|
||||
Self::PushNull => (1, 0),
|
||||
@@ -676,8 +679,9 @@ impl super::Opcode {
|
||||
Self::DictUpdate => (1 + (oparg - 1), 2 + (oparg - 1)),
|
||||
Self::EndAsyncFor => (0, 2),
|
||||
Self::ExtendedArg => (0, 0),
|
||||
Self::ForIter => (2, 1),
|
||||
Self::ForIter => (3, 2),
|
||||
Self::GetAwaitable => (1, 1),
|
||||
Self::GetIter => (2, 1),
|
||||
Self::ImportFrom => (2, 1),
|
||||
Self::ImportName => (1, 2),
|
||||
Self::IsOp => (1, 2),
|
||||
@@ -712,7 +716,7 @@ impl super::Opcode {
|
||||
Self::PopJumpIfTrue => (0, 1),
|
||||
Self::RaiseVarargs => (0, oparg),
|
||||
Self::Reraise => (oparg, 1 + oparg),
|
||||
Self::Send => (2, 2),
|
||||
Self::Send => (3, 3),
|
||||
Self::SetAdd => (1 + (oparg - 1), 2 + (oparg - 1)),
|
||||
Self::SetFunctionAttribute => (1, 2),
|
||||
Self::SetUpdate => (1 + (oparg - 1), 2 + (oparg - 1)),
|
||||
@@ -740,6 +744,7 @@ impl super::Opcode {
|
||||
Self::BinaryOpSubscrListSlice => (1, 2),
|
||||
Self::BinaryOpSubscrStrInt => (1, 2),
|
||||
Self::BinaryOpSubscrTupleInt => (1, 2),
|
||||
Self::BinaryOpSubscrUstrInt => (1, 2),
|
||||
Self::BinaryOpSubtractFloat => (1, 2),
|
||||
Self::BinaryOpSubtractInt => (1, 2),
|
||||
Self::CallAllocAndEnterInit => (0, 2 + oparg),
|
||||
@@ -749,12 +754,14 @@ impl super::Opcode {
|
||||
Self::CallBuiltinFast => (1, 2 + oparg),
|
||||
Self::CallBuiltinFastWithKeywords => (1, 2 + oparg),
|
||||
Self::CallBuiltinO => (1, 2 + oparg),
|
||||
Self::CallIsinstance => (1, 2 + oparg),
|
||||
Self::CallExNonPyGeneral => (1, 4),
|
||||
Self::CallExPy => (0, 4),
|
||||
Self::CallIsinstance => (1, 4),
|
||||
Self::CallKwBoundMethod => (0, 3 + oparg),
|
||||
Self::CallKwNonPy => (1, 3 + oparg),
|
||||
Self::CallKwPy => (0, 3 + oparg),
|
||||
Self::CallLen => (1, 3),
|
||||
Self::CallListAppend => (0, 3),
|
||||
Self::CallListAppend => (1, 3),
|
||||
Self::CallMethodDescriptorFast => (1, 2 + oparg),
|
||||
Self::CallMethodDescriptorFastWithKeywords => (1, 2 + oparg),
|
||||
Self::CallMethodDescriptorNoargs => (1, 2 + oparg),
|
||||
@@ -770,15 +777,18 @@ impl super::Opcode {
|
||||
Self::CompareOpStr => (1, 2),
|
||||
Self::ContainsOpDict => (1, 2),
|
||||
Self::ContainsOpSet => (1, 2),
|
||||
Self::ForIterGen => (1, 1),
|
||||
Self::ForIterList => (2, 1),
|
||||
Self::ForIterRange => (2, 1),
|
||||
Self::ForIterTuple => (2, 1),
|
||||
Self::ForIterGen => (2, 2),
|
||||
Self::ForIterList => (3, 2),
|
||||
Self::ForIterRange => (3, 2),
|
||||
Self::ForIterTuple => (3, 2),
|
||||
Self::ForIterVirtual => (3, 2),
|
||||
Self::GetIterSelf => (2, 1),
|
||||
Self::GetIterVirtual => (2, 1),
|
||||
Self::JumpBackwardJit => (0, 0),
|
||||
Self::JumpBackwardNoJit => (0, 0),
|
||||
Self::LoadAttrClass => (1 + (oparg & 1), 1),
|
||||
Self::LoadAttrClassWithMetaclassCheck => (1 + (oparg & 1), 1),
|
||||
Self::LoadAttrGetattributeOverridden => (1, 1),
|
||||
Self::LoadAttrGetattributeOverridden => (0, 1),
|
||||
Self::LoadAttrInstanceValue => (1 + (oparg & 1), 1),
|
||||
Self::LoadAttrMethodLazyDict => (2, 1),
|
||||
Self::LoadAttrMethodNoDict => (2, 1),
|
||||
@@ -789,14 +799,15 @@ impl super::Opcode {
|
||||
Self::LoadAttrProperty => (0, 1),
|
||||
Self::LoadAttrSlot => (1 + (oparg & 1), 1),
|
||||
Self::LoadAttrWithHint => (1 + (oparg & 1), 1),
|
||||
Self::LoadConstImmortal => (1, 0),
|
||||
Self::LoadConstMortal => (1, 0),
|
||||
Self::LoadGlobalBuiltin => (1 + (oparg & 1), 0),
|
||||
Self::LoadGlobalModule => (1 + (oparg & 1), 0),
|
||||
Self::LoadSuperAttrAttr => (1, 3),
|
||||
Self::LoadSuperAttrMethod => (2, 3),
|
||||
Self::ResumeCheck => (0, 0),
|
||||
Self::SendGen => (1, 2),
|
||||
Self::ResumeCheckJit => (0, 0),
|
||||
Self::SendAsyncGen => (3, 3),
|
||||
Self::SendGen => (2, 3),
|
||||
Self::SendVirtual => (3, 3),
|
||||
Self::StoreAttrInstanceValue => (0, 2),
|
||||
Self::StoreAttrSlot => (0, 2),
|
||||
Self::StoreAttrWithHint => (0, 2),
|
||||
@@ -811,10 +822,10 @@ impl super::Opcode {
|
||||
Self::UnpackSequenceList => (oparg, 1),
|
||||
Self::UnpackSequenceTuple => (oparg, 1),
|
||||
Self::UnpackSequenceTwoTuple => (2, 1),
|
||||
Self::InstrumentedEndFor => (1, 2),
|
||||
Self::InstrumentedPopIter => (0, 1),
|
||||
Self::InstrumentedEndSend => (1, 2),
|
||||
Self::InstrumentedForIter => (2, 1),
|
||||
Self::InstrumentedEndFor => (2, 3),
|
||||
Self::InstrumentedPopIter => (0, 2),
|
||||
Self::InstrumentedEndSend => (1, 3),
|
||||
Self::InstrumentedForIter => (3, 2),
|
||||
Self::InstrumentedInstruction => (0, 0),
|
||||
Self::InstrumentedJumpForward => (0, 0),
|
||||
Self::InstrumentedNotTaken => (0, 0),
|
||||
@@ -833,6 +844,7 @@ impl super::Opcode {
|
||||
Self::InstrumentedJumpBackward => (0, 0),
|
||||
Self::InstrumentedLine => (0, 0),
|
||||
Self::EnterExecutor => (0, 0),
|
||||
Self::TraceRecord => (0, 0),
|
||||
};
|
||||
|
||||
debug_assert!(u32::try_from(pushed).is_ok());
|
||||
|
||||
@@ -554,6 +554,16 @@ fn read_const_value<R: Read, Bag: ConstantBag>(
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
/// Object-valued code fields as read from the stream (`r_object` TYPE_CODE).
|
||||
pub struct CodeFields<V> {
|
||||
pub consts: V,
|
||||
pub names: V,
|
||||
pub localsplusnames: V,
|
||||
pub localspluskinds: V,
|
||||
pub linetable: V,
|
||||
pub exceptiontable: V,
|
||||
}
|
||||
|
||||
pub trait MarshalBag: Copy {
|
||||
type Value: Clone;
|
||||
type ConstantBag: ConstantBag;
|
||||
@@ -580,23 +590,14 @@ pub trait MarshalBag: Copy {
|
||||
|
||||
fn make_tuple(&self, elements: impl Iterator<Item = Self::Value>) -> Self::Value;
|
||||
|
||||
/// Construct a code object. `fields` holds the objects read for the
|
||||
/// object-valued fields that `code` was decoded from.
|
||||
fn make_code(
|
||||
&self,
|
||||
code: CodeObject<<Self::ConstantBag as ConstantBag>::Constant>,
|
||||
fields: CodeFields<Self::Value>,
|
||||
) -> Result<Self::Value>;
|
||||
|
||||
/// Construct a runtime code object while retaining the exact values read
|
||||
/// from ``co_consts``. Compiler bags ignore this second channel; runtime
|
||||
/// bags use it for marshalable values (lists, dicts, sets, recursive
|
||||
/// containers) that their compiler constant representation cannot hold.
|
||||
fn make_code_with_constants(
|
||||
&self,
|
||||
code: CodeObject<<Self::ConstantBag as ConstantBag>::Constant>,
|
||||
_constants: Vec<Self::Value>,
|
||||
) -> Result<Self::Value> {
|
||||
self.make_code(code)
|
||||
}
|
||||
|
||||
/// Decode the public `co_code` byte string into the execution-oriented
|
||||
/// instruction storage.
|
||||
///
|
||||
@@ -608,22 +609,6 @@ pub trait MarshalBag: Copy {
|
||||
CodeUnits::try_from(code_bytes)
|
||||
}
|
||||
|
||||
/// Construct a runtime code object while retaining both its exact public
|
||||
/// `co_code` bytes and the exact values read from `co_consts`.
|
||||
///
|
||||
/// The default ignores the redundant byte spelling because ordinary
|
||||
/// compiler code is represented losslessly by `CodeUnits`. Runtime bags
|
||||
/// that accepted otherwise unrepresentable opcode bytes in
|
||||
/// [`MarshalBag::code_units_from_bytes`] can preserve them here.
|
||||
fn make_code_with_constants_and_bytes(
|
||||
&self,
|
||||
code: CodeObject<<Self::ConstantBag as ConstantBag>::Constant>,
|
||||
constants: Vec<Self::Value>,
|
||||
_code_bytes: Vec<u8>,
|
||||
) -> Result<Self::Value> {
|
||||
self.make_code_with_constants(code, constants)
|
||||
}
|
||||
|
||||
fn make_stop_iter(&self) -> Result<Self::Value>;
|
||||
|
||||
fn make_list(&self, it: impl Iterator<Item = Self::Value>) -> Result<Self::Value>;
|
||||
@@ -786,6 +771,7 @@ impl<Bag: ConstantBag> MarshalBag for Bag {
|
||||
fn make_code(
|
||||
&self,
|
||||
code: CodeObject<<Self::ConstantBag as ConstantBag>::Constant>,
|
||||
_fields: CodeFields<Self::Value>,
|
||||
) -> Result<Self::Value> {
|
||||
Ok(self.make_code(code))
|
||||
}
|
||||
@@ -958,17 +944,20 @@ fn deserialize_code_value_inner<R: Read, Bag: MarshalBag>(
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
let read_strings =
|
||||
|rdr: &mut R, refs: &mut Vec<Option<Bag::Value>>| -> Result<Vec<alloc::string::String>> {
|
||||
let tuple = deserialize_value_depth(rdr, bag, child_depth, refs)?;
|
||||
bag.tuple_elements_from_value(&tuple)
|
||||
.ok_or(MarshalError::BadType)?
|
||||
.iter()
|
||||
.map(|value| bag.str_from_value(value).ok_or(MarshalError::BadType))
|
||||
.collect()
|
||||
};
|
||||
let names_raw = read_strings(rdr, refs)?;
|
||||
let localsplusnames = read_strings(rdr, refs)?;
|
||||
let read_strings = |rdr: &mut R,
|
||||
refs: &mut Vec<Option<Bag::Value>>|
|
||||
-> Result<(Bag::Value, Vec<alloc::string::String>)> {
|
||||
let tuple = deserialize_value_depth(rdr, bag, child_depth, refs)?;
|
||||
let names = bag
|
||||
.tuple_elements_from_value(&tuple)
|
||||
.ok_or(MarshalError::BadType)?
|
||||
.iter()
|
||||
.map(|value| bag.str_from_value(value).ok_or(MarshalError::BadType))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
Ok((tuple, names))
|
||||
};
|
||||
let (names_value, names_raw) = read_strings(rdr, refs)?;
|
||||
let (localsplusnames_value, localsplusnames) = read_strings(rdr, refs)?;
|
||||
|
||||
let kinds_value = deserialize_value_depth(rdr, bag, child_depth, refs)?;
|
||||
let localspluskinds = bag
|
||||
@@ -1050,7 +1039,17 @@ fn deserialize_code_value_inner<R: Read, Bag: MarshalBag>(
|
||||
linetable,
|
||||
exceptiontable,
|
||||
};
|
||||
bag.make_code_with_constants_and_bytes(code, constant_values, code_bytes)
|
||||
bag.make_code(
|
||||
code,
|
||||
CodeFields {
|
||||
consts: consts_value,
|
||||
names: names_value,
|
||||
localsplusnames: localsplusnames_value,
|
||||
localspluskinds: kinds_value,
|
||||
linetable: linetable_value,
|
||||
exceptiontable: exceptiontable_value,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn deserialize_value_typed<R: Read, Bag: MarshalBag>(
|
||||
|
||||
@@ -663,6 +663,21 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
|
||||
self.stack.push(val);
|
||||
Ok(())
|
||||
}
|
||||
Instruction::LoadCommonConstant { idx } => {
|
||||
let val = match idx.get(arg) {
|
||||
oparg::CommonConstant::None => JitValue::None,
|
||||
oparg::CommonConstant::True | oparg::CommonConstant::False => {
|
||||
let value = i64::from(idx.get(arg) == oparg::CommonConstant::True);
|
||||
JitValue::Bool(self.builder.ins().iconst(types::I8, value))
|
||||
}
|
||||
oparg::CommonConstant::MinusOne => {
|
||||
JitValue::Int(self.builder.ins().iconst(types::I64, -1))
|
||||
}
|
||||
_ => return Err(JitCompileError::NotSupported),
|
||||
};
|
||||
self.stack.push(val);
|
||||
Ok(())
|
||||
}
|
||||
Instruction::LoadSmallInt { i } => {
|
||||
let small_int = i.get(arg) as i64;
|
||||
let val = self.builder.ins().iconst(types::I64, small_int);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use core::ops::ControlFlow;
|
||||
use rustpython_compiler_core::bytecode::{
|
||||
CodeObject, ConstantData, Constants, Instruction, OpArg, OpArgState,
|
||||
CodeObject, CommonConstant, ConstantData, Constants, Instruction, OpArg, OpArgState,
|
||||
};
|
||||
use rustpython_jit::{CompiledCode, JitType};
|
||||
use rustpython_wtf8::{Wtf8, Wtf8Buf};
|
||||
@@ -207,6 +207,15 @@ impl StackMachine {
|
||||
Instruction::LoadConst { consti } => {
|
||||
self.stack.push(constants[consti.get(arg)].clone().into())
|
||||
}
|
||||
Instruction::LoadCommonConstant { idx } => {
|
||||
self.stack.push(match idx.get(arg) {
|
||||
CommonConstant::None => StackValue::None,
|
||||
CommonConstant::EmptyStr => StackValue::String(String::new()),
|
||||
constant => unimplemented!(
|
||||
"common constant {constant:?} isn't yet supported in py_function!"
|
||||
),
|
||||
});
|
||||
}
|
||||
Instruction::LoadName { namei } => self
|
||||
.stack
|
||||
.push(StackValue::String(names[namei.get(arg) as usize].clone())),
|
||||
|
||||
+738
-55
@@ -4,7 +4,6 @@ pub(crate) use _opcode::module_def;
|
||||
mod _opcode {
|
||||
use crate::vm::{
|
||||
AsObject, PyObjectRef, PyResult, VirtualMachine,
|
||||
builtins::{PyInt, PyIntRef},
|
||||
bytecode::{AnyOpcode, oparg},
|
||||
};
|
||||
|
||||
@@ -25,7 +24,7 @@ mod _opcode {
|
||||
#[derive(FromArgs)]
|
||||
struct StackEffectArgs {
|
||||
#[pyarg(positional)]
|
||||
opcode: PyIntRef,
|
||||
opcode: i32,
|
||||
#[pyarg(positional, optional)]
|
||||
oparg: Option<PyObjectRef>,
|
||||
#[pyarg(named, optional)]
|
||||
@@ -33,58 +32,33 @@ mod _opcode {
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
#[allow(clippy::unnecessary_cast)] // `c_long` is `i32` or `i64`
|
||||
fn stack_effect(args: StackEffectArgs, vm: &VirtualMachine) -> PyResult<i32> {
|
||||
let oparg = args.oparg.map_or(Ok(0), |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(format!(
|
||||
"'{}' object cannot be interpreted as an integer",
|
||||
v.class().name()
|
||||
))
|
||||
})?
|
||||
.try_to_primitive::<u32>(vm)
|
||||
})?;
|
||||
|
||||
let jump: Option<bool> = match args.jump {
|
||||
Some(v) => {
|
||||
if vm.is_none(&v) {
|
||||
None
|
||||
} else {
|
||||
Some(v.try_to_bool(vm).map_err(|_| {
|
||||
vm.new_value_error("stack_effect: jump must be False, True or None")
|
||||
})?)
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
let oparg = match args.oparg {
|
||||
// (int)PyLong_AsLong(oparg)
|
||||
Some(v) if !vm.is_none(&v) => v.try_into_value::<core::ffi::c_long>(vm)? as i32,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
let instr = args
|
||||
.opcode
|
||||
.try_to_primitive::<u16>(vm)
|
||||
.and_then(|v| {
|
||||
AnyOpcode::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"))?;
|
||||
|
||||
// Raise ValueError if specialized.
|
||||
let jump = match args.jump {
|
||||
None => None,
|
||||
Some(v) if vm.is_none(&v) => None,
|
||||
Some(v) if v.is(&vm.ctx.true_value) => Some(true),
|
||||
Some(v) if v.is(&vm.ctx.false_value) => Some(false),
|
||||
Some(_) => {
|
||||
return Err(vm.new_value_error("stack_effect: jump must be False, True or None"));
|
||||
}
|
||||
};
|
||||
let invalid = || vm.new_value_error("invalid opcode or oparg");
|
||||
let instr = try_from_i32(args.opcode).map_err(|_| invalid())?;
|
||||
// Specialized instructions are not supported.
|
||||
if instr.real().is_some_and(|op| op.deopt().is_some()) {
|
||||
return Err(vm.new_value_error("invalid opcode or oparg"));
|
||||
return Err(invalid());
|
||||
}
|
||||
|
||||
let effect = match jump {
|
||||
Some(true) => instr.stack_effect_jump(oparg),
|
||||
Some(false) => instr.stack_effect(oparg),
|
||||
// jump=None: max of both paths (CPython convention)
|
||||
None => instr
|
||||
.stack_effect(oparg)
|
||||
.max(instr.stack_effect_jump(oparg)),
|
||||
let oparg = u32::try_from(oparg).map_err(|_| invalid())?;
|
||||
let effect = if jump == Some(false) {
|
||||
instr.stack_effect(oparg)
|
||||
} else {
|
||||
instr.stack_effect_jump(oparg)
|
||||
};
|
||||
Ok(effect)
|
||||
}
|
||||
@@ -197,16 +171,20 @@ mod tests {
|
||||
|
||||
macro_rules! assert_dis_snapshot {
|
||||
($value:expr) => {
|
||||
insta::assert_snapshot!(dis($value))
|
||||
insta::assert_snapshot!(dis($value, false))
|
||||
};
|
||||
(positions: $value:expr) => {
|
||||
insta::assert_snapshot!(dis($value, true))
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns the [`dis.dis`](https://docs.python.org/3/library/dis.html#dis.dis) output.
|
||||
/// Returns the [`dis.dis`](https://docs.python.org/3/library/dis.html#dis.dis) output,
|
||||
/// with the source positions of the instructions if `show_positions` is true.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// Memory addresses in the output are replaced with `0xdeadbeef` for consistency.
|
||||
fn dis(source: &str) -> String {
|
||||
fn dis(source: &str, show_positions: bool) -> String {
|
||||
const FNAME: &str = "<?>";
|
||||
|
||||
let builder = vm::Interpreter::builder(Default::default());
|
||||
@@ -223,6 +201,10 @@ mod tests {
|
||||
.map_err(|err| err.into_pyexception(vm, Some(source)))
|
||||
.unwrap();
|
||||
scope.globals.set_item("code", code_obj.into(), vm).unwrap();
|
||||
scope
|
||||
.globals
|
||||
.set_item("show_positions", vm.new_pyobj(show_positions), vm)
|
||||
.unwrap();
|
||||
|
||||
let py_source = r#"
|
||||
import dis
|
||||
@@ -232,13 +214,13 @@ import sys
|
||||
|
||||
old_stdout = sys.stdout
|
||||
sys.stdout = buf = io.StringIO()
|
||||
dis.dis(code)
|
||||
dis.dis(code, show_positions=show_positions)
|
||||
sys.stdout = old_stdout
|
||||
|
||||
tmp_output = buf.getvalue()
|
||||
|
||||
# constant mem address
|
||||
output = re.sub(r'(<code object \w+ at )0x[0-9a-fA-F]+', r'\g<1>0xdeadbeef', tmp_output)
|
||||
output = re.sub(r'(<code object .*? at )0x[0-9a-fA-F]+', r'\g<1>0xdeadbeef', tmp_output)
|
||||
"#;
|
||||
|
||||
let py_code_obj = vm
|
||||
@@ -338,6 +320,707 @@ def f(one: int):
|
||||
my_lst = [1]
|
||||
my_lst[one]: int
|
||||
return my_lst
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_module_implicit_return_location() {
|
||||
// The implicit return is emitted with NO_LOCATION; propagate_line_numbers()
|
||||
// gives it the module RESUME location, whose line is 0.
|
||||
assert_dis_snapshot!(positions: "")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn module_docstring_location() {
|
||||
// codegen_body() loads the docstring at its expression location and stores
|
||||
// __doc__ with NO_LOCATION.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
"doc"
|
||||
x = 1
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn redundant_nop_location() {
|
||||
// basicblock_remove_redundant_nops() copies the full NOP location into a
|
||||
// following no-location jump.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def f(x, y, z):
|
||||
while x:
|
||||
if y:
|
||||
pass
|
||||
elif z:
|
||||
if y < 0:
|
||||
return y
|
||||
if z:
|
||||
y = y + 1
|
||||
elif y:
|
||||
return 1
|
||||
return -1
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn annotated_multiline_function_firstlineno() {
|
||||
// codegen_function() computes firstlineno from the FunctionDef before
|
||||
// compiling annotations.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
|
||||
a = 1
|
||||
def f(
|
||||
x: a,
|
||||
): ...
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn annotation_scope_function_location() {
|
||||
// codegen_function_annotations() receives LOC(function) and uses it for the
|
||||
// annotation closure's BUILD_MAP/RETURN_VALUE and the MAKE_FUNCTION annotate
|
||||
// sequence.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
|
||||
def g():
|
||||
def f(x: not (int is int), /): ...
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn starred_arg_annotation_unpack_location() {
|
||||
// codegen_argannotation() visits `Ts` at the annotation location, then emits
|
||||
// UNPACK_SEQUENCE at LOC(function).
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def f(*args: *Ts): pass
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn module_deferred_annotations_location() {
|
||||
// codegen_process_deferred_annotations() uses the first module statement
|
||||
// location from start_location().
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
import os
|
||||
X: int
|
||||
Y: str
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_compare_location() {
|
||||
// A Compare inside UnaryOp(Not) is parsed with the UnaryOp start location.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def f(self, other):
|
||||
return not self == other
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_chained_compare_location() {
|
||||
// A chained comparison keeps its inner range for the comparison scaffolding
|
||||
// and uses the UnaryOp range only for TO_BOOL and UNARY_NOT.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def f(c):
|
||||
return not (b" " <= c <= b"~")
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn type_param_scope_locations() {
|
||||
// codegen_type_params() emits type parameter ops at LOC(typeparam),
|
||||
// bound/default evaluators at LOC(e), and type alias plumbing at LOC(s).
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
type BoundGenericAlias[X: int] = set[X]
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generic_typealias_wrapper_return_location() {
|
||||
// The generic parameters wrapper of a type alias returns at LOC(type alias).
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
type A[T] = T
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generic_function_annotation_scope_location() {
|
||||
// codegen_function_annotations() gets LOC(function) inside the generic
|
||||
// parameters scope too.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def f[T](x: int): ...
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generic_class_type_params_store_location() {
|
||||
// codegen_class() stores the .type_params cell at LOC(class).
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def outer():
|
||||
class X[T]: ...
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn class_deferred_annotations_location() {
|
||||
// Deferred annotation setup and the class tail inherit the class body
|
||||
// location LOCATION(firstlineno, firstlineno, 0, 0).
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
|
||||
class C:
|
||||
"doc"
|
||||
x: int
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn future_annotation_string_location() {
|
||||
// The stringized annotation is loaded at LOC(annotation) and stored at
|
||||
// LOC(AnnAssign).
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
from __future__ import annotations
|
||||
class Bar:
|
||||
foo: Foo
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lambda_dict_literal_location() {
|
||||
// codegen_dict() and codegen_subdict() use LOC(dict) for BUILD_MAP, MAP_ADD
|
||||
// and DICT_UPDATE.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
f = lambda data: {'x': data}
|
||||
g = lambda i: {**i}
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_or_alternative_locations() {
|
||||
// codegen_pattern_or() emits the COPY of each alternative at LOC(alt).
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def f():
|
||||
x = False
|
||||
match 0:
|
||||
case 0 | 1 | 2 | 3:
|
||||
x = True
|
||||
return x
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_success_jump_location() {
|
||||
// codegen_match_inner() emits the success jump with NO_LOCATION.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def f(self):
|
||||
match 0:
|
||||
case 0:
|
||||
x = True
|
||||
case 0:
|
||||
x = False
|
||||
self.assertIs(x, True)
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_mapping_keys_location() {
|
||||
// codegen_pattern_mapping() returns to LOC(p) for the BUILD_TUPLE/MATCH_KEYS
|
||||
// scaffolding.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def f(self):
|
||||
x = {}
|
||||
y = None
|
||||
match x:
|
||||
case {0: 0}:
|
||||
y = 0
|
||||
self.assertIs(y, None)
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_class_scaffolding_location() {
|
||||
// codegen_pattern_class() returns to LOC(p) after visiting the class.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def f(x):
|
||||
match x:
|
||||
case bool(z):
|
||||
y = 0
|
||||
return y, z
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn taken_constant_boolop_location() {
|
||||
// The surviving load of a short-circuited BoolOp keeps the literal range.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def and_false(x):
|
||||
return False and x
|
||||
|
||||
def or_true(x):
|
||||
return True or x
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assert_false_message_location() {
|
||||
// codegen_assert() emits LOAD_COMMON_CONSTANT and CALL at LOC(assert) and
|
||||
// RAISE_VARARGS at LOC(test).
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def f():
|
||||
assert False, "x"
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn static_swap_implicit_return_location() {
|
||||
// Line numbers are resolved before apply_static_swaps() reorders the stores,
|
||||
// so the return epilogue keeps the location of the last store before the swap.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def f(a, b):
|
||||
a, b = a, b
|
||||
b, a = a, b
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn genexpr_closure_and_call_location() {
|
||||
// codegen_comprehension() uses LOC(e) for the closure, the outer CALL and the
|
||||
// implicit .0 load.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def f(parameters):
|
||||
return ((p, type(p)) for p in parameters)
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn implicit_call_genexpr_range() {
|
||||
// A sole unparenthesized generator expression argument spans the call
|
||||
// parentheses.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def implicit():
|
||||
return list(x for x in range(10))
|
||||
|
||||
def explicit():
|
||||
return list((x for x in range(10)))
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn implicit_call_genexpr_parenthesized_element_range() {
|
||||
// The call parentheses are part of the generator expression range even when
|
||||
// its element starts with a parenthesis.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def bytes_binop():
|
||||
return bytes((x ^ 0x5C) for x in range(256))
|
||||
|
||||
def dict_tuple(d):
|
||||
return dict((v, k) for (k, v) in d.items())
|
||||
|
||||
def plain_tuple_elt(xs):
|
||||
return list((x, y) for x, y in xs)
|
||||
|
||||
def explicit_gen(xs):
|
||||
return list(((x, y) for x, y in xs))
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn genexpr_filter_cleanup_location() {
|
||||
// jump_thread() copies the if_cleanup backedge location, elt_loc, to the
|
||||
// threaded cleanup path.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def simple(names):
|
||||
return (x for x in names if not _ishidden(x))
|
||||
|
||||
def boolop(fields):
|
||||
return (f for f in fields if f.init and not f.kw_only)
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_finally_exception_scaffolding_location() {
|
||||
// codegen_try_finally() emits the exception path scaffolding with NO_LOCATION.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def f(self, node):
|
||||
self.flag = True
|
||||
try:
|
||||
self.body(node)
|
||||
finally:
|
||||
self.flag = False
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adjacent_no_location_entries() {
|
||||
// assemble_location_info() merges adjacent NO_LOCATION instructions into one
|
||||
// linetable entry.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def f(file):
|
||||
if sys.platform == "win32":
|
||||
try:
|
||||
import nt
|
||||
if not nt._supports_virtual_terminal():
|
||||
return False
|
||||
except (ImportError, AttributeError):
|
||||
return False
|
||||
try:
|
||||
return os.isatty(file.fileno())
|
||||
except OSError:
|
||||
return hasattr(file, "isatty") and file.isatty()
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fstring_format_location() {
|
||||
// codegen_formatted_value() emits CONVERT_VALUE and FORMAT_* at
|
||||
// LOC(FormattedValue).
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def simple(self):
|
||||
return f'{self.value}'
|
||||
|
||||
def spec(x):
|
||||
return f'{x!r:>3}'
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn joined_string_literals_location() {
|
||||
// Adjacent f-string literal fragments are one Constant spanning all of them.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def f(a):
|
||||
return (
|
||||
'x'
|
||||
f'y{a}z'
|
||||
'w'
|
||||
)
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decorated_definition_locations() {
|
||||
// Decorators are evaluated first, the closure and the store use LOC(s), and
|
||||
// each decorator CALL uses the decorator location.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def dec(f): return f
|
||||
|
||||
class C:
|
||||
@dec
|
||||
def f(self):
|
||||
yield
|
||||
|
||||
@dec
|
||||
class D:
|
||||
pass
|
||||
|
||||
class E:
|
||||
@dec
|
||||
def g(self, flags: int, /) -> memoryview:
|
||||
raise NotImplementedError
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builtin_any_genexpr_call() {
|
||||
// any(genexpr) is inlined behind a builtin identity check that falls back to
|
||||
// the ordinary call.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def f(xs):
|
||||
return any(x for x in xs)
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builtin_tuple_list_set_genexpr_calls() {
|
||||
// tuple(), list() and set() of a generator expression are inlined behind a
|
||||
// builtin identity check that falls back to the ordinary call.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def tuple_f(xs):
|
||||
return tuple(x for x in xs)
|
||||
|
||||
def list_f(xs):
|
||||
return list(x for x in xs)
|
||||
|
||||
def set_f(xs):
|
||||
return set(x for x in xs)
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn augassign_constant_slice_location() {
|
||||
// codegen_augassign() emits the COPY/COPY/BINARY_OP NB_SUBSCR at LOC(target).
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def aug_const(x, y):
|
||||
x[1:2] += y
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comprehension_list_iterable_location() {
|
||||
// codegen_comprehension_iter() emits GET_ITER at LOC(comp->iter).
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
async def f(i):
|
||||
return i
|
||||
|
||||
async def run_list():
|
||||
return [await c for c in [f(1), f(41)]]
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inlined_comprehension_backedge_location() {
|
||||
// codegen_sync_comprehension_generator() emits the backedges at elt_loc.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
async def f(i):
|
||||
return i
|
||||
|
||||
async def run_list():
|
||||
return [s for c in [f(''), f('abc')] for s in await c]
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inlined_async_comprehension_end_async_for_location() {
|
||||
// codegen_async_comprehension_generator() emits END_ASYNC_FOR at the
|
||||
// comprehension location.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
async def f(it):
|
||||
for i in it:
|
||||
yield i
|
||||
|
||||
async def run_list():
|
||||
return [i + 1 async for i in f([10, 20])]
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn async_for_anext_location() {
|
||||
// codegen_async_for() emits the GET_ANEXT/yield-from scaffolding at LOC(s).
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
async def f(source, buffer):
|
||||
async for i1, i2 in source():
|
||||
buffer.append(i1 + i2)
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn namedexpr_copy_location() {
|
||||
// The COPY of a named expression is at its location, between the value and
|
||||
// the target.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def outer():
|
||||
a = 10
|
||||
def spam():
|
||||
nonlocal a
|
||||
(a := 20)
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_cleanup_excludes_not_taken() {
|
||||
// The NOT_TAKEN after the with body is outside the exception table entries.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def f(c):
|
||||
with c:
|
||||
x = 1
|
||||
y = 2
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn async_for_end_async_for() {
|
||||
// END_ASYNC_FOR jumps back to the END_SEND of its awaitable loop.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
async def f(xs):
|
||||
async for x in xs:
|
||||
pass
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn genexpr_prefix() {
|
||||
// The outermost iterator is acquired before RETURN_GENERATOR, outside the
|
||||
// StopIteration handler.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def f(xs):
|
||||
return (x for x in xs)
|
||||
|
||||
async def g(xs):
|
||||
return (x async for x in xs)
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn yield_from_and_await() {
|
||||
// GET_ITER and GET_AWAITABLE leave the iterator pair that SEND uses.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def f(xs):
|
||||
yield from xs
|
||||
|
||||
async def g(xs):
|
||||
return await xs
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comprehension_collection_depth() {
|
||||
// The collection depth counts the slots of every nested iterator.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def f(xs, ys):
|
||||
return [x for x in xs for y in ys]
|
||||
|
||||
async def g(xs, ys):
|
||||
return [x async for x in xs for y in ys]
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn import_flags() {
|
||||
// The IMPORT_NAME oparg flags depend on the scope and the enclosing handlers.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
import sample
|
||||
from sample import member
|
||||
from sample import *
|
||||
|
||||
def f():
|
||||
import sample
|
||||
|
||||
class C:
|
||||
from sample import member
|
||||
|
||||
try:
|
||||
import sample
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
operation()
|
||||
except Exception:
|
||||
import sample
|
||||
|
||||
try:
|
||||
operation()
|
||||
finally:
|
||||
import sample
|
||||
|
||||
with manager:
|
||||
import sample
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loop_return_cleanup() {
|
||||
// A return pops two slots for a for loop and one for an async for loop.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
def f(xs):
|
||||
for x in xs:
|
||||
return x
|
||||
|
||||
async def g(xs):
|
||||
async for x in xs:
|
||||
return x
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_set_async_genexpr_calls() {
|
||||
// list() and set() of an async generator expression stay ordinary calls.
|
||||
assert_dis_snapshot!(
|
||||
positions: r#"
|
||||
async def f(xs):
|
||||
return list(x async for x in xs)
|
||||
|
||||
async def g(xs):
|
||||
return set(x async for x in xs)
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
Generated
+109
@@ -0,0 +1,109 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef f(file):\n if sys.platform == \"win32\":\n try:\n import nt\n if not nt._supports_virtual_terminal():\n return False\n except (ImportError, AttributeError):\n return False\n try:\n return os.isatty(file.fileno())\n except OSError:\n return hasattr(file, \"isatty\") and file.isatty()\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-12:56 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-12:56 MAKE_FUNCTION
|
||||
1:0-12:56 STORE_NAME 0 (f)
|
||||
1:0-12:56 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-12:56 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:7-2:10 LOAD_GLOBAL 0 (sys)
|
||||
2:7-2:19 LOAD_ATTR 2 (platform)
|
||||
2:23-2:30 LOAD_CONST 0 ('win32')
|
||||
2:7-2:30 COMPARE_OP 88 (bool(==))
|
||||
2:7-2:30 POP_JUMP_IF_FALSE 31 (to L4)
|
||||
2:7-2:30 NOT_TAKEN
|
||||
|
||||
3:8-8:24 NOP
|
||||
|
||||
4:12-4:21 L1: LOAD_SMALL_INT 0
|
||||
4:12-4:21 LOAD_COMMON_CONSTANT 7 (None)
|
||||
4:12-4:21 IMPORT_NAME 10 (nt + eager)
|
||||
4:12-4:21 STORE_FAST 1 (nt)
|
||||
|
||||
5:19-5:21 LOAD_FAST_BORROW 1 (nt)
|
||||
5:19-5:48 LOAD_ATTR 7 (_supports_virtual_terminal + NULL|self)
|
||||
5:19-5:50 CALL 0
|
||||
5:19-5:50 TO_BOOL
|
||||
5:19-5:50 POP_JUMP_IF_TRUE 3 (to L3)
|
||||
5:19-5:50 L2: NOT_TAKEN
|
||||
|
||||
6:23-6:28 LOAD_COMMON_CONSTANT 10 (False)
|
||||
6:23-6:28 RETURN_VALUE
|
||||
|
||||
5:19-5:50 L3: NOP
|
||||
|
||||
9:4-12:56 L4: NOP
|
||||
|
||||
10:15-10:17 L5: LOAD_GLOBAL 12 (os)
|
||||
10:15-10:24 LOAD_ATTR 15 (isatty + NULL|self)
|
||||
10:25-10:29 LOAD_FAST_BORROW 0 (file)
|
||||
10:25-10:36 LOAD_ATTR 17 (fileno + NULL|self)
|
||||
10:25-10:38 CALL 0
|
||||
10:15-10:39 CALL 1
|
||||
10:8-10:39 L6: RETURN_VALUE
|
||||
|
||||
-- L7: PUSH_EXC_INFO
|
||||
|
||||
7:16-7:27 LOAD_GLOBAL 8 (ImportError)
|
||||
7:29-7:43 LOAD_GLOBAL 10 (AttributeError)
|
||||
7:15-7:44 BUILD_TUPLE 2
|
||||
7:8-8:24 CHECK_EXC_MATCH
|
||||
7:8-8:24 POP_JUMP_IF_FALSE 5 (to L11)
|
||||
7:8-8:24 L8: NOT_TAKEN
|
||||
7:8-8:24 L9: POP_TOP
|
||||
|
||||
8:19-8:24 L10: POP_EXCEPT
|
||||
8:19-8:24 LOAD_COMMON_CONSTANT 10 (False)
|
||||
8:19-8:24 RETURN_VALUE
|
||||
|
||||
7:8-8:24 L11: RERAISE 0
|
||||
|
||||
-- L12: COPY 3
|
||||
-- POP_EXCEPT
|
||||
-- RERAISE 1
|
||||
-- L13: PUSH_EXC_INFO
|
||||
|
||||
11:11-11:18 LOAD_GLOBAL 18 (OSError)
|
||||
11:4-12:56 CHECK_EXC_MATCH
|
||||
11:4-12:56 POP_JUMP_IF_FALSE 40 (to L20)
|
||||
11:4-12:56 L14: NOT_TAKEN
|
||||
11:4-12:56 L15: POP_TOP
|
||||
|
||||
12:15-12:22 LOAD_GLOBAL 21 (hasattr + NULL)
|
||||
12:23-12:27 LOAD_FAST 0 (file)
|
||||
12:29-12:37 LOAD_CONST 1 ('isatty')
|
||||
12:15-12:38 CALL 2
|
||||
12:15-12:56 COPY 1
|
||||
12:15-12:56 TO_BOOL
|
||||
12:15-12:56 POP_JUMP_IF_FALSE 17 (to L18)
|
||||
12:15-12:56 L16: NOT_TAKEN
|
||||
12:15-12:56 L17: POP_TOP
|
||||
12:43-12:47 LOAD_FAST 0 (file)
|
||||
12:43-12:54 LOAD_ATTR 15 (isatty + NULL|self)
|
||||
12:43-12:56 CALL 0
|
||||
12:8-12:56 L18: SWAP 2
|
||||
12:8-12:56 L19: POP_EXCEPT
|
||||
12:8-12:56 RETURN_VALUE
|
||||
|
||||
11:4-12:56 L20: RERAISE 0
|
||||
|
||||
-- L21: COPY 3
|
||||
-- POP_EXCEPT
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L2 -> L7 [0]
|
||||
L5 to L6 -> L13 [0]
|
||||
L7 to L8 -> L12 [1] lasti
|
||||
L9 to L10 -> L12 [1] lasti
|
||||
L11 to L12 -> L12 [1] lasti
|
||||
L13 to L14 -> L21 [1] lasti
|
||||
L15 to L16 -> L21 [1] lasti
|
||||
L17 to L19 -> L21 [1] lasti
|
||||
L20 to L21 -> L21 [1] lasti
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\n\na = 1\ndef f(\n x: a,\n): ...\n\"#, true)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:4-1:5 LOAD_SMALL_INT 1
|
||||
1:0-1:1 STORE_NAME 0 (a)
|
||||
|
||||
2:0-4:6 LOAD_CONST 1 (<code object __annotate__ at 0xdeadbeef, file "<?>", line 2>)
|
||||
2:0-4:6 MAKE_FUNCTION
|
||||
2:0-4:6 LOAD_CONST 2 (<code object f at 0xdeadbeef, file "<?>", line 2>)
|
||||
2:0-4:6 MAKE_FUNCTION
|
||||
2:0-4:6 SET_FUNCTION_ATTRIBUTE 16 (annotate)
|
||||
2:0-4:6 STORE_NAME 1 (f)
|
||||
2:0-4:6 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:0-4:6 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object __annotate__ at 0xdeadbeef, file "<?>", line 2>:
|
||||
2:0-2:0 RESUME 0
|
||||
2:0-4:6 LOAD_FAST_BORROW 0 (format)
|
||||
2:0-4:6 LOAD_SMALL_INT 2
|
||||
2:0-4:6 COMPARE_OP 132 (>)
|
||||
2:0-4:6 POP_JUMP_IF_FALSE 3 (to L1)
|
||||
2:0-4:6 NOT_TAKEN
|
||||
2:0-4:6 LOAD_COMMON_CONSTANT 1 (NotImplementedError)
|
||||
2:0-4:6 RAISE_VARARGS 1
|
||||
2:0-4:6 L1: LOAD_CONST 1 ('x')
|
||||
|
||||
3:7-3:8 LOAD_GLOBAL 0 (a)
|
||||
|
||||
2:0-4:6 BUILD_MAP 1
|
||||
2:0-4:6 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 2>:
|
||||
2:0-2:0 RESUME 0
|
||||
|
||||
4:3-4:6 LOAD_COMMON_CONSTANT 7 (None)
|
||||
4:3-4:6 RETURN_VALUE
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\n\ndef g():\n def f(x: not (int is int), /): ...\n\"#, true)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-2:38 LOAD_CONST 0 (<code object g at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-2:38 MAKE_FUNCTION
|
||||
1:0-2:38 STORE_NAME 0 (g)
|
||||
1:0-2:38 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-2:38 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object g at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:4-2:38 LOAD_CONST 0 (<code object __annotate__ at 0xdeadbeef, file "<?>", line 2>)
|
||||
2:4-2:38 MAKE_FUNCTION
|
||||
2:4-2:38 LOAD_CONST 1 (<code object f at 0xdeadbeef, file "<?>", line 2>)
|
||||
2:4-2:38 MAKE_FUNCTION
|
||||
2:4-2:38 SET_FUNCTION_ATTRIBUTE 16 (annotate)
|
||||
2:4-2:38 STORE_FAST 0 (f)
|
||||
2:4-2:38 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:4-2:38 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object __annotate__ at 0xdeadbeef, file "<?>", line 2>:
|
||||
2:0-2:0 RESUME 0
|
||||
2:4-2:38 LOAD_FAST_BORROW 0 (format)
|
||||
2:4-2:38 LOAD_SMALL_INT 2
|
||||
2:4-2:38 COMPARE_OP 132 (>)
|
||||
2:4-2:38 POP_JUMP_IF_FALSE 3 (to L1)
|
||||
2:4-2:38 NOT_TAKEN
|
||||
2:4-2:38 LOAD_COMMON_CONSTANT 1 (NotImplementedError)
|
||||
2:4-2:38 RAISE_VARARGS 1
|
||||
2:4-2:38 L1: LOAD_CONST 1 ('x')
|
||||
2:18-2:21 LOAD_GLOBAL 0 (int)
|
||||
2:25-2:28 LOAD_GLOBAL 0 (int)
|
||||
2:13-2:29 IS_OP 1 (is not)
|
||||
2:4-2:38 BUILD_MAP 1
|
||||
2:4-2:38 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 2>:
|
||||
2:0-2:0 RESUME 0
|
||||
2:35-2:38 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:35-2:38 RETURN_VALUE
|
||||
Generated
+19
@@ -0,0 +1,19 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef f():\n assert False, \"x\"\n\"#, true)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-2:21 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-2:21 MAKE_FUNCTION
|
||||
1:0-2:21 STORE_NAME 0 (f)
|
||||
1:0-2:21 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-2:21 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:4-2:21 LOAD_COMMON_CONSTANT 0 (AssertionError)
|
||||
2:18-2:21 LOAD_CONST 1 ('x')
|
||||
2:4-2:21 CALL 0
|
||||
2:11-2:16 RAISE_VARARGS 1
|
||||
Generated
+57
@@ -0,0 +1,57 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\nasync def f(source, buffer):\n async for i1, i2 in source():\n buffer.append(i1 + i2)\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-3:30 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-3:30 MAKE_FUNCTION
|
||||
1:0-3:30 STORE_NAME 0 (f)
|
||||
1:0-3:30 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-3:30 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:?-1:? RETURN_GENERATOR
|
||||
1:?-1:? POP_TOP
|
||||
1:0-1:0 L1: RESUME 0
|
||||
|
||||
2:24-2:30 LOAD_FAST_BORROW 0 (source)
|
||||
2:24-2:30 PUSH_NULL
|
||||
2:24-2:32 CALL 0
|
||||
2:24-2:32 GET_AITER
|
||||
2:4-3:30 L2: GET_ANEXT
|
||||
2:4-3:30 PUSH_NULL
|
||||
2:4-3:30 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:4-3:30 L3: SEND 4 (to L6)
|
||||
2:4-3:30 L4: YIELD_VALUE 1
|
||||
2:4-3:30 L5: RESUME 3
|
||||
2:4-3:30 JUMP_BACKWARD_NO_INTERRUPT 6 (to L3)
|
||||
2:4-3:30 L6: END_SEND
|
||||
2:4-3:30 L7: NOT_TAKEN
|
||||
2:14-2:20 UNPACK_SEQUENCE 2
|
||||
2:14-2:16 STORE_FAST_STORE_FAST 35 (i1, i2)
|
||||
|
||||
3:8-3:14 LOAD_FAST_BORROW 1 (buffer)
|
||||
3:8-3:21 LOAD_ATTR 1 (append + NULL|self)
|
||||
3:22-3:24 LOAD_FAST_BORROW_LOAD_FAST_BORROW 35 (i1, i2)
|
||||
3:22-3:29 BINARY_OP 0 (+)
|
||||
3:8-3:30 CALL 1
|
||||
3:8-3:30 POP_TOP
|
||||
3:8-3:30 JUMP_BACKWARD 39 (to L2)
|
||||
|
||||
2:4-3:30 L8: CLEANUP_THROW
|
||||
2:4-3:30 L9: JUMP_BACKWARD_NO_INTERRUPT 32 (to L6)
|
||||
2:24-2:32 L10: END_ASYNC_FOR 33 (from L6)
|
||||
2:24-2:32 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:24-2:32 RETURN_VALUE
|
||||
|
||||
-- L11: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L2 -> L11 [0] lasti
|
||||
L2 to L4 -> L10 [1]
|
||||
L4 to L5 -> L8 [4]
|
||||
L5 to L7 -> L10 [1]
|
||||
L7 to L8 -> L11 [0] lasti
|
||||
L8 to L9 -> L10 [1]
|
||||
L10 to L11 -> L11 [0] lasti
|
||||
Generated
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\nasync def f(xs):\n async for x in xs:\n pass\n\"#, true)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-3:12 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-3:12 MAKE_FUNCTION
|
||||
1:0-3:12 STORE_NAME 0 (f)
|
||||
1:0-3:12 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-3:12 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:?-1:? RETURN_GENERATOR
|
||||
1:?-1:? POP_TOP
|
||||
1:0-1:0 L1: RESUME 0
|
||||
|
||||
2:19-2:21 LOAD_FAST_BORROW 0 (xs)
|
||||
2:19-2:21 GET_AITER
|
||||
2:4-3:12 L2: GET_ANEXT
|
||||
2:4-3:12 PUSH_NULL
|
||||
2:4-3:12 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:4-3:12 L3: SEND 4 (to L6)
|
||||
2:4-3:12 L4: YIELD_VALUE 1
|
||||
2:4-3:12 L5: RESUME 3
|
||||
2:4-3:12 JUMP_BACKWARD_NO_INTERRUPT 6 (to L3)
|
||||
2:4-3:12 L6: END_SEND
|
||||
2:4-3:12 L7: NOT_TAKEN
|
||||
2:14-2:15 STORE_FAST 1 (x)
|
||||
|
||||
3:8-3:12 JUMP_BACKWARD 14 (to L2)
|
||||
|
||||
2:4-3:12 L8: CLEANUP_THROW
|
||||
2:4-3:12 L9: JUMP_BACKWARD_NO_INTERRUPT 7 (to L6)
|
||||
2:19-2:21 L10: END_ASYNC_FOR 8 (from L6)
|
||||
2:19-2:21 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:19-2:21 RETURN_VALUE
|
||||
|
||||
-- L11: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L2 -> L11 [0] lasti
|
||||
L2 to L4 -> L10 [1]
|
||||
L4 to L5 -> L8 [4]
|
||||
L5 to L7 -> L10 [1]
|
||||
L7 to L8 -> L11 [0] lasti
|
||||
L8 to L9 -> L10 [1]
|
||||
L10 to L11 -> L11 [0] lasti
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef aug_const(x, y):\n x[1:2] += y\n\"#, true)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-2:15 LOAD_CONST 0 (<code object aug_const at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-2:15 MAKE_FUNCTION
|
||||
1:0-2:15 STORE_NAME 0 (aug_const)
|
||||
1:0-2:15 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-2:15 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object aug_const at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:4-2:5 LOAD_FAST_BORROW 0 (x)
|
||||
2:6-2:9 LOAD_CONST 0 (slice(1, 2, None))
|
||||
2:4-2:10 COPY 2
|
||||
2:4-2:10 COPY 2
|
||||
2:4-2:10 BINARY_OP 26 ([])
|
||||
2:14-2:15 LOAD_FAST_BORROW 1 (y)
|
||||
2:4-2:15 BINARY_OP 13 (+=)
|
||||
2:4-2:10 SWAP 3
|
||||
2:4-2:10 SWAP 2
|
||||
2:4-2:10 STORE_SUBSCR
|
||||
2:4-2:10 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:4-2:10 RETURN_VALUE
|
||||
+1
-1
@@ -11,7 +11,7 @@ expression: "dis(r#\"\ndef f(one: int):\n int.new_attr: int\n [list][0].ne
|
||||
MAKE_FUNCTION
|
||||
SET_FUNCTION_ATTRIBUTE 16 (annotate)
|
||||
STORE_NAME 0 (f)
|
||||
LOAD_CONST 2 (None)
|
||||
LOAD_COMMON_CONSTANT 7 (None)
|
||||
RETURN_VALUE
|
||||
|
||||
Disassembly of <code object __annotate__ at 0xdeadbeef, file "<?>", line 1>:
|
||||
|
||||
Generated
+70
@@ -0,0 +1,70 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef f(xs):\n return any(x for x in xs)\n\"#, true)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-2:29 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-2:29 MAKE_FUNCTION
|
||||
1:0-2:29 STORE_NAME 0 (f)
|
||||
1:0-2:29 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-2:29 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:11-2:14 LOAD_GLOBAL 0 (any)
|
||||
2:11-2:14 COPY 1
|
||||
2:11-2:14 LOAD_COMMON_CONSTANT 4 (<built-in function any>)
|
||||
2:11-2:14 IS_OP 0 (is)
|
||||
2:11-2:14 POP_JUMP_IF_FALSE 28 (to L4)
|
||||
2:11-2:14 NOT_TAKEN
|
||||
2:11-2:14 POP_TOP
|
||||
2:14-2:29 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 2>)
|
||||
2:14-2:29 MAKE_FUNCTION
|
||||
2:26-2:28 LOAD_FAST_BORROW 0 (xs)
|
||||
2:14-2:29 CALL 0
|
||||
2:11-2:14 PUSH_NULL
|
||||
2:11-2:14 L1: FOR_ITER 12 (to L3)
|
||||
2:11-2:14 TO_BOOL
|
||||
2:11-2:14 POP_JUMP_IF_TRUE 3 (to L2)
|
||||
2:11-2:14 NOT_TAKEN
|
||||
2:11-2:14 JUMP_BACKWARD 11 (to L1)
|
||||
2:11-2:14 L2: POP_ITER
|
||||
2:11-2:14 LOAD_COMMON_CONSTANT 9 (True)
|
||||
2:4-2:29 RETURN_VALUE
|
||||
2:11-2:14 L3: END_FOR
|
||||
2:11-2:14 POP_ITER
|
||||
2:11-2:14 LOAD_COMMON_CONSTANT 10 (False)
|
||||
2:4-2:29 RETURN_VALUE
|
||||
2:11-2:14 L4: PUSH_NULL
|
||||
2:14-2:29 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 2>)
|
||||
2:14-2:29 MAKE_FUNCTION
|
||||
2:26-2:28 LOAD_FAST_BORROW 0 (xs)
|
||||
2:14-2:29 CALL 0
|
||||
2:11-2:29 CALL 1
|
||||
2:4-2:29 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object <genexpr> at 0xdeadbeef, file "<?>", line 2>:
|
||||
-- RESUME 4
|
||||
|
||||
2:26-2:28 LOAD_FAST 0 (.0)
|
||||
2:26-2:28 GET_ITER 0
|
||||
2:?-2:? RETURN_GENERATOR
|
||||
2:?-2:? POP_TOP
|
||||
2:0-2:0 L1: RESUME 0
|
||||
2:26-2:28 L2: FOR_ITER 7 (to L3)
|
||||
2:21-2:22 STORE_FAST_LOAD_FAST 17 (x, x)
|
||||
2:15-2:16 YIELD_VALUE 0
|
||||
2:15-2:16 RESUME 9
|
||||
2:15-2:16 POP_TOP
|
||||
2:15-2:16 JUMP_BACKWARD 9 (to L2)
|
||||
2:26-2:28 L3: END_FOR
|
||||
2:26-2:28 POP_ITER
|
||||
2:26-2:28 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:26-2:28 RETURN_VALUE
|
||||
|
||||
-- L4: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L4 -> L4 [2] lasti
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef tuple_f(xs):\n return tuple(x for x in xs)\n\ndef list_f(xs):\n return list(x for x in xs)\n\ndef set_f(xs):\n return set(x for x in xs)\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-2:31 LOAD_CONST 0 (<code object tuple_f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-2:31 MAKE_FUNCTION
|
||||
1:0-2:31 STORE_NAME 0 (tuple_f)
|
||||
|
||||
4:0-5:30 LOAD_CONST 1 (<code object list_f at 0xdeadbeef, file "<?>", line 4>)
|
||||
4:0-5:30 MAKE_FUNCTION
|
||||
4:0-5:30 STORE_NAME 1 (list_f)
|
||||
|
||||
7:0-8:29 LOAD_CONST 2 (<code object set_f at 0xdeadbeef, file "<?>", line 7>)
|
||||
7:0-8:29 MAKE_FUNCTION
|
||||
7:0-8:29 STORE_NAME 2 (set_f)
|
||||
7:0-8:29 LOAD_COMMON_CONSTANT 7 (None)
|
||||
7:0-8:29 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object tuple_f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:11-2:16 LOAD_GLOBAL 0 (tuple)
|
||||
2:11-2:16 COPY 1
|
||||
2:11-2:16 LOAD_COMMON_CONSTANT 2 (tuple)
|
||||
2:11-2:16 IS_OP 0 (is)
|
||||
2:11-2:16 POP_JUMP_IF_FALSE 20 (to L3)
|
||||
2:11-2:16 NOT_TAKEN
|
||||
2:11-2:16 POP_TOP
|
||||
2:11-2:16 BUILD_LIST 0
|
||||
2:16-2:31 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 2>)
|
||||
2:16-2:31 MAKE_FUNCTION
|
||||
2:28-2:30 LOAD_FAST_BORROW 0 (xs)
|
||||
2:16-2:31 CALL 0
|
||||
2:11-2:16 PUSH_NULL
|
||||
2:11-2:16 L1: FOR_ITER 3 (to L2)
|
||||
2:11-2:16 LIST_APPEND 3
|
||||
2:11-2:16 JUMP_BACKWARD 5 (to L1)
|
||||
2:11-2:16 L2: END_FOR
|
||||
2:11-2:16 POP_ITER
|
||||
2:11-2:16 CALL_INTRINSIC_1 6 (INTRINSIC_LIST_TO_TUPLE)
|
||||
2:4-2:31 RETURN_VALUE
|
||||
2:11-2:16 L3: PUSH_NULL
|
||||
2:16-2:31 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 2>)
|
||||
2:16-2:31 MAKE_FUNCTION
|
||||
2:28-2:30 LOAD_FAST_BORROW 0 (xs)
|
||||
2:16-2:31 CALL 0
|
||||
2:11-2:31 CALL 1
|
||||
2:4-2:31 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object <genexpr> at 0xdeadbeef, file "<?>", line 2>:
|
||||
-- RESUME 4
|
||||
|
||||
2:28-2:30 LOAD_FAST 0 (.0)
|
||||
2:28-2:30 GET_ITER 0
|
||||
2:?-2:? RETURN_GENERATOR
|
||||
2:?-2:? POP_TOP
|
||||
2:0-2:0 L1: RESUME 0
|
||||
2:28-2:30 L2: FOR_ITER 7 (to L3)
|
||||
2:23-2:24 STORE_FAST_LOAD_FAST 17 (x, x)
|
||||
2:17-2:18 YIELD_VALUE 0
|
||||
2:17-2:18 RESUME 9
|
||||
2:17-2:18 POP_TOP
|
||||
2:17-2:18 JUMP_BACKWARD 9 (to L2)
|
||||
2:28-2:30 L3: END_FOR
|
||||
2:28-2:30 POP_ITER
|
||||
2:28-2:30 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:28-2:30 RETURN_VALUE
|
||||
|
||||
-- L4: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L4 -> L4 [2] lasti
|
||||
|
||||
Disassembly of <code object list_f at 0xdeadbeef, file "<?>", line 4>:
|
||||
4:0-4:0 RESUME 0
|
||||
|
||||
5:11-5:15 LOAD_GLOBAL 0 (list)
|
||||
5:11-5:15 COPY 1
|
||||
5:11-5:15 LOAD_COMMON_CONSTANT 5 (list)
|
||||
5:11-5:15 IS_OP 0 (is)
|
||||
5:11-5:15 POP_JUMP_IF_FALSE 19 (to L3)
|
||||
5:11-5:15 NOT_TAKEN
|
||||
5:11-5:15 POP_TOP
|
||||
5:11-5:15 BUILD_LIST 0
|
||||
5:15-5:30 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 5>)
|
||||
5:15-5:30 MAKE_FUNCTION
|
||||
5:27-5:29 LOAD_FAST_BORROW 0 (xs)
|
||||
5:15-5:30 CALL 0
|
||||
5:11-5:15 PUSH_NULL
|
||||
5:11-5:15 L1: FOR_ITER 3 (to L2)
|
||||
5:11-5:15 LIST_APPEND 3
|
||||
5:11-5:15 JUMP_BACKWARD 5 (to L1)
|
||||
5:11-5:15 L2: END_FOR
|
||||
5:11-5:15 POP_ITER
|
||||
5:4-5:30 RETURN_VALUE
|
||||
5:11-5:15 L3: PUSH_NULL
|
||||
5:15-5:30 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 5>)
|
||||
5:15-5:30 MAKE_FUNCTION
|
||||
5:27-5:29 LOAD_FAST_BORROW 0 (xs)
|
||||
5:15-5:30 CALL 0
|
||||
5:11-5:30 CALL 1
|
||||
5:4-5:30 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object <genexpr> at 0xdeadbeef, file "<?>", line 5>:
|
||||
-- RESUME 4
|
||||
|
||||
5:27-5:29 LOAD_FAST 0 (.0)
|
||||
5:27-5:29 GET_ITER 0
|
||||
5:?-5:? RETURN_GENERATOR
|
||||
5:?-5:? POP_TOP
|
||||
5:0-5:0 L1: RESUME 0
|
||||
5:27-5:29 L2: FOR_ITER 7 (to L3)
|
||||
5:22-5:23 STORE_FAST_LOAD_FAST 17 (x, x)
|
||||
5:16-5:17 YIELD_VALUE 0
|
||||
5:16-5:17 RESUME 9
|
||||
5:16-5:17 POP_TOP
|
||||
5:16-5:17 JUMP_BACKWARD 9 (to L2)
|
||||
5:27-5:29 L3: END_FOR
|
||||
5:27-5:29 POP_ITER
|
||||
5:27-5:29 LOAD_COMMON_CONSTANT 7 (None)
|
||||
5:27-5:29 RETURN_VALUE
|
||||
|
||||
-- L4: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L4 -> L4 [2] lasti
|
||||
|
||||
Disassembly of <code object set_f at 0xdeadbeef, file "<?>", line 7>:
|
||||
7:0-7:0 RESUME 0
|
||||
|
||||
8:11-8:14 LOAD_GLOBAL 0 (set)
|
||||
8:11-8:14 COPY 1
|
||||
8:11-8:14 LOAD_COMMON_CONSTANT 6 (set)
|
||||
8:11-8:14 IS_OP 0 (is)
|
||||
8:11-8:14 POP_JUMP_IF_FALSE 19 (to L3)
|
||||
8:11-8:14 NOT_TAKEN
|
||||
8:11-8:14 POP_TOP
|
||||
8:11-8:14 BUILD_SET 0
|
||||
8:14-8:29 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 8>)
|
||||
8:14-8:29 MAKE_FUNCTION
|
||||
8:26-8:28 LOAD_FAST_BORROW 0 (xs)
|
||||
8:14-8:29 CALL 0
|
||||
8:11-8:14 PUSH_NULL
|
||||
8:11-8:14 L1: FOR_ITER 3 (to L2)
|
||||
8:11-8:14 SET_ADD 3
|
||||
8:11-8:14 JUMP_BACKWARD 5 (to L1)
|
||||
8:11-8:14 L2: END_FOR
|
||||
8:11-8:14 POP_ITER
|
||||
8:4-8:29 RETURN_VALUE
|
||||
8:11-8:14 L3: PUSH_NULL
|
||||
8:14-8:29 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 8>)
|
||||
8:14-8:29 MAKE_FUNCTION
|
||||
8:26-8:28 LOAD_FAST_BORROW 0 (xs)
|
||||
8:14-8:29 CALL 0
|
||||
8:11-8:29 CALL 1
|
||||
8:4-8:29 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object <genexpr> at 0xdeadbeef, file "<?>", line 8>:
|
||||
-- RESUME 4
|
||||
|
||||
8:26-8:28 LOAD_FAST 0 (.0)
|
||||
8:26-8:28 GET_ITER 0
|
||||
8:?-8:? RETURN_GENERATOR
|
||||
8:?-8:? POP_TOP
|
||||
8:0-8:0 L1: RESUME 0
|
||||
8:26-8:28 L2: FOR_ITER 7 (to L3)
|
||||
8:21-8:22 STORE_FAST_LOAD_FAST 17 (x, x)
|
||||
8:15-8:16 YIELD_VALUE 0
|
||||
8:15-8:16 RESUME 9
|
||||
8:15-8:16 POP_TOP
|
||||
8:15-8:16 JUMP_BACKWARD 9 (to L2)
|
||||
8:26-8:28 L3: END_FOR
|
||||
8:26-8:28 POP_ITER
|
||||
8:26-8:28 LOAD_COMMON_CONSTANT 7 (None)
|
||||
8:26-8:28 RETURN_VALUE
|
||||
|
||||
-- L4: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L4 -> L4 [2] lasti
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\n\nclass C:\n \"doc\"\n x: int\n\"#, true)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-3:10 LOAD_BUILD_CLASS
|
||||
1:0-3:10 PUSH_NULL
|
||||
1:0-3:10 LOAD_CONST 0 (<code object C at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-3:10 MAKE_FUNCTION
|
||||
1:0-3:10 LOAD_CONST 1 ('C')
|
||||
1:0-3:10 CALL 2
|
||||
1:0-3:10 STORE_NAME 0 (C)
|
||||
1:0-3:10 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-3:10 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object C at 0xdeadbeef, file "<?>", line 1>:
|
||||
-- MAKE_CELL 0 (__classdict__)
|
||||
|
||||
1:0-1:0 RESUME 0
|
||||
1:0-1:0 LOAD_NAME 0 (__name__)
|
||||
1:0-1:0 STORE_NAME 1 (__module__)
|
||||
1:0-1:0 LOAD_CONST 0 ('C')
|
||||
1:0-1:0 STORE_NAME 2 (__qualname__)
|
||||
1:0-1:0 LOAD_SMALL_INT 1
|
||||
1:0-1:0 STORE_NAME 3 (__firstlineno__)
|
||||
1:0-1:0 LOAD_LOCALS
|
||||
1:0-1:0 STORE_DEREF 0 (__classdict__)
|
||||
|
||||
2:4-2:9 LOAD_CONST 1 ('doc')
|
||||
2:4-2:9 STORE_NAME 4 (__doc__)
|
||||
|
||||
1:0-1:0 LOAD_FAST_BORROW 0 (__classdict__)
|
||||
1:0-1:0 BUILD_TUPLE 1
|
||||
1:0-1:0 LOAD_CONST 2 (<code object __annotate__ at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-1:0 MAKE_FUNCTION
|
||||
1:0-1:0 SET_FUNCTION_ATTRIBUTE 8 (closure)
|
||||
1:0-1:0 STORE_NAME 5 (__annotate_func__)
|
||||
1:0-1:0 LOAD_CONST 3 (())
|
||||
1:0-1:0 STORE_NAME 6 (__static_attributes__)
|
||||
1:0-1:0 LOAD_FAST_BORROW 0 (__classdict__)
|
||||
1:0-1:0 STORE_NAME 7 (__classdictcell__)
|
||||
1:0-1:0 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-1:0 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object __annotate__ at 0xdeadbeef, file "<?>", line 1>:
|
||||
-- COPY_FREE_VARS 1
|
||||
|
||||
1:0-1:0 RESUME 0
|
||||
1:0-1:0 LOAD_FAST_BORROW 0 (format)
|
||||
1:0-1:0 LOAD_SMALL_INT 2
|
||||
1:0-1:0 COMPARE_OP 132 (>)
|
||||
1:0-1:0 POP_JUMP_IF_FALSE 3 (to L1)
|
||||
1:0-1:0 NOT_TAKEN
|
||||
1:0-1:0 LOAD_COMMON_CONSTANT 1 (NotImplementedError)
|
||||
1:0-1:0 RAISE_VARARGS 1
|
||||
1:0-1:0 L1: BUILD_MAP 0
|
||||
|
||||
3:7-3:10 LOAD_DEREF 1 (__classdict__)
|
||||
3:7-3:10 LOAD_FROM_DICT_OR_GLOBALS 0 (int)
|
||||
3:4-3:10 COPY 2
|
||||
3:4-3:10 LOAD_CONST 1 ('x')
|
||||
|
||||
1:0-1:0 STORE_SUBSCR
|
||||
1:0-1:0 RETURN_VALUE
|
||||
Generated
+110
@@ -0,0 +1,110 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef f(xs, ys):\n return [x for x in xs for y in ys]\n\nasync def g(xs, ys):\n return [x async for x in xs for y in ys]\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-2:38 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-2:38 MAKE_FUNCTION
|
||||
1:0-2:38 STORE_NAME 0 (f)
|
||||
|
||||
4:0-5:44 LOAD_CONST 1 (<code object g at 0xdeadbeef, file "<?>", line 4>)
|
||||
4:0-5:44 MAKE_FUNCTION
|
||||
4:0-5:44 STORE_NAME 1 (g)
|
||||
4:0-5:44 LOAD_COMMON_CONSTANT 7 (None)
|
||||
4:0-5:44 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:23-2:25 LOAD_FAST 0 (xs)
|
||||
2:11-2:38 LOAD_FAST_AND_CLEAR 2 (x)
|
||||
2:11-2:38 LOAD_FAST_AND_CLEAR 3 (y)
|
||||
2:11-2:38 SWAP 3
|
||||
2:11-2:38 L1: BUILD_LIST 0
|
||||
2:11-2:38 SWAP 2
|
||||
2:23-2:25 GET_ITER 0
|
||||
2:23-2:25 L2: FOR_ITER 13 (to L5)
|
||||
2:18-2:19 STORE_FAST_LOAD_FAST 33 (x, ys)
|
||||
2:35-2:37 GET_ITER 0
|
||||
2:35-2:37 L3: FOR_ITER 4 (to L4)
|
||||
2:30-2:31 STORE_FAST_LOAD_FAST 50 (y, x)
|
||||
2:12-2:13 LIST_APPEND 5
|
||||
2:12-2:13 JUMP_BACKWARD 6 (to L3)
|
||||
2:35-2:37 L4: END_FOR
|
||||
2:35-2:37 POP_ITER
|
||||
2:12-2:13 JUMP_BACKWARD 15 (to L2)
|
||||
2:23-2:25 L5: END_FOR
|
||||
2:23-2:25 POP_ITER
|
||||
2:11-2:38 L6: SWAP 3
|
||||
2:11-2:38 STORE_FAST 3 (y)
|
||||
2:11-2:38 STORE_FAST 2 (x)
|
||||
2:4-2:38 RETURN_VALUE
|
||||
|
||||
-- L7: SWAP 2
|
||||
-- POP_TOP
|
||||
|
||||
2:11-2:38 SWAP 3
|
||||
2:11-2:38 STORE_FAST 3 (y)
|
||||
2:11-2:38 STORE_FAST 2 (x)
|
||||
2:11-2:38 RERAISE 0
|
||||
ExceptionTable:
|
||||
L1 to L6 -> L7 [3]
|
||||
|
||||
Disassembly of <code object g at 0xdeadbeef, file "<?>", line 4>:
|
||||
4:?-4:? RETURN_GENERATOR
|
||||
4:?-4:? POP_TOP
|
||||
4:0-4:0 L1: RESUME 0
|
||||
|
||||
5:29-5:31 LOAD_FAST_BORROW 0 (xs)
|
||||
5:11-5:44 LOAD_FAST_AND_CLEAR 2 (x)
|
||||
5:11-5:44 LOAD_FAST_AND_CLEAR 3 (y)
|
||||
5:11-5:44 SWAP 3
|
||||
5:11-5:44 L2: BUILD_LIST 0
|
||||
5:11-5:44 SWAP 2
|
||||
5:29-5:31 GET_AITER
|
||||
5:11-5:44 L3: GET_ANEXT
|
||||
5:11-5:44 PUSH_NULL
|
||||
5:11-5:44 LOAD_COMMON_CONSTANT 7 (None)
|
||||
5:11-5:44 L4: SEND 4 (to L7)
|
||||
5:11-5:44 L5: YIELD_VALUE 1
|
||||
5:11-5:44 L6: RESUME 3
|
||||
5:11-5:44 JUMP_BACKWARD_NO_INTERRUPT 6 (to L4)
|
||||
5:11-5:44 L7: END_SEND
|
||||
5:24-5:25 L8: STORE_FAST_LOAD_FAST 33 (x, ys)
|
||||
5:41-5:43 GET_ITER 0
|
||||
5:41-5:43 L9: FOR_ITER 4 (to L10)
|
||||
5:36-5:37 STORE_FAST_LOAD_FAST 50 (y, x)
|
||||
5:12-5:13 LIST_APPEND 4
|
||||
5:12-5:13 JUMP_BACKWARD 6 (to L9)
|
||||
5:41-5:43 L10: END_FOR
|
||||
5:41-5:43 POP_ITER
|
||||
5:12-5:13 JUMP_BACKWARD 23 (to L3)
|
||||
5:11-5:44 L11: CLEANUP_THROW
|
||||
5:11-5:44 L12: JUMP_BACKWARD_NO_INTERRUPT 16 (to L7)
|
||||
5:11-5:44 L13: END_ASYNC_FOR 17 (from L7)
|
||||
5:11-5:44 L14: SWAP 3
|
||||
5:11-5:44 STORE_FAST 3 (y)
|
||||
5:11-5:44 STORE_FAST 2 (x)
|
||||
5:4-5:44 RETURN_VALUE
|
||||
|
||||
-- L15: SWAP 2
|
||||
-- POP_TOP
|
||||
|
||||
5:11-5:44 SWAP 3
|
||||
5:11-5:44 STORE_FAST 3 (y)
|
||||
5:11-5:44 STORE_FAST 2 (x)
|
||||
5:11-5:44 RERAISE 0
|
||||
|
||||
-- L16: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L2 -> L16 [0] lasti
|
||||
L2 to L3 -> L15 [3]
|
||||
L3 to L5 -> L13 [4]
|
||||
L5 to L6 -> L11 [7]
|
||||
L6 to L8 -> L13 [4]
|
||||
L8 to L11 -> L15 [3]
|
||||
L11 to L12 -> L13 [4]
|
||||
L13 to L14 -> L15 [3]
|
||||
L14 to L16 -> L16 [0] lasti
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\nasync def f(i):\n return i\n\nasync def run_list():\n return [await c for c in [f(1), f(41)]]\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-2:12 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-2:12 MAKE_FUNCTION
|
||||
1:0-2:12 STORE_NAME 0 (f)
|
||||
|
||||
4:0-5:43 LOAD_CONST 1 (<code object run_list at 0xdeadbeef, file "<?>", line 4>)
|
||||
4:0-5:43 MAKE_FUNCTION
|
||||
4:0-5:43 STORE_NAME 1 (run_list)
|
||||
4:0-5:43 LOAD_COMMON_CONSTANT 7 (None)
|
||||
4:0-5:43 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:?-1:? RETURN_GENERATOR
|
||||
1:?-1:? POP_TOP
|
||||
1:0-1:0 L1: RESUME 0
|
||||
|
||||
2:11-2:12 LOAD_FAST_BORROW 0 (i)
|
||||
2:4-2:12 RETURN_VALUE
|
||||
|
||||
-- L2: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L2 -> L2 [0] lasti
|
||||
|
||||
Disassembly of <code object run_list at 0xdeadbeef, file "<?>", line 4>:
|
||||
4:?-4:? RETURN_GENERATOR
|
||||
4:?-4:? POP_TOP
|
||||
4:0-4:0 L1: RESUME 0
|
||||
|
||||
5:30-5:31 LOAD_GLOBAL 1 (f + NULL)
|
||||
5:32-5:33 LOAD_SMALL_INT 1
|
||||
5:30-5:34 CALL 1
|
||||
5:36-5:37 LOAD_GLOBAL 1 (f + NULL)
|
||||
5:38-5:40 LOAD_SMALL_INT 41
|
||||
5:36-5:41 CALL 1
|
||||
5:29-5:42 BUILD_LIST 2
|
||||
5:11-5:43 LOAD_FAST_AND_CLEAR 0 (c)
|
||||
5:11-5:43 SWAP 2
|
||||
5:11-5:43 L2: BUILD_LIST 0
|
||||
5:11-5:43 SWAP 2
|
||||
5:29-5:42 GET_ITER 0
|
||||
5:29-5:42 L3: FOR_ITER 14 (to L8)
|
||||
5:24-5:25 STORE_FAST_LOAD_FAST 0 (c, c)
|
||||
5:12-5:19 GET_AWAITABLE 0
|
||||
5:12-5:19 PUSH_NULL
|
||||
5:12-5:19 LOAD_COMMON_CONSTANT 7 (None)
|
||||
5:12-5:19 L4: SEND 4 (to L7)
|
||||
5:12-5:19 L5: YIELD_VALUE 1
|
||||
5:12-5:19 L6: RESUME 3
|
||||
5:12-5:19 JUMP_BACKWARD_NO_INTERRUPT 6 (to L4)
|
||||
5:12-5:19 L7: END_SEND
|
||||
5:12-5:19 LIST_APPEND 3
|
||||
5:12-5:19 JUMP_BACKWARD 16 (to L3)
|
||||
5:29-5:42 L8: END_FOR
|
||||
5:29-5:42 POP_ITER
|
||||
5:11-5:43 L9: SWAP 2
|
||||
5:11-5:43 STORE_FAST 0 (c)
|
||||
5:4-5:43 RETURN_VALUE
|
||||
5:12-5:19 L10: CLEANUP_THROW
|
||||
5:12-5:19 L11: JUMP_BACKWARD_NO_INTERRUPT 11 (to L7)
|
||||
|
||||
-- L12: SWAP 2
|
||||
-- POP_TOP
|
||||
|
||||
5:11-5:43 SWAP 2
|
||||
5:11-5:43 STORE_FAST 0 (c)
|
||||
5:11-5:43 RERAISE 0
|
||||
|
||||
-- L13: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L2 -> L13 [0] lasti
|
||||
L2 to L5 -> L12 [2]
|
||||
L5 to L6 -> L10 [7]
|
||||
L6 to L9 -> L12 [2]
|
||||
L9 to L10 -> L13 [0] lasti
|
||||
L10 to L11 -> L12 [2]
|
||||
L12 to L13 -> L13 [0] lasti
|
||||
+2
-2
@@ -5,7 +5,7 @@ expression: "dis(r#\"\nx = not True\n\"#)"
|
||||
---
|
||||
0 RESUME 0
|
||||
|
||||
1 LOAD_CONST 2 (False)
|
||||
1 LOAD_COMMON_CONSTANT 10 (False)
|
||||
STORE_NAME 0 (x)
|
||||
LOAD_CONST 1 (None)
|
||||
LOAD_COMMON_CONSTANT 7 (None)
|
||||
RETURN_VALUE
|
||||
+1
-1
@@ -7,5 +7,5 @@ expression: "dis(r#\"\nif 1:\n pass\n\"#)"
|
||||
|
||||
1 NOP
|
||||
|
||||
2 LOAD_CONST 1 (None)
|
||||
2 LOAD_COMMON_CONSTANT 7 (None)
|
||||
RETURN_VALUE
|
||||
Generated
+164
@@ -0,0 +1,164 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef dec(f): return f\n\nclass C:\n @dec\n def f(self):\n yield\n\n@dec\nclass D:\n pass\n\nclass E:\n @dec\n def g(self, flags: int, /) -> memoryview:\n raise NotImplementedError\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-1:20 LOAD_CONST 0 (<code object dec at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-1:20 MAKE_FUNCTION
|
||||
1:0-1:20 STORE_NAME 0 (dec)
|
||||
|
||||
3:0-6:13 LOAD_BUILD_CLASS
|
||||
3:0-6:13 PUSH_NULL
|
||||
3:0-6:13 LOAD_CONST 1 (<code object C at 0xdeadbeef, file "<?>", line 3>)
|
||||
3:0-6:13 MAKE_FUNCTION
|
||||
3:0-6:13 LOAD_CONST 2 ('C')
|
||||
3:0-6:13 CALL 2
|
||||
3:0-6:13 STORE_NAME 1 (C)
|
||||
|
||||
8:1-8:4 LOAD_NAME 0 (dec)
|
||||
|
||||
9:0-10:8 LOAD_BUILD_CLASS
|
||||
9:0-10:8 PUSH_NULL
|
||||
9:0-10:8 LOAD_CONST 3 (<code object D at 0xdeadbeef, file "<?>", line 8>)
|
||||
9:0-10:8 MAKE_FUNCTION
|
||||
9:0-10:8 LOAD_CONST 4 ('D')
|
||||
9:0-10:8 CALL 2
|
||||
|
||||
8:1-8:4 CALL 0
|
||||
|
||||
9:0-10:8 STORE_NAME 2 (D)
|
||||
|
||||
12:0-15:33 LOAD_BUILD_CLASS
|
||||
12:0-15:33 PUSH_NULL
|
||||
12:0-15:33 LOAD_CONST 5 (<code object E at 0xdeadbeef, file "<?>", line 12>)
|
||||
12:0-15:33 MAKE_FUNCTION
|
||||
12:0-15:33 LOAD_CONST 6 ('E')
|
||||
12:0-15:33 CALL 2
|
||||
12:0-15:33 STORE_NAME 3 (E)
|
||||
12:0-15:33 LOAD_COMMON_CONSTANT 7 (None)
|
||||
12:0-15:33 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object dec at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
1:19-1:20 LOAD_FAST_BORROW 0 (f)
|
||||
1:12-1:20 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object C at 0xdeadbeef, file "<?>", line 3>:
|
||||
-- MAKE_CELL 0 (__classdict__)
|
||||
|
||||
3:0-3:0 RESUME 0
|
||||
3:0-3:0 LOAD_NAME 0 (__name__)
|
||||
3:0-3:0 STORE_NAME 1 (__module__)
|
||||
3:0-3:0 LOAD_CONST 0 ('C')
|
||||
3:0-3:0 STORE_NAME 2 (__qualname__)
|
||||
3:0-3:0 LOAD_SMALL_INT 3
|
||||
3:0-3:0 STORE_NAME 3 (__firstlineno__)
|
||||
3:0-3:0 LOAD_LOCALS
|
||||
3:0-3:0 STORE_DEREF 0 (__classdict__)
|
||||
|
||||
4:5-4:8 LOAD_NAME 4 (dec)
|
||||
|
||||
5:4-6:13 LOAD_CONST 1 (<code object f at 0xdeadbeef, file "<?>", line 4>)
|
||||
5:4-6:13 MAKE_FUNCTION
|
||||
|
||||
4:5-4:8 CALL 0
|
||||
|
||||
5:4-6:13 STORE_NAME 5 (f)
|
||||
5:4-6:13 LOAD_CONST 2 (())
|
||||
5:4-6:13 STORE_NAME 6 (__static_attributes__)
|
||||
5:4-6:13 LOAD_FAST_BORROW 0 (__classdict__)
|
||||
5:4-6:13 STORE_NAME 7 (__classdictcell__)
|
||||
5:4-6:13 LOAD_COMMON_CONSTANT 7 (None)
|
||||
5:4-6:13 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 4>:
|
||||
4:?-4:? RETURN_GENERATOR
|
||||
4:?-4:? POP_TOP
|
||||
4:0-4:0 L1: RESUME 0
|
||||
|
||||
6:8-6:13 LOAD_COMMON_CONSTANT 7 (None)
|
||||
6:8-6:13 YIELD_VALUE 0
|
||||
6:8-6:13 RESUME 9
|
||||
6:8-6:13 POP_TOP
|
||||
6:8-6:13 LOAD_COMMON_CONSTANT 7 (None)
|
||||
6:8-6:13 RETURN_VALUE
|
||||
|
||||
-- L2: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L2 -> L2 [0] lasti
|
||||
|
||||
Disassembly of <code object D at 0xdeadbeef, file "<?>", line 8>:
|
||||
8:0-8:0 RESUME 0
|
||||
8:0-8:0 LOAD_NAME 0 (__name__)
|
||||
8:0-8:0 STORE_NAME 1 (__module__)
|
||||
8:0-8:0 LOAD_CONST 0 ('D')
|
||||
8:0-8:0 STORE_NAME 2 (__qualname__)
|
||||
8:0-8:0 LOAD_SMALL_INT 8
|
||||
8:0-8:0 STORE_NAME 3 (__firstlineno__)
|
||||
|
||||
10:4-10:8 LOAD_CONST 1 (())
|
||||
10:4-10:8 STORE_NAME 4 (__static_attributes__)
|
||||
10:4-10:8 LOAD_COMMON_CONSTANT 7 (None)
|
||||
10:4-10:8 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object E at 0xdeadbeef, file "<?>", line 12>:
|
||||
-- MAKE_CELL 0 (__classdict__)
|
||||
|
||||
12:0-12:0 RESUME 0
|
||||
12:0-12:0 LOAD_NAME 0 (__name__)
|
||||
12:0-12:0 STORE_NAME 1 (__module__)
|
||||
12:0-12:0 LOAD_CONST 0 ('E')
|
||||
12:0-12:0 STORE_NAME 2 (__qualname__)
|
||||
12:0-12:0 LOAD_SMALL_INT 12
|
||||
12:0-12:0 STORE_NAME 3 (__firstlineno__)
|
||||
12:0-12:0 LOAD_LOCALS
|
||||
12:0-12:0 STORE_DEREF 0 (__classdict__)
|
||||
|
||||
13:5-13:8 LOAD_NAME 4 (dec)
|
||||
|
||||
14:4-15:33 LOAD_FAST_BORROW 0 (__classdict__)
|
||||
14:4-15:33 BUILD_TUPLE 1
|
||||
14:4-15:33 LOAD_CONST 1 (<code object __annotate__ at 0xdeadbeef, file "<?>", line 14>)
|
||||
14:4-15:33 MAKE_FUNCTION
|
||||
14:4-15:33 SET_FUNCTION_ATTRIBUTE 8 (closure)
|
||||
14:4-15:33 LOAD_CONST 2 (<code object g at 0xdeadbeef, file "<?>", line 13>)
|
||||
14:4-15:33 MAKE_FUNCTION
|
||||
14:4-15:33 SET_FUNCTION_ATTRIBUTE 16 (annotate)
|
||||
|
||||
13:5-13:8 CALL 0
|
||||
|
||||
14:4-15:33 STORE_NAME 5 (g)
|
||||
14:4-15:33 LOAD_CONST 3 (())
|
||||
14:4-15:33 STORE_NAME 6 (__static_attributes__)
|
||||
14:4-15:33 LOAD_FAST_BORROW 0 (__classdict__)
|
||||
14:4-15:33 STORE_NAME 7 (__classdictcell__)
|
||||
14:4-15:33 LOAD_COMMON_CONSTANT 7 (None)
|
||||
14:4-15:33 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object __annotate__ at 0xdeadbeef, file "<?>", line 14>:
|
||||
-- COPY_FREE_VARS 1
|
||||
|
||||
14:0-14:0 RESUME 0
|
||||
14:4-15:33 LOAD_FAST_BORROW 0 (format)
|
||||
14:4-15:33 LOAD_SMALL_INT 2
|
||||
14:4-15:33 COMPARE_OP 132 (>)
|
||||
14:4-15:33 POP_JUMP_IF_FALSE 3 (to L1)
|
||||
14:4-15:33 NOT_TAKEN
|
||||
14:4-15:33 LOAD_COMMON_CONSTANT 1 (NotImplementedError)
|
||||
14:4-15:33 RAISE_VARARGS 1
|
||||
14:4-15:33 L1: LOAD_CONST 1 ('flags')
|
||||
14:23-14:26 LOAD_DEREF 1 (__classdict__)
|
||||
14:23-14:26 LOAD_FROM_DICT_OR_GLOBALS 0 (int)
|
||||
14:4-15:33 LOAD_CONST 2 ('return')
|
||||
14:34-14:44 LOAD_DEREF 1 (__classdict__)
|
||||
14:34-14:44 LOAD_FROM_DICT_OR_GLOBALS 1 (memoryview)
|
||||
14:4-15:33 BUILD_MAP 2
|
||||
14:4-15:33 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object g at 0xdeadbeef, file "<?>", line 13>:
|
||||
13:0-13:0 RESUME 0
|
||||
|
||||
15:14-15:33 LOAD_GLOBAL 0 (NotImplementedError)
|
||||
15:8-15:33 RAISE_VARARGS 1
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(\"\", true)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
0:0-1:0 LOAD_COMMON_CONSTANT 7 (None)
|
||||
0:0-1:0 RETURN_VALUE
|
||||
Generated
+32
@@ -0,0 +1,32 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef simple(self):\n return f'{self.value}'\n\ndef spec(x):\n return f'{x!r:>3}'\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-2:26 LOAD_CONST 0 (<code object simple at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-2:26 MAKE_FUNCTION
|
||||
1:0-2:26 STORE_NAME 0 (simple)
|
||||
|
||||
4:0-5:22 LOAD_CONST 1 (<code object spec at 0xdeadbeef, file "<?>", line 4>)
|
||||
4:0-5:22 MAKE_FUNCTION
|
||||
4:0-5:22 STORE_NAME 1 (spec)
|
||||
4:0-5:22 LOAD_COMMON_CONSTANT 7 (None)
|
||||
4:0-5:22 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object simple at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:14-2:18 LOAD_FAST_BORROW 0 (self)
|
||||
2:14-2:24 LOAD_ATTR 0 (value)
|
||||
2:13-2:25 FORMAT_SIMPLE
|
||||
2:4-2:26 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object spec at 0xdeadbeef, file "<?>", line 4>:
|
||||
4:0-4:0 RESUME 0
|
||||
|
||||
5:14-5:15 LOAD_FAST_BORROW 0 (x)
|
||||
5:13-5:21 CONVERT_VALUE 2 (repr)
|
||||
5:18-5:20 LOAD_CONST 0 ('>3')
|
||||
5:13-5:21 FORMAT_WITH_SPEC
|
||||
5:4-5:22 RETURN_VALUE
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\nfrom __future__ import annotations\nclass Bar:\n foo: Foo\n\"#, true)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-1:34 LOAD_SMALL_INT 0
|
||||
1:0-1:34 LOAD_CONST 1 (('annotations',))
|
||||
1:0-1:34 IMPORT_NAME 0 (__future__)
|
||||
1:0-1:34 IMPORT_FROM 1 (annotations)
|
||||
1:0-1:34 STORE_NAME 1 (annotations)
|
||||
1:0-1:34 POP_TOP
|
||||
|
||||
2:0-3:12 LOAD_BUILD_CLASS
|
||||
2:0-3:12 PUSH_NULL
|
||||
2:0-3:12 LOAD_CONST 2 (<code object Bar at 0xdeadbeef, file "<?>", line 2>)
|
||||
2:0-3:12 MAKE_FUNCTION
|
||||
2:0-3:12 LOAD_CONST 3 ('Bar')
|
||||
2:0-3:12 CALL 2
|
||||
2:0-3:12 STORE_NAME 2 (Bar)
|
||||
2:0-3:12 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:0-3:12 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object Bar at 0xdeadbeef, file "<?>", line 2>:
|
||||
2:0-2:0 RESUME 0
|
||||
2:0-2:0 LOAD_NAME 0 (__name__)
|
||||
2:0-2:0 STORE_NAME 1 (__module__)
|
||||
2:0-2:0 LOAD_CONST 0 ('Bar')
|
||||
2:0-2:0 STORE_NAME 2 (__qualname__)
|
||||
2:0-2:0 LOAD_SMALL_INT 2
|
||||
2:0-2:0 STORE_NAME 3 (__firstlineno__)
|
||||
2:0-2:0 SETUP_ANNOTATIONS
|
||||
|
||||
3:9-3:12 LOAD_CONST 1 ('Foo')
|
||||
3:4-3:12 LOAD_NAME 4 (__annotations__)
|
||||
3:4-3:12 LOAD_CONST 2 ('foo')
|
||||
3:4-3:12 STORE_SUBSCR
|
||||
3:4-3:12 LOAD_CONST 3 (())
|
||||
3:4-3:12 STORE_NAME 5 (__static_attributes__)
|
||||
3:4-3:12 LOAD_COMMON_CONSTANT 7 (None)
|
||||
3:4-3:12 RETURN_VALUE
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef outer():\n class X[T]: ...\n\"#, true)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-2:19 LOAD_CONST 0 (<code object outer at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-2:19 MAKE_FUNCTION
|
||||
1:0-2:19 STORE_NAME 0 (outer)
|
||||
1:0-2:19 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-2:19 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object outer at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:4-2:19 LOAD_CONST 0 (<code object <generic parameters of X> at 0xdeadbeef, file "<?>", line 2>)
|
||||
2:4-2:19 MAKE_FUNCTION
|
||||
2:4-2:19 PUSH_NULL
|
||||
2:4-2:19 CALL 0
|
||||
2:4-2:19 STORE_FAST 0 (X)
|
||||
2:4-2:19 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:4-2:19 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object <generic parameters of X> at 0xdeadbeef, file "<?>", line 2>:
|
||||
-- MAKE_CELL 2 (.type_params)
|
||||
|
||||
2:0-2:0 RESUME 0
|
||||
2:12-2:13 LOAD_CONST 0 ('T')
|
||||
2:12-2:13 CALL_INTRINSIC_1 7 (INTRINSIC_TYPEVAR)
|
||||
2:12-2:13 COPY 1
|
||||
2:12-2:13 STORE_FAST 0 (T)
|
||||
2:12-2:13 BUILD_TUPLE 1
|
||||
2:4-2:19 STORE_DEREF 2 (.type_params)
|
||||
2:4-2:19 LOAD_BUILD_CLASS
|
||||
2:4-2:19 PUSH_NULL
|
||||
2:4-2:19 LOAD_FAST_BORROW 2 (.type_params)
|
||||
2:4-2:19 BUILD_TUPLE 1
|
||||
2:4-2:19 LOAD_CONST 1 (<code object X at 0xdeadbeef, file "<?>", line 2>)
|
||||
2:4-2:19 MAKE_FUNCTION
|
||||
2:4-2:19 SET_FUNCTION_ATTRIBUTE 8 (closure)
|
||||
2:4-2:19 LOAD_CONST 2 ('X')
|
||||
2:4-2:19 LOAD_DEREF 2 (.type_params)
|
||||
2:4-2:19 CALL_INTRINSIC_1 10 (INTRINSIC_SUBSCRIPT_GENERIC)
|
||||
2:4-2:19 STORE_FAST_LOAD_FAST 17 (.generic_base, .generic_base)
|
||||
2:4-2:19 CALL 3
|
||||
2:4-2:19 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object X at 0xdeadbeef, file "<?>", line 2>:
|
||||
-- COPY_FREE_VARS 1
|
||||
|
||||
2:0-2:0 RESUME 0
|
||||
2:0-2:0 LOAD_NAME 0 (__name__)
|
||||
2:0-2:0 STORE_NAME 1 (__module__)
|
||||
2:0-2:0 LOAD_CONST 0 ('outer.<locals>.X')
|
||||
2:0-2:0 STORE_NAME 2 (__qualname__)
|
||||
2:0-2:0 LOAD_SMALL_INT 2
|
||||
2:0-2:0 STORE_NAME 3 (__firstlineno__)
|
||||
2:0-2:0 LOAD_LOCALS
|
||||
2:0-2:0 LOAD_FROM_DICT_OR_DEREF 0 (.type_params)
|
||||
2:0-2:0 STORE_NAME 4 (__type_params__)
|
||||
2:16-2:19 LOAD_CONST 1 (())
|
||||
2:16-2:19 STORE_NAME 5 (__static_attributes__)
|
||||
2:16-2:19 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:16-2:19 RETURN_VALUE
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef f[T](x: int): ...\n\"#, true)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-1:21 LOAD_CONST 0 (<code object <generic parameters of f> at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-1:21 MAKE_FUNCTION
|
||||
1:0-1:21 PUSH_NULL
|
||||
1:0-1:21 CALL 0
|
||||
1:0-1:21 STORE_NAME 0 (f)
|
||||
1:0-1:21 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-1:21 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object <generic parameters of f> at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
1:6-1:7 LOAD_CONST 0 ('T')
|
||||
1:6-1:7 CALL_INTRINSIC_1 7 (INTRINSIC_TYPEVAR)
|
||||
1:6-1:7 COPY 1
|
||||
1:6-1:7 STORE_FAST 1 (T)
|
||||
1:6-1:7 BUILD_TUPLE 1
|
||||
1:0-1:21 LOAD_CONST 1 (<code object __annotate__ at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-1:21 MAKE_FUNCTION
|
||||
1:0-1:21 LOAD_CONST 2 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-1:21 MAKE_FUNCTION
|
||||
1:0-1:21 SET_FUNCTION_ATTRIBUTE 16 (annotate)
|
||||
1:0-1:21 SWAP 2
|
||||
1:0-1:21 CALL_INTRINSIC_2 4 (INTRINSIC_SET_FUNCTION_TYPE_PARAMS)
|
||||
1:0-1:21 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object __annotate__ at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
1:0-1:21 LOAD_FAST_BORROW 0 (format)
|
||||
1:0-1:21 LOAD_SMALL_INT 2
|
||||
1:0-1:21 COMPARE_OP 132 (>)
|
||||
1:0-1:21 POP_JUMP_IF_FALSE 3 (to L1)
|
||||
1:0-1:21 NOT_TAKEN
|
||||
1:0-1:21 LOAD_COMMON_CONSTANT 1 (NotImplementedError)
|
||||
1:0-1:21 RAISE_VARARGS 1
|
||||
1:0-1:21 L1: LOAD_CONST 1 ('x')
|
||||
1:12-1:15 LOAD_GLOBAL 0 (int)
|
||||
1:0-1:21 BUILD_MAP 1
|
||||
1:0-1:21 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
1:18-1:21 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:18-1:21 RETURN_VALUE
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ntype A[T] = T\n\"#, true)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-1:13 LOAD_CONST 0 (<code object <generic parameters of A> at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-1:13 MAKE_FUNCTION
|
||||
1:0-1:13 PUSH_NULL
|
||||
1:0-1:13 CALL 0
|
||||
1:0-1:13 STORE_NAME 0 (A)
|
||||
1:0-1:13 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-1:13 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object <generic parameters of A> at 0xdeadbeef, file "<?>", line 1>:
|
||||
-- MAKE_CELL 0 (T)
|
||||
|
||||
1:0-1:0 RESUME 0
|
||||
1:0-1:13 LOAD_CONST 0 ('A')
|
||||
1:7-1:8 LOAD_CONST 1 ('T')
|
||||
1:7-1:8 CALL_INTRINSIC_1 7 (INTRINSIC_TYPEVAR)
|
||||
1:7-1:8 COPY 1
|
||||
1:7-1:8 STORE_DEREF 0 (T)
|
||||
1:7-1:8 BUILD_TUPLE 1
|
||||
1:0-1:13 LOAD_CONST 2 ((1,))
|
||||
1:0-1:13 LOAD_FAST_BORROW 0 (T)
|
||||
1:0-1:13 BUILD_TUPLE 1
|
||||
1:0-1:13 LOAD_CONST 3 (<code object A at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-1:13 MAKE_FUNCTION
|
||||
1:0-1:13 SET_FUNCTION_ATTRIBUTE 8 (closure)
|
||||
1:0-1:13 SET_FUNCTION_ATTRIBUTE 1 (defaults)
|
||||
1:0-1:13 BUILD_TUPLE 3
|
||||
1:0-1:13 CALL_INTRINSIC_1 11 (INTRINSIC_TYPEALIAS)
|
||||
1:0-1:13 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object A at 0xdeadbeef, file "<?>", line 1>:
|
||||
-- COPY_FREE_VARS 1
|
||||
|
||||
1:0-1:0 RESUME 0
|
||||
1:0-1:13 LOAD_FAST_BORROW 0 (.format)
|
||||
1:0-1:13 LOAD_SMALL_INT 2
|
||||
1:0-1:13 COMPARE_OP 132 (>)
|
||||
1:0-1:13 POP_JUMP_IF_FALSE 3 (to L1)
|
||||
1:0-1:13 NOT_TAKEN
|
||||
1:0-1:13 LOAD_COMMON_CONSTANT 1 (NotImplementedError)
|
||||
1:0-1:13 RAISE_VARARGS 1
|
||||
1:12-1:13 L1: LOAD_DEREF 1 (T)
|
||||
1:0-1:13 RETURN_VALUE
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef f(parameters):\n return ((p, type(p)) for p in parameters)\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-2:45 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-2:45 MAKE_FUNCTION
|
||||
1:0-2:45 STORE_NAME 0 (f)
|
||||
1:0-2:45 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-2:45 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:11-2:45 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 2>)
|
||||
2:11-2:45 MAKE_FUNCTION
|
||||
2:34-2:44 LOAD_FAST_BORROW 0 (parameters)
|
||||
2:11-2:45 CALL 0
|
||||
2:4-2:45 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object <genexpr> at 0xdeadbeef, file "<?>", line 2>:
|
||||
-- RESUME 4
|
||||
|
||||
2:34-2:44 LOAD_FAST 0 (.0)
|
||||
2:34-2:44 GET_ITER 0
|
||||
2:?-2:? RETURN_GENERATOR
|
||||
2:?-2:? POP_TOP
|
||||
2:0-2:0 L1: RESUME 0
|
||||
2:34-2:44 L2: FOR_ITER 18 (to L3)
|
||||
2:29-2:30 STORE_FAST_LOAD_FAST 17 (p, p)
|
||||
2:16-2:20 LOAD_GLOBAL 1 (type + NULL)
|
||||
2:21-2:22 LOAD_FAST_BORROW 1 (p)
|
||||
2:16-2:23 CALL 1
|
||||
2:12-2:24 BUILD_TUPLE 2
|
||||
2:12-2:24 YIELD_VALUE 0
|
||||
2:12-2:24 RESUME 9
|
||||
2:12-2:24 POP_TOP
|
||||
2:12-2:24 JUMP_BACKWARD 20 (to L2)
|
||||
2:34-2:44 L3: END_FOR
|
||||
2:34-2:44 POP_ITER
|
||||
2:34-2:44 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:34-2:44 RETURN_VALUE
|
||||
|
||||
-- L4: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L4 -> L4 [2] lasti
|
||||
Generated
+104
@@ -0,0 +1,104 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef simple(names):\n return (x for x in names if not _ishidden(x))\n\ndef boolop(fields):\n return (f for f in fields if f.init and not f.kw_only)\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-2:49 LOAD_CONST 0 (<code object simple at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-2:49 MAKE_FUNCTION
|
||||
1:0-2:49 STORE_NAME 0 (simple)
|
||||
|
||||
4:0-5:58 LOAD_CONST 1 (<code object boolop at 0xdeadbeef, file "<?>", line 4>)
|
||||
4:0-5:58 MAKE_FUNCTION
|
||||
4:0-5:58 STORE_NAME 1 (boolop)
|
||||
4:0-5:58 LOAD_COMMON_CONSTANT 7 (None)
|
||||
4:0-5:58 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object simple at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:11-2:49 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 2>)
|
||||
2:11-2:49 MAKE_FUNCTION
|
||||
2:23-2:28 LOAD_FAST_BORROW 0 (names)
|
||||
2:11-2:49 CALL 0
|
||||
2:4-2:49 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object <genexpr> at 0xdeadbeef, file "<?>", line 2>:
|
||||
-- RESUME 4
|
||||
|
||||
2:23-2:28 LOAD_FAST 0 (.0)
|
||||
2:23-2:28 GET_ITER 0
|
||||
2:?-2:? RETURN_GENERATOR
|
||||
2:?-2:? POP_TOP
|
||||
2:0-2:0 L1: RESUME 0
|
||||
2:23-2:28 L2: FOR_ITER 27 (to L5)
|
||||
2:18-2:19 STORE_FAST 1 (x)
|
||||
2:36-2:45 LOAD_GLOBAL 1 (_ishidden + NULL)
|
||||
2:46-2:47 LOAD_FAST_BORROW 1 (x)
|
||||
2:36-2:48 CALL 1
|
||||
2:36-2:48 TO_BOOL
|
||||
2:12-2:13 L3: POP_JUMP_IF_FALSE 3 (to L4)
|
||||
2:12-2:13 NOT_TAKEN
|
||||
2:12-2:13 JUMP_BACKWARD 22 (to L2)
|
||||
2:12-2:13 L4: LOAD_FAST_BORROW 1 (x)
|
||||
2:12-2:13 YIELD_VALUE 0
|
||||
2:12-2:13 RESUME 9
|
||||
2:12-2:13 POP_TOP
|
||||
2:12-2:13 JUMP_BACKWARD 29 (to L2)
|
||||
2:23-2:28 L5: END_FOR
|
||||
2:23-2:28 POP_ITER
|
||||
2:23-2:28 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:23-2:28 RETURN_VALUE
|
||||
|
||||
-- L6: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L3 -> L6 [2] lasti
|
||||
L4 to L6 -> L6 [2] lasti
|
||||
|
||||
Disassembly of <code object boolop at 0xdeadbeef, file "<?>", line 4>:
|
||||
4:0-4:0 RESUME 0
|
||||
|
||||
5:11-5:58 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 5>)
|
||||
5:11-5:58 MAKE_FUNCTION
|
||||
5:23-5:29 LOAD_FAST_BORROW 0 (fields)
|
||||
5:11-5:58 CALL 0
|
||||
5:4-5:58 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object <genexpr> at 0xdeadbeef, file "<?>", line 5>:
|
||||
-- RESUME 4
|
||||
|
||||
5:23-5:29 LOAD_FAST 0 (.0)
|
||||
5:23-5:29 GET_ITER 0
|
||||
5:?-5:? RETURN_GENERATOR
|
||||
5:?-5:? POP_TOP
|
||||
5:0-5:0 L1: RESUME 0
|
||||
5:23-5:29 L2: FOR_ITER 47 (to L7)
|
||||
5:18-5:19 STORE_FAST_LOAD_FAST 17 (f, f)
|
||||
5:33-5:39 LOAD_ATTR 0 (init)
|
||||
5:33-5:39 TO_BOOL
|
||||
5:12-5:13 L3: POP_JUMP_IF_TRUE 3 (to L4)
|
||||
5:12-5:13 NOT_TAKEN
|
||||
5:12-5:13 JUMP_BACKWARD 22 (to L2)
|
||||
5:48-5:49 L4: LOAD_FAST_BORROW 1 (f)
|
||||
5:48-5:57 LOAD_ATTR 2 (kw_only)
|
||||
5:48-5:57 TO_BOOL
|
||||
5:12-5:13 L5: POP_JUMP_IF_FALSE 3 (to L6)
|
||||
5:12-5:13 NOT_TAKEN
|
||||
5:12-5:13 JUMP_BACKWARD 42 (to L2)
|
||||
5:12-5:13 L6: LOAD_FAST_BORROW 1 (f)
|
||||
5:12-5:13 YIELD_VALUE 0
|
||||
5:12-5:13 RESUME 9
|
||||
5:12-5:13 POP_TOP
|
||||
5:12-5:13 JUMP_BACKWARD 49 (to L2)
|
||||
5:23-5:29 L7: END_FOR
|
||||
5:23-5:29 POP_ITER
|
||||
5:23-5:29 LOAD_COMMON_CONSTANT 7 (None)
|
||||
5:23-5:29 RETURN_VALUE
|
||||
|
||||
-- L8: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L3 -> L8 [2] lasti
|
||||
L4 to L5 -> L8 [2] lasti
|
||||
L6 to L8 -> L8 [2] lasti
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef f(xs):\n return (x for x in xs)\n\nasync def g(xs):\n return (x async for x in xs)\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-2:26 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-2:26 MAKE_FUNCTION
|
||||
1:0-2:26 STORE_NAME 0 (f)
|
||||
|
||||
4:0-5:32 LOAD_CONST 1 (<code object g at 0xdeadbeef, file "<?>", line 4>)
|
||||
4:0-5:32 MAKE_FUNCTION
|
||||
4:0-5:32 STORE_NAME 1 (g)
|
||||
4:0-5:32 LOAD_COMMON_CONSTANT 7 (None)
|
||||
4:0-5:32 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:11-2:26 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 2>)
|
||||
2:11-2:26 MAKE_FUNCTION
|
||||
2:23-2:25 LOAD_FAST_BORROW 0 (xs)
|
||||
2:11-2:26 CALL 0
|
||||
2:4-2:26 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object <genexpr> at 0xdeadbeef, file "<?>", line 2>:
|
||||
-- RESUME 4
|
||||
|
||||
2:23-2:25 LOAD_FAST 0 (.0)
|
||||
2:23-2:25 GET_ITER 0
|
||||
2:?-2:? RETURN_GENERATOR
|
||||
2:?-2:? POP_TOP
|
||||
2:0-2:0 L1: RESUME 0
|
||||
2:23-2:25 L2: FOR_ITER 7 (to L3)
|
||||
2:18-2:19 STORE_FAST_LOAD_FAST 17 (x, x)
|
||||
2:12-2:13 YIELD_VALUE 0
|
||||
2:12-2:13 RESUME 9
|
||||
2:12-2:13 POP_TOP
|
||||
2:12-2:13 JUMP_BACKWARD 9 (to L2)
|
||||
2:23-2:25 L3: END_FOR
|
||||
2:23-2:25 POP_ITER
|
||||
2:23-2:25 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:23-2:25 RETURN_VALUE
|
||||
|
||||
-- L4: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L4 -> L4 [2] lasti
|
||||
|
||||
Disassembly of <code object g at 0xdeadbeef, file "<?>", line 4>:
|
||||
4:?-4:? RETURN_GENERATOR
|
||||
4:?-4:? POP_TOP
|
||||
4:0-4:0 L1: RESUME 0
|
||||
|
||||
5:11-5:32 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 5>)
|
||||
5:11-5:32 MAKE_FUNCTION
|
||||
5:29-5:31 LOAD_FAST_BORROW 0 (xs)
|
||||
5:11-5:32 CALL 0
|
||||
5:4-5:32 RETURN_VALUE
|
||||
|
||||
-- L2: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L2 -> L2 [0] lasti
|
||||
|
||||
Disassembly of <code object <genexpr> at 0xdeadbeef, file "<?>", line 5>:
|
||||
-- RESUME 4
|
||||
|
||||
5:29-5:31 LOAD_FAST_BORROW 0 (.0)
|
||||
5:29-5:31 GET_AITER
|
||||
5:?-5:? RETURN_GENERATOR
|
||||
5:?-5:? POP_TOP
|
||||
5:0-5:0 L1: RESUME 0
|
||||
5:11-5:32 L2: GET_ANEXT
|
||||
5:11-5:32 PUSH_NULL
|
||||
5:11-5:32 LOAD_COMMON_CONSTANT 7 (None)
|
||||
5:11-5:32 L3: SEND 4 (to L6)
|
||||
5:11-5:32 L4: YIELD_VALUE 1
|
||||
5:11-5:32 L5: RESUME 3
|
||||
5:11-5:32 JUMP_BACKWARD_NO_INTERRUPT 6 (to L3)
|
||||
5:11-5:32 L6: END_SEND
|
||||
5:24-5:25 L7: STORE_FAST_LOAD_FAST 17 (x, x)
|
||||
5:12-5:13 CALL_INTRINSIC_1 4 (INTRINSIC_ASYNC_GEN_WRAP)
|
||||
5:12-5:13 YIELD_VALUE 0
|
||||
5:12-5:13 RESUME 9
|
||||
5:12-5:13 POP_TOP
|
||||
5:12-5:13 JUMP_BACKWARD 18 (to L2)
|
||||
5:11-5:32 L8: CLEANUP_THROW
|
||||
5:11-5:32 L9: JUMP_BACKWARD_NO_INTERRUPT 11 (to L6)
|
||||
5:11-5:32 L10: END_ASYNC_FOR 12 (from L6)
|
||||
5:11-5:32 LOAD_COMMON_CONSTANT 7 (None)
|
||||
5:11-5:32 RETURN_VALUE
|
||||
|
||||
-- L11: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L2 -> L11 [1] lasti
|
||||
L2 to L4 -> L10 [1]
|
||||
L4 to L5 -> L8 [4]
|
||||
L5 to L7 -> L10 [1]
|
||||
L7 to L8 -> L11 [1] lasti
|
||||
L8 to L9 -> L10 [1]
|
||||
L10 to L11 -> L11 [1] lasti
|
||||
+1
-1
@@ -5,5 +5,5 @@ expression: "dis(r#\"\nif True and False and False:\n pass\n\"#)"
|
||||
---
|
||||
0 RESUME 0
|
||||
|
||||
1 LOAD_CONST 1 (None)
|
||||
1 LOAD_COMMON_CONSTANT 7 (None)
|
||||
RETURN_VALUE
|
||||
+1
-1
@@ -5,5 +5,5 @@ expression: "dis(r#\"\nif (True and False) or (False and True):\n pass\n\"#)"
|
||||
---
|
||||
0 RESUME 0
|
||||
|
||||
1 LOAD_CONST 1 (None)
|
||||
1 LOAD_COMMON_CONSTANT 7 (None)
|
||||
RETURN_VALUE
|
||||
+1
-1
@@ -7,5 +7,5 @@ expression: "dis(r#\"\nif True or False or False:\n pass\n\"#)"
|
||||
|
||||
1 NOP
|
||||
|
||||
2 LOAD_CONST 1 (None)
|
||||
2 LOAD_COMMON_CONSTANT 7 (None)
|
||||
RETURN_VALUE
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef bytes_binop():\n return bytes((x ^ 0x5C) for x in range(256))\n\ndef dict_tuple(d):\n return dict((v, k) for (k, v) in d.items())\n\ndef plain_tuple_elt(xs):\n return list((x, y) for x, y in xs)\n\ndef explicit_gen(xs):\n return list(((x, y) for x, y in xs))\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-2:48 LOAD_CONST 0 (<code object bytes_binop at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-2:48 MAKE_FUNCTION
|
||||
1:0-2:48 STORE_NAME 0 (bytes_binop)
|
||||
|
||||
4:0-5:47 LOAD_CONST 1 (<code object dict_tuple at 0xdeadbeef, file "<?>", line 4>)
|
||||
4:0-5:47 MAKE_FUNCTION
|
||||
4:0-5:47 STORE_NAME 1 (dict_tuple)
|
||||
|
||||
7:0-8:38 LOAD_CONST 2 (<code object plain_tuple_elt at 0xdeadbeef, file "<?>", line 7>)
|
||||
7:0-8:38 MAKE_FUNCTION
|
||||
7:0-8:38 STORE_NAME 2 (plain_tuple_elt)
|
||||
|
||||
10:0-11:40 LOAD_CONST 3 (<code object explicit_gen at 0xdeadbeef, file "<?>", line 10>)
|
||||
10:0-11:40 MAKE_FUNCTION
|
||||
10:0-11:40 STORE_NAME 3 (explicit_gen)
|
||||
10:0-11:40 LOAD_COMMON_CONSTANT 7 (None)
|
||||
10:0-11:40 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object bytes_binop at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:11-2:16 LOAD_GLOBAL 1 (bytes + NULL)
|
||||
2:16-2:48 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 2>)
|
||||
2:16-2:48 MAKE_FUNCTION
|
||||
2:37-2:42 LOAD_GLOBAL 3 (range + NULL)
|
||||
2:43-2:46 LOAD_CONST 1 (256)
|
||||
2:37-2:47 CALL 1
|
||||
2:16-2:48 CALL 0
|
||||
2:11-2:48 CALL 1
|
||||
2:4-2:48 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object <genexpr> at 0xdeadbeef, file "<?>", line 2>:
|
||||
-- RESUME 4
|
||||
|
||||
2:37-2:47 LOAD_FAST 0 (.0)
|
||||
2:37-2:47 GET_ITER 0
|
||||
2:?-2:? RETURN_GENERATOR
|
||||
2:?-2:? POP_TOP
|
||||
2:0-2:0 L1: RESUME 0
|
||||
2:37-2:47 L2: FOR_ITER 14 (to L3)
|
||||
2:32-2:33 STORE_FAST_LOAD_FAST 17 (x, x)
|
||||
2:22-2:26 LOAD_SMALL_INT 92
|
||||
2:18-2:26 BINARY_OP 12 (^)
|
||||
2:18-2:26 YIELD_VALUE 0
|
||||
2:18-2:26 RESUME 9
|
||||
2:18-2:26 POP_TOP
|
||||
2:18-2:26 JUMP_BACKWARD 16 (to L2)
|
||||
2:37-2:47 L3: END_FOR
|
||||
2:37-2:47 POP_ITER
|
||||
2:37-2:47 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:37-2:47 RETURN_VALUE
|
||||
|
||||
-- L4: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L4 -> L4 [2] lasti
|
||||
|
||||
Disassembly of <code object dict_tuple at 0xdeadbeef, file "<?>", line 4>:
|
||||
4:0-4:0 RESUME 0
|
||||
|
||||
5:11-5:15 LOAD_GLOBAL 1 (dict + NULL)
|
||||
5:15-5:47 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 5>)
|
||||
5:15-5:47 MAKE_FUNCTION
|
||||
5:37-5:38 LOAD_FAST_BORROW 0 (d)
|
||||
5:37-5:44 LOAD_ATTR 3 (items + NULL|self)
|
||||
5:37-5:46 CALL 0
|
||||
5:15-5:47 CALL 0
|
||||
5:11-5:47 CALL 1
|
||||
5:4-5:47 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object <genexpr> at 0xdeadbeef, file "<?>", line 5>:
|
||||
-- RESUME 4
|
||||
|
||||
5:37-5:46 LOAD_FAST 0 (.0)
|
||||
5:37-5:46 GET_ITER 0
|
||||
5:?-5:? RETURN_GENERATOR
|
||||
5:?-5:? POP_TOP
|
||||
5:0-5:0 L1: RESUME 0
|
||||
5:37-5:46 L2: FOR_ITER 11 (to L3)
|
||||
5:27-5:33 UNPACK_SEQUENCE 2
|
||||
5:28-5:29 STORE_FAST_STORE_FAST 18 (k, v)
|
||||
5:17-5:18 LOAD_FAST_BORROW_LOAD_FAST_BORROW 33 (v, k)
|
||||
5:16-5:22 BUILD_TUPLE 2
|
||||
5:16-5:22 YIELD_VALUE 0
|
||||
5:16-5:22 RESUME 9
|
||||
5:16-5:22 POP_TOP
|
||||
5:16-5:22 JUMP_BACKWARD 13 (to L2)
|
||||
5:37-5:46 L3: END_FOR
|
||||
5:37-5:46 POP_ITER
|
||||
5:37-5:46 LOAD_COMMON_CONSTANT 7 (None)
|
||||
5:37-5:46 RETURN_VALUE
|
||||
|
||||
-- L4: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L4 -> L4 [2] lasti
|
||||
|
||||
Disassembly of <code object plain_tuple_elt at 0xdeadbeef, file "<?>", line 7>:
|
||||
7:0-7:0 RESUME 0
|
||||
|
||||
8:11-8:15 LOAD_GLOBAL 0 (list)
|
||||
8:11-8:15 COPY 1
|
||||
8:11-8:15 LOAD_COMMON_CONSTANT 5 (list)
|
||||
8:11-8:15 IS_OP 0 (is)
|
||||
8:11-8:15 POP_JUMP_IF_FALSE 19 (to L3)
|
||||
8:11-8:15 NOT_TAKEN
|
||||
8:11-8:15 POP_TOP
|
||||
8:11-8:15 BUILD_LIST 0
|
||||
8:15-8:38 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 8>)
|
||||
8:15-8:38 MAKE_FUNCTION
|
||||
8:35-8:37 LOAD_FAST_BORROW 0 (xs)
|
||||
8:15-8:38 CALL 0
|
||||
8:11-8:15 PUSH_NULL
|
||||
8:11-8:15 L1: FOR_ITER 3 (to L2)
|
||||
8:11-8:15 LIST_APPEND 3
|
||||
8:11-8:15 JUMP_BACKWARD 5 (to L1)
|
||||
8:11-8:15 L2: END_FOR
|
||||
8:11-8:15 POP_ITER
|
||||
8:4-8:38 RETURN_VALUE
|
||||
8:11-8:15 L3: PUSH_NULL
|
||||
8:15-8:38 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 8>)
|
||||
8:15-8:38 MAKE_FUNCTION
|
||||
8:35-8:37 LOAD_FAST_BORROW 0 (xs)
|
||||
8:15-8:38 CALL 0
|
||||
8:11-8:38 CALL 1
|
||||
8:4-8:38 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object <genexpr> at 0xdeadbeef, file "<?>", line 8>:
|
||||
-- RESUME 4
|
||||
|
||||
8:35-8:37 LOAD_FAST 0 (.0)
|
||||
8:35-8:37 GET_ITER 0
|
||||
8:?-8:? RETURN_GENERATOR
|
||||
8:?-8:? POP_TOP
|
||||
8:0-8:0 L1: RESUME 0
|
||||
8:35-8:37 L2: FOR_ITER 11 (to L3)
|
||||
8:27-8:31 UNPACK_SEQUENCE 2
|
||||
8:27-8:28 STORE_FAST_STORE_FAST 18 (x, y)
|
||||
8:17-8:18 LOAD_FAST_BORROW_LOAD_FAST_BORROW 18 (x, y)
|
||||
8:16-8:22 BUILD_TUPLE 2
|
||||
8:16-8:22 YIELD_VALUE 0
|
||||
8:16-8:22 RESUME 9
|
||||
8:16-8:22 POP_TOP
|
||||
8:16-8:22 JUMP_BACKWARD 13 (to L2)
|
||||
8:35-8:37 L3: END_FOR
|
||||
8:35-8:37 POP_ITER
|
||||
8:35-8:37 LOAD_COMMON_CONSTANT 7 (None)
|
||||
8:35-8:37 RETURN_VALUE
|
||||
|
||||
-- L4: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L4 -> L4 [2] lasti
|
||||
|
||||
Disassembly of <code object explicit_gen at 0xdeadbeef, file "<?>", line 10>:
|
||||
10:0-10:0 RESUME 0
|
||||
|
||||
11:11-11:15 LOAD_GLOBAL 0 (list)
|
||||
11:11-11:15 COPY 1
|
||||
11:11-11:15 LOAD_COMMON_CONSTANT 5 (list)
|
||||
11:11-11:15 IS_OP 0 (is)
|
||||
11:11-11:15 POP_JUMP_IF_FALSE 19 (to L3)
|
||||
11:11-11:15 NOT_TAKEN
|
||||
11:11-11:15 POP_TOP
|
||||
11:11-11:15 BUILD_LIST 0
|
||||
11:16-11:39 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 11>)
|
||||
11:16-11:39 MAKE_FUNCTION
|
||||
11:36-11:38 LOAD_FAST_BORROW 0 (xs)
|
||||
11:16-11:39 CALL 0
|
||||
11:11-11:15 PUSH_NULL
|
||||
11:11-11:15 L1: FOR_ITER 3 (to L2)
|
||||
11:11-11:15 LIST_APPEND 3
|
||||
11:11-11:15 JUMP_BACKWARD 5 (to L1)
|
||||
11:11-11:15 L2: END_FOR
|
||||
11:11-11:15 POP_ITER
|
||||
11:4-11:40 RETURN_VALUE
|
||||
11:11-11:15 L3: PUSH_NULL
|
||||
11:16-11:39 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 11>)
|
||||
11:16-11:39 MAKE_FUNCTION
|
||||
11:36-11:38 LOAD_FAST_BORROW 0 (xs)
|
||||
11:16-11:39 CALL 0
|
||||
11:11-11:40 CALL 1
|
||||
11:4-11:40 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object <genexpr> at 0xdeadbeef, file "<?>", line 11>:
|
||||
-- RESUME 4
|
||||
|
||||
11:36-11:38 LOAD_FAST 0 (.0)
|
||||
11:36-11:38 GET_ITER 0
|
||||
11:?-11:? RETURN_GENERATOR
|
||||
11:?-11:? POP_TOP
|
||||
11:0-11:0 L1: RESUME 0
|
||||
11:36-11:38 L2: FOR_ITER 11 (to L3)
|
||||
11:28-11:32 UNPACK_SEQUENCE 2
|
||||
11:28-11:29 STORE_FAST_STORE_FAST 18 (x, y)
|
||||
11:18-11:19 LOAD_FAST_BORROW_LOAD_FAST_BORROW 18 (x, y)
|
||||
11:17-11:23 BUILD_TUPLE 2
|
||||
11:17-11:23 YIELD_VALUE 0
|
||||
11:17-11:23 RESUME 9
|
||||
11:17-11:23 POP_TOP
|
||||
11:17-11:23 JUMP_BACKWARD 13 (to L2)
|
||||
11:36-11:38 L3: END_FOR
|
||||
11:36-11:38 POP_ITER
|
||||
11:36-11:38 LOAD_COMMON_CONSTANT 7 (None)
|
||||
11:36-11:38 RETURN_VALUE
|
||||
|
||||
-- L4: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L4 -> L4 [2] lasti
|
||||
Generated
+131
@@ -0,0 +1,131 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef implicit():\n return list(x for x in range(10))\n\ndef explicit():\n return list((x for x in range(10)))\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-2:37 LOAD_CONST 0 (<code object implicit at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-2:37 MAKE_FUNCTION
|
||||
1:0-2:37 STORE_NAME 0 (implicit)
|
||||
|
||||
4:0-5:39 LOAD_CONST 1 (<code object explicit at 0xdeadbeef, file "<?>", line 4>)
|
||||
4:0-5:39 MAKE_FUNCTION
|
||||
4:0-5:39 STORE_NAME 1 (explicit)
|
||||
4:0-5:39 LOAD_COMMON_CONSTANT 7 (None)
|
||||
4:0-5:39 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object implicit at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:11-2:15 LOAD_GLOBAL 0 (list)
|
||||
2:11-2:15 COPY 1
|
||||
2:11-2:15 LOAD_COMMON_CONSTANT 5 (list)
|
||||
2:11-2:15 IS_OP 0 (is)
|
||||
2:11-2:15 POP_JUMP_IF_FALSE 28 (to L3)
|
||||
2:11-2:15 NOT_TAKEN
|
||||
2:11-2:15 POP_TOP
|
||||
2:11-2:15 BUILD_LIST 0
|
||||
2:15-2:37 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 2>)
|
||||
2:15-2:37 MAKE_FUNCTION
|
||||
2:27-2:32 LOAD_GLOBAL 3 (range + NULL)
|
||||
2:33-2:35 LOAD_SMALL_INT 10
|
||||
2:27-2:36 CALL 1
|
||||
2:15-2:37 CALL 0
|
||||
2:11-2:15 PUSH_NULL
|
||||
2:11-2:15 L1: FOR_ITER 3 (to L2)
|
||||
2:11-2:15 LIST_APPEND 3
|
||||
2:11-2:15 JUMP_BACKWARD 5 (to L1)
|
||||
2:11-2:15 L2: END_FOR
|
||||
2:11-2:15 POP_ITER
|
||||
2:4-2:37 RETURN_VALUE
|
||||
2:11-2:15 L3: PUSH_NULL
|
||||
2:15-2:37 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 2>)
|
||||
2:15-2:37 MAKE_FUNCTION
|
||||
2:27-2:32 LOAD_GLOBAL 3 (range + NULL)
|
||||
2:33-2:35 LOAD_SMALL_INT 10
|
||||
2:27-2:36 CALL 1
|
||||
2:15-2:37 CALL 0
|
||||
2:11-2:37 CALL 1
|
||||
2:4-2:37 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object <genexpr> at 0xdeadbeef, file "<?>", line 2>:
|
||||
-- RESUME 4
|
||||
|
||||
2:27-2:36 LOAD_FAST 0 (.0)
|
||||
2:27-2:36 GET_ITER 0
|
||||
2:?-2:? RETURN_GENERATOR
|
||||
2:?-2:? POP_TOP
|
||||
2:0-2:0 L1: RESUME 0
|
||||
2:27-2:36 L2: FOR_ITER 7 (to L3)
|
||||
2:22-2:23 STORE_FAST_LOAD_FAST 17 (x, x)
|
||||
2:16-2:17 YIELD_VALUE 0
|
||||
2:16-2:17 RESUME 9
|
||||
2:16-2:17 POP_TOP
|
||||
2:16-2:17 JUMP_BACKWARD 9 (to L2)
|
||||
2:27-2:36 L3: END_FOR
|
||||
2:27-2:36 POP_ITER
|
||||
2:27-2:36 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:27-2:36 RETURN_VALUE
|
||||
|
||||
-- L4: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L4 -> L4 [2] lasti
|
||||
|
||||
Disassembly of <code object explicit at 0xdeadbeef, file "<?>", line 4>:
|
||||
4:0-4:0 RESUME 0
|
||||
|
||||
5:11-5:15 LOAD_GLOBAL 0 (list)
|
||||
5:11-5:15 COPY 1
|
||||
5:11-5:15 LOAD_COMMON_CONSTANT 5 (list)
|
||||
5:11-5:15 IS_OP 0 (is)
|
||||
5:11-5:15 POP_JUMP_IF_FALSE 28 (to L3)
|
||||
5:11-5:15 NOT_TAKEN
|
||||
5:11-5:15 POP_TOP
|
||||
5:11-5:15 BUILD_LIST 0
|
||||
5:16-5:38 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 5>)
|
||||
5:16-5:38 MAKE_FUNCTION
|
||||
5:28-5:33 LOAD_GLOBAL 3 (range + NULL)
|
||||
5:34-5:36 LOAD_SMALL_INT 10
|
||||
5:28-5:37 CALL 1
|
||||
5:16-5:38 CALL 0
|
||||
5:11-5:15 PUSH_NULL
|
||||
5:11-5:15 L1: FOR_ITER 3 (to L2)
|
||||
5:11-5:15 LIST_APPEND 3
|
||||
5:11-5:15 JUMP_BACKWARD 5 (to L1)
|
||||
5:11-5:15 L2: END_FOR
|
||||
5:11-5:15 POP_ITER
|
||||
5:4-5:39 RETURN_VALUE
|
||||
5:11-5:15 L3: PUSH_NULL
|
||||
5:16-5:38 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 5>)
|
||||
5:16-5:38 MAKE_FUNCTION
|
||||
5:28-5:33 LOAD_GLOBAL 3 (range + NULL)
|
||||
5:34-5:36 LOAD_SMALL_INT 10
|
||||
5:28-5:37 CALL 1
|
||||
5:16-5:38 CALL 0
|
||||
5:11-5:39 CALL 1
|
||||
5:4-5:39 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object <genexpr> at 0xdeadbeef, file "<?>", line 5>:
|
||||
-- RESUME 4
|
||||
|
||||
5:28-5:37 LOAD_FAST 0 (.0)
|
||||
5:28-5:37 GET_ITER 0
|
||||
5:?-5:? RETURN_GENERATOR
|
||||
5:?-5:? POP_TOP
|
||||
5:0-5:0 L1: RESUME 0
|
||||
5:28-5:37 L2: FOR_ITER 7 (to L3)
|
||||
5:23-5:24 STORE_FAST_LOAD_FAST 17 (x, x)
|
||||
5:17-5:18 YIELD_VALUE 0
|
||||
5:17-5:18 RESUME 9
|
||||
5:17-5:18 POP_TOP
|
||||
5:17-5:18 JUMP_BACKWARD 9 (to L2)
|
||||
5:28-5:37 L3: END_FOR
|
||||
5:28-5:37 POP_ITER
|
||||
5:28-5:37 LOAD_COMMON_CONSTANT 7 (None)
|
||||
5:28-5:37 RETURN_VALUE
|
||||
|
||||
-- L4: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L4 -> L4 [2] lasti
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\nimport sample\nfrom sample import member\nfrom sample import *\n\ndef f():\n import sample\n\nclass C:\n from sample import member\n\ntry:\n import sample\nexcept Exception:\n pass\n\ntry:\n operation()\nexcept Exception:\n import sample\n\ntry:\n operation()\nfinally:\n import sample\n\nwith manager:\n import sample\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-1:13 LOAD_SMALL_INT 0
|
||||
1:0-1:13 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-1:13 IMPORT_NAME 0 (sample)
|
||||
1:0-1:13 STORE_NAME 0 (sample)
|
||||
|
||||
2:0-2:25 LOAD_SMALL_INT 0
|
||||
2:0-2:25 LOAD_CONST 1 (('member',))
|
||||
2:0-2:25 IMPORT_NAME 0 (sample)
|
||||
2:0-2:25 IMPORT_FROM 1 (member)
|
||||
2:0-2:25 STORE_NAME 1 (member)
|
||||
2:0-2:25 POP_TOP
|
||||
|
||||
3:0-3:20 LOAD_SMALL_INT 0
|
||||
3:0-3:20 LOAD_CONST 2 (('*',))
|
||||
3:0-3:20 IMPORT_NAME 2 (sample + eager)
|
||||
3:0-3:20 CALL_INTRINSIC_1 2 (INTRINSIC_IMPORT_STAR)
|
||||
3:0-3:20 POP_TOP
|
||||
|
||||
5:0-6:17 LOAD_CONST 3 (<code object f at 0xdeadbeef, file "<?>", line 5>)
|
||||
5:0-6:17 MAKE_FUNCTION
|
||||
5:0-6:17 STORE_NAME 2 (f)
|
||||
|
||||
8:0-9:29 LOAD_BUILD_CLASS
|
||||
8:0-9:29 PUSH_NULL
|
||||
8:0-9:29 LOAD_CONST 4 (<code object C at 0xdeadbeef, file "<?>", line 8>)
|
||||
8:0-9:29 MAKE_FUNCTION
|
||||
8:0-9:29 LOAD_CONST 5 ('C')
|
||||
8:0-9:29 CALL 2
|
||||
8:0-9:29 STORE_NAME 3 (C)
|
||||
|
||||
11:0-14:8 NOP
|
||||
|
||||
12:4-12:17 L1: LOAD_SMALL_INT 0
|
||||
12:4-12:17 LOAD_COMMON_CONSTANT 7 (None)
|
||||
12:4-12:17 IMPORT_NAME 2 (sample + eager)
|
||||
12:4-12:17 STORE_NAME 0 (sample)
|
||||
|
||||
16:0-19:17 L2: NOP
|
||||
|
||||
17:4-17:13 L3: LOAD_NAME 5 (operation)
|
||||
17:4-17:13 PUSH_NULL
|
||||
17:4-17:15 CALL 0
|
||||
17:4-17:15 POP_TOP
|
||||
|
||||
21:0-24:17 L4: NOP
|
||||
|
||||
22:4-22:13 L5: LOAD_NAME 5 (operation)
|
||||
22:4-22:13 PUSH_NULL
|
||||
22:4-22:15 CALL 0
|
||||
22:4-22:15 POP_TOP
|
||||
|
||||
24:4-24:17 L6: LOAD_SMALL_INT 0
|
||||
24:4-24:17 LOAD_COMMON_CONSTANT 7 (None)
|
||||
24:4-24:17 IMPORT_NAME 0 (sample)
|
||||
24:4-24:17 STORE_NAME 0 (sample)
|
||||
|
||||
26:5-26:12 LOAD_NAME 6 (manager)
|
||||
26:5-26:12 COPY 1
|
||||
26:5-26:12 LOAD_SPECIAL 1 (__exit__)
|
||||
26:5-26:12 SWAP 2
|
||||
26:5-26:12 SWAP 3
|
||||
26:5-26:12 LOAD_SPECIAL 0 (__enter__)
|
||||
26:5-26:12 CALL 0
|
||||
26:5-26:12 L7: POP_TOP
|
||||
|
||||
27:4-27:17 LOAD_SMALL_INT 0
|
||||
27:4-27:17 LOAD_COMMON_CONSTANT 7 (None)
|
||||
27:4-27:17 IMPORT_NAME 0 (sample)
|
||||
27:4-27:17 STORE_NAME 0 (sample)
|
||||
|
||||
26:5-26:12 L8: LOAD_COMMON_CONSTANT 7 (None)
|
||||
26:5-26:12 LOAD_COMMON_CONSTANT 7 (None)
|
||||
26:5-26:12 LOAD_COMMON_CONSTANT 7 (None)
|
||||
26:5-26:12 CALL 3
|
||||
26:5-26:12 POP_TOP
|
||||
26:5-26:12 LOAD_COMMON_CONSTANT 7 (None)
|
||||
26:5-26:12 RETURN_VALUE
|
||||
|
||||
-- L9: PUSH_EXC_INFO
|
||||
|
||||
13:7-13:16 LOAD_NAME 4 (Exception)
|
||||
13:0-14:8 CHECK_EXC_MATCH
|
||||
13:0-14:8 POP_JUMP_IF_FALSE 4 (to L13)
|
||||
13:0-14:8 L10: NOT_TAKEN
|
||||
13:0-14:8 L11: POP_TOP
|
||||
|
||||
14:4-14:8 L12: POP_EXCEPT
|
||||
14:4-14:8 JUMP_BACKWARD_NO_INTERRUPT 54 (to L2)
|
||||
|
||||
13:0-14:8 L13: RERAISE 0
|
||||
|
||||
-- L14: COPY 3
|
||||
-- POP_EXCEPT
|
||||
-- RERAISE 1
|
||||
-- L15: PUSH_EXC_INFO
|
||||
|
||||
18:7-18:16 LOAD_NAME 4 (Exception)
|
||||
18:0-19:17 CHECK_EXC_MATCH
|
||||
18:0-19:17 POP_JUMP_IF_FALSE 8 (to L19)
|
||||
18:0-19:17 L16: NOT_TAKEN
|
||||
18:0-19:17 L17: POP_TOP
|
||||
|
||||
19:4-19:17 LOAD_SMALL_INT 0
|
||||
19:4-19:17 LOAD_COMMON_CONSTANT 7 (None)
|
||||
19:4-19:17 IMPORT_NAME 2 (sample + eager)
|
||||
19:4-19:17 STORE_NAME 0 (sample)
|
||||
19:4-19:17 L18: POP_EXCEPT
|
||||
19:4-19:17 JUMP_BACKWARD_NO_INTERRUPT 63 (to L4)
|
||||
|
||||
18:0-19:17 L19: RERAISE 0
|
||||
|
||||
-- L20: COPY 3
|
||||
-- POP_EXCEPT
|
||||
-- RERAISE 1
|
||||
-- L21: PUSH_EXC_INFO
|
||||
|
||||
24:4-24:17 LOAD_SMALL_INT 0
|
||||
24:4-24:17 LOAD_COMMON_CONSTANT 7 (None)
|
||||
24:4-24:17 IMPORT_NAME 2 (sample + eager)
|
||||
24:4-24:17 STORE_NAME 0 (sample)
|
||||
24:4-24:17 RERAISE 0
|
||||
|
||||
-- L22: COPY 3
|
||||
-- POP_EXCEPT
|
||||
-- RERAISE 1
|
||||
|
||||
26:5-26:12 L23: PUSH_EXC_INFO
|
||||
26:5-26:12 WITH_EXCEPT_START
|
||||
26:5-26:12 TO_BOOL
|
||||
26:5-26:12 POP_JUMP_IF_TRUE 2 (to L26)
|
||||
26:5-26:12 L24: NOT_TAKEN
|
||||
26:5-26:12 L25: RERAISE 2
|
||||
26:5-26:12 L26: POP_TOP
|
||||
26:5-26:12 L27: POP_EXCEPT
|
||||
26:5-26:12 POP_TOP
|
||||
26:5-26:12 POP_TOP
|
||||
26:5-26:12 POP_TOP
|
||||
26:5-26:12 LOAD_COMMON_CONSTANT 7 (None)
|
||||
26:5-26:12 RETURN_VALUE
|
||||
|
||||
-- L28: COPY 3
|
||||
-- POP_EXCEPT
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L2 -> L9 [0]
|
||||
L3 to L4 -> L15 [0]
|
||||
L5 to L6 -> L21 [0]
|
||||
L7 to L8 -> L23 [2] lasti
|
||||
L9 to L10 -> L14 [1] lasti
|
||||
L11 to L12 -> L14 [1] lasti
|
||||
L13 to L14 -> L14 [1] lasti
|
||||
L15 to L16 -> L20 [1] lasti
|
||||
L17 to L18 -> L20 [1] lasti
|
||||
L19 to L20 -> L20 [1] lasti
|
||||
L21 to L22 -> L22 [1] lasti
|
||||
L23 to L24 -> L28 [4] lasti
|
||||
L25 to L27 -> L28 [4] lasti
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 5>:
|
||||
5:0-5:0 RESUME 0
|
||||
|
||||
6:4-6:17 LOAD_SMALL_INT 0
|
||||
6:4-6:17 LOAD_COMMON_CONSTANT 7 (None)
|
||||
6:4-6:17 IMPORT_NAME 2 (sample + eager)
|
||||
6:4-6:17 STORE_FAST 0 (sample)
|
||||
6:4-6:17 LOAD_COMMON_CONSTANT 7 (None)
|
||||
6:4-6:17 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object C at 0xdeadbeef, file "<?>", line 8>:
|
||||
8:0-8:0 RESUME 0
|
||||
8:0-8:0 LOAD_NAME 0 (__name__)
|
||||
8:0-8:0 STORE_NAME 1 (__module__)
|
||||
8:0-8:0 LOAD_CONST 0 ('C')
|
||||
8:0-8:0 STORE_NAME 2 (__qualname__)
|
||||
8:0-8:0 LOAD_SMALL_INT 8
|
||||
8:0-8:0 STORE_NAME 3 (__firstlineno__)
|
||||
|
||||
9:4-9:29 LOAD_SMALL_INT 0
|
||||
9:4-9:29 LOAD_CONST 1 (('member',))
|
||||
9:4-9:29 IMPORT_NAME 18 (sample + eager)
|
||||
9:4-9:29 IMPORT_FROM 5 (member)
|
||||
9:4-9:29 STORE_NAME 5 (member)
|
||||
9:4-9:29 POP_TOP
|
||||
9:4-9:29 LOAD_CONST 2 (())
|
||||
9:4-9:29 STORE_NAME 6 (__static_attributes__)
|
||||
9:4-9:29 LOAD_COMMON_CONSTANT 7 (None)
|
||||
9:4-9:29 RETURN_VALUE
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\nasync def f(it):\n for i in it:\n yield i\n\nasync def run_list():\n return [i + 1 async for i in f([10, 20])]\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-3:15 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-3:15 MAKE_FUNCTION
|
||||
1:0-3:15 STORE_NAME 0 (f)
|
||||
|
||||
5:0-6:45 LOAD_CONST 1 (<code object run_list at 0xdeadbeef, file "<?>", line 5>)
|
||||
5:0-6:45 MAKE_FUNCTION
|
||||
5:0-6:45 STORE_NAME 1 (run_list)
|
||||
5:0-6:45 LOAD_COMMON_CONSTANT 7 (None)
|
||||
5:0-6:45 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:?-1:? RETURN_GENERATOR
|
||||
1:?-1:? POP_TOP
|
||||
1:0-1:0 L1: RESUME 0
|
||||
|
||||
2:13-2:15 LOAD_FAST 0 (it)
|
||||
2:13-2:15 GET_ITER 0
|
||||
2:13-2:15 L2: FOR_ITER 9 (to L3)
|
||||
2:8-2:9 STORE_FAST 1 (i)
|
||||
|
||||
3:14-3:15 LOAD_FAST_BORROW 1 (i)
|
||||
3:8-3:15 CALL_INTRINSIC_1 4 (INTRINSIC_ASYNC_GEN_WRAP)
|
||||
3:8-3:15 YIELD_VALUE 0
|
||||
3:8-3:15 RESUME 9
|
||||
3:8-3:15 POP_TOP
|
||||
3:8-3:15 JUMP_BACKWARD 11 (to L2)
|
||||
|
||||
2:13-2:15 L3: END_FOR
|
||||
2:13-2:15 POP_ITER
|
||||
2:13-2:15 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:13-2:15 RETURN_VALUE
|
||||
|
||||
-- L4: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L4 -> L4 [0] lasti
|
||||
|
||||
Disassembly of <code object run_list at 0xdeadbeef, file "<?>", line 5>:
|
||||
5:?-5:? RETURN_GENERATOR
|
||||
5:?-5:? POP_TOP
|
||||
5:0-5:0 L1: RESUME 0
|
||||
|
||||
6:33-6:34 LOAD_GLOBAL 1 (f + NULL)
|
||||
6:36-6:38 LOAD_SMALL_INT 10
|
||||
6:40-6:42 LOAD_SMALL_INT 20
|
||||
6:35-6:43 BUILD_LIST 2
|
||||
6:33-6:44 CALL 1
|
||||
6:11-6:45 LOAD_FAST_AND_CLEAR 0 (i)
|
||||
6:11-6:45 SWAP 2
|
||||
6:11-6:45 L2: BUILD_LIST 0
|
||||
6:11-6:45 SWAP 2
|
||||
6:33-6:44 GET_AITER
|
||||
6:11-6:45 L3: GET_ANEXT
|
||||
6:11-6:45 PUSH_NULL
|
||||
6:11-6:45 LOAD_COMMON_CONSTANT 7 (None)
|
||||
6:11-6:45 L4: SEND 4 (to L7)
|
||||
6:11-6:45 L5: YIELD_VALUE 1
|
||||
6:11-6:45 L6: RESUME 3
|
||||
6:11-6:45 JUMP_BACKWARD_NO_INTERRUPT 6 (to L4)
|
||||
6:11-6:45 L7: END_SEND
|
||||
6:28-6:29 L8: STORE_FAST_LOAD_FAST 0 (i, i)
|
||||
6:16-6:17 LOAD_SMALL_INT 1
|
||||
6:12-6:17 BINARY_OP 0 (+)
|
||||
6:12-6:17 LIST_APPEND 2
|
||||
6:12-6:17 JUMP_BACKWARD 21 (to L3)
|
||||
6:11-6:45 L9: CLEANUP_THROW
|
||||
6:11-6:45 L10: JUMP_BACKWARD_NO_INTERRUPT 14 (to L7)
|
||||
6:11-6:45 L11: END_ASYNC_FOR 15 (from L7)
|
||||
6:11-6:45 L12: SWAP 2
|
||||
6:11-6:45 STORE_FAST 0 (i)
|
||||
6:4-6:45 RETURN_VALUE
|
||||
|
||||
-- L13: SWAP 2
|
||||
-- POP_TOP
|
||||
|
||||
6:11-6:45 SWAP 2
|
||||
6:11-6:45 STORE_FAST 0 (i)
|
||||
6:11-6:45 RERAISE 0
|
||||
|
||||
-- L14: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L2 -> L14 [0] lasti
|
||||
L2 to L3 -> L13 [2]
|
||||
L3 to L5 -> L11 [3]
|
||||
L5 to L6 -> L9 [6]
|
||||
L6 to L8 -> L11 [3]
|
||||
L8 to L9 -> L13 [2]
|
||||
L9 to L10 -> L11 [3]
|
||||
L11 to L12 -> L13 [2]
|
||||
L12 to L14 -> L14 [0] lasti
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\nasync def f(i):\n return i\n\nasync def run_list():\n return [s for c in [f(''), f('abc')] for s in await c]\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-2:12 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-2:12 MAKE_FUNCTION
|
||||
1:0-2:12 STORE_NAME 0 (f)
|
||||
|
||||
4:0-5:58 LOAD_CONST 1 (<code object run_list at 0xdeadbeef, file "<?>", line 4>)
|
||||
4:0-5:58 MAKE_FUNCTION
|
||||
4:0-5:58 STORE_NAME 1 (run_list)
|
||||
4:0-5:58 LOAD_COMMON_CONSTANT 7 (None)
|
||||
4:0-5:58 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:?-1:? RETURN_GENERATOR
|
||||
1:?-1:? POP_TOP
|
||||
1:0-1:0 L1: RESUME 0
|
||||
|
||||
2:11-2:12 LOAD_FAST_BORROW 0 (i)
|
||||
2:4-2:12 RETURN_VALUE
|
||||
|
||||
-- L2: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L2 -> L2 [0] lasti
|
||||
|
||||
Disassembly of <code object run_list at 0xdeadbeef, file "<?>", line 4>:
|
||||
4:?-4:? RETURN_GENERATOR
|
||||
4:?-4:? POP_TOP
|
||||
4:0-4:0 L1: RESUME 0
|
||||
|
||||
5:24-5:25 LOAD_GLOBAL 1 (f + NULL)
|
||||
5:26-5:28 LOAD_COMMON_CONSTANT 8 ('')
|
||||
5:24-5:29 CALL 1
|
||||
5:31-5:32 LOAD_GLOBAL 1 (f + NULL)
|
||||
5:33-5:38 LOAD_CONST 1 ('abc')
|
||||
5:31-5:39 CALL 1
|
||||
5:23-5:40 BUILD_LIST 2
|
||||
5:11-5:58 LOAD_FAST_AND_CLEAR 0 (c)
|
||||
5:11-5:58 LOAD_FAST_AND_CLEAR 1 (s)
|
||||
5:11-5:58 SWAP 3
|
||||
5:11-5:58 L2: BUILD_LIST 0
|
||||
5:11-5:58 SWAP 2
|
||||
5:23-5:40 GET_ITER 0
|
||||
5:23-5:40 L3: FOR_ITER 23 (to L10)
|
||||
5:18-5:19 STORE_FAST_LOAD_FAST 0 (c, c)
|
||||
5:50-5:57 GET_AWAITABLE 0
|
||||
5:50-5:57 PUSH_NULL
|
||||
5:50-5:57 LOAD_COMMON_CONSTANT 7 (None)
|
||||
5:50-5:57 L4: SEND 4 (to L7)
|
||||
5:50-5:57 L5: YIELD_VALUE 1
|
||||
5:50-5:57 L6: RESUME 3
|
||||
5:50-5:57 JUMP_BACKWARD_NO_INTERRUPT 6 (to L4)
|
||||
5:50-5:57 L7: END_SEND
|
||||
5:50-5:57 GET_ITER 0
|
||||
5:50-5:57 L8: FOR_ITER 4 (to L9)
|
||||
5:45-5:46 STORE_FAST_LOAD_FAST 17 (s, s)
|
||||
5:12-5:13 LIST_APPEND 5
|
||||
5:12-5:13 JUMP_BACKWARD 6 (to L8)
|
||||
5:50-5:57 L9: END_FOR
|
||||
5:50-5:57 POP_ITER
|
||||
5:12-5:13 JUMP_BACKWARD 25 (to L3)
|
||||
5:23-5:40 L10: END_FOR
|
||||
5:23-5:40 POP_ITER
|
||||
5:11-5:58 L11: SWAP 3
|
||||
5:11-5:58 STORE_FAST 1 (s)
|
||||
5:11-5:58 STORE_FAST 0 (c)
|
||||
5:4-5:58 RETURN_VALUE
|
||||
5:50-5:57 L12: CLEANUP_THROW
|
||||
5:50-5:57 L13: JUMP_BACKWARD_NO_INTERRUPT 21 (to L7)
|
||||
|
||||
-- L14: SWAP 2
|
||||
-- POP_TOP
|
||||
|
||||
5:11-5:58 SWAP 3
|
||||
5:11-5:58 STORE_FAST 1 (s)
|
||||
5:11-5:58 STORE_FAST 0 (c)
|
||||
5:11-5:58 RERAISE 0
|
||||
|
||||
-- L15: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L2 -> L15 [0] lasti
|
||||
L2 to L5 -> L14 [3]
|
||||
L5 to L6 -> L12 [8]
|
||||
L6 to L11 -> L14 [3]
|
||||
L11 to L12 -> L15 [0] lasti
|
||||
L12 to L13 -> L14 [3]
|
||||
L14 to L15 -> L15 [0] lasti
|
||||
Generated
+24
@@ -0,0 +1,24 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef f(a):\n return (\n 'x'\n f'y{a}z'\n 'w'\n )\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-6:5 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-6:5 MAKE_FUNCTION
|
||||
1:0-6:5 STORE_NAME 0 (f)
|
||||
1:0-6:5 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-6:5 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
3:8-4:11 LOAD_CONST 0 ('xy')
|
||||
|
||||
4:12-4:13 LOAD_FAST_BORROW 0 (a)
|
||||
4:11-4:14 FORMAT_SIMPLE
|
||||
4:14-5:11 LOAD_CONST 1 ('zw')
|
||||
|
||||
3:8-5:11 BUILD_STRING 3
|
||||
|
||||
2:4-6:5 RETURN_VALUE
|
||||
Generated
+29
@@ -0,0 +1,29 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\nf = lambda data: {'x': data}\ng = lambda i: {**i}\n\"#, true)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:4-1:28 LOAD_CONST 0 (<code object <lambda> at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:4-1:28 MAKE_FUNCTION
|
||||
1:0-1:1 STORE_NAME 0 (f)
|
||||
|
||||
2:4-2:19 LOAD_CONST 1 (<code object <lambda> at 0xdeadbeef, file "<?>", line 2>)
|
||||
2:4-2:19 MAKE_FUNCTION
|
||||
2:0-2:1 STORE_NAME 1 (g)
|
||||
2:0-2:1 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:0-2:1 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object <lambda> at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
1:18-1:21 LOAD_CONST 0 ('x')
|
||||
1:23-1:27 LOAD_FAST_BORROW 0 (data)
|
||||
1:17-1:28 BUILD_MAP 1
|
||||
1:17-1:28 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object <lambda> at 0xdeadbeef, file "<?>", line 2>:
|
||||
2:0-2:0 RESUME 0
|
||||
2:14-2:19 BUILD_MAP 0
|
||||
2:17-2:18 LOAD_FAST_BORROW 0 (i)
|
||||
2:14-2:19 DICT_UPDATE 1
|
||||
2:14-2:19 RETURN_VALUE
|
||||
Generated
+129
@@ -0,0 +1,129 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\nasync def f(xs):\n return list(x async for x in xs)\n\nasync def g(xs):\n return set(x async for x in xs)\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-2:36 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-2:36 MAKE_FUNCTION
|
||||
1:0-2:36 STORE_NAME 0 (f)
|
||||
|
||||
4:0-5:35 LOAD_CONST 1 (<code object g at 0xdeadbeef, file "<?>", line 4>)
|
||||
4:0-5:35 MAKE_FUNCTION
|
||||
4:0-5:35 STORE_NAME 1 (g)
|
||||
4:0-5:35 LOAD_COMMON_CONSTANT 7 (None)
|
||||
4:0-5:35 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:?-1:? RETURN_GENERATOR
|
||||
1:?-1:? POP_TOP
|
||||
1:0-1:0 L1: RESUME 0
|
||||
|
||||
2:11-2:15 LOAD_GLOBAL 1 (list + NULL)
|
||||
2:15-2:36 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 2>)
|
||||
2:15-2:36 MAKE_FUNCTION
|
||||
2:33-2:35 LOAD_FAST_BORROW 0 (xs)
|
||||
2:15-2:36 CALL 0
|
||||
2:11-2:36 CALL 1
|
||||
2:4-2:36 RETURN_VALUE
|
||||
|
||||
-- L2: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L2 -> L2 [0] lasti
|
||||
|
||||
Disassembly of <code object <genexpr> at 0xdeadbeef, file "<?>", line 2>:
|
||||
-- RESUME 4
|
||||
|
||||
2:33-2:35 LOAD_FAST_BORROW 0 (.0)
|
||||
2:33-2:35 GET_AITER
|
||||
2:?-2:? RETURN_GENERATOR
|
||||
2:?-2:? POP_TOP
|
||||
2:0-2:0 L1: RESUME 0
|
||||
2:15-2:36 L2: GET_ANEXT
|
||||
2:15-2:36 PUSH_NULL
|
||||
2:15-2:36 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:15-2:36 L3: SEND 4 (to L6)
|
||||
2:15-2:36 L4: YIELD_VALUE 1
|
||||
2:15-2:36 L5: RESUME 3
|
||||
2:15-2:36 JUMP_BACKWARD_NO_INTERRUPT 6 (to L3)
|
||||
2:15-2:36 L6: END_SEND
|
||||
2:28-2:29 L7: STORE_FAST_LOAD_FAST 17 (x, x)
|
||||
2:16-2:17 CALL_INTRINSIC_1 4 (INTRINSIC_ASYNC_GEN_WRAP)
|
||||
2:16-2:17 YIELD_VALUE 0
|
||||
2:16-2:17 RESUME 9
|
||||
2:16-2:17 POP_TOP
|
||||
2:16-2:17 JUMP_BACKWARD 18 (to L2)
|
||||
2:15-2:36 L8: CLEANUP_THROW
|
||||
2:15-2:36 L9: JUMP_BACKWARD_NO_INTERRUPT 11 (to L6)
|
||||
2:15-2:36 L10: END_ASYNC_FOR 12 (from L6)
|
||||
2:15-2:36 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:15-2:36 RETURN_VALUE
|
||||
|
||||
-- L11: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L2 -> L11 [1] lasti
|
||||
L2 to L4 -> L10 [1]
|
||||
L4 to L5 -> L8 [4]
|
||||
L5 to L7 -> L10 [1]
|
||||
L7 to L8 -> L11 [1] lasti
|
||||
L8 to L9 -> L10 [1]
|
||||
L10 to L11 -> L11 [1] lasti
|
||||
|
||||
Disassembly of <code object g at 0xdeadbeef, file "<?>", line 4>:
|
||||
4:?-4:? RETURN_GENERATOR
|
||||
4:?-4:? POP_TOP
|
||||
4:0-4:0 L1: RESUME 0
|
||||
|
||||
5:11-5:14 LOAD_GLOBAL 1 (set + NULL)
|
||||
5:14-5:35 LOAD_CONST 0 (<code object <genexpr> at 0xdeadbeef, file "<?>", line 5>)
|
||||
5:14-5:35 MAKE_FUNCTION
|
||||
5:32-5:34 LOAD_FAST_BORROW 0 (xs)
|
||||
5:14-5:35 CALL 0
|
||||
5:11-5:35 CALL 1
|
||||
5:4-5:35 RETURN_VALUE
|
||||
|
||||
-- L2: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L2 -> L2 [0] lasti
|
||||
|
||||
Disassembly of <code object <genexpr> at 0xdeadbeef, file "<?>", line 5>:
|
||||
-- RESUME 4
|
||||
|
||||
5:32-5:34 LOAD_FAST_BORROW 0 (.0)
|
||||
5:32-5:34 GET_AITER
|
||||
5:?-5:? RETURN_GENERATOR
|
||||
5:?-5:? POP_TOP
|
||||
5:0-5:0 L1: RESUME 0
|
||||
5:14-5:35 L2: GET_ANEXT
|
||||
5:14-5:35 PUSH_NULL
|
||||
5:14-5:35 LOAD_COMMON_CONSTANT 7 (None)
|
||||
5:14-5:35 L3: SEND 4 (to L6)
|
||||
5:14-5:35 L4: YIELD_VALUE 1
|
||||
5:14-5:35 L5: RESUME 3
|
||||
5:14-5:35 JUMP_BACKWARD_NO_INTERRUPT 6 (to L3)
|
||||
5:14-5:35 L6: END_SEND
|
||||
5:27-5:28 L7: STORE_FAST_LOAD_FAST 17 (x, x)
|
||||
5:15-5:16 CALL_INTRINSIC_1 4 (INTRINSIC_ASYNC_GEN_WRAP)
|
||||
5:15-5:16 YIELD_VALUE 0
|
||||
5:15-5:16 RESUME 9
|
||||
5:15-5:16 POP_TOP
|
||||
5:15-5:16 JUMP_BACKWARD 18 (to L2)
|
||||
5:14-5:35 L8: CLEANUP_THROW
|
||||
5:14-5:35 L9: JUMP_BACKWARD_NO_INTERRUPT 11 (to L6)
|
||||
5:14-5:35 L10: END_ASYNC_FOR 12 (from L6)
|
||||
5:14-5:35 LOAD_COMMON_CONSTANT 7 (None)
|
||||
5:14-5:35 RETURN_VALUE
|
||||
|
||||
-- L11: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L2 -> L11 [1] lasti
|
||||
L2 to L4 -> L10 [1]
|
||||
L4 to L5 -> L8 [4]
|
||||
L5 to L7 -> L10 [1]
|
||||
L7 to L8 -> L11 [1] lasti
|
||||
L8 to L9 -> L10 [1]
|
||||
L10 to L11 -> L11 [1] lasti
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef f(xs):\n for x in xs:\n return x\n\nasync def g(xs):\n async for x in xs:\n return x\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-3:16 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-3:16 MAKE_FUNCTION
|
||||
1:0-3:16 STORE_NAME 0 (f)
|
||||
|
||||
5:0-7:16 LOAD_CONST 1 (<code object g at 0xdeadbeef, file "<?>", line 5>)
|
||||
5:0-7:16 MAKE_FUNCTION
|
||||
5:0-7:16 STORE_NAME 1 (g)
|
||||
5:0-7:16 LOAD_COMMON_CONSTANT 7 (None)
|
||||
5:0-7:16 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:13-2:15 LOAD_FAST 0 (xs)
|
||||
2:13-2:15 GET_ITER 0
|
||||
2:13-2:15 FOR_ITER 6 (to L1)
|
||||
2:8-2:9 STORE_FAST 1 (x)
|
||||
|
||||
3:15-3:16 LOAD_FAST_BORROW 1 (x)
|
||||
3:8-3:16 SWAP 3
|
||||
3:8-3:16 POP_TOP
|
||||
3:8-3:16 POP_TOP
|
||||
3:8-3:16 RETURN_VALUE
|
||||
|
||||
2:13-2:15 L1: END_FOR
|
||||
2:13-2:15 POP_ITER
|
||||
2:13-2:15 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:13-2:15 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object g at 0xdeadbeef, file "<?>", line 5>:
|
||||
5:?-5:? RETURN_GENERATOR
|
||||
5:?-5:? POP_TOP
|
||||
5:0-5:0 L1: RESUME 0
|
||||
|
||||
6:19-6:21 LOAD_FAST_BORROW 0 (xs)
|
||||
6:19-6:21 GET_AITER
|
||||
6:4-7:16 L2: GET_ANEXT
|
||||
6:4-7:16 PUSH_NULL
|
||||
6:4-7:16 LOAD_COMMON_CONSTANT 7 (None)
|
||||
6:4-7:16 L3: SEND 4 (to L6)
|
||||
6:4-7:16 L4: YIELD_VALUE 1
|
||||
6:4-7:16 L5: RESUME 3
|
||||
6:4-7:16 JUMP_BACKWARD_NO_INTERRUPT 6 (to L3)
|
||||
6:4-7:16 L6: END_SEND
|
||||
6:4-7:16 L7: NOT_TAKEN
|
||||
6:14-6:15 STORE_FAST 1 (x)
|
||||
|
||||
7:15-7:16 LOAD_FAST_BORROW 1 (x)
|
||||
7:8-7:16 SWAP 2
|
||||
7:8-7:16 POP_TOP
|
||||
7:8-7:16 RETURN_VALUE
|
||||
|
||||
6:4-7:16 L8: CLEANUP_THROW
|
||||
6:4-7:16 L9: JUMP_BACKWARD_NO_INTERRUPT 9 (to L6)
|
||||
6:19-6:21 L10: END_ASYNC_FOR 10 (from L6)
|
||||
6:19-6:21 LOAD_COMMON_CONSTANT 7 (None)
|
||||
6:19-6:21 RETURN_VALUE
|
||||
|
||||
-- L11: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L2 -> L11 [0] lasti
|
||||
L2 to L4 -> L10 [1]
|
||||
L4 to L5 -> L8 [4]
|
||||
L5 to L7 -> L10 [1]
|
||||
L7 to L8 -> L11 [0] lasti
|
||||
L8 to L9 -> L10 [1]
|
||||
L10 to L11 -> L11 [0] lasti
|
||||
crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__match_class_scaffolding_location.snap
Generated
+39
@@ -0,0 +1,39 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef f(x):\n match x:\n case bool(z):\n y = 0\n return y, z\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-5:15 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-5:15 MAKE_FUNCTION
|
||||
1:0-5:15 STORE_NAME 0 (f)
|
||||
1:0-5:15 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-5:15 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:10-2:11 LOAD_FAST_BORROW 0 (x)
|
||||
|
||||
3:13-3:17 LOAD_GLOBAL 0 (bool)
|
||||
3:13-3:20 LOAD_CONST 0 (())
|
||||
3:13-3:20 MATCH_CLASS 1
|
||||
3:13-3:20 COPY 1
|
||||
3:13-3:20 POP_JUMP_IF_NONE 9 (to L1)
|
||||
3:13-3:20 NOT_TAKEN
|
||||
3:13-3:20 UNPACK_SEQUENCE 1
|
||||
3:13-3:20 STORE_FAST 1 (z)
|
||||
|
||||
4:16-4:17 LOAD_SMALL_INT 0
|
||||
4:12-4:13 STORE_FAST 2 (y)
|
||||
|
||||
5:11-5:12 LOAD_FAST_BORROW_LOAD_FAST_BORROW 33 (y, z)
|
||||
5:11-5:15 BUILD_TUPLE 2
|
||||
5:4-5:15 RETURN_VALUE
|
||||
|
||||
3:13-3:20 L1: POP_TOP
|
||||
|
||||
5:11-5:12 LOAD_FAST_CHECK 2 (y)
|
||||
5:14-5:15 LOAD_FAST_CHECK 1 (z)
|
||||
5:11-5:15 BUILD_TUPLE 2
|
||||
5:4-5:15 RETURN_VALUE
|
||||
Generated
+60
@@ -0,0 +1,60 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef f(self):\n x = {}\n y = None\n match x:\n case {0: 0}:\n y = 0\n self.assertIs(y, None)\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-7:26 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-7:26 MAKE_FUNCTION
|
||||
1:0-7:26 STORE_NAME 0 (f)
|
||||
1:0-7:26 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-7:26 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:8-2:10 BUILD_MAP 0
|
||||
2:4-2:5 STORE_FAST 1 (x)
|
||||
|
||||
3:8-3:12 LOAD_COMMON_CONSTANT 7 (None)
|
||||
3:4-3:5 STORE_FAST 2 (y)
|
||||
|
||||
4:10-4:11 LOAD_FAST 1 (x)
|
||||
|
||||
5:13-5:19 MATCH_MAPPING
|
||||
5:13-5:19 POP_JUMP_IF_FALSE 29 (to L3)
|
||||
5:13-5:19 NOT_TAKEN
|
||||
5:13-5:19 GET_LEN
|
||||
5:13-5:19 LOAD_SMALL_INT 1
|
||||
5:13-5:19 COMPARE_OP 172 (>=)
|
||||
5:13-5:19 POP_JUMP_IF_FALSE 22 (to L3)
|
||||
5:13-5:19 NOT_TAKEN
|
||||
5:13-5:19 LOAD_CONST 1 ((0,))
|
||||
5:13-5:19 MATCH_KEYS
|
||||
5:13-5:19 COPY 1
|
||||
5:13-5:19 POP_JUMP_IF_NONE 14 (to L1)
|
||||
5:13-5:19 NOT_TAKEN
|
||||
5:13-5:19 UNPACK_SEQUENCE 1
|
||||
5:17-5:18 LOAD_SMALL_INT 0
|
||||
5:17-5:18 COMPARE_OP 88 (bool(==))
|
||||
5:17-5:18 POP_JUMP_IF_FALSE 7 (to L2)
|
||||
5:17-5:18 NOT_TAKEN
|
||||
5:13-5:19 POP_TOP
|
||||
5:13-5:19 POP_TOP
|
||||
|
||||
6:16-6:17 LOAD_SMALL_INT 0
|
||||
6:12-6:13 STORE_FAST 2 (y)
|
||||
6:12-6:13 JUMP_FORWARD 3 (to L4)
|
||||
|
||||
5:13-5:19 L1: POP_TOP
|
||||
5:13-5:19 L2: POP_TOP
|
||||
5:13-5:19 L3: POP_TOP
|
||||
|
||||
7:4-7:8 L4: LOAD_FAST_BORROW 0 (self)
|
||||
7:4-7:17 LOAD_ATTR 1 (assertIs + NULL|self)
|
||||
7:18-7:19 LOAD_FAST_BORROW 2 (y)
|
||||
7:21-7:25 LOAD_COMMON_CONSTANT 7 (None)
|
||||
7:4-7:26 CALL 2
|
||||
7:4-7:26 POP_TOP
|
||||
7:4-7:26 LOAD_COMMON_CONSTANT 7 (None)
|
||||
7:4-7:26 RETURN_VALUE
|
||||
Generated
+56
@@ -0,0 +1,56 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef f():\n x = False\n match 0:\n case 0 | 1 | 2 | 3:\n x = True\n return x\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-6:12 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-6:12 MAKE_FUNCTION
|
||||
1:0-6:12 STORE_NAME 0 (f)
|
||||
1:0-6:12 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-6:12 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:8-2:13 LOAD_COMMON_CONSTANT 10 (False)
|
||||
2:4-2:5 STORE_FAST 0 (x)
|
||||
|
||||
3:10-3:11 LOAD_SMALL_INT 0
|
||||
|
||||
4:13-4:14 COPY 1
|
||||
4:13-4:14 LOAD_SMALL_INT 0
|
||||
4:13-4:14 COMPARE_OP 88 (bool(==))
|
||||
4:13-4:14 POP_JUMP_IF_FALSE 2 (to L1)
|
||||
4:13-4:14 NOT_TAKEN
|
||||
4:13-4:14 JUMP_FORWARD 27 (to L5)
|
||||
4:17-4:18 L1: COPY 1
|
||||
4:17-4:18 LOAD_SMALL_INT 1
|
||||
4:17-4:18 COMPARE_OP 88 (bool(==))
|
||||
4:17-4:18 POP_JUMP_IF_FALSE 2 (to L2)
|
||||
4:17-4:18 NOT_TAKEN
|
||||
4:17-4:18 JUMP_FORWARD 19 (to L5)
|
||||
4:21-4:22 L2: COPY 1
|
||||
4:21-4:22 LOAD_SMALL_INT 2
|
||||
4:21-4:22 COMPARE_OP 88 (bool(==))
|
||||
4:21-4:22 POP_JUMP_IF_FALSE 2 (to L3)
|
||||
4:21-4:22 NOT_TAKEN
|
||||
4:21-4:22 JUMP_FORWARD 11 (to L5)
|
||||
4:25-4:26 L3: COPY 1
|
||||
4:25-4:26 LOAD_SMALL_INT 3
|
||||
4:25-4:26 COMPARE_OP 88 (bool(==))
|
||||
4:25-4:26 POP_JUMP_IF_FALSE 2 (to L4)
|
||||
4:25-4:26 NOT_TAKEN
|
||||
4:25-4:26 JUMP_FORWARD 3 (to L5)
|
||||
4:13-4:26 L4: POP_TOP
|
||||
|
||||
6:11-6:12 LOAD_FAST_BORROW 0 (x)
|
||||
6:4-6:12 RETURN_VALUE
|
||||
|
||||
4:13-4:26 L5: POP_TOP
|
||||
|
||||
5:16-5:20 LOAD_COMMON_CONSTANT 9 (True)
|
||||
5:12-5:13 STORE_FAST 0 (x)
|
||||
|
||||
6:11-6:12 LOAD_FAST_BORROW 0 (x)
|
||||
6:4-6:12 RETURN_VALUE
|
||||
Generated
+44
@@ -0,0 +1,44 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef f(self):\n match 0:\n case 0:\n x = True\n case 0:\n x = False\n self.assertIs(x, True)\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-7:26 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-7:26 MAKE_FUNCTION
|
||||
1:0-7:26 STORE_NAME 0 (f)
|
||||
1:0-7:26 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-7:26 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:10-2:11 LOAD_SMALL_INT 0
|
||||
|
||||
3:13-3:14 COPY 1
|
||||
3:13-3:14 LOAD_SMALL_INT 0
|
||||
3:13-3:14 COMPARE_OP 88 (bool(==))
|
||||
3:13-3:14 POP_JUMP_IF_FALSE 5 (to L1)
|
||||
3:13-3:14 NOT_TAKEN
|
||||
|
||||
4:16-4:20 POP_TOP
|
||||
4:16-4:20 LOAD_COMMON_CONSTANT 9 (True)
|
||||
4:12-4:13 STORE_FAST 1 (x)
|
||||
4:12-4:13 JUMP_FORWARD 8 (to L2)
|
||||
|
||||
5:13-5:14 L1: LOAD_SMALL_INT 0
|
||||
5:13-5:14 COMPARE_OP 88 (bool(==))
|
||||
5:13-5:14 POP_JUMP_IF_FALSE 3 (to L2)
|
||||
5:13-5:14 NOT_TAKEN
|
||||
|
||||
6:16-6:21 LOAD_COMMON_CONSTANT 10 (False)
|
||||
6:12-6:13 STORE_FAST 1 (x)
|
||||
|
||||
7:4-7:8 L2: LOAD_FAST_BORROW 0 (self)
|
||||
7:4-7:17 LOAD_ATTR 1 (assertIs + NULL|self)
|
||||
7:18-7:19 LOAD_FAST_CHECK 1 (x)
|
||||
7:21-7:25 LOAD_COMMON_CONSTANT 9 (True)
|
||||
7:4-7:26 CALL 2
|
||||
7:4-7:26 POP_TOP
|
||||
7:4-7:26 LOAD_COMMON_CONSTANT 7 (None)
|
||||
7:4-7:26 RETURN_VALUE
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\nimport os\nX: int\nY: str\n\"#, true)"
|
||||
---
|
||||
-- MAKE_CELL 0 (__conditional_annotations__)
|
||||
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-1:9 LOAD_CONST 1 (<code object __annotate__ at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-1:9 MAKE_FUNCTION
|
||||
1:0-1:9 STORE_NAME 2 (__annotate__)
|
||||
1:0-1:9 BUILD_SET 0
|
||||
1:0-1:9 STORE_NAME 0 (__conditional_annotations__)
|
||||
1:0-1:9 LOAD_SMALL_INT 0
|
||||
1:0-1:9 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-1:9 IMPORT_NAME 4 (os)
|
||||
1:0-1:9 STORE_NAME 1 (os)
|
||||
|
||||
2:0-2:6 LOAD_NAME 0 (__conditional_annotations__)
|
||||
2:0-2:6 LOAD_SMALL_INT 0
|
||||
2:0-2:6 SET_ADD 1
|
||||
2:0-2:6 POP_TOP
|
||||
|
||||
3:0-3:6 LOAD_NAME 0 (__conditional_annotations__)
|
||||
3:0-3:6 LOAD_SMALL_INT 1
|
||||
3:0-3:6 SET_ADD 1
|
||||
3:0-3:6 POP_TOP
|
||||
3:0-3:6 LOAD_COMMON_CONSTANT 7 (None)
|
||||
3:0-3:6 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object __annotate__ at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
1:0-1:9 LOAD_FAST_BORROW 0 (format)
|
||||
1:0-1:9 LOAD_SMALL_INT 2
|
||||
1:0-1:9 COMPARE_OP 132 (>)
|
||||
1:0-1:9 POP_JUMP_IF_FALSE 3 (to L1)
|
||||
1:0-1:9 NOT_TAKEN
|
||||
1:0-1:9 LOAD_COMMON_CONSTANT 1 (NotImplementedError)
|
||||
1:0-1:9 RAISE_VARARGS 1
|
||||
1:0-1:9 L1: BUILD_MAP 0
|
||||
|
||||
2:0-2:6 LOAD_SMALL_INT 0
|
||||
2:0-2:6 LOAD_GLOBAL 0 (__conditional_annotations__)
|
||||
2:0-2:6 CONTAINS_OP 0 (in)
|
||||
2:0-2:6 POP_JUMP_IF_FALSE 10 (to L2)
|
||||
2:0-2:6 NOT_TAKEN
|
||||
2:3-2:6 LOAD_GLOBAL 2 (int)
|
||||
2:0-2:6 COPY 2
|
||||
2:0-2:6 LOAD_CONST 1 ('X')
|
||||
|
||||
1:0-1:9 STORE_SUBSCR
|
||||
|
||||
3:0-3:6 L2: LOAD_SMALL_INT 1
|
||||
3:0-3:6 LOAD_GLOBAL 0 (__conditional_annotations__)
|
||||
3:0-3:6 CONTAINS_OP 0 (in)
|
||||
3:0-3:6 POP_JUMP_IF_FALSE 10 (to L3)
|
||||
3:0-3:6 NOT_TAKEN
|
||||
3:3-3:6 LOAD_GLOBAL 4 (str)
|
||||
3:0-3:6 COPY 2
|
||||
3:0-3:6 LOAD_CONST 2 ('Y')
|
||||
|
||||
1:0-1:9 STORE_SUBSCR
|
||||
1:0-1:9 L3: RETURN_VALUE
|
||||
Generated
+13
@@ -0,0 +1,13 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\n\"doc\"\nx = 1\n\"#, true)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-1:5 LOAD_CONST 0 ('doc')
|
||||
1:0-1:5 STORE_NAME 0 (__doc__)
|
||||
|
||||
2:4-2:5 LOAD_SMALL_INT 1
|
||||
2:0-2:1 STORE_NAME 1 (x)
|
||||
2:0-2:1 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:0-2:1 RETURN_VALUE
|
||||
Generated
+40
@@ -0,0 +1,40 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef outer():\n a = 10\n def spam():\n nonlocal a\n (a := 20)\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-5:17 LOAD_CONST 0 (<code object outer at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-5:17 MAKE_FUNCTION
|
||||
1:0-5:17 STORE_NAME 0 (outer)
|
||||
1:0-5:17 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-5:17 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object outer at 0xdeadbeef, file "<?>", line 1>:
|
||||
-- MAKE_CELL 1 (a)
|
||||
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:8-2:10 LOAD_SMALL_INT 10
|
||||
2:4-2:5 STORE_DEREF 1 (a)
|
||||
|
||||
3:4-5:17 LOAD_FAST_BORROW 1 (a)
|
||||
3:4-5:17 BUILD_TUPLE 1
|
||||
3:4-5:17 LOAD_CONST 1 (<code object spam at 0xdeadbeef, file "<?>", line 3>)
|
||||
3:4-5:17 MAKE_FUNCTION
|
||||
3:4-5:17 SET_FUNCTION_ATTRIBUTE 8 (closure)
|
||||
3:4-5:17 STORE_FAST 0 (spam)
|
||||
3:4-5:17 LOAD_COMMON_CONSTANT 7 (None)
|
||||
3:4-5:17 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object spam at 0xdeadbeef, file "<?>", line 3>:
|
||||
-- COPY_FREE_VARS 1
|
||||
|
||||
3:0-3:0 RESUME 0
|
||||
|
||||
5:14-5:16 LOAD_SMALL_INT 20
|
||||
5:9-5:16 COPY 1
|
||||
5:9-5:10 STORE_DEREF 0 (a)
|
||||
5:9-5:10 POP_TOP
|
||||
5:9-5:10 LOAD_COMMON_CONSTANT 7 (None)
|
||||
5:9-5:10 RETURN_VALUE
|
||||
+3
-3
@@ -13,13 +13,13 @@ expression: "dis(r#\"\nx = Test() and False or False\n\"#)"
|
||||
POP_JUMP_IF_FALSE 11 (to L1)
|
||||
NOT_TAKEN
|
||||
POP_TOP
|
||||
LOAD_CONST 0 (False)
|
||||
LOAD_COMMON_CONSTANT 10 (False)
|
||||
COPY 1
|
||||
TO_BOOL
|
||||
POP_JUMP_IF_TRUE 3 (to L2)
|
||||
NOT_TAKEN
|
||||
L1: POP_TOP
|
||||
LOAD_CONST 0 (False)
|
||||
LOAD_COMMON_CONSTANT 10 (False)
|
||||
L2: STORE_NAME 1 (x)
|
||||
LOAD_CONST 1 (None)
|
||||
LOAD_COMMON_CONSTANT 7 (None)
|
||||
RETURN_VALUE
|
||||
Generated
+61
-57
@@ -8,7 +8,7 @@ expression: "dis(r#\"\nasync def test():\n for stop_exc in (StopIteration('sp
|
||||
1 LOAD_CONST 0 (<code object test at 0xdeadbeef, file "<?>", line 1>)
|
||||
MAKE_FUNCTION
|
||||
STORE_NAME 0 (test)
|
||||
LOAD_CONST 1 (None)
|
||||
LOAD_COMMON_CONSTANT 7 (None)
|
||||
RETURN_VALUE
|
||||
|
||||
Disassembly of <code object test at 0xdeadbeef, file "<?>", line 1>:
|
||||
@@ -23,8 +23,8 @@ Disassembly of <code object test at 0xdeadbeef, file "<?>", line 1>:
|
||||
LOAD_CONST 1 ('ham')
|
||||
CALL 1
|
||||
BUILD_TUPLE 2
|
||||
GET_ITER
|
||||
L2: FOR_ITER 71 (to L11)
|
||||
GET_ITER 0
|
||||
L2: FOR_ITER 73 (to L11)
|
||||
STORE_FAST 0 (stop_exc)
|
||||
|
||||
3 LOAD_GLOBAL 4 (self)
|
||||
@@ -53,11 +53,12 @@ Disassembly of <code object test at 0xdeadbeef, file "<?>", line 1>:
|
||||
LOAD_SPECIAL 2 (__aenter__)
|
||||
CALL 0
|
||||
GET_AWAITABLE 1
|
||||
LOAD_CONST 3 (None)
|
||||
L6: SEND 3 (to L9)
|
||||
PUSH_NULL
|
||||
LOAD_COMMON_CONSTANT 7 (None)
|
||||
L6: SEND 4 (to L9)
|
||||
L7: YIELD_VALUE 1
|
||||
L8: RESUME 3
|
||||
JUMP_BACKWARD_NO_INTERRUPT 5 (to L6)
|
||||
JUMP_BACKWARD_NO_INTERRUPT 6 (to L6)
|
||||
L9: END_SEND
|
||||
L10: POP_TOP
|
||||
|
||||
@@ -66,7 +67,7 @@ Disassembly of <code object test at 0xdeadbeef, file "<?>", line 1>:
|
||||
|
||||
2 L11: END_FOR
|
||||
POP_ITER
|
||||
LOAD_CONST 3 (None)
|
||||
LOAD_COMMON_CONSTANT 7 (None)
|
||||
RETURN_VALUE
|
||||
|
||||
5 L12: CLEANUP_THROW
|
||||
@@ -74,11 +75,12 @@ Disassembly of <code object test at 0xdeadbeef, file "<?>", line 1>:
|
||||
L14: PUSH_EXC_INFO
|
||||
WITH_EXCEPT_START
|
||||
GET_AWAITABLE 2
|
||||
LOAD_CONST 3 (None)
|
||||
L15: SEND 4 (to L19)
|
||||
PUSH_NULL
|
||||
LOAD_COMMON_CONSTANT 7 (None)
|
||||
L15: SEND 5 (to L19)
|
||||
L16: YIELD_VALUE 1
|
||||
L17: RESUME 3
|
||||
JUMP_BACKWARD_NO_INTERRUPT 5 (to L15)
|
||||
JUMP_BACKWARD_NO_INTERRUPT 6 (to L15)
|
||||
L18: CLEANUP_THROW
|
||||
L19: END_SEND
|
||||
TO_BOOL
|
||||
@@ -102,86 +104,88 @@ Disassembly of <code object test at 0xdeadbeef, file "<?>", line 1>:
|
||||
LOAD_ATTR 13 (fail + NULL|self)
|
||||
LOAD_FAST 0 (stop_exc)
|
||||
FORMAT_SIMPLE
|
||||
LOAD_CONST 4 (' was suppressed')
|
||||
LOAD_CONST 3 (' was suppressed')
|
||||
BUILD_STRING 2
|
||||
CALL 1
|
||||
POP_TOP
|
||||
JUMP_FORWARD 45 (to L33)
|
||||
JUMP_FORWARD 45 (to L35)
|
||||
|
||||
-- L27: PUSH_EXC_INFO
|
||||
|
||||
7 LOAD_GLOBAL 14 (Exception)
|
||||
CHECK_EXC_MATCH
|
||||
POP_JUMP_IF_FALSE 32 (to L31)
|
||||
NOT_TAKEN
|
||||
STORE_FAST 1 (ex)
|
||||
POP_JUMP_IF_FALSE 32 (to L33)
|
||||
L28: NOT_TAKEN
|
||||
L29: STORE_FAST 1 (ex)
|
||||
|
||||
8 L28: LOAD_GLOBAL 4 (self)
|
||||
8 L30: LOAD_GLOBAL 4 (self)
|
||||
LOAD_ATTR 17 (assertIs + NULL|self)
|
||||
LOAD_FAST_LOAD_FAST 16 (ex, stop_exc)
|
||||
CALL 2
|
||||
POP_TOP
|
||||
L29: POP_EXCEPT
|
||||
LOAD_CONST 3 (None)
|
||||
L31: POP_EXCEPT
|
||||
LOAD_COMMON_CONSTANT 7 (None)
|
||||
STORE_FAST 1 (ex)
|
||||
DELETE_FAST 1 (ex)
|
||||
JUMP_FORWARD 8 (to L33)
|
||||
JUMP_FORWARD 8 (to L35)
|
||||
|
||||
-- L30: LOAD_CONST 3 (None)
|
||||
-- L32: LOAD_COMMON_CONSTANT 7 (None)
|
||||
STORE_FAST 1 (ex)
|
||||
DELETE_FAST 1 (ex)
|
||||
RERAISE 1
|
||||
|
||||
7 L31: RERAISE 0
|
||||
7 L33: RERAISE 0
|
||||
|
||||
-- L32: COPY 3
|
||||
-- L34: COPY 3
|
||||
POP_EXCEPT
|
||||
RERAISE 1
|
||||
|
||||
3 L33: LOAD_CONST 3 (None)
|
||||
LOAD_CONST 3 (None)
|
||||
LOAD_CONST 3 (None)
|
||||
3 L35: LOAD_COMMON_CONSTANT 7 (None)
|
||||
LOAD_COMMON_CONSTANT 7 (None)
|
||||
LOAD_COMMON_CONSTANT 7 (None)
|
||||
CALL 3
|
||||
POP_TOP
|
||||
JUMP_BACKWARD 188 (to L2)
|
||||
L34: PUSH_EXC_INFO
|
||||
JUMP_BACKWARD 192 (to L2)
|
||||
L36: PUSH_EXC_INFO
|
||||
WITH_EXCEPT_START
|
||||
TO_BOOL
|
||||
POP_JUMP_IF_TRUE 2 (to L35)
|
||||
NOT_TAKEN
|
||||
RERAISE 2
|
||||
L35: POP_TOP
|
||||
L36: POP_EXCEPT
|
||||
POP_JUMP_IF_TRUE 2 (to L39)
|
||||
L37: NOT_TAKEN
|
||||
L38: RERAISE 2
|
||||
L39: POP_TOP
|
||||
L40: POP_EXCEPT
|
||||
POP_TOP
|
||||
POP_TOP
|
||||
POP_TOP
|
||||
JUMP_BACKWARD 205 (to L2)
|
||||
JUMP_BACKWARD 209 (to L2)
|
||||
|
||||
-- L37: COPY 3
|
||||
-- L41: COPY 3
|
||||
POP_EXCEPT
|
||||
RERAISE 1
|
||||
L38: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
L42: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L3 -> L38 [0] lasti
|
||||
L3 to L4 -> L34 [3] lasti
|
||||
L5 to L7 -> L27 [3]
|
||||
L7 to L8 -> L12 [7]
|
||||
L8 to L10 -> L27 [3]
|
||||
L10 to L11 -> L14 [5] lasti
|
||||
L11 to L12 -> L38 [0] lasti
|
||||
L12 to L13 -> L27 [3]
|
||||
L14 to L16 -> L24 [7] lasti
|
||||
L16 to L17 -> L18 [10]
|
||||
L17 to L20 -> L24 [7] lasti
|
||||
L21 to L23 -> L24 [7] lasti
|
||||
L23 to L25 -> L27 [3]
|
||||
L26 to L27 -> L34 [3] lasti
|
||||
L27 to L28 -> L32 [4] lasti
|
||||
L28 to L29 -> L30 [4] lasti
|
||||
L29 to L30 -> L34 [3] lasti
|
||||
L30 to L32 -> L32 [4] lasti
|
||||
L32 to L33 -> L34 [3] lasti
|
||||
L33 to L34 -> L38 [0] lasti
|
||||
L34 to L36 -> L37 [5] lasti
|
||||
L36 to L38 -> L38 [0] lasti
|
||||
L1 to L3 -> L42 [0] lasti
|
||||
L3 to L4 -> L36 [4] lasti
|
||||
L5 to L7 -> L27 [4]
|
||||
L7 to L8 -> L12 [9]
|
||||
L8 to L10 -> L27 [4]
|
||||
L10 to L11 -> L14 [6] lasti
|
||||
L11 to L12 -> L42 [0] lasti
|
||||
L12 to L13 -> L27 [4]
|
||||
L14 to L16 -> L24 [8] lasti
|
||||
L16 to L17 -> L18 [12]
|
||||
L17 to L20 -> L24 [8] lasti
|
||||
L21 to L23 -> L24 [8] lasti
|
||||
L23 to L25 -> L27 [4]
|
||||
L26 to L27 -> L36 [4] lasti
|
||||
L27 to L28 -> L34 [5] lasti
|
||||
L29 to L30 -> L34 [5] lasti
|
||||
L30 to L31 -> L32 [5] lasti
|
||||
L31 to L32 -> L36 [4] lasti
|
||||
L32 to L34 -> L34 [5] lasti
|
||||
L34 to L35 -> L36 [4] lasti
|
||||
L35 to L36 -> L42 [0] lasti
|
||||
L36 to L37 -> L41 [6] lasti
|
||||
L38 to L40 -> L41 [6] lasti
|
||||
L40 to L42 -> L42 [0] lasti
|
||||
Generated
+35
@@ -0,0 +1,35 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef f(c):\n return not (b\" \" <= c <= b\"~\")\n\"#, true)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-2:34 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-2:34 MAKE_FUNCTION
|
||||
1:0-2:34 STORE_NAME 0 (f)
|
||||
1:0-2:34 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-2:34 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:16-2:20 LOAD_CONST 0 (b' ')
|
||||
2:24-2:25 LOAD_FAST 0 (c)
|
||||
2:16-2:33 SWAP 2
|
||||
2:16-2:33 COPY 2
|
||||
2:16-2:33 COMPARE_OP 42 (<=)
|
||||
2:16-2:33 COPY 1
|
||||
2:16-2:33 TO_BOOL
|
||||
2:16-2:33 POP_JUMP_IF_FALSE 11 (to L1)
|
||||
2:16-2:33 NOT_TAKEN
|
||||
2:16-2:33 POP_TOP
|
||||
2:29-2:33 LOAD_CONST 1 (b'~')
|
||||
2:16-2:33 COMPARE_OP 42 (<=)
|
||||
2:11-2:34 TO_BOOL
|
||||
2:11-2:34 UNARY_NOT
|
||||
2:4-2:34 RETURN_VALUE
|
||||
2:16-2:33 L1: SWAP 2
|
||||
2:16-2:33 POP_TOP
|
||||
2:11-2:34 TO_BOOL
|
||||
2:11-2:34 UNARY_NOT
|
||||
2:4-2:34 RETURN_VALUE
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef f(self, other):\n return not self == other\n\"#, true)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-2:28 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-2:28 MAKE_FUNCTION
|
||||
1:0-2:28 STORE_NAME 0 (f)
|
||||
1:0-2:28 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-2:28 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:15-2:19 LOAD_FAST_BORROW_LOAD_FAST_BORROW 1 (self, other)
|
||||
2:11-2:28 COMPARE_OP 88 (bool(==))
|
||||
2:11-2:28 UNARY_NOT
|
||||
2:4-2:28 RETURN_VALUE
|
||||
Generated
+65
@@ -0,0 +1,65 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef f(x, y, z):\n while x:\n if y:\n pass\n elif z:\n if y < 0:\n return y\n if z:\n y = y + 1\n elif y:\n return 1\n return -1\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-12:13 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-12:13 MAKE_FUNCTION
|
||||
1:0-12:13 STORE_NAME 0 (f)
|
||||
1:0-12:13 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-12:13 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:10-2:11 L1: LOAD_FAST_BORROW 0 (x)
|
||||
2:10-2:11 TO_BOOL
|
||||
2:10-2:11 POP_JUMP_IF_FALSE 61 (to L7)
|
||||
2:10-2:11 NOT_TAKEN
|
||||
|
||||
3:11-3:12 LOAD_FAST_BORROW 1 (y)
|
||||
3:11-3:12 TO_BOOL
|
||||
3:11-3:12 POP_JUMP_IF_FALSE 3 (to L2)
|
||||
3:11-3:12 NOT_TAKEN
|
||||
|
||||
4:12-4:16 JUMP_BACKWARD 18 (to L1)
|
||||
|
||||
5:13-5:14 L2: LOAD_FAST_BORROW 2 (z)
|
||||
5:13-5:14 TO_BOOL
|
||||
5:13-5:14 POP_JUMP_IF_FALSE 31 (to L5)
|
||||
5:13-5:14 NOT_TAKEN
|
||||
|
||||
6:15-6:16 LOAD_FAST_BORROW 1 (y)
|
||||
6:19-6:20 LOAD_SMALL_INT 0
|
||||
6:15-6:20 COMPARE_OP 18 (bool(<))
|
||||
6:15-6:20 POP_JUMP_IF_FALSE 3 (to L3)
|
||||
6:15-6:20 NOT_TAKEN
|
||||
|
||||
7:23-7:24 LOAD_FAST_BORROW 1 (y)
|
||||
7:16-7:24 RETURN_VALUE
|
||||
|
||||
8:15-8:16 L3: LOAD_FAST_BORROW 2 (z)
|
||||
8:15-8:16 TO_BOOL
|
||||
8:15-8:16 POP_JUMP_IF_FALSE 12 (to L4)
|
||||
8:15-8:16 NOT_TAKEN
|
||||
|
||||
9:20-9:21 LOAD_FAST_BORROW 1 (y)
|
||||
9:24-9:25 LOAD_SMALL_INT 1
|
||||
9:20-9:25 BINARY_OP 0 (+)
|
||||
9:16-9:17 STORE_FAST 1 (y)
|
||||
9:16-9:17 JUMP_BACKWARD 54 (to L1)
|
||||
|
||||
8:15-8:16 L4: JUMP_BACKWARD 56 (to L1)
|
||||
|
||||
10:13-10:14 L5: LOAD_FAST_BORROW 1 (y)
|
||||
10:13-10:14 TO_BOOL
|
||||
10:13-10:14 POP_JUMP_IF_TRUE 3 (to L6)
|
||||
10:13-10:14 NOT_TAKEN
|
||||
10:13-10:14 JUMP_BACKWARD 66 (to L1)
|
||||
|
||||
11:19-11:20 L6: LOAD_SMALL_INT 1
|
||||
11:19-11:20 RETURN_VALUE
|
||||
|
||||
12:11-12:13 L7: LOAD_COMMON_CONSTANT 11 (-1)
|
||||
12:4-12:13 RETURN_VALUE
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef f(*args: *Ts): pass\n\"#, true)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-1:23 LOAD_CONST 0 (<code object __annotate__ at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-1:23 MAKE_FUNCTION
|
||||
1:0-1:23 LOAD_CONST 1 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-1:23 MAKE_FUNCTION
|
||||
1:0-1:23 SET_FUNCTION_ATTRIBUTE 16 (annotate)
|
||||
1:0-1:23 STORE_NAME 0 (f)
|
||||
1:0-1:23 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-1:23 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object __annotate__ at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
1:0-1:23 LOAD_FAST_BORROW 0 (format)
|
||||
1:0-1:23 LOAD_SMALL_INT 2
|
||||
1:0-1:23 COMPARE_OP 132 (>)
|
||||
1:0-1:23 POP_JUMP_IF_FALSE 3 (to L1)
|
||||
1:0-1:23 NOT_TAKEN
|
||||
1:0-1:23 LOAD_COMMON_CONSTANT 1 (NotImplementedError)
|
||||
1:0-1:23 RAISE_VARARGS 1
|
||||
1:0-1:23 L1: LOAD_CONST 1 ('args')
|
||||
1:14-1:16 LOAD_GLOBAL 0 (Ts)
|
||||
1:0-1:23 UNPACK_SEQUENCE 1
|
||||
1:0-1:23 BUILD_MAP 1
|
||||
1:0-1:23 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
1:19-1:23 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:19-1:23 RETURN_VALUE
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef f(a, b):\n a, b = a, b\n b, a = a, b\n\"#, true)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-3:15 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-3:15 MAKE_FUNCTION
|
||||
1:0-3:15 STORE_NAME 0 (f)
|
||||
1:0-3:15 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-3:15 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:11-2:12 LOAD_FAST_LOAD_FAST 1 (a, b)
|
||||
2:7-2:8 STORE_FAST_STORE_FAST 16 (b, a)
|
||||
|
||||
3:11-3:12 LOAD_FAST_LOAD_FAST 1 (a, b)
|
||||
3:7-3:8 STORE_FAST_STORE_FAST 1 (a, b)
|
||||
3:7-3:8 LOAD_COMMON_CONSTANT 7 (None)
|
||||
3:7-3:8 RETURN_VALUE
|
||||
Generated
+27
@@ -0,0 +1,27 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef and_false(x):\n return False and x\n\ndef or_true(x):\n return True or x\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-2:22 LOAD_CONST 0 (<code object and_false at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-2:22 MAKE_FUNCTION
|
||||
1:0-2:22 STORE_NAME 0 (and_false)
|
||||
|
||||
4:0-5:20 LOAD_CONST 1 (<code object or_true at 0xdeadbeef, file "<?>", line 4>)
|
||||
4:0-5:20 MAKE_FUNCTION
|
||||
4:0-5:20 STORE_NAME 1 (or_true)
|
||||
4:0-5:20 LOAD_COMMON_CONSTANT 7 (None)
|
||||
4:0-5:20 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object and_false at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:11-2:16 LOAD_COMMON_CONSTANT 10 (False)
|
||||
2:4-2:22 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object or_true at 0xdeadbeef, file "<?>", line 4>:
|
||||
4:0-4:0 RESUME 0
|
||||
|
||||
5:11-5:15 LOAD_COMMON_CONSTANT 9 (True)
|
||||
5:4-5:20 RETURN_VALUE
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef f(self, node):\n self.flag = True\n try:\n self.body(node)\n finally:\n self.flag = False\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-6:25 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-6:25 MAKE_FUNCTION
|
||||
1:0-6:25 STORE_NAME 0 (f)
|
||||
1:0-6:25 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-6:25 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:16-2:20 LOAD_COMMON_CONSTANT 9 (True)
|
||||
2:4-2:8 LOAD_FAST_BORROW 0 (self)
|
||||
2:4-2:13 STORE_ATTR 0 (flag)
|
||||
|
||||
3:4-6:25 NOP
|
||||
|
||||
4:8-4:12 L1: LOAD_FAST_BORROW 0 (self)
|
||||
4:8-4:17 LOAD_ATTR 3 (body + NULL|self)
|
||||
4:18-4:22 LOAD_FAST_BORROW 1 (node)
|
||||
4:8-4:23 CALL 1
|
||||
4:8-4:23 POP_TOP
|
||||
|
||||
6:20-6:25 L2: LOAD_COMMON_CONSTANT 10 (False)
|
||||
6:8-6:12 LOAD_FAST_BORROW 0 (self)
|
||||
6:8-6:17 STORE_ATTR 0 (flag)
|
||||
6:8-6:17 LOAD_COMMON_CONSTANT 7 (None)
|
||||
6:8-6:17 RETURN_VALUE
|
||||
|
||||
-- L3: PUSH_EXC_INFO
|
||||
|
||||
6:20-6:25 LOAD_COMMON_CONSTANT 10 (False)
|
||||
6:8-6:12 LOAD_FAST 0 (self)
|
||||
6:8-6:17 STORE_ATTR 0 (flag)
|
||||
6:8-6:17 RERAISE 0
|
||||
|
||||
-- L4: COPY 3
|
||||
-- POP_EXCEPT
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L2 -> L3 [0]
|
||||
L3 to L4 -> L4 [1] lasti
|
||||
Generated
+66
@@ -0,0 +1,66 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ntype BoundGenericAlias[X: int] = set[X]\n\"#, true)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-1:39 LOAD_CONST 0 (<code object <generic parameters of BoundGenericAlias> at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-1:39 MAKE_FUNCTION
|
||||
1:0-1:39 PUSH_NULL
|
||||
1:0-1:39 CALL 0
|
||||
1:0-1:39 STORE_NAME 0 (BoundGenericAlias)
|
||||
1:0-1:39 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-1:39 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object <generic parameters of BoundGenericAlias> at 0xdeadbeef, file "<?>", line 1>:
|
||||
-- MAKE_CELL 0 (X)
|
||||
|
||||
1:0-1:0 RESUME 0
|
||||
1:0-1:39 LOAD_CONST 0 ('BoundGenericAlias')
|
||||
1:23-1:29 LOAD_CONST 1 ('X')
|
||||
1:26-1:29 LOAD_CONST 2 ((1,))
|
||||
1:26-1:29 LOAD_CONST 3 (<code object X at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:26-1:29 MAKE_FUNCTION
|
||||
1:26-1:29 SET_FUNCTION_ATTRIBUTE 1 (defaults)
|
||||
1:23-1:29 CALL_INTRINSIC_2 2 (INTRINSIC_TYPEVAR_WITH_BOUND)
|
||||
1:23-1:29 COPY 1
|
||||
1:23-1:29 STORE_DEREF 0 (X)
|
||||
1:23-1:29 BUILD_TUPLE 1
|
||||
1:0-1:39 LOAD_CONST 2 ((1,))
|
||||
1:0-1:39 LOAD_FAST_BORROW 0 (X)
|
||||
1:0-1:39 BUILD_TUPLE 1
|
||||
1:0-1:39 LOAD_CONST 4 (<code object BoundGenericAlias at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-1:39 MAKE_FUNCTION
|
||||
1:0-1:39 SET_FUNCTION_ATTRIBUTE 8 (closure)
|
||||
1:0-1:39 SET_FUNCTION_ATTRIBUTE 1 (defaults)
|
||||
1:0-1:39 BUILD_TUPLE 3
|
||||
1:0-1:39 CALL_INTRINSIC_1 11 (INTRINSIC_TYPEALIAS)
|
||||
1:0-1:39 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object X at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
1:26-1:29 LOAD_FAST_BORROW 0 (.format)
|
||||
1:26-1:29 LOAD_SMALL_INT 2
|
||||
1:26-1:29 COMPARE_OP 132 (>)
|
||||
1:26-1:29 POP_JUMP_IF_FALSE 3 (to L1)
|
||||
1:26-1:29 NOT_TAKEN
|
||||
1:26-1:29 LOAD_COMMON_CONSTANT 1 (NotImplementedError)
|
||||
1:26-1:29 RAISE_VARARGS 1
|
||||
1:26-1:29 L1: LOAD_GLOBAL 0 (int)
|
||||
1:26-1:29 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object BoundGenericAlias at 0xdeadbeef, file "<?>", line 1>:
|
||||
-- COPY_FREE_VARS 1
|
||||
|
||||
1:0-1:0 RESUME 0
|
||||
1:0-1:39 LOAD_FAST_BORROW 0 (.format)
|
||||
1:0-1:39 LOAD_SMALL_INT 2
|
||||
1:0-1:39 COMPARE_OP 132 (>)
|
||||
1:0-1:39 POP_JUMP_IF_FALSE 3 (to L1)
|
||||
1:0-1:39 NOT_TAKEN
|
||||
1:0-1:39 LOAD_COMMON_CONSTANT 1 (NotImplementedError)
|
||||
1:0-1:39 RAISE_VARARGS 1
|
||||
1:33-1:36 L1: LOAD_GLOBAL 0 (set)
|
||||
1:37-1:38 LOAD_DEREF 1 (X)
|
||||
1:33-1:39 BINARY_OP 26 ([])
|
||||
1:0-1:39 RETURN_VALUE
|
||||
Generated
+62
@@ -0,0 +1,62 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef f(c):\n with c:\n x = 1\n y = 2\n\"#, true)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-4:9 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-4:9 MAKE_FUNCTION
|
||||
1:0-4:9 STORE_NAME 0 (f)
|
||||
1:0-4:9 LOAD_COMMON_CONSTANT 7 (None)
|
||||
1:0-4:9 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:0-1:0 RESUME 0
|
||||
|
||||
2:9-2:10 LOAD_FAST_BORROW 0 (c)
|
||||
2:9-2:10 COPY 1
|
||||
2:9-2:10 LOAD_SPECIAL 1 (__exit__)
|
||||
2:9-2:10 SWAP 2
|
||||
2:9-2:10 SWAP 3
|
||||
2:9-2:10 LOAD_SPECIAL 0 (__enter__)
|
||||
2:9-2:10 CALL 0
|
||||
2:9-2:10 L1: POP_TOP
|
||||
|
||||
3:12-3:13 LOAD_SMALL_INT 1
|
||||
3:8-3:9 STORE_FAST 1 (x)
|
||||
|
||||
2:9-2:10 L2: LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:9-2:10 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:9-2:10 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:9-2:10 CALL 3
|
||||
2:9-2:10 POP_TOP
|
||||
|
||||
4:8-4:9 LOAD_SMALL_INT 2
|
||||
4:4-4:5 STORE_FAST 2 (y)
|
||||
4:4-4:5 LOAD_COMMON_CONSTANT 7 (None)
|
||||
4:4-4:5 RETURN_VALUE
|
||||
|
||||
2:9-2:10 L3: PUSH_EXC_INFO
|
||||
2:9-2:10 WITH_EXCEPT_START
|
||||
2:9-2:10 TO_BOOL
|
||||
2:9-2:10 POP_JUMP_IF_TRUE 2 (to L6)
|
||||
2:9-2:10 L4: NOT_TAKEN
|
||||
2:9-2:10 L5: RERAISE 2
|
||||
2:9-2:10 L6: POP_TOP
|
||||
2:9-2:10 L7: POP_EXCEPT
|
||||
2:9-2:10 POP_TOP
|
||||
2:9-2:10 POP_TOP
|
||||
2:9-2:10 POP_TOP
|
||||
|
||||
4:8-4:9 LOAD_SMALL_INT 2
|
||||
4:4-4:5 STORE_FAST 2 (y)
|
||||
4:4-4:5 LOAD_COMMON_CONSTANT 7 (None)
|
||||
4:4-4:5 RETURN_VALUE
|
||||
|
||||
-- L8: COPY 3
|
||||
-- POP_EXCEPT
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L2 -> L3 [2] lasti
|
||||
L3 to L4 -> L8 [4] lasti
|
||||
L5 to L7 -> L8 [4] lasti
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
---
|
||||
source: crates/stdlib/src/_opcode.rs
|
||||
expression: "dis(r#\"\ndef f(xs):\n yield from xs\n\nasync def g(xs):\n return await xs\n\"#,\ntrue)"
|
||||
---
|
||||
0:0-1:0 RESUME 0
|
||||
|
||||
1:0-2:17 LOAD_CONST 0 (<code object f at 0xdeadbeef, file "<?>", line 1>)
|
||||
1:0-2:17 MAKE_FUNCTION
|
||||
1:0-2:17 STORE_NAME 0 (f)
|
||||
|
||||
4:0-5:19 LOAD_CONST 1 (<code object g at 0xdeadbeef, file "<?>", line 4>)
|
||||
4:0-5:19 MAKE_FUNCTION
|
||||
4:0-5:19 STORE_NAME 1 (g)
|
||||
4:0-5:19 LOAD_COMMON_CONSTANT 7 (None)
|
||||
4:0-5:19 RETURN_VALUE
|
||||
|
||||
Disassembly of <code object f at 0xdeadbeef, file "<?>", line 1>:
|
||||
1:?-1:? RETURN_GENERATOR
|
||||
1:?-1:? POP_TOP
|
||||
1:0-1:0 L1: RESUME 0
|
||||
|
||||
2:15-2:17 LOAD_FAST 0 (xs)
|
||||
2:4-2:17 GET_ITER 3
|
||||
2:4-2:17 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:4-2:17 L2: SEND 4 (to L5)
|
||||
2:4-2:17 L3: YIELD_VALUE 1
|
||||
2:4-2:17 L4: RESUME 2
|
||||
2:4-2:17 JUMP_BACKWARD_NO_INTERRUPT 6 (to L2)
|
||||
2:4-2:17 L5: END_SEND
|
||||
2:4-2:17 POP_TOP
|
||||
2:4-2:17 LOAD_COMMON_CONSTANT 7 (None)
|
||||
2:4-2:17 RETURN_VALUE
|
||||
2:4-2:17 L6: CLEANUP_THROW
|
||||
2:4-2:17 L7: JUMP_BACKWARD_NO_INTERRUPT 6 (to L5)
|
||||
|
||||
-- L8: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L3 -> L8 [0] lasti
|
||||
L3 to L4 -> L6 [3]
|
||||
L4 to L7 -> L8 [0] lasti
|
||||
|
||||
Disassembly of <code object g at 0xdeadbeef, file "<?>", line 4>:
|
||||
4:?-4:? RETURN_GENERATOR
|
||||
4:?-4:? POP_TOP
|
||||
4:0-4:0 L1: RESUME 0
|
||||
|
||||
5:17-5:19 LOAD_FAST_BORROW 0 (xs)
|
||||
5:11-5:19 GET_AWAITABLE 0
|
||||
5:11-5:19 PUSH_NULL
|
||||
5:11-5:19 LOAD_COMMON_CONSTANT 7 (None)
|
||||
5:11-5:19 L2: SEND 4 (to L5)
|
||||
5:11-5:19 L3: YIELD_VALUE 1
|
||||
5:11-5:19 L4: RESUME 3
|
||||
5:11-5:19 JUMP_BACKWARD_NO_INTERRUPT 6 (to L2)
|
||||
5:11-5:19 L5: END_SEND
|
||||
5:4-5:19 RETURN_VALUE
|
||||
5:11-5:19 L6: CLEANUP_THROW
|
||||
5:11-5:19 L7: JUMP_BACKWARD_NO_INTERRUPT 4 (to L5)
|
||||
|
||||
-- L8: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
|
||||
-- RERAISE 1
|
||||
ExceptionTable:
|
||||
L1 to L3 -> L8 [0] lasti
|
||||
L3 to L4 -> L6 [3]
|
||||
L4 to L7 -> L8 [0] lasti
|
||||
+321
-95
@@ -1,8 +1,10 @@
|
||||
//! Infamous code object. The python class `code`
|
||||
|
||||
mod const_cache;
|
||||
|
||||
use super::descriptor::{MemberKind, MemberLayout};
|
||||
use super::{PyBytesRef, PyStrRef, PyTupleRef, PyType, set::PyFrozenSet};
|
||||
use crate::common::lock::PyMutex;
|
||||
use crate::common::lock::{OnceCell, PyMutex};
|
||||
#[cfg(feature = "host_env")]
|
||||
use crate::convert::ToPyException;
|
||||
use crate::{
|
||||
@@ -154,11 +156,11 @@ pub struct ReplaceArgs {
|
||||
#[pyarg(named, optional)]
|
||||
co_firstlineno: OptionalArg<u32>,
|
||||
#[pyarg(named, optional)]
|
||||
co_consts: OptionalArg<Vec<PyObjectRef>>,
|
||||
co_consts: OptionalArg<PyTupleRef>,
|
||||
#[pyarg(named, optional)]
|
||||
co_name: OptionalArg<PyStrRef>,
|
||||
#[pyarg(named, optional)]
|
||||
co_names: OptionalArg<Vec<PyObjectRef>>,
|
||||
co_names: OptionalArg<PyTupleRef>,
|
||||
#[pyarg(named, optional)]
|
||||
co_flags: OptionalArg<u32>,
|
||||
#[pyarg(named, optional)]
|
||||
@@ -256,7 +258,34 @@ fn borrow_obj_constant(obj: &PyObject) -> BorrowedConstant<'_, Literal> {
|
||||
})
|
||||
}
|
||||
|
||||
/// `co_consts` viewed as constants.
|
||||
#[derive(Clone)]
|
||||
pub struct LiteralTuple(PyTupleRef);
|
||||
|
||||
impl LiteralTuple {
|
||||
pub const fn as_pytuple(&self) -> &PyTupleRef {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for LiteralTuple {
|
||||
type Target = [Literal];
|
||||
|
||||
fn deref(&self) -> &[Literal] {
|
||||
let elements = self.0.as_slice();
|
||||
// SAFETY: Literal is repr(transparent) over PyObjectRef.
|
||||
unsafe { &*(elements as *const [PyObjectRef] as *const [Literal]) }
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<Literal> for LiteralTuple {
|
||||
fn from_iter<T: IntoIterator<Item = Literal>>(iter: T) -> Self {
|
||||
Self(Context::genesis().new_tuple(iter.into_iter().map(|c| c.0).collect()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Constant for Literal {
|
||||
type Tuple = LiteralTuple;
|
||||
type Name = &'static PyStrInterned;
|
||||
fn borrow_constant(&self) -> BorrowedConstant<'_, Self> {
|
||||
borrow_obj_constant(&self.0)
|
||||
@@ -357,6 +386,13 @@ pub(crate) type CodeObject = bytecode::CodeObject<Literal>;
|
||||
|
||||
pub trait IntoCodeObject {
|
||||
fn into_code_object(self, vm: &VirtualMachine) -> CodeObject;
|
||||
|
||||
fn into_pycode(self, vm: &VirtualMachine) -> PyCode
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
PyCode::new(self.into_code_object(vm), &vm.ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoCodeObject for CodeObject {
|
||||
@@ -367,7 +403,11 @@ impl IntoCodeObject for CodeObject {
|
||||
|
||||
impl IntoCodeObject for bytecode::CodeObject {
|
||||
fn into_code_object(self, vm: &VirtualMachine) -> CodeObject {
|
||||
self.map_bag(PyVmBag(vm))
|
||||
const_cache::into_code_object(self, vm)
|
||||
}
|
||||
|
||||
fn into_pycode(self, vm: &VirtualMachine) -> PyCode {
|
||||
const_cache::into_pycode(self, vm)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,13 +429,21 @@ pub struct CoMonitoringData {
|
||||
pub per_instruction_opcodes: Vec<u8>,
|
||||
}
|
||||
|
||||
/// `_PyCoCached`: objects created on first use.
|
||||
#[derive(Default)]
|
||||
struct CoCached {
|
||||
code: OnceCell<PyBytesRef>,
|
||||
varnames: OnceCell<PyTupleRef>,
|
||||
cellvars: OnceCell<PyTupleRef>,
|
||||
freevars: OnceCell<PyTupleRef>,
|
||||
}
|
||||
|
||||
#[pyclass(module = false, name = "code")]
|
||||
pub struct PyCode {
|
||||
#[pymember(name = "co_argcount", path = "arg_count")]
|
||||
#[pymember(name = "co_posonlyargcount", path = "posonlyarg_count")]
|
||||
#[pymember(name = "co_kwonlyargcount", path = "kwonlyarg_count")]
|
||||
#[pymember(name = "co_stacksize", path = "max_stackdepth")]
|
||||
#[pymember(name = "co_name", path = "obj_name")]
|
||||
#[pymember(name = "co_qualname", path = "qualname")]
|
||||
#[pymember(name = "co_flags", path = "flags")]
|
||||
pub code: CodeObject,
|
||||
@@ -403,6 +451,16 @@ pub struct PyCode {
|
||||
/// Derived once so frame-local proxy operations do not repeatedly scan
|
||||
/// merged cell variables.
|
||||
localsplus_names: Box<[&'static PyStrInterned]>,
|
||||
pub(crate) co_name: PyStrRef,
|
||||
pub(crate) co_names: PyTupleRef,
|
||||
pub(crate) co_localsplusnames: PyTupleRef,
|
||||
pub(crate) co_localspluskinds: PyBytesRef,
|
||||
pub(crate) co_linetable: PyBytesRef,
|
||||
pub(crate) co_exceptiontable: PyBytesRef,
|
||||
co_cached: CoCached,
|
||||
/// `_co_firsttraceable`: the index of the first `RESUME` that does not
|
||||
/// start a generator expression, or the code length if there is none.
|
||||
pub(crate) first_traceable: usize,
|
||||
#[pymember(name = "co_filename")]
|
||||
source_path: AtomicPtr<PyStrInterned>,
|
||||
#[pymember(name = "co_nlocals")]
|
||||
@@ -506,12 +564,17 @@ fn build_localspluskinds(
|
||||
Ok(kinds.into_boxed_slice())
|
||||
}
|
||||
|
||||
fn holds_names(tuple: &super::PyTuple, names: &[&'static PyStrInterned]) -> bool {
|
||||
tuple.len() == names.len() && tuple.iter().zip(names).all(|(item, name)| item.is(*name))
|
||||
}
|
||||
|
||||
impl MemberLayout for CodeFlags {
|
||||
const KIND: MemberKind = MemberKind::Int;
|
||||
}
|
||||
|
||||
impl PyCode {
|
||||
pub fn new(code: CodeObject) -> Self {
|
||||
pub fn new(mut code: CodeObject, ctx: &Context) -> Self {
|
||||
code.instructions.fixup_get_iter(code.flags);
|
||||
let sp = code.source_path as *const PyStrInterned as *mut PyStrInterned;
|
||||
let nlocals = i32::try_from(code.varnames.len()).unwrap_or(i32::MAX);
|
||||
let localsplus_names = {
|
||||
@@ -547,7 +610,27 @@ impl PyCode {
|
||||
| Instruction::InstrumentedEndAsyncFor
|
||||
)
|
||||
});
|
||||
let first_traceable = code
|
||||
.instructions
|
||||
.iter()
|
||||
.position(|unit| {
|
||||
matches!(unit.op, Instruction::Resume { .. })
|
||||
&& u32::from(u8::from(unit.arg)) & bytecode::oparg::ResumeContext::LOCATION_MASK
|
||||
!= bytecode::oparg::ResumeLocation::AtGenExprStart.as_u32()
|
||||
})
|
||||
.unwrap_or(code.instructions.len());
|
||||
let names_tuple = |names: &[&'static PyStrInterned]| {
|
||||
ctx.new_tuple(names.iter().map(|name| name.to_object()).collect())
|
||||
};
|
||||
Self {
|
||||
first_traceable,
|
||||
co_name: code.obj_name.to_owned(),
|
||||
co_names: names_tuple(&code.names),
|
||||
co_localsplusnames: names_tuple(&localsplus_names),
|
||||
co_localspluskinds: ctx.new_bytes(code.localspluskinds.to_vec()),
|
||||
co_linetable: ctx.new_bytes(code.linetable.to_vec()),
|
||||
co_exceptiontable: ctx.new_bytes(code.exceptiontable.to_vec()),
|
||||
co_cached: CoCached::default(),
|
||||
code,
|
||||
localsplus_names,
|
||||
source_path: AtomicPtr::new(sp),
|
||||
@@ -559,6 +642,42 @@ impl PyCode {
|
||||
}
|
||||
}
|
||||
|
||||
/// The code object `r_object` creates from a TYPE_CODE record. It keeps
|
||||
/// the objects read for the object-valued fields.
|
||||
pub(crate) fn from_marshal(
|
||||
mut code: CodeObject,
|
||||
fields: rustpython_compiler_core::marshal::CodeFields<PyObjectRef>,
|
||||
ctx: &Context,
|
||||
) -> Result<Self, rustpython_compiler_core::marshal::MarshalError> {
|
||||
use rustpython_compiler_core::marshal::MarshalError;
|
||||
fn downcast<T: PyPayload>(obj: PyObjectRef) -> Result<PyRef<T>, MarshalError> {
|
||||
obj.downcast().map_err(|_| MarshalError::BadType)
|
||||
}
|
||||
code.constants = bytecode::Constants::new(LiteralTuple(downcast(fields.consts)?));
|
||||
let mut code = Self::new(code, ctx);
|
||||
code.set_names(downcast(fields.names)?);
|
||||
code.set_localsplusnames(downcast(fields.localsplusnames)?);
|
||||
code.co_localspluskinds = downcast(fields.localspluskinds)?;
|
||||
code.co_linetable = downcast(fields.linetable)?;
|
||||
code.co_exceptiontable = downcast(fields.exceptiontable)?;
|
||||
Ok(code)
|
||||
}
|
||||
|
||||
/// Keep `names` as `co_names` when it holds the interned names, the way
|
||||
/// `intern_strings` interns a names tuple in place.
|
||||
fn set_names(&mut self, names: PyTupleRef) {
|
||||
if holds_names(&names, &self.code.names) {
|
||||
self.co_names = names;
|
||||
}
|
||||
}
|
||||
|
||||
/// Keep `localsplusnames` as `co_localsplusnames`; see [`Self::set_names`].
|
||||
fn set_localsplusnames(&mut self, localsplusnames: PyTupleRef) {
|
||||
if holds_names(&localsplusnames, &self.localsplus_names) {
|
||||
self.co_localsplusnames = localsplusnames;
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn localsplus_name(&self, index: usize) -> &'static PyStrInterned {
|
||||
// Bytecode operands and frame-proxy indices are validated against
|
||||
@@ -567,6 +686,41 @@ impl PyCode {
|
||||
self.localsplus_names[index]
|
||||
}
|
||||
|
||||
pub(crate) fn co_consts(&self) -> &PyTupleRef {
|
||||
self.code.constants.as_tuple().as_pytuple()
|
||||
}
|
||||
|
||||
/// `_PyCode_GetCode`
|
||||
pub(crate) fn get_code(&self, vm: &VirtualMachine) -> PyBytesRef {
|
||||
self.co_cached
|
||||
.code
|
||||
.get_or_init(|| vm.ctx.new_bytes(self.deopt_code()))
|
||||
.clone()
|
||||
}
|
||||
|
||||
/// `deopt_code`: the instructions with each opcode replaced by its base
|
||||
/// opcode (`_Py_GetBaseCodeUnit`) and the inline caches zeroed.
|
||||
fn deopt_code(&self) -> Vec<u8> {
|
||||
let monitoring_data = self.monitoring_data.lock();
|
||||
self.code.instructions.original_bytes_with(|i, op| {
|
||||
let Some(data) = monitoring_data.as_ref() else {
|
||||
return op;
|
||||
};
|
||||
let op = match op {
|
||||
Instruction::InstrumentedLine => Instruction::try_from(data.line_opcodes[i])
|
||||
.expect("INSTRUMENTED_LINE without original opcode"),
|
||||
op => op,
|
||||
};
|
||||
match op {
|
||||
Instruction::InstrumentedInstruction => {
|
||||
Instruction::try_from(data.per_instruction_opcodes[i])
|
||||
.expect("INSTRUMENTED_INSTRUCTION without original opcode")
|
||||
}
|
||||
op => op,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn source_path(&self) -> &'static PyStrInterned {
|
||||
// SAFETY: always points to a valid &'static PyStrInterned (interned strings are never deallocated)
|
||||
unsafe { &*self.source_path.load(Ordering::Relaxed) }
|
||||
@@ -580,11 +734,19 @@ impl PyCode {
|
||||
}
|
||||
|
||||
pub fn new_ref_with_bag(vm: &VirtualMachine, code: CodeObject) -> PyRef<Self> {
|
||||
PyRef::new_ref(Self::new(code), vm.ctx.types.code_type.to_owned(), None)
|
||||
PyRef::new_ref(
|
||||
Self::new(code, &vm.ctx),
|
||||
vm.ctx.types.code_type.to_owned(),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_ref_from_bytecode(vm: &VirtualMachine, code: bytecode::CodeObject) -> PyRef<Self> {
|
||||
Self::new_ref_with_bag(vm, code.map_bag(PyVmBag(vm)))
|
||||
PyRef::new_ref(
|
||||
code.into_pycode(vm),
|
||||
vm.ctx.types.code_type.to_owned(),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_ref_from_frozen<B: AsRef<[u8]>>(
|
||||
@@ -721,6 +883,78 @@ impl Representable for PyCode {
|
||||
}
|
||||
}
|
||||
|
||||
/// `_PyCode_ConstantKey`
|
||||
pub(crate) fn constant_key(op: &PyObject, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
|
||||
use super::{PyBool, PyBytes, PyComplex, PyFloat, PyInt, PySlice, PyStr, PyTuple};
|
||||
|
||||
let ctx = &vm.ctx;
|
||||
let class = op.class();
|
||||
let pair = |first: PyObjectRef| ctx.new_tuple(vec![first, op.to_owned()]).into();
|
||||
let triple = |tag: PyObjectRef| {
|
||||
ctx.new_tuple(vec![class.to_owned().into(), op.to_owned(), tag])
|
||||
.into()
|
||||
};
|
||||
let is_neg_zero = |d: f64| d == 0.0 && d.is_sign_negative();
|
||||
|
||||
let key = if vm.is_none(op)
|
||||
|| op.is(&ctx.ellipsis)
|
||||
|| class.is(PyInt::static_type())
|
||||
|| class.is(PyStr::static_type())
|
||||
|| op.downcast_ref::<PyCode>().is_some()
|
||||
{
|
||||
// Objects of these types are always different from object of other
|
||||
// type and from tuples.
|
||||
op.to_owned()
|
||||
} else if class.is(PyBool::static_type()) || class.is(PyBytes::static_type()) {
|
||||
// Make booleans different from integers 0 and 1.
|
||||
pair(class.to_owned().into())
|
||||
} else if let Some(float) = op.downcast_ref_if_exact::<PyFloat>(vm) {
|
||||
if is_neg_zero(float.to_f64()) {
|
||||
triple(ctx.none())
|
||||
} else {
|
||||
pair(class.to_owned().into())
|
||||
}
|
||||
} else if let Some(complex) = op.downcast_ref_if_exact::<PyComplex>(vm) {
|
||||
// All four complex zeros must be distinguished.
|
||||
let z = complex.to_complex();
|
||||
match (is_neg_zero(z.re), is_neg_zero(z.im)) {
|
||||
(true, true) => triple(ctx.new_bool(true).into()),
|
||||
(false, true) => triple(ctx.new_bool(false).into()),
|
||||
(true, false) => triple(ctx.none()),
|
||||
(false, false) => pair(class.to_owned().into()),
|
||||
}
|
||||
} else if let Some(tuple) = op.downcast_ref_if_exact::<PyTuple>(vm) {
|
||||
let keys = tuple
|
||||
.iter()
|
||||
.map(|item| constant_key(item, vm))
|
||||
.collect::<PyResult<_>>()?;
|
||||
pair(ctx.new_tuple(keys).into())
|
||||
} else if let Some(set) = op.downcast_ref_if_exact::<PyFrozenSet>(vm) {
|
||||
let keys = set
|
||||
.elements()
|
||||
.iter()
|
||||
.map(|item| constant_key(item, vm))
|
||||
.collect::<PyResult<Vec<_>>>()?;
|
||||
pair(PyFrozenSet::from_iter(vm, keys)?.into_ref(ctx).into())
|
||||
} else if let Some(slice) = op.downcast_ref::<PySlice>() {
|
||||
let key = |item: Option<&PyObjectRef>| match item {
|
||||
Some(item) => constant_key(item, vm),
|
||||
None => Ok(ctx.none()),
|
||||
};
|
||||
let slice_key = PySlice {
|
||||
start: Some(key(slice.start.as_ref())?),
|
||||
stop: key(Some(&slice.stop))?,
|
||||
step: Some(key(slice.step.as_ref())?),
|
||||
};
|
||||
pair(slice_key.into_ref(ctx).into())
|
||||
} else {
|
||||
// For other types, use the object identifier as a unique identifier
|
||||
// to ensure that they are seen as unequal.
|
||||
pair(ctx.new_int(op.get_id()).into())
|
||||
};
|
||||
Ok(key)
|
||||
}
|
||||
|
||||
impl Comparable for PyCode {
|
||||
fn cmp(
|
||||
zelf: &Py<Self>,
|
||||
@@ -732,35 +966,23 @@ impl Comparable for PyCode {
|
||||
let other = class_or_notimplemented!(Self, other);
|
||||
let a = &zelf.code;
|
||||
let b = &other.code;
|
||||
let eq = a.obj_name == b.obj_name
|
||||
let eq = vm.bool_eq(zelf.co_name.as_object(), other.co_name.as_object())?
|
||||
&& a.arg_count == b.arg_count
|
||||
&& a.posonlyarg_count == b.posonlyarg_count
|
||||
&& a.kwonlyarg_count == b.kwonlyarg_count
|
||||
&& a.flags == b.flags
|
||||
&& a.first_line_number == b.first_line_number
|
||||
&& a.instructions.original_bytes() == b.instructions.original_bytes()
|
||||
&& zelf.get_code(vm).as_bytes() == other.get_code(vm).as_bytes()
|
||||
&& a.linetable == b.linetable
|
||||
&& a.exceptiontable == b.exceptiontable
|
||||
&& a.names == b.names
|
||||
&& a.varnames == b.varnames
|
||||
&& a.freevars == b.freevars
|
||||
&& a.cellvars == b.cellvars
|
||||
&& {
|
||||
let a_consts: Vec<_> = a.constants.iter().map(|c| c.0.clone()).collect();
|
||||
let b_consts: Vec<_> = b.constants.iter().map(|c| c.0.clone()).collect();
|
||||
if a_consts.len() != b_consts.len() {
|
||||
false
|
||||
} else {
|
||||
let mut eq = true;
|
||||
for (ac, bc) in a_consts.iter().zip(b_consts.iter()) {
|
||||
if !vm.bool_eq(ac, bc)? {
|
||||
eq = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
eq
|
||||
}
|
||||
};
|
||||
&& vm.bool_eq(
|
||||
&*constant_key(zelf.co_consts().as_object(), vm)?,
|
||||
&*constant_key(other.co_consts().as_object(), vm)?,
|
||||
)?;
|
||||
Ok(eq.into())
|
||||
})
|
||||
}
|
||||
@@ -771,7 +993,7 @@ impl Hashable for PyCode {
|
||||
let code = &zelf.code;
|
||||
// Hash a tuple of key attributes, matching CPython's code_hash
|
||||
let tuple = vm.ctx.new_tuple(vec![
|
||||
vm.ctx.new_str(code.obj_name.as_str()).into(),
|
||||
zelf.co_name.clone().into(),
|
||||
vm.ctx.new_int(code.arg_count).into(),
|
||||
vm.ctx.new_int(code.posonlyarg_count).into(),
|
||||
vm.ctx.new_int(code.kwonlyarg_count).into(),
|
||||
@@ -780,7 +1002,7 @@ impl Hashable for PyCode {
|
||||
vm.ctx
|
||||
.new_int(code.first_line_number.map_or(0, |n| n.get()) as i64)
|
||||
.into(),
|
||||
vm.ctx.new_bytes(code.instructions.original_bytes()).into(),
|
||||
zelf.get_code(vm).into(),
|
||||
{
|
||||
let consts: Vec<_> = code.constants.iter().map(|c| c.0.clone()).collect();
|
||||
vm.ctx.new_tuple(consts).into()
|
||||
@@ -884,16 +1106,7 @@ impl Constructor for PyCode {
|
||||
let instructions = CodeUnits::try_from(bytecode_bytes)
|
||||
.map_err(|e| vm.new_value_error(format!("invalid bytecode: {e}")))?;
|
||||
|
||||
// Convert constants
|
||||
let constants = args
|
||||
.consts
|
||||
.as_slice()
|
||||
.iter()
|
||||
.map(|obj| {
|
||||
// Convert PyObject to Literal constant. For now, just wrap it
|
||||
Literal(obj.clone())
|
||||
})
|
||||
.collect();
|
||||
let constants = bytecode::Constants::new(LiteralTuple(args.consts));
|
||||
|
||||
// Create locations (start and end pairs)
|
||||
let row = if args.firstlineno > 0 {
|
||||
@@ -955,7 +1168,12 @@ impl Constructor for PyCode {
|
||||
exceptiontable: args.exceptiontable.as_bytes().to_vec().into_boxed_slice(),
|
||||
};
|
||||
|
||||
Ok(Self::new(code))
|
||||
let mut code = Self::new(code, &vm.ctx);
|
||||
code.co_name = args.name;
|
||||
code.set_names(args.names);
|
||||
code.co_linetable = args.linetable;
|
||||
code.co_exceptiontable = args.exceptiontable;
|
||||
Ok(code)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -973,12 +1191,13 @@ impl PyCode {
|
||||
impl Py<PyCode> {
|
||||
#[pygetset]
|
||||
pub fn co_cellvars(&self, vm: &VirtualMachine) -> PyTupleRef {
|
||||
let cellvars = self
|
||||
self.co_cached
|
||||
.cellvars
|
||||
.iter()
|
||||
.map(|name| name.to_pyobject(vm))
|
||||
.collect();
|
||||
vm.ctx.new_tuple(cellvars)
|
||||
.get_or_init(|| {
|
||||
vm.ctx
|
||||
.new_tuple(self.cellvars.iter().map(|name| name.to_object()).collect())
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
@@ -987,36 +1206,43 @@ impl Py<PyCode> {
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn co_consts(&self, vm: &VirtualMachine) -> PyTupleRef {
|
||||
let consts = self.code.constants.iter().map(|x| x.0.clone()).collect();
|
||||
vm.ctx.new_tuple(consts)
|
||||
fn co_name(&self) -> PyStrRef {
|
||||
self.co_name.clone()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn co_names(&self, vm: &VirtualMachine) -> PyTupleRef {
|
||||
let names = self
|
||||
.code
|
||||
.names
|
||||
.deref()
|
||||
.iter()
|
||||
.map(|name| name.to_pyobject(vm))
|
||||
.collect();
|
||||
vm.ctx.new_tuple(names)
|
||||
fn co_consts(&self) -> PyTupleRef {
|
||||
PyCode::co_consts(self).clone()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn co_names(&self) -> PyTupleRef {
|
||||
self.co_names.clone()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
pub fn co_varnames(&self, vm: &VirtualMachine) -> PyTupleRef {
|
||||
let varnames = self.code.varnames.iter().map(|s| s.to_object()).collect();
|
||||
vm.ctx.new_tuple(varnames)
|
||||
self.co_cached
|
||||
.varnames
|
||||
.get_or_init(|| {
|
||||
vm.ctx.new_tuple(
|
||||
self.code
|
||||
.varnames
|
||||
.iter()
|
||||
.map(|name| name.to_object())
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
pub fn co_code(&self, vm: &VirtualMachine) -> crate::builtins::PyBytesRef {
|
||||
vm.ctx.new_bytes(self.code.instructions.original_bytes())
|
||||
pub fn co_code(&self, vm: &VirtualMachine) -> PyBytesRef {
|
||||
self.get_code(vm)
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
pub fn _co_code_adaptive(&self, vm: &VirtualMachine) -> crate::builtins::PyBytesRef {
|
||||
pub fn _co_code_adaptive(&self, vm: &VirtualMachine) -> PyBytesRef {
|
||||
// Return current (possibly quickened/specialized) bytecode
|
||||
let bytes = unsafe {
|
||||
core::slice::from_raw_parts(
|
||||
@@ -1029,26 +1255,28 @@ impl Py<PyCode> {
|
||||
|
||||
#[pygetset]
|
||||
pub fn co_freevars(&self, vm: &VirtualMachine) -> PyTupleRef {
|
||||
let names = self
|
||||
.code
|
||||
self.co_cached
|
||||
.freevars
|
||||
.deref()
|
||||
.iter()
|
||||
.map(|name| name.to_pyobject(vm))
|
||||
.collect();
|
||||
vm.ctx.new_tuple(names)
|
||||
.get_or_init(|| {
|
||||
vm.ctx.new_tuple(
|
||||
self.code
|
||||
.freevars
|
||||
.iter()
|
||||
.map(|name| name.to_object())
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
pub fn co_linetable(&self, vm: &VirtualMachine) -> crate::builtins::PyBytesRef {
|
||||
// Return the actual linetable from the code object
|
||||
vm.ctx.new_bytes(self.code.linetable.to_vec())
|
||||
pub fn co_linetable(&self) -> PyBytesRef {
|
||||
self.co_linetable.clone()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
pub fn co_exceptiontable(&self, vm: &VirtualMachine) -> crate::builtins::PyBytesRef {
|
||||
// Return the actual exception table from the code object
|
||||
vm.ctx.new_bytes(self.code.exceptiontable.to_vec())
|
||||
pub fn co_exceptiontable(&self) -> PyBytesRef {
|
||||
self.co_exceptiontable.clone()
|
||||
}
|
||||
|
||||
// spell-checker: ignore lnotab
|
||||
@@ -1380,23 +1608,17 @@ impl Py<PyCode> {
|
||||
|
||||
let constants = match co_consts {
|
||||
OptionalArg::Present(constants) => constants,
|
||||
OptionalArg::Missing => self.code.constants.iter().map(|x| x.0.clone()).collect(),
|
||||
OptionalArg::Missing => PyCode::co_consts(self).clone(),
|
||||
};
|
||||
|
||||
let obj_name = match co_name {
|
||||
OptionalArg::Present(obj_name) => obj_name,
|
||||
OptionalArg::Missing => self.code.obj_name.to_owned(),
|
||||
OptionalArg::Missing => self.co_name.clone(),
|
||||
};
|
||||
|
||||
let names = match co_names {
|
||||
OptionalArg::Present(names) => names,
|
||||
OptionalArg::Missing => self
|
||||
.code
|
||||
.names
|
||||
.deref()
|
||||
.iter()
|
||||
.map(|name| name.to_pyobject(vm))
|
||||
.collect(),
|
||||
OptionalArg::Missing => self.co_names.clone(),
|
||||
};
|
||||
|
||||
let flags = CodeFlags::from_bits_truncate(match co_flags {
|
||||
@@ -1427,7 +1649,8 @@ impl Py<PyCode> {
|
||||
CodeUnits::try_from(code_bytes.as_bytes())
|
||||
.map_err(|e| vm.new_value_error(format!("invalid bytecode: {e}")))?
|
||||
}
|
||||
OptionalArg::Missing => self.code.instructions.clone(),
|
||||
OptionalArg::Missing => CodeUnits::try_from(self.get_code(vm).as_bytes())
|
||||
.map_err(|e| vm.new_value_error(format!("invalid bytecode: {e}")))?,
|
||||
};
|
||||
|
||||
let intern_all = |objs: Vec<PyObjectRef>, field: &str| -> PyResult<Box<[_]>> {
|
||||
@@ -1482,15 +1705,13 @@ impl Py<PyCode> {
|
||||
|
||||
// Handle linetable and exceptiontable
|
||||
let linetable = match co_linetable {
|
||||
OptionalArg::Present(linetable) => linetable.as_bytes().to_vec().into_boxed_slice(),
|
||||
OptionalArg::Missing => self.code.linetable.clone(),
|
||||
OptionalArg::Present(linetable) => linetable,
|
||||
OptionalArg::Missing => self.co_linetable.clone(),
|
||||
};
|
||||
|
||||
let exceptiontable = match co_exceptiontable {
|
||||
OptionalArg::Present(exceptiontable) => {
|
||||
exceptiontable.as_bytes().to_vec().into_boxed_slice()
|
||||
}
|
||||
OptionalArg::Missing => self.code.exceptiontable.clone(),
|
||||
OptionalArg::Present(exceptiontable) => exceptiontable,
|
||||
OptionalArg::Missing => self.co_exceptiontable.clone(),
|
||||
};
|
||||
|
||||
let new_code = CodeObject {
|
||||
@@ -1508,17 +1729,22 @@ impl Py<PyCode> {
|
||||
// FIXME: invalid locations. Actually locations is a duplication of linetable.
|
||||
// It can be removed once we move every other code to use linetable only.
|
||||
locations: self.code.locations.clone(),
|
||||
constants: constants.into_iter().map(Literal).collect(),
|
||||
names: intern_all(names, "co_names")?,
|
||||
constants: bytecode::Constants::new(LiteralTuple(constants)),
|
||||
names: intern_all(names.as_slice().to_vec(), "co_names")?,
|
||||
varnames,
|
||||
cellvars,
|
||||
freevars,
|
||||
localspluskinds,
|
||||
linetable,
|
||||
exceptiontable,
|
||||
linetable: linetable.as_bytes().into(),
|
||||
exceptiontable: exceptiontable.as_bytes().into(),
|
||||
};
|
||||
|
||||
Ok(PyCode::new(new_code))
|
||||
let mut code = PyCode::new(new_code, &vm.ctx);
|
||||
code.co_name = obj_name;
|
||||
code.set_names(names);
|
||||
code.co_linetable = linetable;
|
||||
code.co_exceptiontable = exceptiontable;
|
||||
Ok(code)
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
//! The compiler's constant cache (`c_const_cache`).
|
||||
//!
|
||||
//! Equal constants and code fields produced by one compilation share a single
|
||||
//! object, merged under their `_PyCode_ConstantKey`.
|
||||
|
||||
use super::*;
|
||||
use crate::builtins::{PyDict, PyDictRef, PyTuple};
|
||||
|
||||
/// `const_cache_insert`. Items of a tuple or frozenset constant are created
|
||||
/// through the cache before the container, so a single non-recursive insert
|
||||
/// also merges them.
|
||||
fn const_cache_insert(
|
||||
const_cache: &Py<PyDict>,
|
||||
o: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyObjectRef> {
|
||||
// None and Ellipsis are singletons, and key is the singleton.
|
||||
if vm.is_none(&o) || o.is(&vm.ctx.ellipsis) {
|
||||
return Ok(o);
|
||||
}
|
||||
let key = constant_key(&o, vm)?;
|
||||
let key = match const_cache.get_item_opt(&*key, vm)? {
|
||||
Some(existing) => existing,
|
||||
None => {
|
||||
const_cache.set_item(&*key, key.clone(), vm)?;
|
||||
key
|
||||
}
|
||||
};
|
||||
// See constant_key()
|
||||
Ok(match key.downcast_ref_if_exact::<PyTuple>(vm) {
|
||||
Some(key) => key.as_slice()[1].clone(),
|
||||
None => key,
|
||||
})
|
||||
}
|
||||
|
||||
/// A [`ConstantBag`] creating the constants of one compilation through its
|
||||
/// constant cache.
|
||||
#[derive(Clone, Copy)]
|
||||
struct CompileBag<'a> {
|
||||
vm: &'a VirtualMachine,
|
||||
const_cache: &'a Py<PyDict>,
|
||||
}
|
||||
|
||||
impl CompileBag<'_> {
|
||||
/// `_PyCompile_ConstCacheMergeOne`
|
||||
fn merge_one<T: PyPayload>(&self, obj: PyRef<T>) -> PyRef<T> {
|
||||
self.merge(obj.into())
|
||||
.downcast()
|
||||
.expect("an equal constant key implies the same type")
|
||||
}
|
||||
|
||||
fn merge(&self, obj: PyObjectRef) -> PyObjectRef {
|
||||
const_cache_insert(self.const_cache, obj, self.vm)
|
||||
.expect("constant keys are hashable and comparable")
|
||||
}
|
||||
|
||||
/// Create a code object whose fields are merged the way `assemble.c`
|
||||
/// merges them, in the same order.
|
||||
fn new_code(&self, code: CodeObject) -> PyCode {
|
||||
let mut code = PyCode::new(code, &self.vm.ctx);
|
||||
code.co_exceptiontable = self.merge_one(code.co_exceptiontable);
|
||||
code.co_linetable = self.merge_one(code.co_linetable);
|
||||
code.co_names = self.merge_one(code.co_names);
|
||||
let consts = self.merge_one(code.code.constants.as_tuple().as_pytuple().clone());
|
||||
code.code.constants = bytecode::Constants::new(LiteralTuple(consts));
|
||||
code.co_localsplusnames = self.merge_one(code.co_localsplusnames);
|
||||
code
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstantBag for CompileBag<'_> {
|
||||
type Constant = Literal;
|
||||
|
||||
fn make_constant<C: Constant>(&self, constant: BorrowedConstant<'_, C>) -> Literal {
|
||||
let vm = self.vm;
|
||||
let obj = match constant {
|
||||
BorrowedConstant::Code { code } => return self.make_code(code.map_clone_bag(self)),
|
||||
BorrowedConstant::Tuple { elements } => vm
|
||||
.ctx
|
||||
.new_tuple(
|
||||
elements
|
||||
.iter()
|
||||
.map(|item| self.make_constant(item.borrow_constant()).0)
|
||||
.collect(),
|
||||
)
|
||||
.into(),
|
||||
BorrowedConstant::Frozenset { elements } => PyFrozenSet::from_iter(
|
||||
vm,
|
||||
elements
|
||||
.iter()
|
||||
.map(|item| self.make_constant(item.borrow_constant()).0),
|
||||
)
|
||||
.unwrap()
|
||||
.into_ref(&vm.ctx)
|
||||
.into(),
|
||||
BorrowedConstant::Slice { elements } => {
|
||||
let [start, stop, step] = elements
|
||||
.each_ref()
|
||||
.map(|item| self.make_constant(item.borrow_constant()).0);
|
||||
crate::builtins::PySlice {
|
||||
start: Some(start),
|
||||
stop,
|
||||
step: Some(step),
|
||||
}
|
||||
.into_ref(&vm.ctx)
|
||||
.into()
|
||||
}
|
||||
_ => PyVmBag(vm).make_constant(constant).0,
|
||||
};
|
||||
Literal(self.merge(obj))
|
||||
}
|
||||
|
||||
fn make_name(&self, name: &str) -> &'static PyStrInterned {
|
||||
self.vm.ctx.intern_str(name)
|
||||
}
|
||||
|
||||
fn make_int(&self, value: BigInt) -> Literal {
|
||||
Literal(self.merge(self.vm.ctx.new_int(value).into()))
|
||||
}
|
||||
|
||||
fn make_tuple(&self, elements: impl Iterator<Item = Literal>) -> Literal {
|
||||
let tuple = self.vm.ctx.new_tuple(elements.map(|item| item.0).collect());
|
||||
Literal(self.merge(tuple.into()))
|
||||
}
|
||||
|
||||
fn make_code(&self, code: CodeObject) -> Literal {
|
||||
let code = self.new_code(code).into_ref(&self.vm.ctx);
|
||||
Literal(self.merge(code.into()))
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn into_code_object(code: bytecode::CodeObject, vm: &VirtualMachine) -> CodeObject {
|
||||
let const_cache: PyDictRef = vm.ctx.new_dict();
|
||||
code.map_bag(CompileBag {
|
||||
vm,
|
||||
const_cache: &const_cache,
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn into_pycode(code: bytecode::CodeObject, vm: &VirtualMachine) -> PyCode {
|
||||
let const_cache: PyDictRef = vm.ctx.new_dict();
|
||||
let bag = CompileBag {
|
||||
vm,
|
||||
const_cache: &const_cache,
|
||||
};
|
||||
bag.new_code(code.map_bag(bag))
|
||||
}
|
||||
@@ -174,7 +174,7 @@ impl Destructor for PyCoroutine {
|
||||
if zelf.inner.closed() || zelf.inner.running() {
|
||||
return Ok(());
|
||||
}
|
||||
if zelf.inner.frame_opt().is_none_or(|f| f.lasti() == 0) {
|
||||
if !zelf.inner.started() {
|
||||
crate::warn::warn_unawaited_coroutine(zelf.as_object(), &zelf.inner.qualname(), vm);
|
||||
zelf.inner.closed.store(true);
|
||||
return Ok(());
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::{
|
||||
class::PyClassImpl,
|
||||
frame::{FrameObject, FrameOwner},
|
||||
function::PySetterValue,
|
||||
types::{Destructor, Representable},
|
||||
types::Representable,
|
||||
};
|
||||
use core::sync::atomic::Ordering::Relaxed;
|
||||
use num_traits::Zero;
|
||||
@@ -242,29 +242,30 @@ pub(crate) mod stack_analysis {
|
||||
}
|
||||
}
|
||||
}
|
||||
Instruction::GetIter | Instruction::GetAiter => {
|
||||
Instruction::GetIter { .. } => {
|
||||
next_stack = push_value(pop_value(next_stack), Kind::Iterator as i64);
|
||||
next_stack = push_value(next_stack, Kind::Iterator as i64);
|
||||
if next_i < stacks.len() {
|
||||
stacks[next_i] = next_stack;
|
||||
}
|
||||
}
|
||||
Instruction::GetAiter => {
|
||||
next_stack = push_value(pop_value(next_stack), Kind::Iterator as i64);
|
||||
if next_i < stacks.len() {
|
||||
stacks[next_i] = next_stack;
|
||||
}
|
||||
}
|
||||
Instruction::ForIter { .. } => {
|
||||
// Fall-through (iteration continues): pushes the next value
|
||||
let body_stack = push_value(next_stack, Kind::Object as i64);
|
||||
// Both the body and the END_FOR target start with the
|
||||
// next value pushed.
|
||||
let target_stack = push_value(next_stack, Kind::Object as i64);
|
||||
if next_i < stacks.len() {
|
||||
stacks[next_i] = body_stack;
|
||||
stacks[next_i] = target_stack;
|
||||
}
|
||||
// Exhaustion path: relative forward from after_caches
|
||||
let mut j = next_i + oparg as usize;
|
||||
if j < instructions.len() {
|
||||
let target_op =
|
||||
instructions[j].op.to_base().unwrap_or(instructions[j].op);
|
||||
if matches!(target_op, Instruction::EndFor) {
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
if j < stacks.len() && stacks[j] == UNINITIALIZED {
|
||||
stacks[j] = next_stack;
|
||||
let j = next_i + oparg as usize;
|
||||
if j < stacks.len() {
|
||||
debug_assert!(stacks[j] == UNINITIALIZED || stacks[j] == target_stack);
|
||||
stacks[j] = target_stack;
|
||||
}
|
||||
}
|
||||
Instruction::EndAsyncFor => {
|
||||
@@ -514,19 +515,19 @@ impl Py<FrameObject> {
|
||||
);
|
||||
match owner {
|
||||
FrameOwner::Generator => {
|
||||
// FRAME_SUSPENDED (lasti > 0) cannot be cleared. FRAME_CREATED
|
||||
// and finished frames go through the owner finalizer.
|
||||
if self.lasti() != 0 {
|
||||
return Err(vm.new_runtime_error("cannot clear a suspended frame"));
|
||||
}
|
||||
// RETURN_GENERATOR executes the creation preamble, so lasti
|
||||
// no longer distinguishes a created frame from a suspended one.
|
||||
if let Some(owner) = self.iframe().generator.to_owned() {
|
||||
if let Some(coro) = owner.downcast_ref::<PyCoroutine>() {
|
||||
let _ = PyCoroutine::del(coro, vm);
|
||||
let coro = if let Some(coro) = owner.downcast_ref::<PyCoroutine>() {
|
||||
coro.as_coro()
|
||||
} else if let Some(async_gen) = owner.downcast_ref::<PyAsyncGen>() {
|
||||
let _ = PyAsyncGen::del(async_gen, vm);
|
||||
async_gen.as_coro()
|
||||
} else if let Some(generator) = owner.downcast_ref::<PyGenerator>() {
|
||||
let _ = PyGenerator::del(generator, vm);
|
||||
}
|
||||
generator.as_coro()
|
||||
} else {
|
||||
unreachable!("generator-owned frame has no coroutine owner")
|
||||
};
|
||||
coro.clear_frame(&owner, vm)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
@@ -630,6 +631,11 @@ impl Py<FrameObject> {
|
||||
let mut cur = crate::vm::thread::get_current_frame();
|
||||
while !cur.is_null() {
|
||||
if core::ptr::eq(cur, prev) {
|
||||
// SAFETY: cur was found on this thread's live active chain.
|
||||
let cur = unsafe { crate::frame::first_complete_iframe(cur) };
|
||||
if cur.is_null() {
|
||||
return None;
|
||||
}
|
||||
let iframe_ref = unsafe { &*cur };
|
||||
let fo = iframe_ref.materialize(vm);
|
||||
return Some(fo.to_owned());
|
||||
@@ -652,6 +658,11 @@ impl Py<FrameObject> {
|
||||
// return and free the stack-allocated iframe at any time.
|
||||
vm.state.stop_the_world.stop_the_world(&vm.state);
|
||||
scopeguard::defer! { vm.state.stop_the_world.start_the_world(&vm.state); }
|
||||
// SAFETY: the world is stopped, preserving the live chain.
|
||||
let prev = unsafe { crate::frame::first_complete_iframe(prev) };
|
||||
if prev.is_null() {
|
||||
return None;
|
||||
}
|
||||
let prev_ref = unsafe { &*prev };
|
||||
// Fast path: already materialized.
|
||||
if let Some(fo) = prev_ref.frame_obj() {
|
||||
@@ -659,7 +670,7 @@ impl Py<FrameObject> {
|
||||
}
|
||||
// Slow path: copy the whole chain, linked through retained_back.
|
||||
// SAFETY: the world is stopped, so the owning thread is parked.
|
||||
let fo = unsafe { prev_ref.materialize_detached_chain(vm) };
|
||||
let fo = unsafe { prev_ref.materialize_complete_detached_chain(vm) };
|
||||
return Some(fo);
|
||||
}
|
||||
|
||||
|
||||
@@ -637,6 +637,7 @@ impl Py<PyFunction> {
|
||||
);
|
||||
self.fill_locals_from_args(&frame, func_args, vm)?;
|
||||
if is_gen || is_coro || is_async_gen {
|
||||
vm.with_generator_frame(frame.clone(), |f| f.prepare_generator(vm))?;
|
||||
return Ok(self.make_generator_or_coro(frame, vm));
|
||||
}
|
||||
// Tracing active: use heap frame with full trace support.
|
||||
@@ -844,7 +845,7 @@ impl Py<PyFunction> {
|
||||
&self,
|
||||
args: impl ExactSizeIterator<Item = PyObjectRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyObjectRef {
|
||||
) -> PyResult {
|
||||
let code: PyRef<PyCode> = (*self.code).to_owned();
|
||||
|
||||
debug_assert_eq!(args.len(), code.arg_count as usize);
|
||||
@@ -886,7 +887,8 @@ impl Py<PyFunction> {
|
||||
}
|
||||
}
|
||||
|
||||
self.make_generator_or_coro(frame, vm)
|
||||
vm.with_generator_frame(frame.clone(), |f| f.prepare_generator(vm))?;
|
||||
Ok(self.make_generator_or_coro(frame, vm))
|
||||
}
|
||||
|
||||
pub(crate) fn invoke_prepared_exact_args(
|
||||
@@ -946,7 +948,7 @@ impl Py<PyFunction> {
|
||||
// specialization classification, but calling one produces a
|
||||
// generator/coroutine object instead of running the frame.
|
||||
if self.is_generator_like() {
|
||||
return Ok(self.make_generator_exact_args(args.into_iter(), vm));
|
||||
return self.make_generator_exact_args(args.into_iter(), vm);
|
||||
}
|
||||
self.invoke_prepared_exact_args(args.into_iter(), vm)
|
||||
}
|
||||
@@ -968,7 +970,7 @@ impl Py<PyFunction> {
|
||||
// specialization classification, but calling one produces a
|
||||
// generator/coroutine object instead of running the frame.
|
||||
if self.is_generator_like() {
|
||||
return Ok(self.make_generator_exact_args(taken, vm));
|
||||
return self.make_generator_exact_args(taken, vm);
|
||||
}
|
||||
self.invoke_prepared_exact_args(taken, vm)
|
||||
}
|
||||
@@ -1779,7 +1781,7 @@ pub(crate) fn vectorcall_function(
|
||||
// is called in, and a fresh `MAKE_FUNCTION` each time keeps those out
|
||||
// of the call-site specialization that would otherwise catch it.
|
||||
args.truncate(nargs);
|
||||
return Ok(zelf.make_generator_exact_args(args.into_iter(), vm));
|
||||
return zelf.make_generator_exact_args(args.into_iter(), vm);
|
||||
}
|
||||
|
||||
if !has_kwargs && base_simple && nargs == code.arg_count as usize {
|
||||
|
||||
@@ -122,6 +122,19 @@ impl Py<PyGenerator> {
|
||||
self.inner.running()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn gi_state(&self) -> &'static str {
|
||||
if self.inner.closed() {
|
||||
"GEN_CLOSED"
|
||||
} else if self.inner.running() {
|
||||
"GEN_RUNNING"
|
||||
} else if self.inner.started() {
|
||||
"GEN_SUSPENDED"
|
||||
} else {
|
||||
"GEN_CREATED"
|
||||
}
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn gi_code(&self, _vm: &VirtualMachine) -> PyRef<PyCode> {
|
||||
self.inner.code()
|
||||
|
||||
+74
-47
@@ -42,6 +42,37 @@ impl FormatArgument<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// `FORMAT_ERROR` for an argument of the wrong type.
|
||||
fn wrong_type_error(
|
||||
vm: &VirtualMachine,
|
||||
spec: &CFormatSpec,
|
||||
obj: &PyObject,
|
||||
argument: FormatArgument<'_>,
|
||||
required: &str,
|
||||
) -> PyResult<PyBaseExceptionRef> {
|
||||
Ok(vm.new_type_error(format!(
|
||||
"{}: %{} requires {required}, not {}",
|
||||
argument.description(vm)?,
|
||||
spec.format_type.to_char(),
|
||||
obj.class().slot_name()
|
||||
)))
|
||||
}
|
||||
|
||||
fn format_float_arg(
|
||||
vm: &VirtualMachine,
|
||||
spec: &CFormatSpec,
|
||||
obj: PyObjectRef,
|
||||
argument: FormatArgument<'_>,
|
||||
) -> PyResult<f64> {
|
||||
match ArgIntoFloat::try_from_object(vm, obj.clone()) {
|
||||
Ok(value) => Ok(value.into()),
|
||||
Err(error) if error.fast_isinstance(vm.ctx.exceptions.type_error) => {
|
||||
Err(wrong_type_error(vm, spec, &obj, argument, "a real number")?)
|
||||
}
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
|
||||
fn format_decimal_object(
|
||||
vm: &VirtualMachine,
|
||||
spec: &CFormatSpec,
|
||||
@@ -55,31 +86,41 @@ fn format_decimal_object(
|
||||
Some(Err(error)) if !error.fast_isinstance(vm.ctx.exceptions.type_error) => {
|
||||
return Err(error);
|
||||
}
|
||||
_ => {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"{}: %{} requires a real number, not {}",
|
||||
argument.description(vm)?,
|
||||
spec.format_type.to_char(),
|
||||
obj.class().slot_name()
|
||||
)));
|
||||
}
|
||||
_ => return Err(wrong_type_error(vm, spec, obj, argument, "a real number")?),
|
||||
};
|
||||
check_int_to_str_digits(i.as_bigint(), vm)?;
|
||||
Ok(spec.format_number(i.as_bigint()))
|
||||
}
|
||||
|
||||
fn unsupported_format_error(
|
||||
ch: CodePoint,
|
||||
index: usize,
|
||||
format_start: usize,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyBaseExceptionRef {
|
||||
let c = ch.to_char_lossy();
|
||||
let msg = if c.is_ascii_alphabetic() {
|
||||
format!("unsupported format %{c} at position {format_start}")
|
||||
} else {
|
||||
format!(
|
||||
"unsupported format character '{c}' ({:#x}) at index {index}",
|
||||
ch.to_u32()
|
||||
)
|
||||
};
|
||||
vm.new_value_error(msg)
|
||||
}
|
||||
|
||||
fn spec_format_bytes(
|
||||
vm: &VirtualMachine,
|
||||
spec: &CFormatSpec,
|
||||
obj: PyObjectRef,
|
||||
argument: FormatArgument<'_>,
|
||||
format_start: usize,
|
||||
) -> PyResult<Vec<u8>> {
|
||||
match &spec.format_type {
|
||||
CFormatType::Unsupported { ch, index } => Err(vm.new_value_error(format!(
|
||||
"unsupported format character '{}' ({:#x}) at index {index}",
|
||||
ch.to_char_lossy(),
|
||||
ch.to_u32(),
|
||||
))),
|
||||
CFormatType::Unsupported { ch, index } => {
|
||||
Err(unsupported_format_error(*ch, *index, format_start, vm))
|
||||
}
|
||||
// Unlike strings, %r and %a are identical for bytes: the behaviour corresponds to
|
||||
// %a for strings (not %r)
|
||||
CFormatType::String(CFormatConversion::Repr | CFormatConversion::Ascii) => {
|
||||
@@ -132,26 +173,13 @@ fn spec_format_bytes(
|
||||
let i = int_result?;
|
||||
Ok(spec.format_number(i.as_bigint()).into_bytes())
|
||||
} else {
|
||||
Err(vm.new_type_error(format!(
|
||||
"%{} format: an integer is required, not {}",
|
||||
spec.format_type.to_char(),
|
||||
obj.class().name()
|
||||
)))
|
||||
Err(wrong_type_error(vm, spec, &obj, argument, "an integer")?)
|
||||
}
|
||||
}
|
||||
},
|
||||
CFormatType::Float(_) => {
|
||||
let class = obj.class().to_owned();
|
||||
let value = ArgIntoFloat::try_from_object(vm, obj).map_err(|e| {
|
||||
if e.fast_isinstance(vm.ctx.exceptions.type_error) {
|
||||
// formatfloat in bytesobject.c generates its own specific exception
|
||||
// text in this case, mirror it here.
|
||||
vm.new_type_error(format!("float argument required, not {}", class.name()))
|
||||
} else {
|
||||
e
|
||||
}
|
||||
})?;
|
||||
Ok(spec.format_float(value.into()).into_bytes())
|
||||
let value = format_float_arg(vm, spec, obj, argument)?;
|
||||
Ok(spec.format_float(value).into_bytes())
|
||||
}
|
||||
CFormatType::Character(CCharacterType::Character) => {
|
||||
// CPython parity: bytes `%c` accepts a single byte or any object
|
||||
@@ -203,13 +231,12 @@ fn spec_format_string(
|
||||
spec: &CFormatSpec,
|
||||
obj: PyObjectRef,
|
||||
argument: FormatArgument<'_>,
|
||||
format_start: usize,
|
||||
) -> PyResult<Wtf8Buf> {
|
||||
match &spec.format_type {
|
||||
CFormatType::Unsupported { ch, index } => Err(vm.new_value_error(format!(
|
||||
"unsupported format character '{}' ({:#x}) at index {index}",
|
||||
ch.to_char_lossy(),
|
||||
ch.to_u32(),
|
||||
))),
|
||||
CFormatType::Unsupported { ch, index } => {
|
||||
Err(unsupported_format_error(*ch, *index, format_start, vm))
|
||||
}
|
||||
CFormatType::String(conversion) => {
|
||||
let result = match conversion {
|
||||
CFormatConversion::Ascii => builtins::ascii(obj, vm)?.as_wtf8().to_owned(),
|
||||
@@ -244,17 +271,13 @@ fn spec_format_string(
|
||||
let i = int_result?;
|
||||
Ok(spec.format_number(i.as_bigint()).into())
|
||||
} else {
|
||||
Err(vm.new_type_error(format!(
|
||||
"%{} format: an integer is required, not {}",
|
||||
spec.format_type.to_char(),
|
||||
obj.class().name()
|
||||
)))
|
||||
Err(wrong_type_error(vm, spec, &obj, argument, "an integer")?)
|
||||
}
|
||||
}
|
||||
},
|
||||
CFormatType::Float(_) => {
|
||||
let value = ArgIntoFloat::try_from_object(vm, obj)?;
|
||||
Ok(spec.format_float(value.into()).into())
|
||||
let value = format_float_arg(vm, spec, obj, argument)?;
|
||||
Ok(spec.format_float(value).into())
|
||||
}
|
||||
CFormatType::Character(CCharacterType::Character) => {
|
||||
// CPython parity: `%c` accepts a single-char str or any object with
|
||||
@@ -389,7 +412,7 @@ pub(crate) fn cformat_bytes(
|
||||
}
|
||||
|
||||
// dict
|
||||
for (_, part) in format {
|
||||
for (format_start, part) in format {
|
||||
match part {
|
||||
CFormatPart::Literal(literal) => result.extend(literal),
|
||||
CFormatPart::Spec(CFormatSpecKeyed { mapping_key, spec }) => {
|
||||
@@ -401,6 +424,7 @@ pub(crate) fn cformat_bytes(
|
||||
&spec,
|
||||
value,
|
||||
FormatArgument::Mapping(key.as_object()),
|
||||
format_start,
|
||||
)?;
|
||||
result.extend(part_result);
|
||||
}
|
||||
@@ -425,7 +449,7 @@ pub(crate) fn cformat_bytes(
|
||||
let mut value_iter = value_iter.inspect(|_| argument_index.set(argument_index.get() + 1));
|
||||
let is_tuple = values_obj.downcast_ref::<tuple::PyTuple>().is_some();
|
||||
|
||||
for (_, part) in format {
|
||||
for (format_start, part) in format {
|
||||
match part {
|
||||
CFormatPart::Literal(literal) => result.extend(literal),
|
||||
CFormatPart::Spec(CFormatSpecKeyed { mut spec, .. }) => {
|
||||
@@ -446,7 +470,8 @@ pub(crate) fn cformat_bytes(
|
||||
} else {
|
||||
FormatArgument::Scalar
|
||||
};
|
||||
let part_result = spec_format_bytes(vm, &spec, value.to_owned(), argument)?;
|
||||
let part_result =
|
||||
spec_format_bytes(vm, &spec, value.to_owned(), argument, format_start)?;
|
||||
result.extend(part_result);
|
||||
}
|
||||
}
|
||||
@@ -504,7 +529,7 @@ pub(crate) fn cformat_string(
|
||||
}
|
||||
|
||||
// dict
|
||||
for (_, part) in format {
|
||||
for (format_start, part) in format {
|
||||
match part {
|
||||
CFormatPart::Literal(literal) => result.push_wtf8(&literal),
|
||||
CFormatPart::Spec(CFormatSpecKeyed { mapping_key, spec }) => {
|
||||
@@ -516,6 +541,7 @@ pub(crate) fn cformat_string(
|
||||
&spec,
|
||||
value,
|
||||
FormatArgument::Mapping(key.as_object()),
|
||||
format_start,
|
||||
)?;
|
||||
result.push_wtf8(&part_result);
|
||||
}
|
||||
@@ -540,7 +566,7 @@ pub(crate) fn cformat_string(
|
||||
let mut value_iter = value_iter.inspect(|_| argument_index.set(argument_index.get() + 1));
|
||||
let is_tuple = values_obj.downcast_ref::<tuple::PyTuple>().is_some();
|
||||
|
||||
for (_, part) in format {
|
||||
for (format_start, part) in format {
|
||||
match part {
|
||||
CFormatPart::Literal(literal) => result.push_wtf8(&literal),
|
||||
CFormatPart::Spec(CFormatSpecKeyed { mut spec, .. }) => {
|
||||
@@ -561,7 +587,8 @@ pub(crate) fn cformat_string(
|
||||
} else {
|
||||
FormatArgument::Scalar
|
||||
};
|
||||
let part_result = spec_format_string(vm, &spec, value.to_owned(), argument)?;
|
||||
let part_result =
|
||||
spec_format_string(vm, &spec, value.to_owned(), argument, format_start)?;
|
||||
result.push_wtf8(&part_result);
|
||||
}
|
||||
}
|
||||
|
||||
+46
-17
@@ -40,6 +40,8 @@ pub struct Coro {
|
||||
code: PyRef<PyCode>,
|
||||
pub closed: AtomicCell<bool>, // TODO: https://github.com/RustPython/RustPython/pull/3183#discussion_r720560652
|
||||
running: AtomicCell<bool>,
|
||||
/// Whether execution has resumed beyond the eager creation preamble.
|
||||
started: AtomicCell<bool>,
|
||||
// _weakreflist
|
||||
name: PyMutex<PyStrRef>,
|
||||
qualname: PyMutex<PyStrRef>,
|
||||
@@ -159,16 +161,16 @@ pub(crate) fn flat_resume_enter(
|
||||
if coro.closed.load() {
|
||||
return Ok(FlatEnter::Exhausted);
|
||||
}
|
||||
let value = if coro.frame_opt().is_some_and(|f| f.lasti() > 0) {
|
||||
Some(value)
|
||||
} else if !vm.is_none(&value) {
|
||||
if !coro.started() && !vm.is_none(&value) {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"can't send non-None value to a just-started {}",
|
||||
gen_name(&jen, vm),
|
||||
)));
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
let value = coro
|
||||
.frame_opt()
|
||||
.is_some_and(|f| f.lasti() > 0)
|
||||
.then_some(value);
|
||||
// SAFETY: exclusive access guaranteed by the claim
|
||||
let gen_exc = unsafe { coro.exception.swap(None) };
|
||||
let frame_ref = coro.frame();
|
||||
@@ -184,6 +186,7 @@ pub(crate) fn flat_resume_enter(
|
||||
return Err(exc);
|
||||
}
|
||||
};
|
||||
coro.started.store(true);
|
||||
// The claim outlives this scope; `flat_resume_exit` drops it.
|
||||
core::mem::forget(_claim);
|
||||
Ok(FlatEnter::Entered {
|
||||
@@ -234,6 +237,7 @@ impl Coro {
|
||||
code,
|
||||
closed: AtomicCell::new(false),
|
||||
running: AtomicCell::new(false),
|
||||
started: AtomicCell::new(false),
|
||||
exception: PyAtomicRef::from(None),
|
||||
name: PyMutex::new(name),
|
||||
qualname: PyMutex::new(qualname),
|
||||
@@ -307,6 +311,7 @@ impl Coro {
|
||||
|
||||
let frame = self.frame();
|
||||
vm.resume_gen_frame(&frame, gen_exc, |f| {
|
||||
self.started.store(true);
|
||||
let result = func(f);
|
||||
// SAFETY: exclusive access guaranteed by the claim
|
||||
let _old = unsafe { (*exception_ptr).swap(vm.current_exception()) };
|
||||
@@ -401,16 +406,16 @@ impl Coro {
|
||||
if self.closed.load() {
|
||||
return Self::send_when_closed(jen, vm);
|
||||
}
|
||||
let value = if self.frame_opt().is_some_and(|f| f.lasti() > 0) {
|
||||
Some(value)
|
||||
} else if !vm.is_none(&value) {
|
||||
if !self.started() && !vm.is_none(&value) {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"can't send non-None value to a just-started {}",
|
||||
gen_name(jen, vm),
|
||||
)));
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
let value = self
|
||||
.frame_opt()
|
||||
.is_some_and(|f| f.lasti() > 0)
|
||||
.then_some(value);
|
||||
let result = self.run_claimed(&claim, vm, |f| f.resume(value, vm));
|
||||
self.maybe_close(&result, &claim);
|
||||
drop(claim);
|
||||
@@ -462,7 +467,7 @@ impl Coro {
|
||||
return Ok(vm.ctx.none());
|
||||
}
|
||||
// FRAME_CREATED: mark finished and clear the iframe.
|
||||
if self.frame_opt().is_none_or(|f| f.lasti() == 0) {
|
||||
if !self.started() {
|
||||
self.closed.store(true);
|
||||
self.clear_except_code();
|
||||
return Ok(vm.ctx.none());
|
||||
@@ -490,16 +495,40 @@ impl Coro {
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear a created generator frame without resuming its body.
|
||||
pub(crate) fn clear_frame(&self, jen: &PyObject, vm: &VirtualMachine) -> PyResult<()> {
|
||||
if self.closed.load() {
|
||||
return Ok(());
|
||||
}
|
||||
if self.running.compare_exchange(false, true).is_err() {
|
||||
return Err(vm.new_runtime_error("cannot clear an executing frame"));
|
||||
}
|
||||
let claim = RunningGuard(self);
|
||||
if self.closed.load() {
|
||||
return Ok(());
|
||||
}
|
||||
if self.started() {
|
||||
return Err(vm.new_runtime_error("cannot clear a suspended frame"));
|
||||
}
|
||||
self.closed.store(true);
|
||||
// Closed owners cannot resume. Publish that state and release the
|
||||
// running claim before a warning hook can observe or clear us again.
|
||||
drop(claim);
|
||||
if jen.class().is(vm.ctx.types.coroutine_type) {
|
||||
crate::warn::warn_unawaited_coroutine(jen, &self.qualname(), vm);
|
||||
}
|
||||
self.clear_except_code();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Whether the frame has run at all, i.e. is stopped at a `yield` rather
|
||||
/// than at its start.
|
||||
pub(crate) fn started(&self) -> bool {
|
||||
self.frame_opt().is_some_and(|f| f.lasti() > 0)
|
||||
self.started.load()
|
||||
}
|
||||
|
||||
pub fn suspended(&self) -> bool {
|
||||
!self.closed.load()
|
||||
&& !self.running.load()
|
||||
&& self.frame_opt().is_some_and(|f| f.lasti() > 0)
|
||||
!self.closed.load() && !self.running.load() && self.started()
|
||||
}
|
||||
|
||||
pub fn running(&self) -> bool {
|
||||
|
||||
+227
-113
@@ -20,6 +20,7 @@ use crate::{
|
||||
},
|
||||
bytecode::{
|
||||
self, ADAPTIVE_COOLDOWN_VALUE, Instruction, LoadAttr, LoadSuperAttr, Opcode, SpecialMethod,
|
||||
oparg::{ResumeContext, ResumeLocation},
|
||||
},
|
||||
convert::{ToPyObject, ToPyResult},
|
||||
coroutine::Coro,
|
||||
@@ -105,6 +106,40 @@ pub fn current_thread_frame_materialize(vm: &VirtualMachine) -> Option<FrameObje
|
||||
Some(iframe.materialize(vm).to_owned())
|
||||
}
|
||||
|
||||
/// `_PyFrame_GetFirstComplete`: the first complete frame on a live raw
|
||||
/// execution chain.
|
||||
///
|
||||
/// # Safety
|
||||
/// Every non-null pointer traversed must belong to a live active frame chain
|
||||
/// on the current thread, or the caller must hold the owning thread stopped.
|
||||
/// The returned pointer is valid only while that chain remains alive.
|
||||
pub(crate) unsafe fn first_complete_iframe(
|
||||
mut current: *const InterpreterFrame,
|
||||
) -> *const InterpreterFrame {
|
||||
while !current.is_null() {
|
||||
// SAFETY: the caller guarantees the chain remains alive.
|
||||
let frame = unsafe { &*current };
|
||||
if !frame.is_incomplete() {
|
||||
break;
|
||||
}
|
||||
current = frame.previous();
|
||||
}
|
||||
current
|
||||
}
|
||||
|
||||
/// Resolve the active frame for a legacy trace/profile callback, skipping
|
||||
/// incomplete generator-creation frames before materializing a frame object.
|
||||
/// Raw execution-chain users deliberately keep seeing the actual active frame.
|
||||
pub(crate) fn current_complete_frame_for_trace(vm: &VirtualMachine) -> Option<FrameObjectRef> {
|
||||
// SAFETY: synchronous inspection of the current thread's active chain.
|
||||
let current = unsafe { first_complete_iframe(crate::vm::thread::get_current_frame()) };
|
||||
if current.is_null() {
|
||||
return None;
|
||||
}
|
||||
// SAFETY: the selected frame is alive on this thread's execution chain.
|
||||
Some(unsafe { (*current).materialize(vm).to_owned() })
|
||||
}
|
||||
|
||||
/// Read the globals dict from the topmost frame on this thread's chain.
|
||||
/// Returns `None` if the chain is empty.
|
||||
#[must_use]
|
||||
@@ -137,12 +172,13 @@ pub fn current_builtins() -> Option<PyObjectRef> {
|
||||
Some(unsafe { (*ptr).builtins().to_owned() })
|
||||
}
|
||||
|
||||
/// The frame `offset` positions below the current thread's top frame (offset 0
|
||||
/// is the top), or `None` if the stack is not that deep.
|
||||
/// The complete frame `offset` positions below the current thread's first
|
||||
/// complete frame, or `None` if the stack is not that deep.
|
||||
/// Materializes the FrameObject on demand for stack-allocated frames.
|
||||
#[must_use]
|
||||
pub fn frame_at_offset(offset: usize, vm: &VirtualMachine) -> Option<FrameObjectRef> {
|
||||
let mut cur = crate::vm::thread::get_current_frame();
|
||||
// SAFETY: synchronous inspection of this thread's active chain.
|
||||
let mut cur = unsafe { first_complete_iframe(crate::vm::thread::get_current_frame()) };
|
||||
let mut remaining = offset;
|
||||
while !cur.is_null() {
|
||||
if remaining == 0 {
|
||||
@@ -150,7 +186,8 @@ pub fn frame_at_offset(offset: usize, vm: &VirtualMachine) -> Option<FrameObject
|
||||
return Some(iframe.materialize(vm).to_owned());
|
||||
}
|
||||
remaining -= 1;
|
||||
cur = unsafe { (*cur).previous.load(Relaxed) as *const InterpreterFrame };
|
||||
// SAFETY: `cur` and its predecessors remain alive on this chain.
|
||||
cur = unsafe { first_complete_iframe((*cur).previous()) };
|
||||
}
|
||||
None
|
||||
}
|
||||
@@ -1155,6 +1192,19 @@ impl InterpreterFrame {
|
||||
Some((base, total_bytes))
|
||||
}
|
||||
|
||||
/// `_PyFrame_IsIncomplete`. A generator frame is complete even before its
|
||||
/// first resume. Its owner is temporarily Thread while it executes, so the
|
||||
/// owning generator's back-reference is checked too.
|
||||
pub(crate) fn is_incomplete(&self) -> bool {
|
||||
if FrameOwner::from_i8(self.owner.load(atomic::Ordering::Acquire)) == FrameOwner::Generator
|
||||
|| self.generator.load().is_some()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// lasti is one past the in-flight instruction.
|
||||
(self.lasti.load(Relaxed) as usize) <= self.code().first_traceable
|
||||
}
|
||||
|
||||
/// Get the last instruction index.
|
||||
#[inline(always)]
|
||||
pub fn get_lasti(&self) -> u32 {
|
||||
@@ -1228,10 +1278,43 @@ impl InterpreterFrame {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
pub(crate) unsafe fn materialize_detached_chain(&self, vm: &VirtualMachine) -> FrameObjectRef {
|
||||
// SAFETY: forwarded unchanged from this method's contract.
|
||||
unsafe { self.materialize_detached_chain_impl::<false>(vm) }
|
||||
}
|
||||
|
||||
/// Copy the complete ancestors used by Python-visible `f_back` traversal.
|
||||
/// The raw snapshot path above deliberately preserves every active node.
|
||||
///
|
||||
/// # Safety
|
||||
/// The caller must hold the world stopped and select a complete top frame.
|
||||
#[cfg(feature = "threading")]
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
pub(crate) unsafe fn materialize_complete_detached_chain(
|
||||
&self,
|
||||
vm: &VirtualMachine,
|
||||
) -> FrameObjectRef {
|
||||
// SAFETY: forwarded unchanged from this method's contract.
|
||||
unsafe { self.materialize_detached_chain_impl::<true>(vm) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "threading")]
|
||||
#[cold]
|
||||
unsafe fn materialize_detached_chain_impl<const SKIP_INCOMPLETE: bool>(
|
||||
&self,
|
||||
vm: &VirtualMachine,
|
||||
) -> FrameObjectRef {
|
||||
let top = unsafe { self.materialize_detached(vm) };
|
||||
let mut child = top.clone();
|
||||
let mut cur = self.previous();
|
||||
while !cur.is_null() {
|
||||
loop {
|
||||
if SKIP_INCOMPLETE {
|
||||
// SAFETY: the caller holds the owning thread stopped.
|
||||
cur = unsafe { first_complete_iframe(cur) };
|
||||
}
|
||||
if cur.is_null() {
|
||||
break;
|
||||
}
|
||||
let caller = unsafe { &*cur };
|
||||
let caller_fo = unsafe { caller.materialize_detached(vm) };
|
||||
{
|
||||
@@ -1756,6 +1839,7 @@ pub enum ExecutionResult {
|
||||
/// CACHE <- the parked frame's lasti is here + 1
|
||||
/// YIELD_VALUE 1
|
||||
/// resumed_at: RESUME
|
||||
/// CACHE
|
||||
/// JUMP_BACKWARD_NO_INTERRUPT -> send_idx
|
||||
/// exit: END_SEND
|
||||
/// ```
|
||||
@@ -2529,6 +2613,18 @@ impl Py<FrameObject> {
|
||||
self.with_exec(vm, |mut exec| exec.run(vm))
|
||||
}
|
||||
|
||||
/// Execute the eager generator preamble and suspend immediately after
|
||||
/// RETURN_GENERATOR. The caller links this frame with `vm.with_generator_frame`.
|
||||
/// The first resume supplies the value consumed by the following POP_TOP.
|
||||
pub(crate) fn prepare_generator(&self, vm: &VirtualMachine) -> PyResult<()> {
|
||||
self.with_exec(vm, |mut exec| match exec.run_until::<true>(vm)? {
|
||||
None => Ok(()),
|
||||
Some(_) => {
|
||||
Err(vm.new_runtime_error("generator preamble did not reach RETURN_GENERATOR"))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Resume a suspended generator or coroutine body, pushing `value` as the
|
||||
/// result of the `yield` it stopped at.
|
||||
///
|
||||
@@ -2788,10 +2884,10 @@ pub(crate) fn gen_collapse_allowed(vm: &VirtualMachine) -> bool {
|
||||
/// delegating to, if the trampoline can resume it in this frame's place.
|
||||
///
|
||||
/// Recognizes the shape documented on [`GenCont`] — `lasti` at the `RESUME`
|
||||
/// that follows the delegating `YIELD_VALUE`, with the delegate on top of the
|
||||
/// stack — and requires the `SEND` to have already specialized to `SendGen`,
|
||||
/// so that a `Send` still collecting specialization feedback keeps running
|
||||
/// normally. The frame is left untouched; `park_at_send` commits to it.
|
||||
/// that follows the delegating `YIELD_VALUE`, with the delegate below its
|
||||
/// index-or-NULL stack slot — and requires the `SEND` to have already
|
||||
/// specialized to `SendGen`, so that a `Send` still collecting feedback keeps
|
||||
/// running normally. The frame is left untouched; `park_at_send` commits to it.
|
||||
pub(crate) fn yield_from_delegate(
|
||||
iframe: &InterpreterFrame,
|
||||
vm: &VirtualMachine,
|
||||
@@ -2824,10 +2920,11 @@ pub(crate) fn yield_from_delegate(
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let delegate = match iframe.localsplus.stack_last() {
|
||||
Some(Some(top)) => top.as_object(),
|
||||
_ => return None,
|
||||
};
|
||||
let stack = iframe.localsplus.stack_as_slice();
|
||||
let delegate = stack
|
||||
.get(stack.len().checked_sub(2)?)?
|
||||
.as_ref()?
|
||||
.as_object();
|
||||
// The same guards `SendGen` applies before resuming a generator itself.
|
||||
if delegate.downcast_ref_if_exact::<PyGenerator>(vm).is_none()
|
||||
&& delegate.downcast_ref_if_exact::<PyCoroutine>(vm).is_none()
|
||||
@@ -3072,7 +3169,8 @@ pub(crate) fn release_datastack_frame(frame: &Py<FrameObject>, vm: &VirtualMachi
|
||||
{
|
||||
let mut guard = frame.iframe().cold().retained_back.lock();
|
||||
if guard.is_none() {
|
||||
let prev = frame.previous_iframe();
|
||||
// SAFETY: this frame is returning into its still-active caller.
|
||||
let prev = unsafe { first_complete_iframe(frame.previous_iframe()) };
|
||||
*guard = unsafe { owned_chain_frame(prev) };
|
||||
}
|
||||
}
|
||||
@@ -3440,7 +3538,11 @@ impl ExecutingFrame<'_> {
|
||||
/// (COPY_FREE_VARS / RETURN_GENERATOR that precede it are not a 'call',
|
||||
/// and later RESUMEs in a SEND loop are not a new call either).
|
||||
fn trace_call_from_resume(&mut self, vm: &VirtualMachine, resume_type: u32) -> PyResult<()> {
|
||||
if self.call_traced {
|
||||
// RESUME_AT_GEN_EXPR_START runs before the frame is complete. The
|
||||
// first traceable instruction is the ordinary RESUME after creation.
|
||||
if resume_type & ResumeContext::LOCATION_MASK == ResumeLocation::AtGenExprStart.as_u32()
|
||||
|| self.call_traced
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
self.call_traced = true;
|
||||
@@ -3626,7 +3728,22 @@ impl ExecutingFrame<'_> {
|
||||
/// Matches `_PyEval_MonitorRaise` → `PY_MONITORING_EVENT_RAISE` →
|
||||
/// `sys_trace_exception_func` in legacy_tracing.c.
|
||||
fn fire_exception_trace(&self, exc: &Py<PyBaseException>, vm: &VirtualMachine) -> PyResult<()> {
|
||||
if vm.use_tracing.get() && self.trace_is_set(vm) {
|
||||
if !vm.use_tracing.get() {
|
||||
return Ok(());
|
||||
}
|
||||
let trace_is_set = if self.iframe().is_incomplete() {
|
||||
// Legacy tracing resolves PyEval_GetFrame to the first complete
|
||||
// caller while sys.monitoring continues to use this code object.
|
||||
current_complete_frame_for_trace(vm).is_some_and(|frame| {
|
||||
frame
|
||||
.iframe()
|
||||
.cold_opt()
|
||||
.is_some_and(|cold| cold.trace.lock().is_some())
|
||||
})
|
||||
} else {
|
||||
self.trace_is_set(vm)
|
||||
};
|
||||
if trace_is_set {
|
||||
let exc_type: PyObjectRef = exc.class().to_owned().into();
|
||||
let exc_value: PyObjectRef = exc.to_owned().into();
|
||||
let exc_tb: PyObjectRef = exc
|
||||
@@ -3639,6 +3756,14 @@ impl ExecutingFrame<'_> {
|
||||
}
|
||||
|
||||
fn run(&mut self, vm: &VirtualMachine) -> PyResult<ExecutionResult> {
|
||||
self.run_until::<false>(vm)
|
||||
.map(|result| result.expect("ordinary execution cannot stop at a generator preamble"))
|
||||
}
|
||||
|
||||
fn run_until<const PREPARE_GENERATOR: bool>(
|
||||
&mut self,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<Option<ExecutionResult>> {
|
||||
flame_guard!(format!(
|
||||
"FrameObject::run({obj_name})",
|
||||
obj_name = self.code.obj_name
|
||||
@@ -3681,7 +3806,7 @@ impl ExecutingFrame<'_> {
|
||||
// principle toggle it, because we refresh `tracing` below right
|
||||
// after that call returns (that cold path is only taken when
|
||||
// tracing was already on, so it costs nothing on the hot path).
|
||||
let mut tracing = vm.use_tracing.get();
|
||||
let mut tracing = !PREPARE_GENERATOR && vm.use_tracing.get();
|
||||
|
||||
// Fire 'line' trace event when line number changes.
|
||||
// Only fire if this frame has a per-frame trace function set
|
||||
@@ -3726,7 +3851,7 @@ impl ExecutingFrame<'_> {
|
||||
idx = lasti_cell.load(Relaxed) as usize;
|
||||
continue;
|
||||
}
|
||||
Ok(Some(value)) => break Ok(value),
|
||||
Ok(Some(value)) => break Ok(Some(value)),
|
||||
Err(e) => break Err(e),
|
||||
}
|
||||
}
|
||||
@@ -3734,7 +3859,7 @@ impl ExecutingFrame<'_> {
|
||||
// The trace callback may have toggled tracing (e.g. via
|
||||
// sys.settrace(None)); refresh before the opcode-trace check
|
||||
// below reuses this flag.
|
||||
tracing = vm.use_tracing.get();
|
||||
tracing = !PREPARE_GENERATOR && vm.use_tracing.get();
|
||||
// Trace callback may have changed lasti via set_f_lineno.
|
||||
// Re-read and restart the loop from the new position.
|
||||
if lasti_cell.load(Relaxed) != (idx as u32 + 1) {
|
||||
@@ -3811,9 +3936,10 @@ impl ExecutingFrame<'_> {
|
||||
frame: &mut ExecutingFrame<'_>,
|
||||
exception: PyBaseExceptionRef,
|
||||
idx: usize,
|
||||
complete_frame: bool,
|
||||
vm: &VirtualMachine,
|
||||
) -> FrameResult {
|
||||
if let Some((loc, _end_loc)) = frame.code.locations.get(idx) {
|
||||
if complete_frame && let Some((loc, _end_loc)) = frame.code.locations.get(idx) {
|
||||
let next = exception.traceback();
|
||||
let new_traceback = PyTraceback::new(
|
||||
next,
|
||||
@@ -3832,10 +3958,10 @@ impl ExecutingFrame<'_> {
|
||||
},
|
||||
)
|
||||
}
|
||||
match handle_signal_exception(self, exception, idx, vm) {
|
||||
match handle_signal_exception(self, exception, idx, !PREPARE_GENERATOR, vm) {
|
||||
Ok(None) => {}
|
||||
Ok(Some(value)) => {
|
||||
break Ok(value);
|
||||
break Ok(Some(value));
|
||||
}
|
||||
Err(exception) => {
|
||||
break Err(exception);
|
||||
@@ -3859,6 +3985,13 @@ impl ExecutingFrame<'_> {
|
||||
crate::opcode_histogram::record(prev_op, op_byte);
|
||||
prev_op = op_byte;
|
||||
}
|
||||
if PREPARE_GENERATOR && matches!(op, Instruction::ReturnGenerator) {
|
||||
// The generator takes over the frame with its instruction
|
||||
// pointer advanced to POP_TOP. The iterator pair a generator
|
||||
// expression pushed must outlive this execution borrow.
|
||||
self.localsplus.promote_stack();
|
||||
break Ok(None);
|
||||
}
|
||||
let result = self.execute_instruction(op, arg, &mut do_extend_arg, vm);
|
||||
// Skip inline cache entries if instruction fell through (no jump).
|
||||
// `cache_entries()` is a table lookup, so it is done here rather
|
||||
@@ -3874,7 +4007,7 @@ impl ExecutingFrame<'_> {
|
||||
match result {
|
||||
Ok(None) => {}
|
||||
Ok(Some(value)) => {
|
||||
break Ok(value);
|
||||
break Ok(Some(value));
|
||||
}
|
||||
// Instruction raised an exception
|
||||
Err(exception) => {
|
||||
@@ -3885,6 +4018,7 @@ impl ExecutingFrame<'_> {
|
||||
idx: usize,
|
||||
is_reraise: bool,
|
||||
is_new_raise: bool,
|
||||
complete_frame: bool,
|
||||
vm: &VirtualMachine,
|
||||
) -> FrameResult {
|
||||
// 1. Extract traceback from exception's '__traceback__' attr.
|
||||
@@ -3903,7 +4037,9 @@ impl ExecutingFrame<'_> {
|
||||
// checking for duplicates. Each time an exception passes through
|
||||
// a frame (e.g., in a loop with repeated raise statements),
|
||||
// a new traceback entry is added.
|
||||
if let Some((loc, _end_loc)) = frame.code.locations.get(idx) {
|
||||
if complete_frame
|
||||
&& let Some((loc, _end_loc)) = frame.code.locations.get(idx)
|
||||
{
|
||||
let next = exception.traceback();
|
||||
|
||||
let new_traceback = PyTraceback::new(
|
||||
@@ -4001,12 +4137,21 @@ impl ExecutingFrame<'_> {
|
||||
self.fire_exception_trace(&exception, vm)?;
|
||||
}
|
||||
|
||||
match handle_exception(self, exception, idx, is_reraise, is_new_raise, vm) {
|
||||
match handle_exception(
|
||||
self,
|
||||
exception,
|
||||
idx,
|
||||
is_reraise,
|
||||
is_new_raise,
|
||||
!PREPARE_GENERATOR,
|
||||
vm,
|
||||
) {
|
||||
// The handler unwound to a new position, so the next
|
||||
// index is whatever it left in the frame.
|
||||
Ok(None) => next_idx = lasti_cell.load(Relaxed),
|
||||
Ok(Some(result)) => break Ok(result),
|
||||
Ok(Some(result)) => break Ok(Some(result)),
|
||||
Err(exception) => {
|
||||
monitoring::reset_reraise_pending();
|
||||
// Fire PY_UNWIND: exception escapes this frame
|
||||
let exception = if vm.state.monitoring_events.load()
|
||||
& MonitoringEvent::PyUnwind.mask()
|
||||
@@ -4040,16 +4185,18 @@ impl ExecutingFrame<'_> {
|
||||
// In RustPython, we check:
|
||||
// 1. lasti points to RESUME (after YIELD_VALUE)
|
||||
// 2. The previous instruction was YIELD_VALUE with arg >= 1
|
||||
// 3. Stack top is the delegate (receiver)
|
||||
// 3. The delegate (receiver) is below its index-or-NULL slot
|
||||
//
|
||||
// First check if stack is empty - if so, we can't be in yield-from
|
||||
if self.localsplus.stack_is_empty() {
|
||||
if self.localsplus.stack_len() < 2 {
|
||||
return None;
|
||||
}
|
||||
let lasti = self.lasti() as usize;
|
||||
if let Some(unit) = self.code.instructions.get(lasti) {
|
||||
match &unit.op {
|
||||
Instruction::Send { .. } => return Some(self.top_value()),
|
||||
Instruction::Send { .. } | Instruction::SendGen => {
|
||||
return Some(self.nth_value(1));
|
||||
}
|
||||
Instruction::Resume { .. } | Instruction::InstrumentedResume => {
|
||||
// Check if previous instruction was YIELD_VALUE with arg >= 1
|
||||
// This indicates yield-from/await context
|
||||
@@ -4063,8 +4210,8 @@ impl ExecutingFrame<'_> {
|
||||
// YIELD_VALUE arg: 0 = direct yield, >= 1 = yield-from/await
|
||||
// OpArgByte.0 is the raw byte value
|
||||
if u8::from(prev_unit.arg) >= 1 {
|
||||
// In yield-from/await context, delegate is on top of stack
|
||||
return Some(self.top_value());
|
||||
// The iterator pair remains after YIELD_VALUE.
|
||||
return Some(self.nth_value(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4288,6 +4435,7 @@ impl ExecutingFrame<'_> {
|
||||
}
|
||||
Ok(Some(result)) => Ok(result),
|
||||
Err(exception) => {
|
||||
monitoring::reset_reraise_pending();
|
||||
// Fire PY_UNWIND: exception escapes the generator frame.
|
||||
let exception =
|
||||
if vm.state.monitoring_events.load() & MonitoringEvent::PyUnwind.mask() != 0 {
|
||||
@@ -4915,36 +5063,23 @@ impl ExecutingFrame<'_> {
|
||||
self.push_value(iter);
|
||||
Ok(None)
|
||||
}
|
||||
Instruction::GetIter => {
|
||||
let iterated_obj = self.pop_value();
|
||||
let iter_obj = PyIter::try_from_object(vm, iterated_obj)?;
|
||||
self.push_value(iter_obj.into());
|
||||
Ok(None)
|
||||
}
|
||||
Instruction::GetYieldFromIter => {
|
||||
// GET_YIELD_FROM_ITER: prepare iterator for yield from
|
||||
// If iterable is a coroutine, ensure we're in a coroutine context
|
||||
// If iterable is a generator, use it directly
|
||||
// Otherwise, call iter() on it
|
||||
Instruction::GetIter { mode } => {
|
||||
// _PyEval_GetIter
|
||||
let iterable = self.pop_value();
|
||||
let iter = if iterable.class().is(vm.ctx.types.coroutine_type) {
|
||||
// Coroutine requires CO_COROUTINE or CO_ITERABLE_COROUTINE flag
|
||||
if !self.code.flags.intersects(
|
||||
bytecode::CodeFlags::COROUTINE | bytecode::CodeFlags::ITERABLE_COROUTINE,
|
||||
) {
|
||||
let yield_from = mode.get(arg);
|
||||
debug_assert_ne!(yield_from, bytecode::oparg::GET_ITER_YIELD_FROM);
|
||||
let iter = if yield_from != 0 && iterable.class().is(vm.ctx.types.coroutine_type) {
|
||||
if yield_from == bytecode::oparg::GET_ITER_YIELD_FROM_CORO_CHECK {
|
||||
return Err(vm.new_type_error(
|
||||
"cannot 'yield from' a coroutine object in a non-coroutine generator",
|
||||
));
|
||||
}
|
||||
iterable
|
||||
} else if iterable.class().is(vm.ctx.types.generator_type) {
|
||||
// Generator can be used directly
|
||||
iterable
|
||||
} else {
|
||||
// Otherwise, get iterator
|
||||
PyIter::try_from_object(vm, iterable)?.into()
|
||||
iterable.get_iter(vm)?.into()
|
||||
};
|
||||
self.push_value(iter);
|
||||
self.push_null();
|
||||
Ok(None)
|
||||
}
|
||||
Instruction::GetLen => {
|
||||
@@ -4960,7 +5095,7 @@ impl ExecutingFrame<'_> {
|
||||
Ok(None)
|
||||
}
|
||||
Instruction::ImportName { namei: idx } => {
|
||||
self.import(vm, Some(self.code.names[idx.get(arg) as usize]))?;
|
||||
self.import(vm, Some(self.code.names[(idx.get(arg) >> 2) as usize]))?;
|
||||
Ok(None)
|
||||
}
|
||||
Instruction::IsOp { invert } => {
|
||||
@@ -5103,18 +5238,6 @@ impl ExecutingFrame<'_> {
|
||||
}
|
||||
Instruction::LoadConst { consti } => {
|
||||
self.push_value(self.code.constants[consti.get(arg)].clone().into());
|
||||
// Mirror CPython's LOAD_CONST family transition. RustPython does
|
||||
// not currently distinguish immortal constants at runtime.
|
||||
let instr_idx = self.lasti() as usize - 1;
|
||||
unsafe {
|
||||
self.code
|
||||
.instructions
|
||||
.replace_op(instr_idx, Instruction::LoadConstMortal);
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
Instruction::LoadConstMortal | Instruction::LoadConstImmortal => {
|
||||
self.push_value(self.code.constants[u32::from(arg).into()].clone().into());
|
||||
Ok(None)
|
||||
}
|
||||
Instruction::LoadCommonConstant { idx } => {
|
||||
@@ -5139,6 +5262,11 @@ impl ExecutingFrame<'_> {
|
||||
.expect("builtin_any not initialized"),
|
||||
CommonConstant::BuiltinList => vm.ctx.types.list_type.to_owned().into(),
|
||||
CommonConstant::BuiltinSet => vm.ctx.types.set_type.to_owned().into(),
|
||||
CommonConstant::None => vm.ctx.none(),
|
||||
CommonConstant::EmptyStr => vm.ctx.empty_str.to_owned().into(),
|
||||
CommonConstant::True => vm.ctx.new_bool(true).into(),
|
||||
CommonConstant::False => vm.ctx.new_bool(false).into(),
|
||||
CommonConstant::MinusOne => vm.ctx.new_int(-1).into(),
|
||||
};
|
||||
self.push_value(value);
|
||||
Ok(None)
|
||||
@@ -5615,8 +5743,8 @@ impl ExecutingFrame<'_> {
|
||||
Ok(None)
|
||||
}
|
||||
Instruction::PopTop => {
|
||||
// Pop value from stack and ignore.
|
||||
self.pop_stackref();
|
||||
// Loop unwinding also uses POP_TOP for index-or-NULL slots.
|
||||
self.pop_stackref_opt();
|
||||
Ok(None)
|
||||
}
|
||||
Instruction::EndFor => {
|
||||
@@ -5625,7 +5753,8 @@ impl ExecutingFrame<'_> {
|
||||
Ok(None)
|
||||
}
|
||||
Instruction::PopIter => {
|
||||
// Pop the iterator from stack (end of for loop)
|
||||
// Discard the iterator's index-or-NULL slot and the iterator.
|
||||
self.pop_stackref_opt();
|
||||
self.pop_stackref();
|
||||
Ok(None)
|
||||
}
|
||||
@@ -5930,10 +6059,10 @@ impl ExecutingFrame<'_> {
|
||||
Ok(Some(ExecutionResult::Yield(self.pop_value())))
|
||||
}
|
||||
Instruction::Send { .. } => {
|
||||
// (receiver, v -- receiver, retval)
|
||||
// (receiver, null_or_index, v -- receiver, null_or_index, retval)
|
||||
self.adaptive(|s, ii, cb| s.specialize_send(vm, ii, cb));
|
||||
let exit_label = bytecode::Label::from_u32(self.lasti() + 1 + u32::from(arg));
|
||||
let receiver = self.nth_value(1);
|
||||
let receiver = self.nth_value(2);
|
||||
let can_fast_send = !self.specialization_eval_frame_active(vm)
|
||||
&& (receiver.downcast_ref_if_exact::<PyGenerator>(vm).is_some()
|
||||
|| receiver.downcast_ref_if_exact::<PyCoroutine>(vm).is_some())
|
||||
@@ -5941,7 +6070,7 @@ impl ExecutingFrame<'_> {
|
||||
.builtin_coro(receiver)
|
||||
.is_some_and(|coro| !coro.running() && !coro.closed());
|
||||
let val = self.pop_value();
|
||||
let receiver = self.top_value();
|
||||
let receiver = self.nth_value(1);
|
||||
let ret = if can_fast_send {
|
||||
let coro = self.builtin_coro(receiver).unwrap();
|
||||
if vm.is_none(&val) {
|
||||
@@ -5971,8 +6100,8 @@ impl ExecutingFrame<'_> {
|
||||
}
|
||||
Instruction::SendGen => {
|
||||
let exit_label = bytecode::Label::from_u32(self.lasti() + 1 + u32::from(arg));
|
||||
// Stack: [receiver, val] — peek receiver before popping
|
||||
let receiver = self.nth_value(1);
|
||||
// Stack: [receiver, null_or_index, val] — peek before popping
|
||||
let receiver = self.nth_value(2);
|
||||
let mut started = false;
|
||||
let can_fast_send = !self.specialization_eval_frame_active(vm)
|
||||
&& (receiver.downcast_ref_if_exact::<PyGenerator>(vm).is_some()
|
||||
@@ -5984,7 +6113,7 @@ impl ExecutingFrame<'_> {
|
||||
let val = self.pop_value();
|
||||
|
||||
if can_fast_send {
|
||||
let receiver = self.top_value();
|
||||
let receiver = self.nth_value(1);
|
||||
// Hand an already-suspended generator to the trampoline,
|
||||
// which runs its frame in this same eval loop and re-yields
|
||||
// for this frame — no Rust frame and no instruction per
|
||||
@@ -6019,7 +6148,7 @@ impl ExecutingFrame<'_> {
|
||||
}
|
||||
}
|
||||
}
|
||||
let receiver = self.top_value();
|
||||
let receiver = self.nth_value(1);
|
||||
match self._send(receiver, val, vm)? {
|
||||
PyIterReturn::Return(value) => {
|
||||
self.push_value(value);
|
||||
@@ -6038,9 +6167,9 @@ impl ExecutingFrame<'_> {
|
||||
}
|
||||
}
|
||||
Instruction::EndSend => {
|
||||
// Stack: (receiver, value) -> (value)
|
||||
// Pops receiver, leaves value
|
||||
// (receiver, null_or_index, value) -> (value)
|
||||
let value = self.pop_value();
|
||||
self.pop_stackref_opt(); // discard index-or-NULL
|
||||
self.pop_stackref(); // discard receiver
|
||||
self.push_value(value);
|
||||
Ok(None)
|
||||
@@ -6057,39 +6186,24 @@ impl ExecutingFrame<'_> {
|
||||
Ok(None)
|
||||
}
|
||||
Instruction::CleanupThrow => {
|
||||
// CLEANUP_THROW: (sub_iter, last_sent_val, exc) -> (None, value) OR re-raise
|
||||
// If StopIteration: pop all 3, extract value, push (None, value)
|
||||
// Otherwise: pop all 3, return Err(exc) for unwind_blocks to handle
|
||||
//
|
||||
// Unlike CPython where exception_unwind pops the triple as part of
|
||||
// stack cleanup to handler depth, RustPython pops here explicitly
|
||||
// and lets unwind_blocks find outer handlers.
|
||||
// Compiler sets handler_depth = base + 2 (before exc is pushed).
|
||||
|
||||
// First peek at exc_value (top of stack) without popping
|
||||
// (sub_iter, null_in, last_sent_val, exc) -> (None, null_out, value)
|
||||
// Leave nonmatching exceptions and their stack for unwinding.
|
||||
let exc = self.top_value();
|
||||
|
||||
// Check if it's a StopIteration
|
||||
if let Some(exc_ref) = exc.downcast_ref::<PyBaseException>()
|
||||
&& exc_ref.fast_isinstance(vm.ctx.exceptions.stop_iteration)
|
||||
{
|
||||
// Extract value from StopIteration
|
||||
let value = exc_ref.get_arg(0).unwrap_or_else(|| vm.ctx.none());
|
||||
// Now pop all three
|
||||
self.pop_stackref(); // exc
|
||||
self.pop_stackref(); // last_sent_val
|
||||
let index_or_null = self.pop_stackref_opt();
|
||||
self.pop_stackref(); // sub_iter
|
||||
self.push_value(vm.ctx.none());
|
||||
self.push_stackref_opt(index_or_null);
|
||||
self.push_value(value);
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// Re-raise other exceptions: pop all three and return Err(exc)
|
||||
let exc = self.pop_value(); // exc
|
||||
self.pop_stackref(); // last_sent_val
|
||||
self.pop_stackref(); // sub_iter
|
||||
|
||||
let exc = exc
|
||||
.to_owned()
|
||||
.downcast::<PyBaseException>()
|
||||
.map_err(|_| vm.new_type_error("exception expected"))?;
|
||||
Err(exc)
|
||||
@@ -7947,7 +8061,7 @@ impl ExecutingFrame<'_> {
|
||||
}
|
||||
Instruction::ForIterRange => {
|
||||
let target = bytecode::Label::from_u32(self.lasti() + 1 + u32::from(arg));
|
||||
let iter = self.top_value();
|
||||
let iter = self.nth_value(1);
|
||||
if let Some(range_iter) = iter.downcast_ref_if_exact::<PyRangeIterator>(vm) {
|
||||
if let Some(value) = range_iter.fast_next() {
|
||||
self.push_value(vm.ctx.new_int(value).into());
|
||||
@@ -7962,7 +8076,7 @@ impl ExecutingFrame<'_> {
|
||||
}
|
||||
Instruction::ForIterList => {
|
||||
let target = bytecode::Label::from_u32(self.lasti() + 1 + u32::from(arg));
|
||||
let iter = self.top_value();
|
||||
let iter = self.nth_value(1);
|
||||
if let Some(list_iter) = iter.downcast_ref_if_exact::<PyListIterator>(vm) {
|
||||
if let Some(value) = list_iter.fast_next() {
|
||||
self.push_value(value);
|
||||
@@ -7977,7 +8091,7 @@ impl ExecutingFrame<'_> {
|
||||
}
|
||||
Instruction::ForIterTuple => {
|
||||
let target = bytecode::Label::from_u32(self.lasti() + 1 + u32::from(arg));
|
||||
let iter = self.top_value();
|
||||
let iter = self.nth_value(1);
|
||||
if let Some(tuple_iter) = iter.downcast_ref_if_exact::<PyTupleIterator>(vm) {
|
||||
if let Some(value) = tuple_iter.fast_next() {
|
||||
self.push_value(value);
|
||||
@@ -7992,7 +8106,7 @@ impl ExecutingFrame<'_> {
|
||||
}
|
||||
Instruction::ForIterGen => {
|
||||
let target = bytecode::Label::from_u32(self.lasti() + 1 + u32::from(arg));
|
||||
let iter = self.top_value();
|
||||
let iter = self.nth_value(1);
|
||||
if self.specialization_eval_frame_active(vm) {
|
||||
self.execute_for_iter(vm, target)?;
|
||||
return Ok(None);
|
||||
@@ -8285,10 +8399,10 @@ impl ExecutingFrame<'_> {
|
||||
Ok(None)
|
||||
}
|
||||
Instruction::InstrumentedEndFor => {
|
||||
// Stack: [value, receiver(iter), ...]
|
||||
// Stack: [..., receiver(iter), null_or_index, value]
|
||||
// PyGen_Check: only fire STOP_ITERATION for generators
|
||||
let is_gen = self
|
||||
.nth_value(1)
|
||||
.nth_value(2)
|
||||
.downcast_ref::<crate::builtins::PyGenerator>()
|
||||
.is_some();
|
||||
let value = self.pop_value();
|
||||
@@ -8300,6 +8414,7 @@ impl ExecutingFrame<'_> {
|
||||
}
|
||||
Instruction::InstrumentedEndSend => {
|
||||
let value = self.pop_value();
|
||||
self.pop_stackref_opt();
|
||||
let receiver = self.pop_value();
|
||||
// PyGen_Check || PyCoro_CheckExact
|
||||
let is_gen_or_coro = receiver
|
||||
@@ -8388,6 +8503,7 @@ impl ExecutingFrame<'_> {
|
||||
}
|
||||
Instruction::InstrumentedPopIter => {
|
||||
// BRANCH_RIGHT is fired by InstrumentedForIter, not here.
|
||||
self.pop_stackref_opt();
|
||||
self.pop_stackref();
|
||||
Ok(None)
|
||||
}
|
||||
@@ -8759,6 +8875,7 @@ impl ExecutingFrame<'_> {
|
||||
if let Some(entry) =
|
||||
bytecode::find_exception_handler(&self.code.exceptiontable, offset)
|
||||
{
|
||||
monitoring::reset_reraise_pending();
|
||||
// Fire EXCEPTION_HANDLED before setting up handler.
|
||||
// If the callback raises, the handler is NOT set up and the
|
||||
// new exception propagates instead.
|
||||
@@ -9473,7 +9590,7 @@ impl ExecutingFrame<'_> {
|
||||
value: Option<PyObjectRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
let iter = self.top_value();
|
||||
let iter = self.nth_value(1);
|
||||
if iter.downcast_ref_if_exact::<PyListIterator>(vm).is_some()
|
||||
|| iter.downcast_ref_if_exact::<PyRangeIterator>(vm).is_some()
|
||||
|| iter.downcast_ref_if_exact::<PyTupleIterator>(vm).is_some()
|
||||
@@ -9497,14 +9614,14 @@ impl ExecutingFrame<'_> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Advance the iterator on top of stack.
|
||||
/// Advance the iterator below its index-or-NULL stack slot.
|
||||
/// Returns `true` if iteration continued (item pushed), `false` if exhausted (jumped).
|
||||
fn execute_for_iter(
|
||||
&mut self,
|
||||
vm: &VirtualMachine,
|
||||
target: bytecode::Label,
|
||||
) -> Result<bool, PyBaseExceptionRef> {
|
||||
let top = self.top_value();
|
||||
let top = self.nth_value(1);
|
||||
|
||||
// FOR_ITER_RANGE: bypass generic iterator protocol for range iterators
|
||||
if let Some(range_iter) = top.downcast_ref_if_exact::<PyRangeIterator>(vm) {
|
||||
@@ -9535,10 +9652,7 @@ impl ExecutingFrame<'_> {
|
||||
self.jump(self.for_iter_jump_target(target));
|
||||
Ok(false)
|
||||
}
|
||||
Err(next_error) => {
|
||||
self.pop_stackref();
|
||||
Err(next_error)
|
||||
}
|
||||
Err(next_error) => Err(next_error),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11440,8 +11554,8 @@ impl ExecutingFrame<'_> {
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// Stack: [receiver, val] — receiver is at position 1
|
||||
let receiver = self.nth_value(1);
|
||||
// Stack: [receiver, null_or_index, val] — receiver is at position 2
|
||||
let receiver = self.nth_value(2);
|
||||
let is_exact_gen_or_coro = receiver.downcast_ref_if_exact::<PyGenerator>(vm).is_some()
|
||||
|| receiver.downcast_ref_if_exact::<PyCoroutine>(vm).is_some();
|
||||
if is_exact_gen_or_coro && !self.specialization_eval_frame_active(vm) {
|
||||
@@ -11707,7 +11821,7 @@ impl ExecutingFrame<'_> {
|
||||
) {
|
||||
return;
|
||||
}
|
||||
let iter = self.top_value();
|
||||
let iter = self.nth_value(1);
|
||||
|
||||
let new_op = if iter.downcast_ref_if_exact::<PyRangeIterator>(vm).is_some() {
|
||||
Some(Instruction::ForIterRange)
|
||||
|
||||
@@ -267,7 +267,7 @@ impl VirtualMachine {
|
||||
let is_profile_event = event.is_profile_event();
|
||||
let is_opcode_event = event.is_opcode_event();
|
||||
|
||||
let Some(frame_ref) = crate::frame::current_thread_frame_materialize(self) else {
|
||||
let Some(frame_ref) = crate::frame::current_complete_frame_for_trace(self) else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
|
||||
@@ -124,6 +124,8 @@ mod decl {
|
||||
|
||||
struct WriterRefEntry {
|
||||
idx: u32,
|
||||
// The table holds a reference, as w_ref does, so the id stays unique.
|
||||
_owner: PyObjectRef,
|
||||
/// Set between `reserve` and `complete` for the object kinds whose
|
||||
/// immutable representation cannot be rebuilt from a back-reference.
|
||||
incomplete: bool,
|
||||
@@ -158,8 +160,14 @@ mod decl {
|
||||
}
|
||||
fn reserve(&mut self, obj: &PyObject, incomplete: bool) -> u32 {
|
||||
let idx = self.next_idx;
|
||||
self.map
|
||||
.insert(obj.get_id(), WriterRefEntry { idx, incomplete });
|
||||
self.map.insert(
|
||||
obj.get_id(),
|
||||
WriterRefEntry {
|
||||
idx,
|
||||
incomplete,
|
||||
_owner: obj.to_owned(),
|
||||
},
|
||||
);
|
||||
self.next_idx += 1;
|
||||
idx
|
||||
}
|
||||
@@ -233,8 +241,14 @@ mod decl {
|
||||
|| obj.is(PyStopIteration::static_type())
|
||||
|| obj.downcast_ref::<crate::builtins::PyEllipsis>().is_some();
|
||||
|
||||
// FLAG_REF: check if already written, otherwise reserve slot
|
||||
if !is_singleton && let Some(rt) = refs.as_mut() {
|
||||
// If it has only one reference, it definitely isn't shared. But
|
||||
// TYPE_REF is always used for interned strings, to keep PYC files as
|
||||
// stable as possible.
|
||||
let use_ref = refs.is_some()
|
||||
&& !is_singleton
|
||||
&& (obj.strong_count() != 1
|
||||
|| (obj.class().is(PyStr::static_type()) && obj.is_interned()));
|
||||
if use_ref && let Some(rt) = refs.as_mut() {
|
||||
match rt.try_ref(buf, obj) {
|
||||
Ok(true) => return Ok(()),
|
||||
Ok(false) => {}
|
||||
@@ -247,7 +261,6 @@ mod decl {
|
||||
}
|
||||
}
|
||||
let type_pos = buf.len();
|
||||
let use_ref = refs.is_some() && !is_singleton;
|
||||
// A code or slice entry stays incomplete until its contents are
|
||||
// written: the reader rebuilds both from their fields, so a
|
||||
// back-reference issued while those fields are still being emitted
|
||||
@@ -390,14 +403,35 @@ mod decl {
|
||||
return Err(vm.new_value_error("marshalling code objects is disallowed"));
|
||||
}
|
||||
buf.write_u8(b'c');
|
||||
// `Literal` holds the exact object a constant was built from, so
|
||||
// route `co_consts` back through the object writer: it reaches the
|
||||
// values `BorrowedConstant` cannot describe and shares the one
|
||||
// reference table the reader indexes against.
|
||||
marshal::serialize_code_with(buf, &co.code, |buf, constant| {
|
||||
let constant = PyObjectRef::from(constant.clone());
|
||||
write_object_depth(buf, &constant, refs, version, allow_code, vm, depth - 1)
|
||||
})?;
|
||||
let code = &co.code;
|
||||
for value in [
|
||||
code.arg_count,
|
||||
code.posonlyarg_count,
|
||||
code.kwonlyarg_count,
|
||||
code.max_stackdepth,
|
||||
code.flags.bits(),
|
||||
] {
|
||||
buf.write_u32(value);
|
||||
}
|
||||
let co_code = co.get_code(vm);
|
||||
let mut write_field = |buf: &mut Vec<u8>, field: &PyObject| {
|
||||
write_object_depth(buf, field, refs, version, allow_code, vm, depth - 1)
|
||||
};
|
||||
for field in [
|
||||
co_code.as_object(),
|
||||
co.co_consts().as_object(),
|
||||
co.co_names.as_object(),
|
||||
co.co_localsplusnames.as_object(),
|
||||
co.co_localspluskinds.as_object(),
|
||||
co.source_path().as_object(),
|
||||
co.co_name.as_object(),
|
||||
code.qualname.as_object(),
|
||||
] {
|
||||
write_field(buf, field)?;
|
||||
}
|
||||
buf.write_u32(code.first_line_number.map_or(0, |n| n.get() as u32));
|
||||
write_field(buf, co.co_linetable.as_object())?;
|
||||
write_field(buf, co.co_exceptiontable.as_object())?;
|
||||
} else if let Some(sl) = obj.downcast_ref::<crate::builtins::PySlice>() {
|
||||
if version < 5 {
|
||||
return Err(vm.new_value_error("unmarshallable object"));
|
||||
@@ -593,14 +627,19 @@ mod decl {
|
||||
unsafe { tuple.payload.set_marshal_item(index, value) };
|
||||
Ok(())
|
||||
}
|
||||
fn make_code(&self, code: CodeObject) -> Result<Self::Value, marshal::MarshalError> {
|
||||
fn make_code(
|
||||
&self,
|
||||
code: CodeObject,
|
||||
fields: marshal::CodeFields<Self::Value>,
|
||||
) -> Result<Self::Value, marshal::MarshalError> {
|
||||
if !self.allow_code {
|
||||
return Err(self.remember_python_error(
|
||||
self.vm
|
||||
.new_value_error("unmarshalling code objects is disallowed"),
|
||||
));
|
||||
}
|
||||
Ok(crate::builtins::PyCode::new_ref_with_bag(self.vm, code).into())
|
||||
let code = PyCode::from_marshal(code, fields, &self.vm.ctx)?;
|
||||
Ok(code.into_ref(&self.vm.ctx).into())
|
||||
}
|
||||
fn make_stop_iter(&self) -> Result<Self::Value, marshal::MarshalError> {
|
||||
Ok(self.vm.ctx.exceptions.stop_iteration.to_owned().into())
|
||||
|
||||
@@ -9,8 +9,7 @@ use std::collections::{HashMap, HashSet};
|
||||
|
||||
pub(crate) const TOOL_LIMIT: usize = 6;
|
||||
const EVENTS_COUNT: usize = 19;
|
||||
const LOCAL_EVENTS_COUNT: usize = 11;
|
||||
const UNGROUPED_EVENTS_COUNT: usize = 18;
|
||||
const UNGROUPED_EVENTS_COUNT: usize = 16;
|
||||
|
||||
/// Event identifier (`PY_MONITORING_EVENT_*`), stored in `tstate->what_event`.
|
||||
/// `None` on the VM field is the `< 0` sentinel (not in a monitoring callback).
|
||||
@@ -204,6 +203,11 @@ fn parse_single_event(event: i32, vm: &VirtualMachine) -> PyResult<usize> {
|
||||
Ok(event_id)
|
||||
}
|
||||
|
||||
/// `PY_MONITORING_IS_INSTRUMENTED_EVENT`
|
||||
const fn is_instrumented_event(event_id: usize) -> bool {
|
||||
event_id <= MonitoringEvent::StopIteration as usize
|
||||
}
|
||||
|
||||
fn normalize_event_set(event_set: i32, local: bool, vm: &VirtualMachine) -> PyResult<u32> {
|
||||
let kind = if local {
|
||||
"local event set"
|
||||
@@ -232,7 +236,7 @@ fn normalize_event_set(event_set: i32, local: bool, vm: &VirtualMachine) -> PyRe
|
||||
event_set |= MonitoringEvent::BranchLeft.mask() | MonitoringEvent::BranchRight.mask();
|
||||
}
|
||||
|
||||
if local && event_set >= (1 << LOCAL_EVENTS_COUNT) {
|
||||
if local && event_set >= (1 << UNGROUPED_EVENTS_COUNT) {
|
||||
return Err(vm.new_value_error(format!("invalid local event set 0x{event_set:x}")));
|
||||
}
|
||||
|
||||
@@ -281,23 +285,6 @@ fn opcode_event_is_active(
|
||||
event_for_opcode(op, oparg).is_some_and(|ev| events & ev.mask() != 0)
|
||||
}
|
||||
|
||||
/// Walk real instructions, skipping specialization CACHE payloads.
|
||||
/// Cache slots may contain pointer bits that are not valid opcodes.
|
||||
fn for_each_instruction(
|
||||
code: &Py<PyCode>,
|
||||
mut f: impl FnMut(usize, rustpython_compiler_core::bytecode::Instruction, u8),
|
||||
) {
|
||||
let len = code.code.instructions.len();
|
||||
let mut i = 0;
|
||||
while i < len {
|
||||
let op = code.code.instructions.read_op(i);
|
||||
let oparg = code.code.instructions.read_arg(i).as_u8();
|
||||
let caches = op.deoptimize().cache_entries();
|
||||
f(i, op, oparg);
|
||||
i += 1 + caches;
|
||||
}
|
||||
}
|
||||
|
||||
/// Rewrite a code object's bytecode in-place with layered instrumentation.
|
||||
///
|
||||
/// Three layers (outermost first):
|
||||
@@ -402,26 +389,12 @@ pub(crate) fn instrument_code(code: &Py<PyCode>, events: u32) {
|
||||
data.line_opcodes.resize(len, 0);
|
||||
data.per_instruction_opcodes.resize(len, 0);
|
||||
|
||||
// Find _co_firsttraceable: index of first RESUME instruction
|
||||
let mut first_traceable = None;
|
||||
for_each_instruction(code, |i, op, _| {
|
||||
if first_traceable.is_none()
|
||||
&& matches!(
|
||||
op,
|
||||
Instruction::Resume { .. }
|
||||
| Instruction::ResumeCheck
|
||||
| Instruction::InstrumentedResume
|
||||
)
|
||||
{
|
||||
first_traceable = Some(i);
|
||||
}
|
||||
});
|
||||
let first_traceable = first_traceable.unwrap_or(0);
|
||||
let first_traceable = code.first_traceable;
|
||||
|
||||
// Phase 4: Place regular INSTRUMENTED_* opcodes whose event is active.
|
||||
// Walk by cache_entries so specialized CACHE payloads are not decoded as opcodes.
|
||||
{
|
||||
let mut i = 0;
|
||||
let mut i = first_traceable;
|
||||
while i < len {
|
||||
let op = code.code.instructions.read_op(i);
|
||||
let oparg = code.code.instructions.read_arg(i).as_u8();
|
||||
@@ -903,10 +876,20 @@ fn fire(
|
||||
for (tool, cb) in callbacks {
|
||||
let result = cb.call(args.clone(), vm)?;
|
||||
if disable_sentinel.as_ref().is_some_and(|d| result.is(d)) {
|
||||
// Only local events (event_id < LOCAL_EVENTS_COUNT) can be disabled.
|
||||
// Non-local events (RAISE, EXCEPTION_HANDLED, PY_UNWIND, etc.)
|
||||
// cannot be disabled per code object.
|
||||
if event_id >= LOCAL_EVENTS_COUNT {
|
||||
if is_instrumented_event(event_id) {
|
||||
let mut state = vm.state.monitoring.lock();
|
||||
state.disabled.insert((code_id, offset as usize, tool));
|
||||
} else if event_id < UNGROUPED_EVENTS_COUNT {
|
||||
// Other (non-instrumented) event: disable for this code object.
|
||||
let mut state = vm.state.monitoring.lock();
|
||||
if let Some(events) = state.local_events.get_mut(&(tool, code_id)) {
|
||||
*events &= !event_mask;
|
||||
if *events == 0 {
|
||||
state.local_events.remove(&(tool, code_id));
|
||||
}
|
||||
}
|
||||
update_events_mask(vm, &state);
|
||||
} else {
|
||||
// Remove the callback.
|
||||
let mut state = vm.state.monitoring.lock();
|
||||
state.callbacks.remove(&(tool, event_id));
|
||||
@@ -915,8 +898,6 @@ fn fire(
|
||||
EVENT_NAMES[event_id]
|
||||
)));
|
||||
}
|
||||
let mut state = vm.state.monitoring.lock();
|
||||
state.disabled.insert((code_id, offset as usize, tool));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -1071,6 +1052,12 @@ pub(crate) fn fire_raise(
|
||||
)
|
||||
}
|
||||
|
||||
/// End suppression when the exception reaches a handler or escapes a frame,
|
||||
/// independently of whether either lifecycle event has a registered callback.
|
||||
pub(crate) fn reset_reraise_pending() {
|
||||
RERAISE_PENDING.with(|f| f.set(false));
|
||||
}
|
||||
|
||||
/// Only fires if no RERAISE has been fired since the last EXCEPTION_HANDLED,
|
||||
/// preventing duplicate events from chained cleanup handlers.
|
||||
pub(crate) fn fire_reraise(
|
||||
@@ -1102,7 +1089,7 @@ pub(crate) fn fire_exception_handled(
|
||||
offset: u32,
|
||||
exception: &PyObject,
|
||||
) -> PyResult<()> {
|
||||
RERAISE_PENDING.with(|f| f.set(false));
|
||||
reset_reraise_pending();
|
||||
fire(
|
||||
vm,
|
||||
MonitoringEvent::ExceptionHandled,
|
||||
@@ -1118,7 +1105,7 @@ pub(crate) fn fire_py_unwind(
|
||||
offset: u32,
|
||||
exception: &PyObject,
|
||||
) -> PyResult<()> {
|
||||
RERAISE_PENDING.with(|f| f.set(false));
|
||||
reset_reraise_pending();
|
||||
fire(
|
||||
vm,
|
||||
MonitoringEvent::PyUnwind,
|
||||
|
||||
@@ -69,8 +69,8 @@ pub const RUSTPYTHON_VERSION: &str = const {
|
||||
};
|
||||
|
||||
// Must be aligned to Lib/importlib/_bootstrap_external.py
|
||||
// Matches CPython 3.14 (Include/internal/pycore_magic_number.h).
|
||||
pub const PYC_MAGIC_NUMBER: u16 = 3627;
|
||||
// Matches CPython 3.15 (Include/internal/pycore_magic_number.h).
|
||||
pub const PYC_MAGIC_NUMBER: u16 = 3666;
|
||||
|
||||
// CPython format: magic_number | ('\r' << 16) | ('\n' << 24)
|
||||
// This protects against text-mode file reads
|
||||
|
||||
@@ -15,7 +15,7 @@ use crate::{
|
||||
},
|
||||
bytecode::{
|
||||
BorrowedConstant, CodeFlags, Constant, Instruction,
|
||||
oparg::{OpArg, OpArgState},
|
||||
oparg::{CommonConstant, OpArg, OpArgState},
|
||||
},
|
||||
protocol::PyBuffer,
|
||||
};
|
||||
@@ -829,42 +829,26 @@ pub(crate) fn code_returns_only_none(code: &Py<PyCode>) -> bool {
|
||||
return false;
|
||||
}
|
||||
let units = &code.instructions;
|
||||
let Some(last) = units.len().checked_sub(1) else {
|
||||
return true;
|
||||
};
|
||||
// The last instruction either returns or raises. We can take advantage
|
||||
// of that for a quick exit.
|
||||
let final_op = units.read_op(last).deoptimize();
|
||||
|
||||
// Look up None in co_consts.
|
||||
let Some(none_index) = code
|
||||
let none_index = code
|
||||
.constants
|
||||
.iter()
|
||||
.position(|c| matches!(c.borrow_constant(), BorrowedConstant::None))
|
||||
else {
|
||||
// None wasn't there, which means there was no implicit return,
|
||||
// "return", or "return None".
|
||||
|
||||
// That means there must be an explicit return (non-None), or it only
|
||||
// raises.
|
||||
if matches!(final_op, Instruction::ReturnValue) {
|
||||
// It was an explicit return (non-None).
|
||||
return false;
|
||||
}
|
||||
// It must end with a raise then. We still have to walk the bytecode
|
||||
// to see if there's any explicit return (non-None).
|
||||
return !walk_instructions(code).any(|(_, op, _)| matches!(op, Instruction::ReturnValue));
|
||||
};
|
||||
// Walk the bytecode, looking for RETURN_VALUE.
|
||||
.position(|c| matches!(c.borrow_constant(), BorrowedConstant::None));
|
||||
// We don't worry about EXTENDED_ARG for now.
|
||||
for (at, op, _) in walk_instructions(code) {
|
||||
if !matches!(op, Instruction::ReturnValue) {
|
||||
continue;
|
||||
}
|
||||
// Ignore it if it returns None.
|
||||
if let Some(prev) = at.checked_sub(1)
|
||||
&& matches!(units.read_op(prev).deoptimize(), Instruction::LoadConst { .. })
|
||||
// We don't worry about EXTENDED_ARG for now.
|
||||
&& usize::from(u8::from(units.read_arg(prev))) == none_index
|
||||
debug_assert_ne!(at, 0);
|
||||
let prev_op = units.read_op(at - 1).deoptimize();
|
||||
let prev_arg = u32::from(u8::from(units.read_arg(at - 1)));
|
||||
if matches!(prev_op, Instruction::LoadCommonConstant { .. })
|
||||
&& prev_arg == u32::from(CommonConstant::None)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if matches!(prev_op, Instruction::LoadConst { .. })
|
||||
&& none_index.is_some_and(|none_index| prev_arg as usize == none_index)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
+41
-9
@@ -2686,6 +2686,25 @@ impl VirtualMachine {
|
||||
&self,
|
||||
frame: FrameObjectRef,
|
||||
f: F,
|
||||
) -> PyResult<R> {
|
||||
self.with_frame_impl::<true, R, F>(frame, f)
|
||||
}
|
||||
|
||||
/// Run the incomplete frame of a generator being created. Its caller link
|
||||
/// must not escape, as RETURN_GENERATOR copies the frame with
|
||||
/// `previous = NULL`.
|
||||
pub(crate) fn with_generator_frame<R, F: FnOnce(FrameObjectRef) -> PyResult<R>>(
|
||||
&self,
|
||||
frame: FrameObjectRef,
|
||||
f: F,
|
||||
) -> PyResult<R> {
|
||||
self.with_frame_impl::<false, R, F>(frame, f)
|
||||
}
|
||||
|
||||
fn with_frame_impl<const RETAIN_BACK: bool, R, F: FnOnce(FrameObjectRef) -> PyResult<R>>(
|
||||
&self,
|
||||
frame: FrameObjectRef,
|
||||
f: F,
|
||||
) -> PyResult<R> {
|
||||
self.check_recursive_call("")?;
|
||||
|
||||
@@ -2732,7 +2751,7 @@ impl VirtualMachine {
|
||||
|
||||
// Capture f_back before clearing previous so code holding a
|
||||
// reference to this FrameObject can walk the chain after return.
|
||||
if !old_chain.is_null() {
|
||||
if RETAIN_BACK && !old_chain.is_null() {
|
||||
let strong = frame.as_object().strong_count();
|
||||
// Only set retained_back if someone else holds a reference (escaped)
|
||||
// AND the caller already has a FrameObject. Materializing the caller
|
||||
@@ -2743,8 +2762,11 @@ impl VirtualMachine {
|
||||
if strong > 1 {
|
||||
let mut guard = frame.iframe().cold().retained_back.lock();
|
||||
if guard.is_none() {
|
||||
let prev_iframe = unsafe { &*old_chain };
|
||||
if let Some(fo) = prev_iframe.frame_obj() {
|
||||
// SAFETY: old_chain remains live until this synchronous entry exits.
|
||||
let visible = unsafe { crate::frame::first_complete_iframe(old_chain) };
|
||||
if let Some(prev_iframe) = unsafe { visible.as_ref() }
|
||||
&& let Some(fo) = prev_iframe.frame_obj()
|
||||
{
|
||||
*guard = Some(fo.to_owned());
|
||||
}
|
||||
}
|
||||
@@ -2870,8 +2892,9 @@ impl VirtualMachine {
|
||||
// The slots above are the last write this thread makes into
|
||||
// the frame object, so it is now readable from anywhere.
|
||||
fo.iframe().detach();
|
||||
if !old_chain.is_null() {
|
||||
let prev_iframe = unsafe { &*old_chain };
|
||||
// SAFETY: old_chain belongs to this still-active thread chain.
|
||||
let visible = unsafe { crate::frame::first_complete_iframe(old_chain) };
|
||||
if let Some(prev_iframe) = unsafe { visible.as_ref() } {
|
||||
let back_fo = prev_iframe.materialize_chain(self);
|
||||
*fo.iframe().cold().retained_back.lock() = Some(back_fo);
|
||||
}
|
||||
@@ -3057,10 +3080,19 @@ impl VirtualMachine {
|
||||
if result.is_err()
|
||||
&& self.use_tracing.get()
|
||||
&& (!self.is_none(&self.profile_func.borrow())
|
||||
|| frame
|
||||
.iframe()
|
||||
.cold_opt()
|
||||
.is_some_and(|c| c.trace.lock().is_some()))
|
||||
|| if frame.iframe().is_incomplete() {
|
||||
crate::frame::current_complete_frame_for_trace(self).is_some_and(|visible| {
|
||||
visible
|
||||
.iframe()
|
||||
.cold_opt()
|
||||
.is_some_and(|c| c.trace.lock().is_some())
|
||||
})
|
||||
} else {
|
||||
frame
|
||||
.iframe()
|
||||
.cold_opt()
|
||||
.is_some_and(|c| c.trace.lock().is_some())
|
||||
})
|
||||
{
|
||||
let ret_result = self.trace_event_what(
|
||||
TraceEvent::Return,
|
||||
|
||||
@@ -1094,13 +1094,14 @@ pub fn get_current_frame() -> *const InterpreterFrame {
|
||||
/// Called from push_exception/pop_exception/set_exception.
|
||||
#[cfg(feature = "threading")]
|
||||
pub fn update_thread_exception(exc: Option<PyBaseExceptionRef>) {
|
||||
CURRENT_THREAD_SLOT.with(|slot| {
|
||||
if let Some(s) = slot.borrow().as_ref() {
|
||||
// SAFETY: Called only from the owning thread. The old ref is dropped
|
||||
// here on the owning thread, which is safe.
|
||||
let _old = unsafe { s.exception.swap(exc) };
|
||||
}
|
||||
// Release the TLS borrow before dropping either exception: traceback
|
||||
// locals can finalize another interpreter and re-enter this thread slot.
|
||||
let slot = current_thread_slot();
|
||||
let old = slot.and_then(|s| {
|
||||
// SAFETY: Called only from the owning thread.
|
||||
unsafe { s.exception.swap(exc) }
|
||||
});
|
||||
drop(old);
|
||||
}
|
||||
|
||||
/// Collect all threads' current exceptions for sys._current_exceptions().
|
||||
|
||||
@@ -55,8 +55,8 @@ impl VirtualMachine {
|
||||
}
|
||||
|
||||
pub fn new_code(&self, code: impl IntoCodeObject) -> PyRef<PyCode> {
|
||||
let code = code.into_code_object(self);
|
||||
PyRef::new_ref(PyCode::new(code), self.ctx.types.code_type.to_owned(), None)
|
||||
let code = code.into_pycode(self);
|
||||
PyRef::new_ref(code, self.ctx.types.code_type.to_owned(), None)
|
||||
}
|
||||
|
||||
pub fn new_tuple(&self, value: impl IntoPyTuple) -> PyTupleRef {
|
||||
|
||||
+46
-22
@@ -100,14 +100,33 @@ impl WarningsState {
|
||||
|
||||
/// None matches everything; plain strings do exact comparison;
|
||||
/// regex objects use .match().
|
||||
fn check_matched(obj: &PyObject, arg: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
fn check_matched(
|
||||
obj: &PyObject,
|
||||
arg: Option<&PyObject>,
|
||||
arg2: Option<&Py<PyStr>>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<bool> {
|
||||
// A 'None' filter always matches
|
||||
if vm.is_none(obj) {
|
||||
return Ok(true);
|
||||
}
|
||||
// An internal plain text default filter must match exactly
|
||||
if obj.class().is(vm.ctx.types.str_type) {
|
||||
return obj.rich_compare_bool(arg, crate::types::PyComparisonOp::Eq, vm);
|
||||
return match arg {
|
||||
Some(arg) => obj.rich_compare_bool(arg, crate::types::PyComparisonOp::Eq, vm),
|
||||
None => Ok(false),
|
||||
};
|
||||
}
|
||||
let result = vm.call_method(obj, "match", (arg.to_owned(),))?;
|
||||
// Otherwise assume a regex filter and call its match() method
|
||||
let result = match arg {
|
||||
Some(arg) => vm.call_method(obj, "match", (arg.to_owned(),))?,
|
||||
None => {
|
||||
let match_filename = vm
|
||||
.import("_py_warnings", 0)?
|
||||
.get_attr("_match_filename", vm)?;
|
||||
match_filename.call((obj.to_owned(), arg2.map(|f| f.to_owned())), vm)?
|
||||
}
|
||||
};
|
||||
result.is_true(vm)
|
||||
}
|
||||
|
||||
@@ -336,22 +355,14 @@ fn update_registry(
|
||||
already_warned(registry, &altkey, true, vm)
|
||||
}
|
||||
|
||||
fn normalize_module(filename: &Py<PyStr>, vm: &VirtualMachine) -> PyObjectRef {
|
||||
match filename.byte_len() {
|
||||
0 => vm.new_pyobj("<unknown>"),
|
||||
len if len >= 3 && filename.as_bytes().ends_with(b".py") => {
|
||||
vm.new_pyobj(&filename.as_wtf8()[..len - 3])
|
||||
}
|
||||
_ => filename.as_object().to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Search a filters list for a matching action.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn filter_search(
|
||||
category: &PyObject,
|
||||
text: &PyObject,
|
||||
lineno: usize,
|
||||
module: &PyObject,
|
||||
module: Option<&PyObject>,
|
||||
filename: &Py<PyStr>,
|
||||
filters: &PyListRef,
|
||||
list_name: &str,
|
||||
vm: &VirtualMachine,
|
||||
@@ -378,9 +389,9 @@ fn filter_search(
|
||||
action.class().name()
|
||||
)));
|
||||
}
|
||||
let good_msg = check_matched(&tmp_item.as_slice()[1], text, vm)?;
|
||||
let good_msg = check_matched(&tmp_item.as_slice()[1], Some(text), None, vm)?;
|
||||
let is_subclass = category.is_subclass(&tmp_item.as_slice()[2], vm)?;
|
||||
let good_mod = check_matched(&tmp_item.as_slice()[3], module, vm)?;
|
||||
let good_mod = check_matched(&tmp_item.as_slice()[3], module, Some(filename), vm)?;
|
||||
let ln: usize = tmp_item.as_slice()[4]
|
||||
.try_int(vm)
|
||||
.map_or(0, |v| v.as_u32_mask() as _);
|
||||
@@ -397,7 +408,8 @@ fn get_filter(
|
||||
category: &PyObject,
|
||||
text: &PyObject,
|
||||
lineno: usize,
|
||||
module: &PyObject,
|
||||
module: Option<&PyObject>,
|
||||
filename: &Py<PyStr>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult {
|
||||
if let Some(context_filters) = get_warnings_context_filters(vm)? {
|
||||
@@ -406,6 +418,7 @@ fn get_filter(
|
||||
text,
|
||||
lineno,
|
||||
module,
|
||||
filename,
|
||||
&context_filters,
|
||||
"_warnings_context _filters",
|
||||
vm,
|
||||
@@ -416,7 +429,9 @@ fn get_filter(
|
||||
}
|
||||
|
||||
let filters = get_warnings_filters(vm)?;
|
||||
if let Some(action) = filter_search(category, text, lineno, module, &filters, "filters", vm)? {
|
||||
if let Some(action) = filter_search(
|
||||
category, text, lineno, module, filename, &filters, "filters", vm,
|
||||
)? {
|
||||
return Ok(action);
|
||||
}
|
||||
get_default_action(vm)
|
||||
@@ -504,9 +519,11 @@ pub fn warn_explicit(
|
||||
source: Option<PyObjectRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
// Normalize module. None → silent return (late-shutdown safety).
|
||||
let module = module.unwrap_or_else(|| normalize_module(&filename, vm));
|
||||
if vm.is_none(&module) {
|
||||
// module can be None if a warning is emitted late during Python shutdown.
|
||||
// In this case, the Python warnings module was probably unloaded, filters
|
||||
// are no more available to choose as action. It is safer to ignore the
|
||||
// warning and do nothing.
|
||||
if module.as_ref().is_some_and(|module| vm.is_none(module)) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -543,7 +560,14 @@ pub fn warn_explicit(
|
||||
}
|
||||
|
||||
// Get filter action
|
||||
let action = get_filter(category.as_object(), text.as_object(), lineno, &module, vm)?;
|
||||
let action = get_filter(
|
||||
category.as_object(),
|
||||
text.as_object(),
|
||||
lineno,
|
||||
module.as_deref(),
|
||||
&filename,
|
||||
vm,
|
||||
)?;
|
||||
let action_str = PyStrRef::try_from_object(vm, action)
|
||||
.map_err(|_| vm.new_type_error("action must be a string"))?;
|
||||
|
||||
|
||||
@@ -69,6 +69,92 @@ sys.settrace(None)
|
||||
|
||||
assert ("demo", "call", None) in events
|
||||
|
||||
# The eager iterator prefix of a generator expression is not traced, but
|
||||
# monitoring still reports the error it raises.
|
||||
|
||||
|
||||
class GoodIterable:
|
||||
def __iter__(self):
|
||||
return iter([1])
|
||||
|
||||
|
||||
class BadIterable:
|
||||
def __iter__(self):
|
||||
raise ValueError("eager iterator")
|
||||
|
||||
|
||||
def create_genexpr(iterable):
|
||||
return (value for value in iterable)
|
||||
|
||||
|
||||
events = []
|
||||
|
||||
|
||||
def trc(frame, event, arg):
|
||||
if frame.f_code.co_name in ("create_genexpr", "<genexpr>", "__iter__"):
|
||||
events.append((frame.f_code.co_name, event))
|
||||
return trc
|
||||
|
||||
|
||||
sys.settrace(trc)
|
||||
try:
|
||||
create_genexpr(GoodIterable()).close()
|
||||
try:
|
||||
create_genexpr(BadIterable())
|
||||
except ValueError as error:
|
||||
tb_names = []
|
||||
tb = error.__traceback__
|
||||
while tb is not None:
|
||||
tb_names.append(tb.tb_frame.f_code.co_name)
|
||||
tb = tb.tb_next
|
||||
else:
|
||||
raise AssertionError("expected eager iterator error")
|
||||
finally:
|
||||
sys.settrace(None)
|
||||
assert events == [
|
||||
("create_genexpr", "call"),
|
||||
("create_genexpr", "line"),
|
||||
("__iter__", "call"),
|
||||
("__iter__", "line"),
|
||||
("__iter__", "return"),
|
||||
("create_genexpr", "return"),
|
||||
("create_genexpr", "call"),
|
||||
("create_genexpr", "line"),
|
||||
("__iter__", "call"),
|
||||
("__iter__", "line"),
|
||||
("__iter__", "exception"),
|
||||
("__iter__", "return"),
|
||||
("create_genexpr", "exception"),
|
||||
("create_genexpr", "return"),
|
||||
("create_genexpr", "exception"),
|
||||
("create_genexpr", "return"),
|
||||
], events
|
||||
assert tb_names == ["<module>", "create_genexpr", "__iter__"], tb_names
|
||||
|
||||
monitoring = sys.monitoring
|
||||
event_names = ("PY_START", "PY_RESUME", "PY_UNWIND", "RAISE", "LINE", "INSTRUCTION")
|
||||
records = []
|
||||
monitoring.use_tool_id(2, "genexpr-prefix")
|
||||
try:
|
||||
for name in event_names:
|
||||
|
||||
def callback(code, *args, name=name):
|
||||
if code.co_name == "<genexpr>":
|
||||
records.append(name)
|
||||
|
||||
monitoring.register_callback(2, getattr(monitoring.events, name), callback)
|
||||
monitoring.set_events(
|
||||
2, sum(getattr(monitoring.events, name) for name in event_names)
|
||||
)
|
||||
create_genexpr(GoodIterable()).close()
|
||||
assert records == [], records
|
||||
with assert_raises(ValueError):
|
||||
create_genexpr(BadIterable())
|
||||
finally:
|
||||
monitoring.set_events(2, 0)
|
||||
monitoring.free_tool_id(2)
|
||||
assert records == ["RAISE", "PY_UNWIND"], records
|
||||
|
||||
assert sys.exc_info() == (None, None, None)
|
||||
|
||||
try:
|
||||
|
||||
Loaded 100 of 102 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user