Skip to content

Commit 676130f

Browse files
committed
Use refcells in frames to solve frame mutability problem.
1 parent 4dd6592 commit 676130f

File tree

4 files changed

+47
-61
lines changed

4 files changed

+47
-61
lines changed

tests/snippets/getframe.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ def test_function():
1111
assert sys._getframe().f_locals is not locals_dict
1212
assert sys._getframe().f_locals['x'] == 17
1313
assert sys._getframe(1).f_locals['foo'] == 'bar'
14-
print(sys._getframe(1).f_locals)
1514

1615
test_function()
1716

vm/src/frame.rs

Lines changed: 43 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,13 @@ enum BlockType {
4545
},
4646
}
4747

48-
#[derive(Clone)]
4948
pub struct Frame {
5049
pub code: bytecode::CodeObject,
5150
// We need 1 stack per frame
5251
stack: RefCell<Vec<PyObjectRef>>, // The main data frame of the stack machine
53-
blocks: Vec<Block>, // Block frames, for controlling loops and exceptions
52+
blocks: RefCell<Vec<Block>>, // Block frames, for controlling loops and exceptions
5453
pub locals: PyObjectRef, // Variables
55-
pub lasti: usize, // index of last instruction ran
54+
pub lasti: RefCell<usize>, // index of last instruction ran
5655
}
5756

5857
// Running a frame can result in one of the below:
@@ -80,15 +79,15 @@ impl Frame {
8079
Frame {
8180
code: objcode::get_value(&code),
8281
stack: RefCell::new(vec![]),
83-
blocks: vec![],
82+
blocks: RefCell::new(vec![]),
8483
// save the callargs as locals
8584
// globals: locals.clone(),
8685
locals,
87-
lasti: 0,
86+
lasti: RefCell::new(0),
8887
}
8988
}
9089

91-
pub fn run(&mut self, vm: &mut VirtualMachine) -> Result<ExecutionResult, PyObjectRef> {
90+
pub fn run(&self, vm: &mut VirtualMachine) -> Result<ExecutionResult, PyObjectRef> {
9291
let filename = &self.code.source_path.to_string();
9392

9493
// This is the name of the object being run:
@@ -142,16 +141,16 @@ impl Frame {
142141
value
143142
}
144143

145-
pub fn fetch_instruction(&mut self) -> bytecode::Instruction {
144+
pub fn fetch_instruction(&self) -> bytecode::Instruction {
146145
// TODO: an immutable reference is enough, we should not
147146
// clone the instruction.
148-
let ins2 = self.code.instructions[self.lasti].clone();
149-
self.lasti += 1;
147+
let ins2 = self.code.instructions[*self.lasti.borrow()].clone();
148+
*self.lasti.borrow_mut() += 1;
150149
ins2
151150
}
152151

153152
// Execute a single instruction:
154-
fn execute_instruction(&mut self, vm: &mut VirtualMachine) -> FrameResult {
153+
fn execute_instruction(&self, vm: &mut VirtualMachine) -> FrameResult {
155154
let instruction = self.fetch_instruction();
156155
{
157156
trace!("=======");
@@ -344,7 +343,7 @@ impl Frame {
344343
match next_obj {
345344
Some(value) => {
346345
// Set back program counter:
347-
self.lasti -= 1;
346+
*self.lasti.borrow_mut() -= 1;
348347
Ok(Some(ExecutionResult::Yield(value)))
349348
}
350349
None => {
@@ -662,7 +661,7 @@ impl Frame {
662661
}
663662

664663
fn get_elements(
665-
&mut self,
664+
&self,
666665
vm: &mut VirtualMachine,
667666
size: usize,
668667
unpack: bool,
@@ -683,7 +682,7 @@ impl Frame {
683682
}
684683

685684
fn import(
686-
&mut self,
685+
&self,
687686
vm: &mut VirtualMachine,
688687
module: &str,
689688
symbol: &Option<String>,
@@ -701,7 +700,7 @@ impl Frame {
701700
Ok(None)
702701
}
703702

704-
fn import_star(&mut self, vm: &mut VirtualMachine, module: &str) -> FrameResult {
703+
fn import_star(&self, vm: &mut VirtualMachine, module: &str) -> FrameResult {
705704
let current_path = {
706705
let mut source_pathbuf = PathBuf::from(&self.code.source_path);
707706
source_pathbuf.pop();
@@ -719,7 +718,7 @@ impl Frame {
719718
}
720719

721720
// Unwind all blocks:
722-
fn unwind_blocks(&mut self, vm: &mut VirtualMachine) -> Option<PyObjectRef> {
721+
fn unwind_blocks(&self, vm: &mut VirtualMachine) -> Option<PyObjectRef> {
723722
while let Some(block) = self.pop_block() {
724723
match block.typ {
725724
BlockType::Loop { .. } => {}
@@ -743,9 +742,9 @@ impl Frame {
743742
None
744743
}
745744

746-
fn unwind_loop(&mut self, vm: &mut VirtualMachine) -> Block {
745+
fn unwind_loop(&self, vm: &mut VirtualMachine) -> Block {
747746
loop {
748-
let block = self.current_block().cloned().expect("not in a loop");
747+
let block = self.current_block().expect("not in a loop");
749748
match block.typ {
750749
BlockType::Loop { .. } => break block,
751750
BlockType::TryExcept { .. } => {
@@ -765,11 +764,7 @@ impl Frame {
765764
}
766765
}
767766

768-
fn unwind_exception(
769-
&mut self,
770-
vm: &mut VirtualMachine,
771-
exc: PyObjectRef,
772-
) -> Option<PyObjectRef> {
767+
fn unwind_exception(&self, vm: &mut VirtualMachine, exc: PyObjectRef) -> Option<PyObjectRef> {
773768
// unwind block stack on exception and find any handlers:
774769
while let Some(block) = self.pop_block() {
775770
match block.typ {
@@ -837,13 +832,13 @@ impl Frame {
837832
vm.call_method(context_manager, "__exit__", args)
838833
}
839834

840-
fn store_name(&mut self, vm: &mut VirtualMachine, name: &str) -> FrameResult {
835+
fn store_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult {
841836
let obj = self.pop_value();
842837
vm.ctx.set_attr(&self.locals, name, obj);
843838
Ok(None)
844839
}
845840

846-
fn delete_name(&mut self, vm: &mut VirtualMachine, name: &str) -> FrameResult {
841+
fn delete_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult {
847842
let locals = match self.locals.payload {
848843
PyObjectPayload::Scope { ref scope } => scope.borrow().locals.clone(),
849844
_ => panic!("We really expect our scope to be a scope!"),
@@ -855,7 +850,7 @@ impl Frame {
855850
Ok(None)
856851
}
857852

858-
fn load_name(&mut self, vm: &mut VirtualMachine, name: &str) -> FrameResult {
853+
fn load_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult {
859854
// Lookup name in scope and put it onto the stack!
860855
let mut scope = self.locals.clone();
861856
loop {
@@ -874,33 +869,33 @@ impl Frame {
874869
}
875870
}
876871

877-
fn subscript(&mut self, vm: &mut VirtualMachine, a: PyObjectRef, b: PyObjectRef) -> PyResult {
872+
fn subscript(&self, vm: &mut VirtualMachine, a: PyObjectRef, b: PyObjectRef) -> PyResult {
878873
vm.call_method(&a, "__getitem__", vec![b])
879874
}
880875

881-
fn execute_store_subscript(&mut self, vm: &mut VirtualMachine) -> FrameResult {
876+
fn execute_store_subscript(&self, vm: &mut VirtualMachine) -> FrameResult {
882877
let idx = self.pop_value();
883878
let obj = self.pop_value();
884879
let value = self.pop_value();
885880
vm.call_method(&obj, "__setitem__", vec![idx, value])?;
886881
Ok(None)
887882
}
888883

889-
fn execute_delete_subscript(&mut self, vm: &mut VirtualMachine) -> FrameResult {
884+
fn execute_delete_subscript(&self, vm: &mut VirtualMachine) -> FrameResult {
890885
let idx = self.pop_value();
891886
let obj = self.pop_value();
892887
vm.call_method(&obj, "__delitem__", vec![idx])?;
893888
Ok(None)
894889
}
895890

896-
fn jump(&mut self, label: bytecode::Label) {
891+
fn jump(&self, label: bytecode::Label) {
897892
let target_pc = self.code.label_map[&label];
898893
trace!("program counter from {:?} to {:?}", self.lasti, target_pc);
899-
self.lasti = target_pc;
894+
*self.lasti.borrow_mut() = target_pc;
900895
}
901896

902897
fn execute_binop(
903-
&mut self,
898+
&self,
904899
vm: &mut VirtualMachine,
905900
op: &bytecode::BinaryOperator,
906901
inplace: bool,
@@ -943,11 +938,7 @@ impl Frame {
943938
Ok(None)
944939
}
945940

946-
fn execute_unop(
947-
&mut self,
948-
vm: &mut VirtualMachine,
949-
op: &bytecode::UnaryOperator,
950-
) -> FrameResult {
941+
fn execute_unop(&self, vm: &mut VirtualMachine, op: &bytecode::UnaryOperator) -> FrameResult {
951942
let a = self.pop_value();
952943
let value = match *op {
953944
bytecode::UnaryOperator::Minus => vm.call_method(&a, "__neg__", vec![])?,
@@ -968,7 +959,7 @@ impl Frame {
968959

969960
// https://docs.python.org/3/reference/expressions.html#membership-test-operations
970961
fn _membership(
971-
&mut self,
962+
&self,
972963
vm: &mut VirtualMachine,
973964
needle: PyObjectRef,
974965
haystack: &PyObjectRef,
@@ -978,12 +969,7 @@ impl Frame {
978969
// not implemented.
979970
}
980971

981-
fn _in(
982-
&mut self,
983-
vm: &mut VirtualMachine,
984-
needle: PyObjectRef,
985-
haystack: PyObjectRef,
986-
) -> PyResult {
972+
fn _in(&self, vm: &mut VirtualMachine, needle: PyObjectRef, haystack: PyObjectRef) -> PyResult {
987973
match self._membership(vm, needle, &haystack) {
988974
Ok(found) => Ok(found),
989975
Err(_) => Err(vm.new_type_error(format!(
@@ -994,7 +980,7 @@ impl Frame {
994980
}
995981

996982
fn _not_in(
997-
&mut self,
983+
&self,
998984
vm: &mut VirtualMachine,
999985
needle: PyObjectRef,
1000986
haystack: PyObjectRef,
@@ -1020,7 +1006,7 @@ impl Frame {
10201006
}
10211007

10221008
fn execute_compare(
1023-
&mut self,
1009+
&self,
10241010
vm: &mut VirtualMachine,
10251011
op: &bytecode::ComparisonOperator,
10261012
) -> FrameResult {
@@ -1043,47 +1029,47 @@ impl Frame {
10431029
Ok(None)
10441030
}
10451031

1046-
fn load_attr(&mut self, vm: &mut VirtualMachine, attr_name: &str) -> FrameResult {
1032+
fn load_attr(&self, vm: &mut VirtualMachine, attr_name: &str) -> FrameResult {
10471033
let parent = self.pop_value();
10481034
let attr_name = vm.new_str(attr_name.to_string());
10491035
let obj = vm.get_attribute(parent, attr_name)?;
10501036
self.push_value(obj);
10511037
Ok(None)
10521038
}
10531039

1054-
fn store_attr(&mut self, vm: &mut VirtualMachine, attr_name: &str) -> FrameResult {
1040+
fn store_attr(&self, vm: &mut VirtualMachine, attr_name: &str) -> FrameResult {
10551041
let parent = self.pop_value();
10561042
let value = self.pop_value();
10571043
vm.ctx.set_attr(&parent, attr_name, value);
10581044
Ok(None)
10591045
}
10601046

1061-
fn delete_attr(&mut self, vm: &mut VirtualMachine, attr_name: &str) -> FrameResult {
1047+
fn delete_attr(&self, vm: &mut VirtualMachine, attr_name: &str) -> FrameResult {
10621048
let parent = self.pop_value();
10631049
let name = vm.ctx.new_str(attr_name.to_string());
10641050
vm.del_attr(&parent, name)?;
10651051
Ok(None)
10661052
}
10671053

10681054
pub fn get_lineno(&self) -> ast::Location {
1069-
self.code.locations[self.lasti].clone()
1055+
self.code.locations[*self.lasti.borrow()].clone()
10701056
}
10711057

1072-
fn push_block(&mut self, typ: BlockType) {
1073-
self.blocks.push(Block {
1058+
fn push_block(&self, typ: BlockType) {
1059+
self.blocks.borrow_mut().push(Block {
10741060
typ,
10751061
level: self.stack.borrow().len(),
10761062
});
10771063
}
10781064

1079-
fn pop_block(&mut self) -> Option<Block> {
1080-
let block = self.blocks.pop()?;
1065+
fn pop_block(&self) -> Option<Block> {
1066+
let block = self.blocks.borrow_mut().pop()?;
10811067
self.stack.borrow_mut().truncate(block.level);
10821068
Some(block)
10831069
}
10841070

1085-
fn current_block(&self) -> Option<&Block> {
1086-
self.blocks.last()
1071+
fn current_block(&self) -> Option<Block> {
1072+
self.blocks.borrow().last().cloned()
10871073
}
10881074

10891075
pub fn push_value(&self, obj: PyObjectRef) {
@@ -1094,7 +1080,7 @@ impl Frame {
10941080
self.stack.borrow_mut().pop().unwrap()
10951081
}
10961082

1097-
fn pop_multiple(&mut self, count: usize) -> Vec<PyObjectRef> {
1083+
fn pop_multiple(&self, count: usize) -> Vec<PyObjectRef> {
10981084
let mut objs: Vec<PyObjectRef> = Vec::new();
10991085
for _x in 0..count {
11001086
objs.push(self.pop_value());
@@ -1123,6 +1109,7 @@ impl fmt::Debug for Frame {
11231109
.join("");
11241110
let block_str = self
11251111
.blocks
1112+
.borrow()
11261113
.iter()
11271114
.map(|elem| format!("\n > {:?}", elem))
11281115
.collect::<Vec<_>>()

vm/src/obj/objframe.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@ fn frame_flocals(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
4141

4242
fn frame_fcode(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
4343
arg_check!(vm, args, required = [(frame, Some(vm.ctx.frame_type()))]);
44-
Ok(vm.ctx.new_code_object(get_value(frame).code))
44+
Ok(vm.ctx.new_code_object(get_value(frame).code.clone()))
4545
}
4646

47-
pub fn get_value(obj: &PyObjectRef) -> Frame {
47+
pub fn get_value(obj: &PyObjectRef) -> &Frame {
4848
if let PyObjectPayload::Frame { frame } = &obj.payload {
49-
frame.clone()
49+
frame
5050
} else {
5151
panic!("Inner error getting int {:?}", obj);
5252
}

vm/src/vm.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ impl VirtualMachine {
8282

8383
pub fn run_frame(&mut self, frame: PyObjectRef) -> Result<ExecutionResult, PyObjectRef> {
8484
self.frames.push(frame.clone());
85-
let mut frame = objframe::get_value(&frame);
85+
let frame = objframe::get_value(&frame);
8686
let result = frame.run(self);
8787
self.frames.pop();
8888
result

0 commit comments

Comments
 (0)