Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions tests/snippets/sysmod.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import sys

from testutils import assert_raises

print('python executable:', sys.executable)
print(sys.argv)
assert sys.argv[0].endswith('.py')
Expand Down Expand Up @@ -49,3 +51,16 @@ def demo(x):
exc_info = sys.exc_info()
assert exc_info[0] == type(exc) == ZeroDivisionError
assert exc_info[1] == exc


# Recursion:

def recursive_call(n):
if n > 0:
recursive_call(n - 1)

sys.setrecursionlimit(200)
assert sys.getrecursionlimit() == 200

with assert_raises(RecursionError):
recursive_call(300)
1 change: 1 addition & 0 deletions vm/src/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,7 @@ pub fn make_module(vm: &VirtualMachine, module: PyObjectRef) {
"ReferenceError" => ctx.exceptions.reference_error.clone(),
"SyntaxError" => ctx.exceptions.syntax_error.clone(),
"NotImplementedError" => ctx.exceptions.not_implemented_error.clone(),
"RecursionError" => ctx.exceptions.recursion_error.clone(),
"TypeError" => ctx.exceptions.type_error.clone(),
"ValueError" => ctx.exceptions.value_error.clone(),
"IndexError" => ctx.exceptions.index_error.clone(),
Expand Down
3 changes: 3 additions & 0 deletions vm/src/exceptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ pub struct ExceptionZoo {
pub module_not_found_error: PyClassRef,
pub name_error: PyClassRef,
pub not_implemented_error: PyClassRef,
pub recursion_error: PyClassRef,
pub os_error: PyClassRef,
pub overflow_error: PyClassRef,
pub permission_error: PyClassRef,
Expand Down Expand Up @@ -280,6 +281,7 @@ impl ExceptionZoo {
let zero_division_error = create_type("ZeroDivisionError", &type_type, &arithmetic_error);
let module_not_found_error = create_type("ModuleNotFoundError", &type_type, &import_error);
let not_implemented_error = create_type("NotImplementedError", &type_type, &runtime_error);
let recursion_error = create_type("RecursionError", &type_type, &runtime_error);
let file_not_found_error = create_type("FileNotFoundError", &type_type, &os_error);
let permission_error = create_type("PermissionError", &type_type, &os_error);
let file_exists_error = create_type("FileExistsError", &type_type, &os_error);
Expand Down Expand Up @@ -321,6 +323,7 @@ impl ExceptionZoo {
module_not_found_error,
name_error,
not_implemented_error,
recursion_error,
os_error,
overflow_error,
permission_error,
Expand Down
20 changes: 20 additions & 0 deletions vm/src/sysmodule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,24 @@ fn update_use_tracing(vm: &VirtualMachine) {
vm.use_tracing.replace(tracing);
}

fn sys_getrecursionlimit(vm: &VirtualMachine) -> usize {
vm.recursion_limit.get()
}

fn sys_setrecursionlimit(recursion_limit: usize, vm: &VirtualMachine) -> PyResult {
let recursion_depth = vm.frames.borrow().len();

if recursion_limit > recursion_depth + 1 {
vm.recursion_limit.set(recursion_limit);
Ok(vm.ctx.none())
} else {
Err(vm.new_recursion_error(format!(
"cannot set the recursion limit to {} at the recursion depth {}: the limit is too low",
recursion_limit, recursion_depth
)))
}
}

// TODO implement string interning, this will be key for performance
fn sys_intern(value: PyStringRef, _vm: &VirtualMachine) -> PyStringRef {
value
Expand Down Expand Up @@ -325,6 +343,7 @@ settrace() -- set the global debug tracing function
"executable" => executable(ctx),
"flags" => flags,
"getrefcount" => ctx.new_rustfunc(sys_getrefcount),
"getrecursionlimit" => ctx.new_rustfunc(sys_getrecursionlimit),
"getsizeof" => ctx.new_rustfunc(sys_getsizeof),
"implementation" => implementation,
"getfilesystemencoding" => ctx.new_rustfunc(sys_getfilesystemencoding),
Expand All @@ -349,6 +368,7 @@ settrace() -- set the global debug tracing function
"pycache_prefix" => vm.get_none(),
"dont_write_bytecode" => vm.new_bool(vm.settings.dont_write_bytecode),
"setprofile" => ctx.new_rustfunc(sys_setprofile),
"setrecursionlimit" => ctx.new_rustfunc(sys_setrecursionlimit),
"settrace" => ctx.new_rustfunc(sys_settrace),
"version" => vm.new_str(version::get_version()),
"version_info" => version_info,
Expand Down
18 changes: 17 additions & 1 deletion vm/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! https://github.com/ProgVal/pythonvm-rust/blob/master/src/processor/mod.rs
//!

use std::cell::{Ref, RefCell};
use std::cell::{Cell, Ref, RefCell};
use std::collections::hash_map::HashMap;
use std::collections::hash_set::HashSet;
use std::fmt;
Expand Down Expand Up @@ -65,6 +65,7 @@ pub struct VirtualMachine {
pub use_tracing: RefCell<bool>,
pub signal_handlers: RefCell<[PyObjectRef; NSIG]>,
pub settings: PySettings,
pub recursion_limit: Cell<usize>,
}

pub const NSIG: usize = 64;
Expand Down Expand Up @@ -186,6 +187,7 @@ impl VirtualMachine {
use_tracing: RefCell::new(false),
signal_handlers,
settings,
recursion_limit: Cell::new(512),
};

objmodule::init_module_dict(
Expand Down Expand Up @@ -223,12 +225,21 @@ impl VirtualMachine {
}

pub fn run_frame(&self, frame: FrameRef) -> PyResult<ExecutionResult> {
self.check_recursive_call("")?;
self.frames.borrow_mut().push(frame.clone());
let result = frame.run(self);
self.frames.borrow_mut().pop();
result
}

fn check_recursive_call(&self, _where: &str) -> PyResult<()> {
if self.frames.borrow().len() > self.recursion_limit.get() {
Err(self.new_recursion_error(format!("maximum recursion depth exceeded {}", _where)))
} else {
Ok(())
}
}

pub fn frame_throw(
&self,
frame: FrameRef,
Expand Down Expand Up @@ -385,6 +396,11 @@ impl VirtualMachine {
self.new_exception(not_implemented_error, msg)
}

pub fn new_recursion_error(&self, msg: String) -> PyObjectRef {
let recursion_error = self.ctx.exceptions.recursion_error.clone();
self.new_exception(recursion_error, msg)
}

pub fn new_zero_division_error(&self, msg: String) -> PyObjectRef {
let zero_division_error = self.ctx.exceptions.zero_division_error.clone();
self.new_exception(zero_division_error, msg)
Expand Down