diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0b397d450..2db52bb04 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -34,6 +34,7 @@ env: CARGO_PROFILE_RELEASE_DEBUG: 0 CARGO_TERM_COLOR: always FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true # TODO: Remove on 2026/06/02 + CI: true jobs: determine_changes: @@ -111,7 +112,9 @@ jobs: uses: ./.github/actions/install-macos-deps - name: run rust tests - run: cargo test --workspace ${{ env.WORKSPACE_EXCLUDES }} --verbose --features threading ${{ env.CARGO_ARGS }} + run: cargo test --workspace ${{ env.WORKSPACE_EXCLUDES }} --features threading ${{ env.CARGO_ARGS }} + env: + INSTA_WORKSPACE_ROOT: ${{ github.workspace }} - run: cargo doc --locked if: runner.os == 'Linux' diff --git a/Cargo.lock b/Cargo.lock index ba534d806..2a6bb10bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3158,7 +3158,6 @@ dependencies = [ "ahash", "bitflags 2.11.0", "indexmap", - "insta", "itertools 0.14.0", "log", "malachite-bigint", @@ -3427,6 +3426,7 @@ dependencies = [ "icu_normalizer", "icu_properties", "indexmap", + "insta", "itertools 0.14.0", "libc", "liblzma", @@ -3464,6 +3464,7 @@ dependencies = [ "rustpython-common", "rustpython-derive", "rustpython-host_env", + "rustpython-pylib", "rustpython-ruff_python_ast", "rustpython-ruff_python_parser", "rustpython-ruff_source_file", diff --git a/crates/codegen/Cargo.toml b/crates/codegen/Cargo.toml index 78065962f..4f32fcc3c 100644 --- a/crates/codegen/Cargo.toml +++ b/crates/codegen/Cargo.toml @@ -33,7 +33,6 @@ unicode_names2 = { workspace = true } [dev-dependencies] ruff_python_parser = { workspace = true } -insta = { workspace = true } [lints] workspace = true diff --git a/crates/codegen/src/compile.rs b/crates/codegen/src/compile.rs index c3ec99be7..7ec6f7192 100644 --- a/crates/codegen/src/compile.rs +++ b/crates/codegen/src/compile.rs @@ -12275,26 +12275,6 @@ def f(sys, os, file): }) } - macro_rules! assert_dis_snapshot { - ($value:expr) => { - insta::assert_snapshot!( - insta::internals::AutoName, - $value.display_expand_code_objects().to_string(), - stringify!($value) - ) - }; - } - - #[test] - fn test_if_ors() { - assert_dis_snapshot!(compile_exec( - "\ -if True or False or False: - pass -" - )); - } - #[test] fn test_trace_assert_true_try_pair() { let trace = compile_exec_late_cfg_trace( @@ -12529,44 +12509,6 @@ def f(self): ); } - #[test] - fn test_if_ands() { - assert_dis_snapshot!(compile_exec( - "\ -if True and False and False: - pass -" - )); - } - - #[test] - fn test_if_mixed() { - assert_dis_snapshot!(compile_exec( - "\ -if (True and False) or (False and True): - pass -" - )); - } - - #[test] - fn test_nested_bool_op() { - assert_dis_snapshot!(compile_exec( - "\ -x = Test() and False or False -" - )); - } - - #[test] - fn test_const_bool_not_op() { - assert_dis_snapshot!(compile_exec_optimized( - "\ -x = not True -" - )); - } - #[test] fn test_plain_constant_bool_op_folds_to_selected_operand() { let code = compile_exec( @@ -12840,24 +12782,6 @@ def f(self, mod): ); } - #[test] - fn test_nested_double_async_with() { - assert_dis_snapshot!(compile_exec( - "\ -async def test(): - for stop_exc in (StopIteration('spam'), StopAsyncIteration('ham')): - with self.subTest(type=type(stop_exc)): - try: - async with egg(): - raise stop_exc - except Exception as ex: - self.assertIs(ex, stop_exc) - else: - self.fail(f'{stop_exc} was suppressed') -" - )); - } - #[test] fn test_scope_exit_instructions_keep_line_numbers() { let code = compile_exec( @@ -14006,20 +13930,6 @@ def f(expected_ns, namespace): } } - #[test] - fn test_bare_function_annotations_check_attribute_and_subscript_expressions() { - assert_dis_snapshot!(compile_exec( - "\ -def f(one: int): - int.new_attr: int - [list][0].new_attr: [int, str] - my_lst = [1] - my_lst[one]: int - return my_lst -" - )); - } - #[test] fn test_non_simple_bare_name_annotation_does_not_create_local_binding() { let code = compile_exec( @@ -14052,16 +13962,6 @@ def f2bad(): ); } - #[test] - fn test_constant_true_if_pass_keeps_line_anchor_nop() { - assert_dis_snapshot!(compile_exec( - "\ -if 1: - pass -" - )); - } - #[test] fn test_negative_constant_binop_folds_after_unary_folding() { let code = compile_exec( diff --git a/crates/codegen/src/ir.rs b/crates/codegen/src/ir.rs index 21052d43c..112e6553c 100644 --- a/crates/codegen/src/ir.rs +++ b/crates/codegen/src/ir.rs @@ -9378,8 +9378,7 @@ impl CodeInfo { } else { OpArg::new(ins.target.0) }; - let instr_display = instr.display(display_arg, self); - eprint!("{instr_display}: {depth} {effect:+} => "); + eprint!("{display_arg:?}: {depth} {effect:+} => "); } let new_depth = depth.checked_add_signed(effect).ok_or({ if effect < 0 { diff --git a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__bare_function_annotations_check_attribute_and_subscript_expressions.snap b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__bare_function_annotations_check_attribute_and_subscript_expressions.snap deleted file mode 100644 index 840f4397d..000000000 --- a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__bare_function_annotations_check_attribute_and_subscript_expressions.snap +++ /dev/null @@ -1,67 +0,0 @@ ---- -source: crates/codegen/src/compile.rs -assertion_line: 12138 -expression: "compile_exec(\"\\\ndef f(one: int):\n int.new_attr: int\n [list][0].new_attr: [int, str]\n my_lst = [1]\n my_lst[one]: int\n return my_lst\n\")" ---- - 1 0 RESUME (0) - 1 LOAD_CONST (): 1 0 RESUME (0) - 1 LOAD_FAST_BORROW (0, format) - 2 LOAD_SMALL_INT (2) - >> 3 COMPARE_OP (>) - 4 CACHE - 5 POP_JUMP_IF_FALSE (3) - 6 CACHE - 7 NOT_TAKEN - 8 LOAD_COMMON_CONSTANT (NotImplementedError) - 9 RAISE_VARARGS (Raise) - 10 LOAD_CONST ("one") - 11 LOAD_GLOBAL (0, int) - 12 CACHE - 13 CACHE - 14 CACHE - 15 CACHE - 16 BUILD_MAP (1) - 17 RETURN_VALUE - - 2 MAKE_FUNCTION - 3 LOAD_CONST (): 1 0 RESUME (0) - - 2 1 LOAD_GLOBAL (0, int) - 2 CACHE - 3 CACHE - 4 CACHE - 5 CACHE - 6 POP_TOP - - 3 7 LOAD_GLOBAL (2, list) - 8 CACHE - 9 CACHE - 10 CACHE - 11 CACHE - 12 BUILD_LIST (1) - 13 LOAD_SMALL_INT (0) - 14 BINARY_OP ([]) - 15 CACHE - 16 CACHE - 17 CACHE - 18 CACHE - 19 CACHE - 20 POP_TOP - - 4 21 LOAD_SMALL_INT (1) - 22 BUILD_LIST (1) - 23 STORE_FAST (1, my_lst) - - 5 24 LOAD_FAST_BORROW (1, my_lst) - 25 POP_TOP - 26 LOAD_FAST_BORROW (0, one) - 27 POP_TOP - - 6 28 LOAD_FAST_BORROW (1, my_lst) - 29 RETURN_VALUE - - 4 MAKE_FUNCTION - 5 SET_FUNCTION_ATTRIBUTE(Annotate) - 6 STORE_NAME (0, f) - 7 LOAD_CONST (None) - 8 RETURN_VALUE diff --git a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__const_bool_not_op.snap b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__const_bool_not_op.snap deleted file mode 100644 index f9a74c205..000000000 --- a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__const_bool_not_op.snap +++ /dev/null @@ -1,9 +0,0 @@ ---- -source: crates/codegen/src/compile.rs -expression: "compile_exec_optimized(\"\\\nx = not True\n\")" ---- - 1 0 RESUME (0) - 1 LOAD_CONST (False) - 2 STORE_NAME (0, x) - 3 LOAD_CONST (None) - 4 RETURN_VALUE diff --git a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__constant_true_if_pass_keeps_line_anchor_nop.snap b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__constant_true_if_pass_keeps_line_anchor_nop.snap deleted file mode 100644 index a600a8298..000000000 --- a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__constant_true_if_pass_keeps_line_anchor_nop.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: crates/codegen/src/compile.rs -assertion_line: 12222 -expression: "compile_exec(\"\\\nif 1:\n pass\n\")" ---- - 1 0 RESUME (0) - 1 NOP - - 2 2 LOAD_CONST (None) - 3 RETURN_VALUE diff --git a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ands.snap b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ands.snap deleted file mode 100644 index 8e9bb5d25..000000000 --- a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ands.snap +++ /dev/null @@ -1,8 +0,0 @@ ---- -source: crates/codegen/src/compile.rs -assertion_line: 11413 -expression: "compile_exec(\"\\\nif True and False and False:\n pass\n\")" ---- - 1 0 RESUME (0) - 1 LOAD_CONST (None) - 2 RETURN_VALUE diff --git a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_mixed.snap b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_mixed.snap deleted file mode 100644 index f7df6f4f3..000000000 --- a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_mixed.snap +++ /dev/null @@ -1,8 +0,0 @@ ---- -source: crates/codegen/src/compile.rs -assertion_line: 11423 -expression: "compile_exec(\"\\\nif (True and False) or (False and True):\n pass\n\")" ---- - 1 0 RESUME (0) - 1 LOAD_CONST (None) - 2 RETURN_VALUE diff --git a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ors.snap b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ors.snap deleted file mode 100644 index f38d3c2c5..000000000 --- a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ors.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: crates/codegen/src/compile.rs -assertion_line: 11039 -expression: "compile_exec(\"\\\nif True or False or False:\n pass\n\")" ---- - 1 0 RESUME (0) - 1 NOP - - 2 2 LOAD_CONST (None) - 3 RETURN_VALUE diff --git a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_bool_op.snap b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_bool_op.snap deleted file mode 100644 index 3ad96c564..000000000 --- a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_bool_op.snap +++ /dev/null @@ -1,35 +0,0 @@ ---- -source: crates/codegen/src/compile.rs -assertion_line: 11769 -expression: "compile_exec(\"\\\nx = Test() and False or False\n\")" ---- - 1 0 RESUME (0) - 1 LOAD_NAME (0, Test) - 2 PUSH_NULL - >> 3 CALL (0) - 4 CACHE - 5 CACHE - 6 CACHE - 7 COPY (1) - 8 TO_BOOL - 9 CACHE - 10 CACHE - >> 11 CACHE - 12 POP_JUMP_IF_FALSE (11) - 13 CACHE - 14 NOT_TAKEN - 15 POP_TOP - 16 LOAD_CONST (False) - 17 COPY (1) - 18 TO_BOOL - 19 CACHE - 20 CACHE - 21 CACHE - 22 POP_JUMP_IF_TRUE (3) - 23 CACHE - 24 NOT_TAKEN - 25 POP_TOP - 26 LOAD_CONST (False) - 27 STORE_NAME (1, x) - 28 LOAD_CONST (None) - 29 RETURN_VALUE diff --git a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap deleted file mode 100644 index 27ae2ae18..000000000 --- a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap +++ /dev/null @@ -1,258 +0,0 @@ ---- -source: crates/codegen/src/compile.rs -assertion_line: 11847 -expression: "compile_exec(\"\\\nasync def test():\n for stop_exc in (StopIteration('spam'), StopAsyncIteration('ham')):\n with self.subTest(type=type(stop_exc)):\n try:\n async with egg():\n raise stop_exc\n except Exception as ex:\n self.assertIs(ex, stop_exc)\n else:\n self.fail(f'{stop_exc} was suppressed')\n\")" ---- - 1 0 RESUME (0) - 1 LOAD_CONST (): 1 0 RETURN_GENERATOR - 1 POP_TOP - >> 2 RESUME (0) - - 2 >> 3 LOAD_GLOBAL (1, NULL + StopIteration) - >> 4 CACHE - >> 5 CACHE - 6 CACHE - 7 CACHE - >> 8 LOAD_CONST ("spam") - 9 CALL (1) - >> 10 CACHE - 11 CACHE - 12 CACHE - 13 LOAD_GLOBAL (3, NULL + StopAsyncIteration) - 14 CACHE - 15 CACHE - 16 CACHE - 17 CACHE - 18 LOAD_CONST ("ham") - 19 CALL (1) - 20 CACHE - 21 CACHE - 22 CACHE - 23 BUILD_TUPLE (2) - 24 GET_ITER - 25 FOR_ITER (71) - 26 CACHE - 27 STORE_FAST (0, stop_exc) - - 3 28 LOAD_GLOBAL (4, self) - 29 CACHE - 30 CACHE - 31 CACHE - >> 32 CACHE - 33 LOAD_ATTR (7, subTest, method=true) - 34 CACHE - 35 CACHE - 36 CACHE - 37 CACHE - 38 CACHE - 39 CACHE - 40 CACHE - 41 CACHE - 42 CACHE - 43 LOAD_GLOBAL (9, NULL + type) - 44 CACHE - >> 45 CACHE - 46 CACHE - 47 CACHE - 48 LOAD_FAST_BORROW (0, stop_exc) - 49 CALL (1) - 50 CACHE - 51 CACHE - 52 CACHE - 53 LOAD_CONST (("type")) - 54 CALL_KW (1) - 55 CACHE - 56 CACHE - 57 CACHE - 58 COPY (1) - 59 LOAD_SPECIAL (__exit__) - 60 SWAP (2) - 61 SWAP (3) - 62 LOAD_SPECIAL (__enter__) - 63 CALL (0) - 64 CACHE - 65 CACHE - 66 CACHE - 67 POP_TOP - - 4 68 NOP - - 5 69 LOAD_GLOBAL (11, NULL + egg) - 70 CACHE - >> 71 CACHE - 72 CACHE - 73 CACHE - 74 CALL (0) - 75 CACHE - 76 CACHE - 77 CACHE - 78 COPY (1) - 79 LOAD_SPECIAL (__aexit__) - 80 SWAP (2) - 81 SWAP (3) - 82 LOAD_SPECIAL (__aenter__) - 83 CALL (0) - 84 CACHE - 85 CACHE - 86 CACHE - 87 GET_AWAITABLE (1) - 88 LOAD_CONST (None) - 89 SEND (3) - 90 CACHE - 91 YIELD_VALUE (1) - 92 RESUME (3) - 93 JUMP_BACKWARD_NO_INTERRUPT(5) - 94 END_SEND - 95 POP_TOP - - 6 96 LOAD_FAST_BORROW (0, stop_exc) - 97 RAISE_VARARGS (Raise) - - 2 98 END_FOR - 99 POP_ITER - 100 LOAD_CONST (None) - 101 RETURN_VALUE - - 5 102 CLEANUP_THROW - 103 JUMP_BACKWARD_NO_INTERRUPT(10) - 104 PUSH_EXC_INFO - 105 WITH_EXCEPT_START - 106 GET_AWAITABLE (2) - 107 LOAD_CONST (None) - 108 SEND (4) - 109 CACHE - 110 YIELD_VALUE (1) - 111 RESUME (3) - 112 JUMP_BACKWARD_NO_INTERRUPT(5) - 113 CLEANUP_THROW - 114 END_SEND - 115 TO_BOOL - 116 CACHE - 117 CACHE - 118 CACHE - 119 POP_JUMP_IF_TRUE (2) - 120 CACHE - 121 NOT_TAKEN - 122 RERAISE (2) - 123 POP_TOP - 124 POP_EXCEPT - 125 POP_TOP - 126 POP_TOP - 127 POP_TOP - 128 JUMP_FORWARD (3) - 129 COPY (3) - 130 POP_EXCEPT - 131 RERAISE (1) - 132 NOP - - 10 133 LOAD_GLOBAL (4, self) - 134 CACHE - 135 CACHE - 136 CACHE - 137 CACHE - 138 LOAD_ATTR (13, fail, method=true) - 139 CACHE - 140 CACHE - 141 CACHE - 142 CACHE - 143 CACHE - 144 CACHE - 145 CACHE - 146 CACHE - 147 CACHE - 148 LOAD_FAST (0, stop_exc) - 149 FORMAT_SIMPLE - 150 LOAD_CONST (" was suppressed") - 151 BUILD_STRING (2) - 152 CALL (1) - 153 CACHE - 154 CACHE - 155 CACHE - 156 POP_TOP - 157 JUMP_FORWARD (45) - 158 PUSH_EXC_INFO - - 7 159 LOAD_GLOBAL (14, Exception) - 160 CACHE - 161 CACHE - 162 CACHE - 163 CACHE - 164 CHECK_EXC_MATCH - 165 POP_JUMP_IF_FALSE (32) - 166 CACHE - 167 NOT_TAKEN - 168 STORE_FAST (1, ex) - - 8 169 LOAD_GLOBAL (4, self) - 170 CACHE - 171 CACHE - 172 CACHE - 173 CACHE - 174 LOAD_ATTR (17, assertIs, method=true) - 175 CACHE - 176 CACHE - 177 CACHE - 178 CACHE - 179 CACHE - 180 CACHE - 181 CACHE - 182 CACHE - 183 CACHE - 184 LOAD_FAST_LOAD_FAST (ex, stop_exc) - 185 CALL (2) - 186 CACHE - 187 CACHE - >> 188 CACHE - 189 POP_TOP - 190 POP_EXCEPT - 191 LOAD_CONST (None) - 192 STORE_FAST (1, ex) - 193 DELETE_FAST (1, ex) - 194 JUMP_FORWARD (8) - 195 LOAD_CONST (None) - 196 STORE_FAST (1, ex) - 197 DELETE_FAST (1, ex) - 198 RERAISE (1) - - 7 199 RERAISE (0) - 200 COPY (3) - 201 POP_EXCEPT - 202 RERAISE (1) - - 3 203 LOAD_CONST (None) - 204 LOAD_CONST (None) - >> 205 LOAD_CONST (None) - 206 CALL (3) - 207 CACHE - 208 CACHE - 209 CACHE - 210 POP_TOP - 211 JUMP_BACKWARD (188) - 212 CACHE - 213 PUSH_EXC_INFO - 214 WITH_EXCEPT_START - 215 TO_BOOL - 216 CACHE - 217 CACHE - 218 CACHE - 219 POP_JUMP_IF_TRUE (2) - 220 CACHE - 221 NOT_TAKEN - 222 RERAISE (2) - 223 POP_TOP - 224 POP_EXCEPT - 225 POP_TOP - 226 POP_TOP - 227 POP_TOP - 228 JUMP_BACKWARD (205) - 229 CACHE - 230 COPY (3) - 231 POP_EXCEPT - 232 RERAISE (1) - 233 CALL_INTRINSIC_1 (StopIterationError) - 234 RERAISE (1) - - 2 MAKE_FUNCTION - 3 STORE_NAME (0, test) - 4 LOAD_CONST (None) - 5 RETURN_VALUE diff --git a/crates/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_ands.snap b/crates/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_ands.snap deleted file mode 100644 index bc88cf234..000000000 --- a/crates/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_ands.snap +++ /dev/null @@ -1,14 +0,0 @@ ---- -source: compiler/src/compile.rs -expression: "compile_exec(\"\\\nif True and False and False:\n pass\n\")" ---- - 1 0 LoadConst (True) - 1 PopJumpIfFalse (6) - 2 LoadConst (False) - 3 PopJumpIfFalse (6) - 4 LoadConst (False) - 5 PopJumpIfFalse (6) - - 2 >> 6 LoadConst (None) - 7 ReturnValue - diff --git a/crates/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_mixed.snap b/crates/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_mixed.snap deleted file mode 100644 index b19cbb119..000000000 --- a/crates/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_mixed.snap +++ /dev/null @@ -1,16 +0,0 @@ ---- -source: compiler/src/compile.rs -expression: "compile_exec(\"\\\nif (True and False) or (False and True):\n pass\n\")" ---- - 1 0 LoadConst (True) - 1 PopJumpIfFalse (4) - 2 LoadConst (False) - 3 PopJumpIfTrue (8) - >> 4 LoadConst (False) - 5 PopJumpIfFalse (8) - 6 LoadConst (True) - 7 PopJumpIfFalse (8) - - 2 >> 8 LoadConst (None) - 9 ReturnValue - diff --git a/crates/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_ors.snap b/crates/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_ors.snap deleted file mode 100644 index 3d1f5a1d6..000000000 --- a/crates/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_ors.snap +++ /dev/null @@ -1,14 +0,0 @@ ---- -source: compiler/src/compile.rs -expression: "compile_exec(\"\\\nif True or False or False:\n pass\n\")" ---- - 1 0 LoadConst (True) - 1 PopJumpIfTrue (6) - 2 LoadConst (False) - 3 PopJumpIfTrue (6) - 4 LoadConst (False) - 5 PopJumpIfFalse (6) - - 2 >> 6 LoadConst (None) - 7 ReturnValue - diff --git a/crates/compiler-core/src/bytecode.rs b/crates/compiler-core/src/bytecode.rs index ab530e703..99278a28d 100644 --- a/crates/compiler-core/src/bytecode.rs +++ b/crates/compiler-core/src/bytecode.rs @@ -1151,65 +1151,6 @@ impl CodeObject { label_targets } - fn display_inner( - &self, - f: &mut fmt::Formatter<'_>, - expand_code_objects: bool, - level: usize, - ) -> fmt::Result { - let label_targets = self.label_targets(); - let line_digits = (3).max(self.locations.last().unwrap().0.line.digits().get()); - let offset_digits = (4).max(1 + self.instructions.len().ilog10() as usize); - let mut last_line = OneIndexed::MAX; - let mut arg_state = OpArgState::default(); - for (offset, &instruction) in self.instructions.iter().enumerate() { - let (instruction, arg) = arg_state.get(instruction); - // optional line number - let line = self.locations[offset].0.line; - if line != last_line { - if last_line != OneIndexed::MAX { - writeln!(f)?; - } - last_line = line; - write!(f, "{line:line_digits$}")?; - } else { - for _ in 0..line_digits { - write!(f, " ")?; - } - } - write!(f, " ")?; - - // level indent - for _ in 0..level { - write!(f, " ")?; - } - - // arrow and offset - let arrow = if label_targets.contains(&Label::from_u32(offset as u32)) { - ">>" - } else { - " " - }; - write!(f, "{arrow} {offset:offset_digits$} ")?; - - // instruction - instruction.fmt_dis(arg, f, self, expand_code_objects, 21, level)?; - writeln!(f)?; - } - Ok(()) - } - - /// Recursively display this CodeObject - pub fn display_expand_code_objects(&self) -> impl fmt::Display + '_ { - struct Display<'a, C: Constant>(&'a CodeObject); - impl fmt::Display for Display<'_, C> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.display_inner(f, true, 1) - } - } - Display(self) - } - /// Map this CodeObject to one that holds a Bag::Constant pub fn map_bag(self, bag: Bag) -> CodeObject { let map_names = |names: Box<[C::Name]>| { @@ -1279,19 +1220,6 @@ impl CodeObject { } } -impl fmt::Display for CodeObject { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.display_inner(f, false, 1)?; - for constant in &*self.constants { - if let BorrowedConstant::Code { code } = constant.borrow_constant() { - writeln!(f, "\nDisassembly of {code:?}")?; - code.fmt(f)?; - } - } - Ok(()) - } -} - pub trait InstrDisplayContext { type Constant: Constant; diff --git a/crates/compiler-core/src/bytecode/instruction.rs b/crates/compiler-core/src/bytecode/instruction.rs index 06d336564..218696015 100644 --- a/crates/compiler-core/src/bytecode/instruction.rs +++ b/crates/compiler-core/src/bytecode/instruction.rs @@ -1,14 +1,11 @@ use core::{fmt, marker::PhantomData}; use crate::{ - bytecode::{ - BorrowedConstant, Constant, InstrDisplayContext, - oparg::{ - self, BinaryOperator, BuildSliceArgCount, CommonConstant, ComparisonOperator, - ConvertValueOparg, IntrinsicFunction1, IntrinsicFunction2, Invert, Label, LoadAttr, - LoadSuperAttr, MakeFunctionFlag, NameIdx, OpArg, OpArgByte, OpArgType, RaiseKind, - SpecialMethod, UnpackExArgs, - }, + bytecode::oparg::{ + self, BinaryOperator, BuildSliceArgCount, CommonConstant, ComparisonOperator, + ConvertValueOparg, IntrinsicFunction1, IntrinsicFunction2, Invert, Label, LoadAttr, + LoadSuperAttr, MakeFunctionFlag, NameIdx, OpArg, OpArgByte, OpArgType, RaiseKind, + SpecialMethod, UnpackExArgs, }, marshal::MarshalError, }; @@ -1025,262 +1022,6 @@ impl InstructionMetadata for Instruction { // In CPython 3.14 the metadata-based stack_effect is the same for both // fallthrough and branch paths for all real instructions. // Only pseudo-instructions (SETUP_*) differ — see PseudoInstruction. - - #[allow(clippy::too_many_arguments)] - fn fmt_dis( - &self, - arg: OpArg, - f: &mut fmt::Formatter<'_>, - ctx: &impl InstrDisplayContext, - expand_code_objects: bool, - pad: usize, - level: usize, - ) -> fmt::Result { - let opcode = self.opcode(); - - macro_rules! w { - // No oparg. Show only opcode name. - () => { - write!(f, "{}", opcode) - }; - - // Oparg needs to be passed into a function. - ($map:ident = $arg_marker:expr) => {{ - let arg = $arg_marker.get(arg); - write!(f, "{:pad$}({}, {})", opcode, arg, $map(arg)) - }}; - - // Oparg to be shown via `fmt::Display` - ($arg_marker:expr) => { - write!(f, "{:pad$}({})", opcode, $arg_marker.get(arg)) - }; - - // Oparg to be shown via `fmt::Debug` - (?$arg_marker:expr) => { - write!(f, "{:pad$}({:?})", opcode, $arg_marker.get(arg)) - }; - } - - let varname = |var_num: oparg::VarNum| ctx.get_varname(var_num); - let name = |i: u32| ctx.get_name(i as usize); - let cell_name = |i: oparg::VarNum| ctx.get_localsplus_name(i); - - match self { - Self::BinarySlice => w!(), - Self::BinaryOp { op } => w!(op), - Self::BinaryOpInplaceAddUnicode => w!(), - Self::BuildList { count } => w!(count), - Self::BuildMap { count } => w!(count), - Self::BuildSet { count } => w!(count), - Self::BuildSlice { argc } => w!(?argc), - Self::BuildString { count } => w!(count), - Self::BuildTuple { count } => w!(count), - Self::Call { argc } => w!(argc), - Self::CallFunctionEx => w!(), - Self::CallKw { argc } => w!(argc), - Self::CallIntrinsic1 { func } => w!(?func), - Self::CallIntrinsic2 { func } => w!(?func), - Self::Cache => w!(), - Self::CheckEgMatch => w!(), - Self::CheckExcMatch => w!(), - Self::CleanupThrow => w!(), - Self::CompareOp { opname } => { - let op = opname.get(arg); - if u32::from(arg) & oparg::COMPARE_OP_BOOL_MASK != 0 { - write!(f, "{:pad$}(bool({}))", opcode, op) - } else { - write!(f, "{:pad$}({})", opcode, op) - } - } - Self::ContainsOp { invert } => w!(invert), - Self::ConvertValue { oparg } => { - let oparg = oparg.get(arg); - write!(f, "{:pad$} {} ({})", opcode, oparg.as_u8(), oparg) - } - Self::Copy { i } => w!(i), - Self::CopyFreeVars { n } => w!(n), - Self::DeleteAttr { namei } => w!(name = namei), - Self::DeleteDeref { i } => w!(cell_name = i), - Self::DeleteFast { var_num } => w!(varname = var_num), - Self::DeleteGlobal { namei } => w!(name = namei), - Self::DeleteName { namei } => w!(name = namei), - Self::DeleteSubscr => w!(), - Self::DictMerge { i } => w!(i), - Self::DictUpdate { i } => w!(i), - Self::EndAsyncFor => w!(), - Self::EndSend => w!(), - Self::ExtendedArg => w!(Arg::::marker()), - Self::ExitInitCheck => w!(), - Self::ForIter { delta } => w!(delta), - Self::FormatSimple => w!(), - Self::FormatWithSpec => w!(), - Self::GetAIter => w!(), - Self::GetANext => w!(), - Self::GetAwaitable { r#where } => w!(r#where), - Self::Reserved => w!(), - Self::GetIter => w!(), - Self::GetLen => w!(), - Self::ImportFrom { namei } => w!(name = namei), - Self::ImportName { namei } => w!(name = namei), - Self::InterpreterExit => w!(), - Self::IsOp { invert } => w!(invert), - Self::JumpBackward { delta } => w!(delta), - Self::JumpBackwardNoInterrupt { delta } => w!(delta), - Self::JumpForward { delta } => w!(delta), - Self::ListAppend { i } => w!(i), - Self::ListExtend { i } => w!(i), - Self::LoadAttr { namei } => { - let oparg = namei.get(arg); - let oparg_u32 = u32::from(oparg); - let attr_name = name(oparg.name_idx()); - if oparg.is_method() { - write!(f, "{opcode:pad$}({oparg_u32}, {attr_name}, method=true)",) - } else { - write!(f, "{opcode:pad$}({oparg_u32}, {attr_name})") - } - } - Self::LoadBuildClass => w!(), - Self::LoadCommonConstant { idx } => w!(?idx), - Self::LoadFromDictOrDeref { i } => w!(cell_name = i), - Self::LoadConst { consti } => { - let value = ctx.get_constant(consti.get(arg)); - match value.borrow_constant() { - BorrowedConstant::Code { code } if expand_code_objects => { - write!(f, "{opcode:pad$}({code:?}):")?; - code.display_inner(f, true, level + 1)?; - Ok(()) - } - c => { - write!(f, "{opcode:pad$}(")?; - c.fmt_display(f)?; - write!(f, ")") - } - } - } - Self::LoadSmallInt { i } => w!(i), - Self::LoadDeref { i } => w!(cell_name = i), - Self::LoadFast { var_num } => w!(varname = var_num), - Self::LoadFastAndClear { var_num } => w!(varname = var_num), - Self::LoadFastBorrow { var_num } => w!(varname = var_num), - Self::LoadFastCheck { var_num } => w!(varname = var_num), - Self::LoadFastLoadFast { var_nums } => { - let oparg = var_nums.get(arg); - let (idx1, idx2) = oparg.indexes(); - let name1 = varname(idx1); - let name2 = varname(idx2); - write!(f, "{:pad$}({}, {})", opcode, name1, name2) - } - Self::LoadFastBorrowLoadFastBorrow { var_nums } => { - let oparg = var_nums.get(arg); - let (idx1, idx2) = oparg.indexes(); - let name1 = varname(idx1); - let name2 = varname(idx2); - write!(f, "{:pad$}({}, {})", opcode, name1, name2) - } - Self::LoadFromDictOrGlobals { i } => w!(name = i), - Self::LoadGlobal { namei } => { - let oparg = namei.get(arg); - let name_idx = oparg >> 1; - if (oparg & 1) != 0 { - write!(f, "{:pad$}({}, NULL + {})", opcode, oparg, name(name_idx)) - } else { - write!(f, "{:pad$}({}, {})", opcode, oparg, name(name_idx)) - } - } - Self::LoadGlobalBuiltin => { - let oparg = u32::from(arg); - let name_idx = oparg >> 1; - if (oparg & 1) != 0 { - write!(f, "{:pad$}({}, NULL + {})", opcode, oparg, name(name_idx)) - } else { - write!(f, "{:pad$}({}, {})", opcode, oparg, name(name_idx)) - } - } - Self::LoadGlobalModule => { - let oparg = u32::from(arg); - let name_idx = oparg >> 1; - if (oparg & 1) != 0 { - write!(f, "{:pad$}({}, NULL + {})", opcode, oparg, name(name_idx)) - } else { - write!(f, "{:pad$}({}, {})", opcode, oparg, name(name_idx)) - } - } - Self::LoadLocals => w!(), - Self::LoadName { namei } => w!(name = namei), - Self::LoadSpecial { method } => w!(method), - Self::LoadSuperAttr { namei } => { - let oparg = namei.get(arg); - write!( - f, - "{:pad$}({}, {}, method={}, class={})", - opcode, - u32::from(oparg), - name(oparg.name_idx()), - oparg.is_load_method(), - oparg.has_class() - ) - } - Self::MakeCell { i } => w!(cell_name = i), - Self::MakeFunction => w!(), - Self::MapAdd { i } => w!(i), - Self::MatchClass { count } => w!(count), - Self::MatchKeys => w!(), - Self::MatchMapping => w!(), - Self::MatchSequence => w!(), - Self::Nop => w!(), - Self::NotTaken => w!(), - Self::PopExcept => w!(), - Self::PopJumpIfFalse { delta } => w!(delta), - Self::PopJumpIfNone { delta } => w!(delta), - Self::PopJumpIfNotNone { delta } => w!(delta), - Self::PopJumpIfTrue { delta } => w!(delta), - Self::PopTop => w!(), - Self::EndFor => w!(), - Self::PopIter => w!(), - Self::PushExcInfo => w!(), - Self::PushNull => w!(), - Self::RaiseVarargs { argc } => w!(?argc), - Self::Reraise { depth } => w!(depth), - Self::Resume { context } => w!(context), - Self::ReturnValue => w!(), - Self::ReturnGenerator => w!(), - Self::Send { delta } => w!(delta), - Self::SetAdd { i } => w!(i), - Self::SetFunctionAttribute { flag } => w!(?flag), - Self::SetupAnnotations => w!(), - Self::SetUpdate { i } => w!(i), - Self::StoreAttr { namei } => w!(name = namei), - Self::StoreDeref { i } => w!(cell_name = i), - Self::StoreFast { var_num } => w!(varname = var_num), - Self::StoreFastLoadFast { var_nums } => { - let oparg = var_nums.get(arg); - let (store_idx, load_idx) = oparg.indexes(); - write!(f, "{:pad$}({}, {})", opcode, store_idx, load_idx) - } - Self::StoreFastStoreFast { var_nums } => { - let oparg = var_nums.get(arg); - let (idx1, idx2) = oparg.indexes(); - write!(f, "{:pad$}({}, {})", opcode, varname(idx1), varname(idx2)) - } - Self::StoreGlobal { namei } => w!(name = namei), - Self::StoreName { namei } => w!(name = namei), - Self::StoreSlice => w!(), - Self::StoreSubscr => w!(), - Self::Swap { i } => w!(i), - Self::ToBool => w!(), - Self::UnpackEx { counts } => w!(counts), - Self::UnpackSequence { count } => w!(count), - Self::WithExceptStart => w!(), - Self::UnaryInvert => w!(), - Self::UnaryNegative => w!(), - Self::UnaryNot => w!(), - Self::YieldValue { arg } => w!(arg), - Self::GetYieldFromIter => w!(), - Self::BuildTemplate => w!(), - Self::BuildInterpolation { format } => w!(format), - _ => w!(), - } - } } define_opcodes!( @@ -1381,18 +1122,6 @@ impl InstructionMetadata for PseudoInstruction { fn is_unconditional_jump(&self) -> bool { matches!(self, Self::Jump { .. } | Self::JumpNoInterrupt { .. }) } - - fn fmt_dis( - &self, - _arg: OpArg, - _f: &mut fmt::Formatter<'_>, - _ctx: &impl InstrDisplayContext, - _expand_code_objects: bool, - _pad: usize, - _level: usize, - ) -> fmt::Result { - unimplemented!() - } } #[derive(Clone, Copy, Debug)] @@ -1476,16 +1205,6 @@ impl InstructionMetadata for AnyInstruction { inst_either!(fn stack_effect_jump(&self, oparg: u32) -> i32); inst_either!(fn stack_effect_info(&self, oparg: u32) -> StackEffect); - - inst_either!(fn fmt_dis( - &self, - arg: OpArg, - f: &mut fmt::Formatter<'_>, - ctx: &impl InstrDisplayContext, - expand_code_objects: bool, - pad: usize, - level: usize - ) -> fmt::Result); } impl AnyInstruction { @@ -1715,21 +1434,6 @@ pub trait InstructionMetadata { fn stack_effect_jump(&self, oparg: u32) -> i32 { self.stack_effect(oparg) } - - #[allow(clippy::too_many_arguments)] - fn fmt_dis( - &self, - arg: OpArg, - f: &mut fmt::Formatter<'_>, - ctx: &impl InstrDisplayContext, - expand_code_objects: bool, - pad: usize, - level: usize, - ) -> fmt::Result; - - fn display(&self, arg: OpArg, ctx: &impl InstrDisplayContext) -> impl fmt::Display { - fmt::from_fn(move |f| self.fmt_dis(arg, f, ctx, false, 0, 0)) - } } #[derive(Copy, Clone)] diff --git a/crates/stdlib/Cargo.toml b/crates/stdlib/Cargo.toml index 8c8b183a7..47bd45c62 100644 --- a/crates/stdlib/Cargo.toml +++ b/crates/stdlib/Cargo.toml @@ -165,5 +165,10 @@ features = [ [target.'cfg(target_os = "macos")'.dependencies] system-configuration = { workspace = true } +[dev-dependencies] +insta = { workspace = true } +rustpython-pylib = { workspace = true, features = [ "freeze-stdlib" ] } + + [lints] workspace = true diff --git a/crates/stdlib/src/_opcode.rs b/crates/stdlib/src/_opcode.rs index 3f46608ad..513aadafc 100644 --- a/crates/stdlib/src/_opcode.rs +++ b/crates/stdlib/src/_opcode.rs @@ -255,3 +255,155 @@ mod _opcode { vm.ctx.none() } } + +#[cfg(test)] +mod tests { + use crate::vm::{self, compiler::Mode}; + + macro_rules! assert_dis_snapshot { + ($value:expr) => { + insta::assert_snapshot!(dis($value)) + }; + } + + /// Returns the [`dis.dis`](https://docs.python.org/3/library/dis.html#dis.dis) output. + /// + /// # Notes + /// + /// Memory addresses in the output are replaced with `0xdeadbeef` for consistency. + fn dis(source: &str) -> String { + let fname = String::from(""); + + let builder = vm::Interpreter::builder(Default::default()); + let stdlib_defs = crate::stdlib_module_defs(&builder.ctx); + let interp = builder + .add_native_modules(&stdlib_defs) + .add_frozen_modules(rustpython_pylib::FROZEN_STDLIB) + .build(); + + interp.enter(|vm| { + let scope = vm.new_scope_with_builtins(); + let code_obj = vm + .compile(source.trim(), Mode::Exec, fname.clone()) + .map_err(|err| vm.new_syntax_error(&err, Some(source))) + .unwrap(); + scope.globals.set_item("code", code_obj.into(), vm).unwrap(); + + let py_source = r#" +import dis +import io +import re +import sys + +old_stdout = sys.stdout +sys.stdout = buf = io.StringIO() +dis.dis(code) +sys.stdout = old_stdout + +tmp_output = buf.getvalue() + +# constant mem address +output = re.sub(r'(0xdeadbeef', tmp_output) +"#; + + let py_code_obj = vm + .compile(py_source, Mode::Exec, fname) + .map_err(|err| vm.new_syntax_error(&err, Some(py_source))) + .unwrap(); + + vm.run_code_obj(py_code_obj, scope.clone()).unwrap(); + let py_output = scope.globals.get_item("output", vm).unwrap(); + py_output.str(vm).unwrap().to_string() + }) + } + + #[test] + fn test_if_ors() { + assert_dis_snapshot!( + r#" +if True or False or False: + pass +"# + ) + } + + #[test] + fn test_if_ands() { + assert_dis_snapshot!( + r#" +if True and False and False: + pass +"# + ) + } + + #[test] + fn test_if_mixed() { + assert_dis_snapshot!( + r#" +if (True and False) or (False and True): + pass +"# + ) + } + + #[test] + fn test_nested_bool_op() { + assert_dis_snapshot!( + r#" +x = Test() and False or False +"# + ) + } + + #[test] + fn test_const_no_op() { + assert_dis_snapshot!( + r#" +x = not True +"# + ) + } + + #[test] + fn test_constant_true_if_pass_keeps_line_anchor_nop() { + assert_dis_snapshot!( + r#" +if 1: + pass +"# + ) + } + + #[test] + fn test_nested_double_async_with() { + assert_dis_snapshot!( + r#" +async def test(): + for stop_exc in (StopIteration('spam'), StopAsyncIteration('ham')): + with self.subTest(type=type(stop_exc)): + try: + async with egg(): + raise stop_exc + except Exception as ex: + self.assertIs(ex, stop_exc) + else: + self.fail(f'{stop_exc} was suppressed') +"# + ) + } + + #[test] + fn test_bare_function_annotations_check_attribute_and_subscript_expressions() { + assert_dis_snapshot!( + r#" +def f(one: int): + int.new_attr: int + [list][0].new_attr: [int, str] + my_lst = [1] + my_lst[one]: int + return my_lst +"# + ) + } +} diff --git a/crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__bare_function_annotations_check_attribute_and_subscript_expressions.snap b/crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__bare_function_annotations_check_attribute_and_subscript_expressions.snap new file mode 100644 index 000000000..3274352b9 --- /dev/null +++ b/crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__bare_function_annotations_check_attribute_and_subscript_expressions.snap @@ -0,0 +1,52 @@ +--- +source: crates/stdlib/src/_opcode.rs +expression: "dis(r#\"\ndef f(one: int):\n int.new_attr: int\n [list][0].new_attr: [int, str]\n my_lst = [1]\n my_lst[one]: int\n return my_lst\n\"#)" +--- + 0 RESUME 0 + + 1 LOAD_CONST 0 (", line 1>) + MAKE_FUNCTION + LOAD_CONST 1 (", line 1>) + MAKE_FUNCTION + SET_FUNCTION_ATTRIBUTE 16 (annotate) + STORE_NAME 0 (f) + LOAD_CONST 2 (None) + RETURN_VALUE + +Disassembly of ", line 1>: + 1 RESUME 0 + LOAD_FAST_BORROW 0 (format) + LOAD_SMALL_INT 2 + COMPARE_OP 132 (>) + POP_JUMP_IF_FALSE 3 (to L1) + NOT_TAKEN + LOAD_COMMON_CONSTANT 1 (NotImplementedError) + RAISE_VARARGS 1 + L1: LOAD_CONST 1 ('one') + LOAD_GLOBAL 0 (int) + BUILD_MAP 1 + RETURN_VALUE + +Disassembly of ", line 1>: + 1 RESUME 0 + + 2 LOAD_GLOBAL 0 (int) + POP_TOP + + 3 LOAD_GLOBAL 2 (list) + BUILD_LIST 1 + LOAD_SMALL_INT 0 + BINARY_OP 26 ([]) + POP_TOP + + 4 LOAD_SMALL_INT 1 + BUILD_LIST 1 + STORE_FAST 1 (my_lst) + + 5 LOAD_FAST_BORROW 1 (my_lst) + POP_TOP + LOAD_FAST_BORROW 0 (one) + POP_TOP + + 6 LOAD_FAST_BORROW 1 (my_lst) + RETURN_VALUE diff --git a/crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__const_no_op.snap b/crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__const_no_op.snap new file mode 100644 index 000000000..347e58767 --- /dev/null +++ b/crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__const_no_op.snap @@ -0,0 +1,10 @@ +--- +source: crates/stdlib/src/_opcode.rs +expression: x = not True +--- + 0 RESUME 0 + + 1 LOAD_CONST 2 (False) + STORE_NAME 0 (x) + LOAD_CONST 1 (None) + RETURN_VALUE diff --git a/crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__constant_true_if_pass_keeps_line_anchor_nop.snap b/crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__constant_true_if_pass_keeps_line_anchor_nop.snap new file mode 100644 index 000000000..02e247350 --- /dev/null +++ b/crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__constant_true_if_pass_keeps_line_anchor_nop.snap @@ -0,0 +1,10 @@ +--- +source: crates/stdlib/src/_opcode.rs +expression: "if 1:\n pass" +--- + 0 RESUME 0 + + 1 NOP + + 2 LOAD_CONST 1 (None) + RETURN_VALUE diff --git a/crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__if_ands.snap b/crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__if_ands.snap new file mode 100644 index 000000000..b5957dda5 --- /dev/null +++ b/crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__if_ands.snap @@ -0,0 +1,8 @@ +--- +source: crates/stdlib/src/_opcode.rs +expression: "if True and False and False:\n pass" +--- + 0 RESUME 0 + + 1 LOAD_CONST 1 (None) + RETURN_VALUE diff --git a/crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__if_mixed.snap b/crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__if_mixed.snap new file mode 100644 index 000000000..f8976b8c6 --- /dev/null +++ b/crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__if_mixed.snap @@ -0,0 +1,8 @@ +--- +source: crates/stdlib/src/_opcode.rs +expression: "if (True and False) or (False and True):\n pass" +--- + 0 RESUME 0 + + 1 LOAD_CONST 1 (None) + RETURN_VALUE diff --git a/crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__if_ors.snap b/crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__if_ors.snap new file mode 100644 index 000000000..f8cc3a1f2 --- /dev/null +++ b/crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__if_ors.snap @@ -0,0 +1,10 @@ +--- +source: crates/stdlib/src/_opcode.rs +expression: "if True or False or False:\n pass" +--- + 0 RESUME 0 + + 1 NOP + + 2 LOAD_CONST 1 (None) + RETURN_VALUE diff --git a/crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__nested_bool_op.snap b/crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__nested_bool_op.snap new file mode 100644 index 000000000..c0e365948 --- /dev/null +++ b/crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__nested_bool_op.snap @@ -0,0 +1,24 @@ +--- +source: crates/stdlib/src/_opcode.rs +expression: x = Test() and False or False +--- + 0 RESUME 0 + + 1 LOAD_NAME 0 (Test) + PUSH_NULL + CALL 0 + COPY 1 + TO_BOOL + POP_JUMP_IF_FALSE 11 (to L1) + NOT_TAKEN + POP_TOP + LOAD_CONST 0 (False) + COPY 1 + TO_BOOL + POP_JUMP_IF_TRUE 3 (to L2) + NOT_TAKEN + L1: POP_TOP + LOAD_CONST 0 (False) + L2: STORE_NAME 1 (x) + LOAD_CONST 1 (None) + RETURN_VALUE diff --git a/crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__nested_double_async_with.snap b/crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__nested_double_async_with.snap new file mode 100644 index 000000000..04684cb6c --- /dev/null +++ b/crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__nested_double_async_with.snap @@ -0,0 +1,183 @@ +--- +source: crates/stdlib/src/_opcode.rs +expression: "dis(r#\"\nasync def test():\n for stop_exc in (StopIteration('spam'), StopAsyncIteration('ham')):\n with self.subTest(type=type(stop_exc)):\n try:\n async with egg():\n raise stop_exc\n except Exception as ex:\n self.assertIs(ex, stop_exc)\n else:\n self.fail(f'{stop_exc} was suppressed')\n\"#)" +--- + 0 RESUME 0 + + 1 LOAD_CONST 0 (", line 1>) + MAKE_FUNCTION + STORE_NAME 0 (test) + LOAD_CONST 1 (None) + RETURN_VALUE + +Disassembly of ", line 1>: + 1 RETURN_GENERATOR + POP_TOP + RESUME 0 + + 2 L1: LOAD_GLOBAL 1 (StopIteration + NULL) + LOAD_CONST 0 ('spam') + CALL 1 + LOAD_GLOBAL 3 (StopAsyncIteration + NULL) + LOAD_CONST 1 ('ham') + CALL 1 + BUILD_TUPLE 2 + GET_ITER + L2: FOR_ITER 71 (to L11) + STORE_FAST 0 (stop_exc) + + 3 LOAD_GLOBAL 4 (self) + LOAD_ATTR 7 (subTest + NULL|self) + LOAD_GLOBAL 9 (type + NULL) + LOAD_FAST_BORROW 0 (stop_exc) + CALL 1 + LOAD_CONST 2 (('type',)) + CALL_KW 1 + COPY 1 + LOAD_SPECIAL 1 (__exit__) + SWAP 2 + SWAP 3 + LOAD_SPECIAL 0 (__enter__) + CALL 0 + L3: POP_TOP + + 4 L4: NOP + + 5 L5: LOAD_GLOBAL 11 (egg + NULL) + CALL 0 + COPY 1 + LOAD_SPECIAL 3 (__aexit__) + SWAP 2 + SWAP 3 + LOAD_SPECIAL 2 (__aenter__) + CALL 0 + GET_AWAITABLE 1 + LOAD_CONST 3 (None) + L6: SEND 3 (to L9) + L7: YIELD_VALUE 1 + L8: RESUME 3 + JUMP_BACKWARD_NO_INTERRUPT 5 (to L6) + L9: END_SEND + L10: POP_TOP + + 6 LOAD_FAST_BORROW 0 (stop_exc) + RAISE_VARARGS 1 + + 2 L11: END_FOR + POP_ITER + LOAD_CONST 3 (None) + RETURN_VALUE + + 5 L12: CLEANUP_THROW + L13: JUMP_BACKWARD_NO_INTERRUPT 10 (to L9) + L14: PUSH_EXC_INFO + WITH_EXCEPT_START + GET_AWAITABLE 2 + LOAD_CONST 3 (None) + L15: SEND 4 (to L19) + L16: YIELD_VALUE 1 + L17: RESUME 3 + JUMP_BACKWARD_NO_INTERRUPT 5 (to L15) + L18: CLEANUP_THROW + L19: END_SEND + TO_BOOL + POP_JUMP_IF_TRUE 2 (to L20) + NOT_TAKEN + RERAISE 2 + L20: POP_TOP + L21: POP_EXCEPT + POP_TOP + POP_TOP + POP_TOP + JUMP_FORWARD 3 (to L23) + L22: COPY 3 + POP_EXCEPT + RERAISE 1 + L23: NOP + + 10 L24: LOAD_GLOBAL 4 (self) + LOAD_ATTR 13 (fail + NULL|self) + LOAD_FAST 0 (stop_exc) + FORMAT_SIMPLE + LOAD_CONST 4 (' was suppressed') + BUILD_STRING 2 + CALL 1 + POP_TOP + JUMP_FORWARD 45 (to L31) + + -- L25: PUSH_EXC_INFO + + 7 LOAD_GLOBAL 14 (Exception) + CHECK_EXC_MATCH + POP_JUMP_IF_FALSE 32 (to L29) + NOT_TAKEN + STORE_FAST 1 (ex) + + 8 L26: LOAD_GLOBAL 4 (self) + LOAD_ATTR 17 (assertIs + NULL|self) + LOAD_FAST_LOAD_FAST 16 (ex, stop_exc) + CALL 2 + POP_TOP + L27: POP_EXCEPT + LOAD_CONST 3 (None) + STORE_FAST 1 (ex) + DELETE_FAST 1 (ex) + JUMP_FORWARD 8 (to L31) + + -- L28: LOAD_CONST 3 (None) + STORE_FAST 1 (ex) + DELETE_FAST 1 (ex) + RERAISE 1 + + 7 L29: RERAISE 0 + + -- L30: COPY 3 + POP_EXCEPT + RERAISE 1 + + 3 L31: LOAD_CONST 3 (None) + LOAD_CONST 3 (None) + LOAD_CONST 3 (None) + CALL 3 + POP_TOP + JUMP_BACKWARD 188 (to L2) + L32: PUSH_EXC_INFO + WITH_EXCEPT_START + TO_BOOL + POP_JUMP_IF_TRUE 2 (to L33) + NOT_TAKEN + RERAISE 2 + L33: POP_TOP + L34: POP_EXCEPT + POP_TOP + POP_TOP + POP_TOP + JUMP_BACKWARD 205 (to L2) + L35: COPY 3 + POP_EXCEPT + RERAISE 1 + + -- L36: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR) + RERAISE 1 +ExceptionTable: + L1 to L3 -> L36 [0] lasti + L3 to L4 -> L32 [3] lasti + L5 to L7 -> L25 [3] + L7 to L8 -> L12 [7] + L8 to L10 -> L25 [3] + L10 to L11 -> L14 [5] lasti + L11 to L12 -> L36 [0] lasti + L12 to L13 -> L25 [3] + L14 to L16 -> L22 [7] lasti + L16 to L17 -> L18 [10] + L17 to L21 -> L22 [7] lasti + L21 to L23 -> L25 [3] + L24 to L25 -> L32 [3] lasti + L25 to L26 -> L30 [4] lasti + L26 to L27 -> L28 [4] lasti + L27 to L28 -> L32 [3] lasti + L28 to L30 -> L30 [4] lasti + L30 to L31 -> L32 [3] lasti + L31 to L32 -> L36 [0] lasti + L32 to L34 -> L35 [5] lasti + L34 to L36 -> L36 [0] lasti diff --git a/crates/vm/src/builtins/code.rs b/crates/vm/src/builtins/code.rs index 456be793f..8f0c8784d 100644 --- a/crates/vm/src/builtins/code.rs +++ b/crates/vm/src/builtins/code.rs @@ -1462,12 +1462,6 @@ impl PyCode { } } -impl fmt::Display for PyCode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - impl ToPyObject for CodeObject { fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef { vm.ctx.new_code(self).into() diff --git a/examples/dis.rs b/examples/dis.rs deleted file mode 100644 index 0b6190dde..000000000 --- a/examples/dis.rs +++ /dev/null @@ -1,90 +0,0 @@ -//! This an example usage of the rustpython_compiler crate. -//! This program reads, parses, and compiles a file you provide -//! to RustPython bytecode, and then displays the output in the -//! `dis.dis` format. -//! -//! example usage: -//! $ cargo run --release --example dis demo*.py - -#[macro_use] -extern crate log; - -use core::error::Error; -use lexopt::ValueExt; -use rustpython_compiler as compiler; -use std::fs; -use std::path::{Path, PathBuf}; - -fn main() -> Result<(), lexopt::Error> { - env_logger::init(); - - let mut scripts = vec![]; - let mut mode = compiler::Mode::Exec; - let mut expand_code_objects = true; - let mut optimize = 0; - - let mut parser = lexopt::Parser::from_env(); - while let Some(arg) = parser.next()? { - use lexopt::Arg::*; - match arg { - Long("help") | Short('h') => { - let bin_name = parser.bin_name().unwrap_or("dis"); - println!( - "usage: {bin_name} [-m,--mode=exec|single|eval] [-x,--no-expand] [-O]" - ); - println!( - "Compiles and disassembles python script files for viewing their bytecode." - ); - return Ok(()); - } - Value(x) => scripts.push(PathBuf::from(x)), - Long("mode") | Short('m') => { - mode = parser - .value()? - .parse_with(|s| s.parse::().map_err(|e| e.to_string()))? - } - Long("no-expand") | Short('x') => expand_code_objects = false, - Short('O') => optimize += 1, - _ => return Err(arg.unexpected()), - } - } - - if scripts.is_empty() { - return Err("expected at least one argument".into()); - } - - let opts = compiler::CompileOpts { - optimize, - debug_ranges: true, - }; - - for script in &scripts { - if script.exists() && script.is_file() { - let res = display_script(script, mode, opts, expand_code_objects); - if let Err(e) = res { - error!("Error while compiling {script:?}: {e}"); - } - } else { - eprintln!("{script:?} is not a file."); - } - } - - Ok(()) -} - -fn display_script( - path: &Path, - mode: compiler::Mode, - opts: compiler::CompileOpts, - expand_code_objects: bool, -) -> Result<(), Box> { - let source = fs::read_to_string(path)?; - let code = compiler::compile(&source, mode, &path.to_string_lossy(), opts)?; - println!("{}:", path.display()); - if expand_code_objects { - println!("{}", code.display_expand_code_objects()); - } else { - println!("{code}"); - } - Ok(()) -}