Skip to content

Commit 4dd6592

Browse files
committed
Keep a stack of frames in the VM.
1 parent 6c8584c commit 4dd6592

File tree

6 files changed

+85
-51
lines changed

6 files changed

+85
-51
lines changed

tests/snippets/getframe.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ def test_function():
1010
x = 17
1111
assert sys._getframe().f_locals is not locals_dict
1212
assert sys._getframe().f_locals['x'] == 17
13+
assert sys._getframe(1).f_locals['foo'] == 'bar'
14+
print(sys._getframe(1).f_locals)
1315

1416
test_function()
1517

vm/src/frame.rs

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
extern crate rustpython_parser;
22

33
use self::rustpython_parser::ast;
4+
use std::cell::RefCell;
45
use std::fmt;
5-
use std::mem;
66
use std::path::PathBuf;
77

88
use crate::builtins;
@@ -49,10 +49,10 @@ enum BlockType {
4949
pub struct Frame {
5050
pub code: bytecode::CodeObject,
5151
// We need 1 stack per frame
52-
stack: Vec<PyObjectRef>, // The main data frame of the stack machine
53-
blocks: Vec<Block>, // Block frames, for controlling loops and exceptions
54-
pub locals: PyObjectRef, // Variables
55-
pub lasti: usize, // index of last instruction ran
52+
stack: RefCell<Vec<PyObjectRef>>, // The main data frame of the stack machine
53+
blocks: Vec<Block>, // Block frames, for controlling loops and exceptions
54+
pub locals: PyObjectRef, // Variables
55+
pub lasti: usize, // index of last instruction ran
5656
}
5757

5858
// Running a frame can result in one of the below:
@@ -79,7 +79,7 @@ impl Frame {
7979

8080
Frame {
8181
code: objcode::get_value(&code),
82-
stack: vec![],
82+
stack: RefCell::new(vec![]),
8383
blocks: vec![],
8484
// save the callargs as locals
8585
// globals: locals.clone(),
@@ -88,18 +88,9 @@ impl Frame {
8888
}
8989
}
9090

91-
pub fn run_frame_full(&mut self, vm: &mut VirtualMachine) -> PyResult {
92-
match self.run_frame(vm)? {
93-
ExecutionResult::Return(value) => Ok(value),
94-
_ => panic!("Got unexpected result from function"),
95-
}
96-
}
97-
98-
pub fn run_frame(&mut self, vm: &mut VirtualMachine) -> Result<ExecutionResult, PyObjectRef> {
91+
pub fn run(&mut self, vm: &mut VirtualMachine) -> Result<ExecutionResult, PyObjectRef> {
9992
let filename = &self.code.source_path.to_string();
10093

101-
let prev_frame = mem::replace(&mut vm.current_frame, Some(vm.ctx.new_frame(self.clone())));
102-
10394
// This is the name of the object being run:
10495
let run_obj_name = &self.code.obj_name.to_string();
10596

@@ -148,7 +139,6 @@ impl Frame {
148139
}
149140
};
150141

151-
vm.current_frame = prev_frame;
152142
value
153143
}
154144

@@ -1082,50 +1072,51 @@ impl Frame {
10821072
fn push_block(&mut self, typ: BlockType) {
10831073
self.blocks.push(Block {
10841074
typ,
1085-
level: self.stack.len(),
1075+
level: self.stack.borrow().len(),
10861076
});
10871077
}
10881078

10891079
fn pop_block(&mut self) -> Option<Block> {
10901080
let block = self.blocks.pop()?;
1091-
self.stack.truncate(block.level);
1081+
self.stack.borrow_mut().truncate(block.level);
10921082
Some(block)
10931083
}
10941084

10951085
fn current_block(&self) -> Option<&Block> {
10961086
self.blocks.last()
10971087
}
10981088

1099-
pub fn push_value(&mut self, obj: PyObjectRef) {
1100-
self.stack.push(obj);
1089+
pub fn push_value(&self, obj: PyObjectRef) {
1090+
self.stack.borrow_mut().push(obj);
11011091
}
11021092

1103-
fn pop_value(&mut self) -> PyObjectRef {
1104-
self.stack.pop().unwrap()
1093+
fn pop_value(&self) -> PyObjectRef {
1094+
self.stack.borrow_mut().pop().unwrap()
11051095
}
11061096

11071097
fn pop_multiple(&mut self, count: usize) -> Vec<PyObjectRef> {
11081098
let mut objs: Vec<PyObjectRef> = Vec::new();
11091099
for _x in 0..count {
1110-
objs.push(self.stack.pop().unwrap());
1100+
objs.push(self.pop_value());
11111101
}
11121102
objs.reverse();
11131103
objs
11141104
}
11151105

11161106
fn last_value(&self) -> PyObjectRef {
1117-
self.stack.last().unwrap().clone()
1107+
self.stack.borrow().last().unwrap().clone()
11181108
}
11191109

11201110
fn nth_value(&self, depth: usize) -> PyObjectRef {
1121-
self.stack[self.stack.len() - depth - 1].clone()
1111+
self.stack.borrow_mut()[self.stack.borrow().len() - depth - 1].clone()
11221112
}
11231113
}
11241114

11251115
impl fmt::Debug for Frame {
11261116
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
11271117
let stack_str = self
11281118
.stack
1119+
.borrow()
11291120
.iter()
11301121
.map(|elem| format!("\n > {:?}", elem))
11311122
.collect::<Vec<_>>()

vm/src/obj/objgenerator.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22
* The mythical generator.
33
*/
44

5-
use std::cell::RefCell;
6-
7-
use crate::frame::{ExecutionResult, Frame};
5+
use crate::frame::ExecutionResult;
86
use crate::pyobject::{
97
PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol,
108
};
@@ -29,11 +27,9 @@ pub fn init(context: &PyContext) {
2927
);
3028
}
3129

32-
pub fn new_generator(vm: &mut VirtualMachine, frame: Frame) -> PyResult {
30+
pub fn new_generator(vm: &mut VirtualMachine, frame: PyObjectRef) -> PyResult {
3331
Ok(PyObject::new(
34-
PyObjectPayload::Generator {
35-
frame: RefCell::new(frame),
36-
},
32+
PyObjectPayload::Generator { frame: frame },
3733
vm.ctx.generator_type.clone(),
3834
))
3935
}
@@ -60,9 +56,13 @@ fn generator_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
6056

6157
fn send(vm: &mut VirtualMachine, gen: &PyObjectRef, value: &PyObjectRef) -> PyResult {
6258
if let PyObjectPayload::Generator { ref frame } = gen.payload {
63-
let mut frame_mut = frame.borrow_mut();
64-
frame_mut.push_value(value.clone());
65-
match frame_mut.run_frame(vm)? {
59+
if let PyObjectPayload::Frame { ref frame } = frame.payload {
60+
frame.push_value(value.clone());
61+
} else {
62+
panic!("Generator frame isn't a frame.");
63+
}
64+
65+
match vm.run_frame(frame.clone())? {
6666
ExecutionResult::Yield(value) => Ok(value),
6767
ExecutionResult::Return(_value) => {
6868
// Stop iteration!

vm/src/pyobject.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -591,8 +591,13 @@ impl PyContext {
591591
)
592592
}
593593

594-
pub fn new_frame(&self, frame: Frame) -> PyObjectRef {
595-
PyObject::new(PyObjectPayload::Frame { frame }, self.frame_type())
594+
pub fn new_frame(&self, code: PyObjectRef, scope: PyObjectRef) -> PyObjectRef {
595+
PyObject::new(
596+
PyObjectPayload::Frame {
597+
frame: Frame::new(code, scope),
598+
},
599+
self.frame_type(),
600+
)
596601
}
597602

598603
pub fn new_property<F: 'static + Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult>(
@@ -1246,7 +1251,7 @@ pub enum PyObjectPayload {
12461251
defaults: PyObjectRef,
12471252
},
12481253
Generator {
1249-
frame: RefCell<Frame>,
1254+
frame: PyObjectRef,
12501255
},
12511256
BoundMethod {
12521257
function: PyObjectRef,

vm/src/sysmodule.rs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
use crate::obj::objint;
12
use crate::pyobject::{DictProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol};
23
use crate::vm::VirtualMachine;
4+
use num_traits::ToPrimitive;
35
use std::rc::Rc;
46
use std::{env, mem};
57

@@ -13,12 +15,30 @@ fn argv(ctx: &PyContext) -> PyObjectRef {
1315
ctx.new_list(argv)
1416
}
1517

16-
fn getframe(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult {
17-
if let Some(frame) = &vm.current_frame {
18-
Ok(frame.clone())
19-
} else {
20-
panic!("Current frame is undefined!")
18+
fn frame_idx(vm: &mut VirtualMachine, offset: Option<&PyObjectRef>) -> Result<usize, PyObjectRef> {
19+
if let Some(int) = offset {
20+
if let Some(offset) = objint::get_value(&int).to_usize() {
21+
if offset > vm.frames.len() - 1 {
22+
return Err(vm.new_value_error("call stack is not deep enough".to_string()));
23+
}
24+
return Ok(offset);
25+
}
2126
}
27+
return Ok(0);
28+
}
29+
30+
fn getframe(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
31+
arg_check!(
32+
vm,
33+
args,
34+
required = [],
35+
optional = [(offset, Some(vm.ctx.int_type()))]
36+
);
37+
38+
let idx = frame_idx(vm, offset)?;
39+
let idx = vm.frames.len() - idx - 1;
40+
let frame = &vm.frames[idx];
41+
Ok(frame.clone())
2242
}
2343

2444
fn sys_getrefcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {

vm/src/vm.rs

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ use std::sync::{Mutex, MutexGuard};
1212

1313
use crate::builtins;
1414
use crate::bytecode;
15-
use crate::frame::Frame;
15+
use crate::frame::ExecutionResult;
1616
use crate::obj::objbool;
1717
use crate::obj::objcode;
18+
use crate::obj::objframe;
1819
use crate::obj::objgenerator;
1920
use crate::obj::objiter;
2021
use crate::obj::objsequence;
@@ -39,7 +40,7 @@ pub struct VirtualMachine {
3940
pub sys_module: PyObjectRef,
4041
pub stdlib_inits: HashMap<String, stdlib::StdlibInitFunc>,
4142
pub ctx: PyContext,
42-
pub current_frame: Option<PyObjectRef>,
43+
pub frames: Vec<PyObjectRef>,
4344
pub wasm_id: Option<String>,
4445
}
4546

@@ -62,14 +63,29 @@ impl VirtualMachine {
6263
sys_module: sysmod,
6364
stdlib_inits,
6465
ctx,
65-
current_frame: None,
66+
frames: vec![],
6667
wasm_id: None,
6768
}
6869
}
6970

7071
pub fn run_code_obj(&mut self, code: PyObjectRef, scope: PyObjectRef) -> PyResult {
71-
let mut frame = Frame::new(code, scope);
72-
frame.run_frame_full(self)
72+
let frame = self.ctx.new_frame(code, scope);
73+
self.run_frame_full(frame)
74+
}
75+
76+
pub fn run_frame_full(&mut self, frame: PyObjectRef) -> PyResult {
77+
match self.run_frame(frame)? {
78+
ExecutionResult::Return(value) => Ok(value),
79+
_ => panic!("Got unexpected result from function"),
80+
}
81+
}
82+
83+
pub fn run_frame(&mut self, frame: PyObjectRef) -> Result<ExecutionResult, PyObjectRef> {
84+
self.frames.push(frame.clone());
85+
let mut frame = objframe::get_value(&frame);
86+
let result = frame.run(self);
87+
self.frames.pop();
88+
result
7389
}
7490

7591
/// Create a new python string object.
@@ -312,13 +328,13 @@ impl VirtualMachine {
312328
self.fill_scope_from_args(&code_object, &scope, args, defaults)?;
313329

314330
// Construct frame:
315-
let mut frame = Frame::new(code.clone(), scope);
331+
let frame = self.ctx.new_frame(code.clone(), scope);
316332

317333
// If we have a generator, create a new generator
318334
if code_object.is_generator {
319335
objgenerator::new_generator(self, frame)
320336
} else {
321-
frame.run_frame_full(self)
337+
self.run_frame_full(frame)
322338
}
323339
}
324340

0 commit comments

Comments
 (0)