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 repairs 2191a9d, 1f1fe04, a2dacaf on 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 repairs 476bb74 on 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 repairs 6482adf on 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 repairs 246c309 on 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 repairs 7ecedcf on 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 repairs ec206b1, 191fe58 on 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:
Jeong, YunWonanddot authored and GitHub committed 2026-10-14 22:38:42 +09:00
1 parent b429155ff6
commit 2a09ee02bd
102 files changed
+8290 -3628

No files matched your search

+1
View File
@@ -12,6 +12,7 @@ pyast
pyattr
pyclass
pyclassmethod
pycode
pyexception
pyfunction
pygetset
+1 -3
View File
@@ -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)
Generated Vendored
+381 -366
View File
@@ -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
+83 -24
View File
@@ -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
View File
@@ -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',
+87 -74
View File
@@ -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()
}
})
+18 -13
View File
@@ -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
+51 -10
View File
@@ -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']
+350 -310
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -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):
+22 -24
View File
@@ -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
+442 -115
View File
@@ -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()
+313 -61
View File
@@ -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 = [
+21 -10
View File
@@ -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)]
File diff suppressed because it is too large. Load diff
+275 -242
View File
@@ -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 { .. })
));
}
+15 -10
View File
@@ -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
})
);
}
+47 -15
View File
@@ -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));
+214 -205
View File
@@ -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,
+26 -6
View File
@@ -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
View File
@@ -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());
+39 -40
View File
@@ -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>(
+15
View File
@@ -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);
+10 -1
View File
@@ -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
View File
@@ -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)
"#
)
}
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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>:
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
View File
@@ -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]
+147
View File
@@ -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))
}
+1 -1
View File
@@ -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(());
+38 -27
View File
@@ -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);
}
+7 -5
View File
@@ -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 {
+13
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+1 -1
View File
@@ -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);
};
+54 -15
View File
@@ -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())
+31 -44
View File
@@ -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,
+2 -2
View File
@@ -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
+14 -30
View File
@@ -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
View File
@@ -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,
+7 -6
View File
@@ -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().
+2 -2
View File
@@ -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
View File
@@ -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"))?;
+86
View File
@@ -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