diff --git a/.cspell.dict/cpython.txt b/.cspell.dict/cpython.txt index 698ae734644..5cc189ff72e 100644 --- a/.cspell.dict/cpython.txt +++ b/.cspell.dict/cpython.txt @@ -31,6 +31,8 @@ fromlist heaptype HIGHRES IMMUTABLETYPE +INCREF +inplace ismine Itertool keeped @@ -39,6 +41,7 @@ kwonlyarg kwonlyargs lasti linearise +Lshift lsprof maxdepth mult @@ -62,12 +65,15 @@ pyrepl pythonw PYTHREAD_NAME releasebuffer +repr +Rshift SA_ONSTACK SOABI stackdepth stginfo stringlib structseq +subscr subkwargs subparams swappedbytes diff --git a/Lib/_opcode_metadata.py b/Lib/_opcode_metadata.py index db1c3146766..6f426d452c3 100644 --- a/Lib/_opcode_metadata.py +++ b/Lib/_opcode_metadata.py @@ -212,10 +212,6 @@ 'UNPACK_SEQUENCE_LIST': 209, 'UNPACK_SEQUENCE_TUPLE': 210, 'UNPACK_SEQUENCE_TWO_TUPLE': 211, - 'BEFORE_ASYNC_WITH': 212, - 'BEFORE_WITH': 213, - 'BINARY_SUBSCR': 214, - 'BUILD_CONST_KEY_MAP': 215, 'INSTRUMENTED_END_FOR': 234, 'INSTRUMENTED_POP_ITER': 235, 'INSTRUMENTED_END_SEND': 236, diff --git a/crates/codegen/src/compile.rs b/crates/codegen/src/compile.rs index 4386bba6fdf..c1d3c9c4917 100644 --- a/crates/codegen/src/compile.rs +++ b/crates/codegen/src/compile.rs @@ -30,7 +30,7 @@ use rustpython_compiler_core::{ bytecode::{ self, AnyInstruction, Arg as OpArgMarker, BinaryOperator, BuildSliceArgCount, CodeObject, ComparisonOperator, ConstantData, ConvertValueOparg, Instruction, IntrinsicFunction1, - Invert, OpArg, OpArgType, PseudoInstruction, UnpackExArgs, + Invert, OpArg, OpArgType, PseudoInstruction, SpecialMethod, UnpackExArgs, }, }; use rustpython_wtf8::Wtf8Buf; @@ -489,7 +489,12 @@ impl Compiler { match ctx { ast::ExprContext::Load => { emit!(self, Instruction::BuildSlice { argc }); - emit!(self, Instruction::BinarySubscr); + emit!( + self, + Instruction::BinaryOp { + op: BinaryOperator::Subscr + } + ); } ast::ExprContext::Store => { emit!(self, Instruction::BuildSlice { argc }); @@ -503,7 +508,12 @@ impl Compiler { // Emit appropriate instruction based on context match ctx { - ast::ExprContext::Load => emit!(self, Instruction::BinarySubscr), + ast::ExprContext::Load => emit!( + self, + Instruction::BinaryOp { + op: BinaryOperator::Subscr + } + ), ast::ExprContext::Store => emit!(self, Instruction::StoreSubscr), ast::ExprContext::Del => emit!(self, Instruction::DeleteSubscr), ast::ExprContext::Invalid => { @@ -4680,20 +4690,55 @@ impl Compiler { let exc_handler_block = self.new_block(); let after_block = self.new_block(); - // Compile context expression and BEFORE_WITH + // Compile context expression and load __enter__/__exit__ methods self.compile_expression(&item.context_expr)?; self.set_source_range(with_range); + // Stack: [cm] + emit!(self, Instruction::Copy { index: 1_u32 }); // [cm, cm] + if is_async { if self.ctx.func != FunctionContext::AsyncFunction { return Err(self.error(CodegenErrorType::InvalidAsyncWith)); } - emit!(self, Instruction::BeforeAsyncWith); + // Load __aexit__ and __aenter__, then call __aenter__ + emit!( + self, + Instruction::LoadSpecial { + method: SpecialMethod::AExit + } + ); // [cm, bound_aexit] + emit!(self, Instruction::Swap { index: 2_u32 }); // [bound_aexit, cm] + emit!( + self, + Instruction::LoadSpecial { + method: SpecialMethod::AEnter + } + ); // [bound_aexit, bound_aenter] + // bound_aenter is already bound, call with NULL self_or_null + emit!(self, Instruction::PushNull); // [bound_aexit, bound_aenter, NULL] + emit!(self, Instruction::Call { nargs: 0 }); // [bound_aexit, awaitable] emit!(self, Instruction::GetAwaitable); self.emit_load_const(ConstantData::None); self.compile_yield_from_sequence(true)?; } else { - emit!(self, Instruction::BeforeWith); + // Load __exit__ and __enter__, then call __enter__ + emit!( + self, + Instruction::LoadSpecial { + method: SpecialMethod::Exit + } + ); // [cm, bound_exit] + emit!(self, Instruction::Swap { index: 2_u32 }); // [bound_exit, cm] + emit!( + self, + Instruction::LoadSpecial { + method: SpecialMethod::Enter + } + ); // [bound_exit, bound_enter] + // bound_enter is already bound, call with NULL self_or_null + emit!(self, Instruction::PushNull); // [bound_exit, bound_enter, NULL] + emit!(self, Instruction::Call { nargs: 0 }); // [bound_exit, enter_result] } // Stack: [..., __exit__, enter_result] @@ -5179,7 +5224,12 @@ impl Compiler { ); } // Use BINARY_OP/NB_SUBSCR to extract the element. - emit!(self, Instruction::BinarySubscr); + emit!( + self, + Instruction::BinaryOp { + op: BinaryOperator::Subscr + } + ); // Compile the subpattern in irrefutable mode. self.compile_pattern_subpattern(pattern, pc)?; } @@ -6206,7 +6256,12 @@ impl Compiler { self.compile_expression(slice)?; emit!(self, Instruction::Copy { index: 2_u32 }); emit!(self, Instruction::Copy { index: 2_u32 }); - emit!(self, Instruction::BinarySubscr); + emit!( + self, + Instruction::BinaryOp { + op: BinaryOperator::Subscr + } + ); AugAssignKind::Subscript } ast::Expr::Attribute(ast::ExprAttribute { value, attr, .. }) => { 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 index b13f6e8e32e..664bbf56be2 100644 --- 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 @@ -1,5 +1,6 @@ --- source: crates/codegen/src/compile.rs +assertion_line: 8655 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\")" --- 3 0 LOAD_CONST (): 1 0 RESUME (0) @@ -14,7 +15,7 @@ expression: "compile_exec(\"\\\nasync def test():\n for stop_exc in (StopIter 8 CALL (1) 9 BUILD_TUPLE (2) 10 GET_ITER - >> 11 FOR_ITER (129) + >> 11 FOR_ITER (139) 12 STORE_FAST (0, stop_exc) 3 13 LOAD_GLOBAL (2, self) @@ -25,124 +26,134 @@ expression: "compile_exec(\"\\\nasync def test():\n for stop_exc in (StopIter 18 CALL (1) 19 LOAD_CONST (("type")) 20 CALL_KW (1) - 21 BEFORE_WITH - 22 POP_TOP + 21 COPY (1) + 22 LOAD_SPECIAL (__exit__) + 23 SWAP (2) + 24 LOAD_SPECIAL (__enter__) + 25 PUSH_NULL + 26 CALL (0) + 27 POP_TOP - 5 23 LOAD_GLOBAL (5, egg) - 24 PUSH_NULL - 25 CALL (0) - 26 BEFORE_ASYNC_WITH - 27 GET_AWAITABLE - 28 LOAD_CONST (None) - >> 29 SEND (34) - 30 YIELD_VALUE (1) - 31 RESUME (3) - 32 JUMP_BACKWARD (29) - 33 CLEANUP_THROW - >> 34 END_SEND - 35 POP_TOP + 5 28 LOAD_GLOBAL (5, egg) + 29 PUSH_NULL + 30 CALL (0) + 31 COPY (1) + 32 LOAD_SPECIAL (__aexit__) + 33 SWAP (2) + 34 LOAD_SPECIAL (__aenter__) + 35 PUSH_NULL + 36 CALL (0) + 37 GET_AWAITABLE + 38 LOAD_CONST (None) + >> 39 SEND (44) + 40 YIELD_VALUE (1) + 41 RESUME (3) + 42 JUMP_BACKWARD (39) + 43 CLEANUP_THROW + >> 44 END_SEND + 45 POP_TOP - 6 36 LOAD_FAST (0, stop_exc) - 37 RAISE_VARARGS (Raise) + 6 46 LOAD_FAST (0, stop_exc) + 47 RAISE_VARARGS (Raise) - 5 38 PUSH_NULL - 39 LOAD_CONST (None) - 40 LOAD_CONST (None) - 41 LOAD_CONST (None) - 42 CALL (3) - 43 GET_AWAITABLE - 44 LOAD_CONST (None) - >> 45 SEND (50) - 46 YIELD_VALUE (1) - 47 RESUME (3) - 48 JUMP_BACKWARD (45) - 49 CLEANUP_THROW - >> 50 END_SEND - 51 POP_TOP - 52 JUMP_FORWARD (74) - 53 PUSH_EXC_INFO - 54 WITH_EXCEPT_START - 55 GET_AWAITABLE - 56 LOAD_CONST (None) - >> 57 SEND (62) - 58 YIELD_VALUE (1) - 59 RESUME (3) - 60 JUMP_BACKWARD (57) - 61 CLEANUP_THROW - >> 62 END_SEND - 63 TO_BOOL - 64 POP_JUMP_IF_TRUE (66) - 65 RERAISE (2) - >> 66 POP_TOP - 67 POP_EXCEPT - 68 POP_TOP - 69 POP_TOP - 70 JUMP_FORWARD (74) - 71 COPY (3) - 72 POP_EXCEPT - 73 RERAISE (1) - >> 74 JUMP_FORWARD (100) - 75 PUSH_EXC_INFO + 5 48 PUSH_NULL + 49 LOAD_CONST (None) + 50 LOAD_CONST (None) + 51 LOAD_CONST (None) + 52 CALL (3) + 53 GET_AWAITABLE + 54 LOAD_CONST (None) + >> 55 SEND (60) + 56 YIELD_VALUE (1) + 57 RESUME (3) + 58 JUMP_BACKWARD (55) + 59 CLEANUP_THROW + >> 60 END_SEND + 61 POP_TOP + 62 JUMP_FORWARD (84) + 63 PUSH_EXC_INFO + 64 WITH_EXCEPT_START + 65 GET_AWAITABLE + 66 LOAD_CONST (None) + >> 67 SEND (72) + 68 YIELD_VALUE (1) + 69 RESUME (3) + 70 JUMP_BACKWARD (67) + 71 CLEANUP_THROW + >> 72 END_SEND + 73 TO_BOOL + 74 POP_JUMP_IF_TRUE (76) + 75 RERAISE (2) + >> 76 POP_TOP + 77 POP_EXCEPT + 78 POP_TOP + 79 POP_TOP + 80 JUMP_FORWARD (84) + 81 COPY (3) + 82 POP_EXCEPT + 83 RERAISE (1) + >> 84 JUMP_FORWARD (110) + 85 PUSH_EXC_INFO - 7 76 LOAD_GLOBAL (6, Exception) - 77 CHECK_EXC_MATCH - 78 POP_JUMP_IF_FALSE (96) - 79 STORE_FAST (1, ex) + 7 86 LOAD_GLOBAL (6, Exception) + 87 CHECK_EXC_MATCH + 88 POP_JUMP_IF_FALSE (106) + 89 STORE_FAST (1, ex) - 8 80 LOAD_GLOBAL (2, self) - 81 LOAD_ATTR (15, assertIs, method=true) - 82 LOAD_FAST (1, ex) - 83 LOAD_FAST (0, stop_exc) - 84 CALL (2) - 85 POP_TOP - 86 JUMP_FORWARD (91) - 87 LOAD_CONST (None) - 88 STORE_FAST (1, ex) - 89 DELETE_FAST (1, ex) - 90 RAISE_VARARGS (ReraiseFromStack) - >> 91 POP_EXCEPT - 92 LOAD_CONST (None) - 93 STORE_FAST (1, ex) - 94 DELETE_FAST (1, ex) - 95 JUMP_FORWARD (108) - >> 96 RAISE_VARARGS (ReraiseFromStack) - 97 COPY (3) - 98 POP_EXCEPT - 99 RAISE_VARARGS (ReraiseFromStack) + 8 90 LOAD_GLOBAL (2, self) + 91 LOAD_ATTR (15, assertIs, method=true) + 92 LOAD_FAST (1, ex) + 93 LOAD_FAST (0, stop_exc) + 94 CALL (2) + 95 POP_TOP + 96 JUMP_FORWARD (101) + 97 LOAD_CONST (None) + 98 STORE_FAST (1, ex) + 99 DELETE_FAST (1, ex) + 100 RAISE_VARARGS (ReraiseFromStack) + >> 101 POP_EXCEPT + 102 LOAD_CONST (None) + 103 STORE_FAST (1, ex) + 104 DELETE_FAST (1, ex) + 105 JUMP_FORWARD (118) + >> 106 RAISE_VARARGS (ReraiseFromStack) + 107 COPY (3) + 108 POP_EXCEPT + 109 RAISE_VARARGS (ReraiseFromStack) - 10 >> 100 LOAD_GLOBAL (2, self) - 101 LOAD_ATTR (17, fail, method=true) - 102 LOAD_FAST (0, stop_exc) - 103 FORMAT_SIMPLE - 104 LOAD_CONST (" was suppressed") - 105 BUILD_STRING (2) - 106 CALL (1) - 107 POP_TOP + 10 >> 110 LOAD_GLOBAL (2, self) + 111 LOAD_ATTR (17, fail, method=true) + 112 LOAD_FAST (0, stop_exc) + 113 FORMAT_SIMPLE + 114 LOAD_CONST (" was suppressed") + 115 BUILD_STRING (2) + 116 CALL (1) + 117 POP_TOP - 3 >> 108 PUSH_NULL - 109 LOAD_CONST (None) - 110 LOAD_CONST (None) - 111 LOAD_CONST (None) - 112 CALL (3) - 113 POP_TOP - 114 JUMP_FORWARD (128) - 115 PUSH_EXC_INFO - 116 WITH_EXCEPT_START - 117 TO_BOOL - 118 POP_JUMP_IF_TRUE (120) - 119 RERAISE (2) - >> 120 POP_TOP - 121 POP_EXCEPT - 122 POP_TOP + 3 >> 118 PUSH_NULL + 119 LOAD_CONST (None) + 120 LOAD_CONST (None) + 121 LOAD_CONST (None) + 122 CALL (3) 123 POP_TOP - 124 JUMP_FORWARD (128) - 125 COPY (3) - 126 POP_EXCEPT - 127 RERAISE (1) - >> 128 JUMP_BACKWARD (11) - >> 129 POP_TOP - 130 LOAD_CONST (None) - 131 RETURN_VALUE + 124 JUMP_FORWARD (138) + 125 PUSH_EXC_INFO + 126 WITH_EXCEPT_START + 127 TO_BOOL + 128 POP_JUMP_IF_TRUE (130) + 129 RERAISE (2) + >> 130 POP_TOP + 131 POP_EXCEPT + 132 POP_TOP + 133 POP_TOP + 134 JUMP_FORWARD (138) + 135 COPY (3) + 136 POP_EXCEPT + 137 RERAISE (1) + >> 138 JUMP_BACKWARD (11) + >> 139 POP_TOP + 140 LOAD_CONST (None) + 141 RETURN_VALUE 1 MAKE_FUNCTION 2 STORE_NAME (0, test) diff --git a/crates/compiler-core/src/bytecode.rs b/crates/compiler-core/src/bytecode.rs index c59a64fea3d..daf954166e6 100644 --- a/crates/compiler-core/src/bytecode.rs +++ b/crates/compiler-core/src/bytecode.rs @@ -23,7 +23,7 @@ pub use crate::bytecode::{ oparg::{ BinaryOperator, BuildSliceArgCount, ComparisonOperator, ConvertValueOparg, IntrinsicFunction1, IntrinsicFunction2, Invert, Label, MakeFunctionFlags, NameIdx, OpArg, - OpArgByte, OpArgState, OpArgType, RaiseKind, ResumeType, UnpackExArgs, + OpArgByte, OpArgState, OpArgType, RaiseKind, ResumeType, SpecialMethod, UnpackExArgs, }, }; diff --git a/crates/compiler-core/src/bytecode/instruction.rs b/crates/compiler-core/src/bytecode/instruction.rs index 3165b0f08aa..41619702b86 100644 --- a/crates/compiler-core/src/bytecode/instruction.rs +++ b/crates/compiler-core/src/bytecode/instruction.rs @@ -6,7 +6,7 @@ use crate::{ oparg::{ BinaryOperator, BuildSliceArgCount, ComparisonOperator, ConvertValueOparg, IntrinsicFunction1, IntrinsicFunction2, Invert, Label, MakeFunctionFlags, NameIdx, - OpArg, OpArgByte, OpArgType, RaiseKind, UnpackExArgs, + OpArg, OpArgByte, OpArgType, RaiseKind, SpecialMethod, UnpackExArgs, }, }, marshal::MarshalError, @@ -192,8 +192,8 @@ pub enum Instruction { idx: Arg, } = 94, // Placeholder LoadSpecial { - arg: Arg, - } = 95, // Placeholder + method: Arg, + } = 95, LoadSuperAttr { arg: Arg, } = 96, @@ -258,14 +258,6 @@ pub enum Instruction { YieldValue { arg: Arg, } = 120, - // RustPython-only instructions (212-225) - // These either don't exist in CPython 3.14 or are RustPython-specific. - BeforeAsyncWith = 212, - BeforeWith = 213, - BinarySubscr = 214, - BuildConstKeyMap { - size: Arg, - } = 215, // Placeholder // CPython 3.14 RESUME (128) Resume { arg: Arg, @@ -408,15 +400,8 @@ impl TryFrom for Instruction { let instrumented_start = u8::from(Self::InstrumentedEndFor); let instrumented_end = u8::from(Self::InstrumentedLine); - // RustPython-only opcodes (explicit list to avoid gaps) - let custom_ops: &[u8] = &[ - u8::from(Self::BeforeAsyncWith), - u8::from(Self::BeforeWith), - u8::from(Self::BinarySubscr), - u8::from(Self::BuildConstKeyMap { - size: Arg::marker(), - }), - ]; + // No RustPython-only opcodes anymore - all opcodes match CPython 3.14 + let custom_ops: &[u8] = &[]; if (cpython_start..=cpython_end).contains(&value) || value == resume_id @@ -493,7 +478,6 @@ impl InstructionMetadata for Instruction { Self::Reserved => 0, Self::BinaryOp { .. } => -1, Self::CompareOp { .. } => -1, - Self::BinarySubscr => -1, Self::Copy { .. } => 1, Self::PopTop => -1, Self::Swap { .. } => 0, @@ -542,7 +526,6 @@ impl InstructionMetadata for Instruction { Self::CheckExcMatch => 0, // [exc, type] -> [exc, bool] (pops type, pushes bool) Self::Reraise { .. } => 0, // Exception raised, stack effect doesn't matter Self::SetupAnnotations => 0, - Self::BeforeWith => 1, // push __exit__, then replace ctx_mgr with __enter__ result Self::WithExceptStart => 1, // push __exit__ result Self::RaiseVarargs { kind } => { // Stack effects for different raise kinds: @@ -591,7 +574,6 @@ impl InstructionMetadata for Instruction { Self::PopExcept => 0, Self::PopIter => -1, Self::GetAwaitable => 0, - Self::BeforeAsyncWith => 1, Self::GetAIter => 0, Self::GetANext => 1, Self::EndAsyncFor => -2, // pops (awaitable, exc) from stack @@ -621,7 +603,6 @@ impl InstructionMetadata for Instruction { Self::LoadLocals => 0, Self::ReturnGenerator => 0, Self::StoreSlice => 0, - Self::BuildConstKeyMap { .. } => 0, Self::CopyFreeVars { .. } => 0, Self::EnterExecutor => 0, Self::JumpBackwardNoInterrupt { .. } => 0, @@ -803,10 +784,7 @@ impl InstructionMetadata for Instruction { }; match self { - Self::BeforeAsyncWith => w!(BEFORE_ASYNC_WITH), - Self::BeforeWith => w!(BEFORE_WITH), Self::BinaryOp { op } => write!(f, "{:pad$}({})", "BINARY_OP", op.get(arg)), - Self::BinarySubscr => w!(BINARY_SUBSCR), Self::BuildList { size } => w!(BUILD_LIST, size), Self::BuildMap { size } => w!(BUILD_MAP, size), Self::BuildSet { size } => w!(BUILD_SET, size), @@ -875,6 +853,7 @@ impl InstructionMetadata for Instruction { Self::LoadFastAndClear(idx) => w!(LOAD_FAST_AND_CLEAR, varname = idx), Self::LoadGlobal(idx) => w!(LOAD_GLOBAL, name = idx), Self::LoadName(idx) => w!(LOAD_NAME, name = idx), + Self::LoadSpecial { method } => w!(LOAD_SPECIAL, method), Self::LoadSuperAttr { arg: idx } => { let encoded = idx.get(arg); let (name_idx, load_method, has_class) = decode_load_super_attr_arg(encoded); diff --git a/crates/compiler-core/src/bytecode/oparg.rs b/crates/compiler-core/src/bytecode/oparg.rs index 3130da59750..658d8d3ec35 100644 --- a/crates/compiler-core/src/bytecode/oparg.rs +++ b/crates/compiler-core/src/bytecode/oparg.rs @@ -428,6 +428,8 @@ op_arg_enum!( InplaceTrueDivide = 24, /// `^=` InplaceXor = 25, + /// `[]` subscript + Subscr = 26, } ); @@ -493,6 +495,7 @@ impl fmt::Display for BinaryOperator { Self::InplaceSubtract => "-=", Self::InplaceTrueDivide => "/=", Self::InplaceXor => "^=", + Self::Subscr => "[]", }; write!(f, "{op}") } @@ -516,6 +519,34 @@ op_arg_enum!( } ); +op_arg_enum!( + /// Special method for LOAD_SPECIAL opcode (context managers). + #[repr(u8)] + #[derive(Debug, Copy, Clone, PartialEq, Eq)] + pub enum SpecialMethod { + /// `__enter__` for sync context manager + Enter = 0, + /// `__exit__` for sync context manager + Exit = 1, + /// `__aenter__` for async context manager + AEnter = 2, + /// `__aexit__` for async context manager + AExit = 3, + } +); + +impl fmt::Display for SpecialMethod { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let method_name = match self { + Self::Enter => "__enter__", + Self::Exit => "__exit__", + Self::AEnter => "__aenter__", + Self::AExit => "__aexit__", + }; + write!(f, "{method_name}") + } +} + /// Specifies if a slice is built with either 2 or 3 arguments. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum BuildSliceArgCount { diff --git a/crates/vm/src/frame.rs b/crates/vm/src/frame.rs index 2059014bf0a..d0de7d8709f 100644 --- a/crates/vm/src/frame.rs +++ b/crates/vm/src/frame.rs @@ -617,39 +617,7 @@ impl ExecutingFrame<'_> { } match instruction { - Instruction::BeforeAsyncWith => { - let mgr = self.pop_value(); - let error_string = || -> String { - format!( - "'{:.200}' object does not support the asynchronous context manager protocol", - mgr.class().name(), - ) - }; - - let aenter_res = vm - .get_special_method(&mgr, identifier!(vm, __aenter__))? - .ok_or_else(|| vm.new_type_error(error_string()))? - .invoke((), vm)?; - let aexit = mgr - .get_attr(identifier!(vm, __aexit__), vm) - .map_err(|_exc| { - vm.new_type_error({ - format!("{} (missed __aexit__ method)", error_string()) - }) - })?; - self.push_value(aexit); - self.push_value(aenter_res); - - Ok(None) - } Instruction::BinaryOp { op } => self.execute_bin_op(vm, op.get(arg)), - Instruction::BinarySubscr => { - let key = self.pop_value(); - let container = self.pop_value(); - let result = container.get_item(key.as_object(), vm)?; - self.push_value(result); - Ok(None) - } Instruction::BuildList { size } => { let sz = size.get(arg) as usize; @@ -1263,6 +1231,40 @@ impl ExecutingFrame<'_> { } Ok(None) } + Instruction::LoadSpecial { method } => { + // Stack effect: 0 (replaces TOS with bound method) + // Input: [..., obj] + // Output: [..., bound_method] + use crate::vm::PyMethod; + use bytecode::SpecialMethod; + + let obj = self.pop_value(); + let method_name = match method.get(arg) { + SpecialMethod::Enter => identifier!(vm, __enter__), + SpecialMethod::Exit => identifier!(vm, __exit__), + SpecialMethod::AEnter => identifier!(vm, __aenter__), + SpecialMethod::AExit => identifier!(vm, __aexit__), + }; + + let bound = match vm.get_special_method(&obj, method_name)? { + Some(PyMethod::Function { target, func }) => { + // Create bound method: PyBoundMethod(object=target, function=func) + crate::builtins::PyBoundMethod::new(target, func) + .into_ref(&vm.ctx) + .into() + } + Some(PyMethod::Attribute(bound)) => bound, + None => { + return Err(vm.new_type_error(format!( + "'{}' object does not support the context manager protocol (missed {} method)", + obj.class().name(), + method_name + ))); + } + }; + self.push_value(bound); + Ok(None) + } Instruction::MakeFunction => self.execute_make_function(vm), Instruction::MapAdd { i } => { let value = self.pop_value(); @@ -1629,35 +1631,6 @@ impl ExecutingFrame<'_> { self.execute_set_function_attribute(vm, attr.get(arg)) } Instruction::SetupAnnotations => self.setup_annotations(vm), - Instruction::BeforeWith => { - // TOS: context_manager - // Result: [..., __exit__, __enter__ result] - let context_manager = self.pop_value(); - let error_string = || -> String { - format!( - "'{:.200}' object does not support the context manager protocol", - context_manager.class().name(), - ) - }; - - // Get __exit__ first (before calling __enter__) - let exit = context_manager - .get_attr(identifier!(vm, __exit__), vm) - .map_err(|_exc| { - vm.new_type_error(format!("{} (missed __exit__ method)", error_string())) - })?; - - // Get and call __enter__ - let enter_res = vm - .get_special_method(&context_manager, identifier!(vm, __enter__))? - .ok_or_else(|| vm.new_type_error(error_string()))? - .invoke((), vm)?; - - // Push __exit__ first, then enter result - self.push_value(exit); - self.push_value(enter_res); - Ok(None) - } Instruction::StoreAttr { idx } => self.store_attr(vm, idx.get(arg)), Instruction::StoreDeref(i) => { let value = self.pop_value(); @@ -2400,6 +2373,7 @@ impl ExecutingFrame<'_> { bytecode::BinaryOperator::InplaceXor => vm._ixor(a_ref, b_ref), bytecode::BinaryOperator::InplaceOr => vm._ior(a_ref, b_ref), bytecode::BinaryOperator::InplaceAnd => vm._iand(a_ref, b_ref), + bytecode::BinaryOperator::Subscr => a_ref.get_item(b_ref.as_object(), vm), }?; self.push_value(value);