mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
Split vm.rs into segments
This commit is contained in:
@@ -1703,7 +1703,7 @@ impl ExecutingFrame<'_> {
|
||||
needle: PyObjectRef,
|
||||
haystack: PyObjectRef,
|
||||
) -> PyResult<bool> {
|
||||
let found = vm._membership(haystack, needle)?;
|
||||
let found = vm._contains(haystack, needle)?;
|
||||
found.try_to_bool(vm)
|
||||
}
|
||||
|
||||
@@ -1713,8 +1713,7 @@ impl ExecutingFrame<'_> {
|
||||
needle: PyObjectRef,
|
||||
haystack: PyObjectRef,
|
||||
) -> PyResult<bool> {
|
||||
let found = vm._membership(haystack, needle)?;
|
||||
Ok(!found.try_to_bool(vm)?)
|
||||
Ok(!self._in(vm, needle, haystack)?)
|
||||
}
|
||||
|
||||
fn _is(&self, a: PyObjectRef, b: PyObjectRef) -> bool {
|
||||
|
||||
@@ -76,6 +76,9 @@ pub mod types;
|
||||
pub mod utils;
|
||||
pub mod version;
|
||||
mod vm;
|
||||
mod vm_new;
|
||||
mod vm_object;
|
||||
mod vm_ops;
|
||||
|
||||
mod pyobject {
|
||||
pub use super::_pyobject::*;
|
||||
|
||||
@@ -211,7 +211,7 @@ mod _operator {
|
||||
/// Return the outcome of the test b in a. Note the reversed operands.
|
||||
#[pyfunction]
|
||||
fn contains(a: PyObjectRef, b: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
vm._membership(a, b)
|
||||
vm._contains(a, b)
|
||||
}
|
||||
|
||||
/// Return the number of occurrences of b in a.
|
||||
|
||||
1008
vm/src/vm.rs
1008
vm/src/vm.rs
File diff suppressed because it is too large
Load Diff
295
vm/src/vm_new.rs
Normal file
295
vm/src/vm_new.rs
Normal file
@@ -0,0 +1,295 @@
|
||||
#[cfg(feature = "rustpython-compiler")]
|
||||
use crate::compile::{CompileError, CompileErrorType};
|
||||
use crate::{
|
||||
builtins::{
|
||||
code::{self, PyCode},
|
||||
pystr::IntoPyStrRef,
|
||||
tuple::{IntoPyTuple, PyTupleRef},
|
||||
PyBaseException, PyBaseExceptionRef, PyDictRef, PyModule, PyStrRef, PyTypeRef,
|
||||
},
|
||||
function::IntoPyObject,
|
||||
scope::Scope,
|
||||
vm::VirtualMachine,
|
||||
PyObject, PyObjectRef, PyObjectWrap, PyRef, TypeProtocol,
|
||||
};
|
||||
|
||||
/// Collection of object creation helpers
|
||||
impl VirtualMachine {
|
||||
/// Create a new python object
|
||||
pub fn new_pyobj(&self, value: impl IntoPyObject) -> PyObjectRef {
|
||||
value.into_pyobject(self)
|
||||
}
|
||||
|
||||
pub fn new_tuple(&self, value: impl IntoPyTuple) -> PyTupleRef {
|
||||
value.into_pytuple(self)
|
||||
}
|
||||
|
||||
pub fn new_code_object(&self, code: impl code::IntoCodeObject) -> PyRef<PyCode> {
|
||||
PyCode::new_ref(code.into_codeobj(self), &self.ctx)
|
||||
}
|
||||
|
||||
pub fn new_module(&self, name: &str, dict: PyDictRef, doc: Option<&str>) -> PyObjectRef {
|
||||
let module = PyRef::new_ref(PyModule {}, self.ctx.types.module_type.clone(), Some(dict));
|
||||
module.init_module_dict(
|
||||
self.new_pyobj(name.to_owned()),
|
||||
doc.map(|doc| self.new_pyobj(doc.to_owned()))
|
||||
.unwrap_or_else(|| self.ctx.none()),
|
||||
self,
|
||||
);
|
||||
module.into_object()
|
||||
}
|
||||
|
||||
pub fn new_scope_with_builtins(&self) -> Scope {
|
||||
Scope::with_builtins(None, self.ctx.new_dict(), self)
|
||||
}
|
||||
|
||||
/// Instantiate an exception with arguments.
|
||||
/// This function should only be used with builtin exception types; if a user-defined exception
|
||||
/// type is passed in, it may not be fully initialized; try using
|
||||
/// [`vm.invoke_exception()`][Self::invoke_exception] or
|
||||
/// [`exceptions::ExceptionCtor`][crate::exceptions::ExceptionCtor] instead.
|
||||
pub fn new_exception(&self, exc_type: PyTypeRef, args: Vec<PyObjectRef>) -> PyBaseExceptionRef {
|
||||
// TODO: add repr of args into logging?
|
||||
|
||||
PyRef::new_ref(
|
||||
// TODO: this costructor might be invalid, because multiple
|
||||
// exception (even builtin ones) are using custom constructors,
|
||||
// see `OSError` as an example:
|
||||
PyBaseException::new(args, self),
|
||||
exc_type,
|
||||
Some(self.ctx.new_dict()),
|
||||
)
|
||||
}
|
||||
|
||||
/// Instantiate an exception with no arguments.
|
||||
/// This function should only be used with builtin exception types; if a user-defined exception
|
||||
/// type is passed in, it may not be fully initialized; try using
|
||||
/// [`vm.invoke_exception()`][Self::invoke_exception] or
|
||||
/// [`exceptions::ExceptionCtor`][crate::exceptions::ExceptionCtor] instead.
|
||||
pub fn new_exception_empty(&self, exc_type: PyTypeRef) -> PyBaseExceptionRef {
|
||||
self.new_exception(exc_type, vec![])
|
||||
}
|
||||
|
||||
/// Instantiate an exception with `msg` as the only argument.
|
||||
/// This function should only be used with builtin exception types; if a user-defined exception
|
||||
/// type is passed in, it may not be fully initialized; try using
|
||||
/// [`vm.invoke_exception()`][Self::invoke_exception] or
|
||||
/// [`exceptions::ExceptionCtor`][crate::exceptions::ExceptionCtor] instead.
|
||||
pub fn new_exception_msg(&self, exc_type: PyTypeRef, msg: String) -> PyBaseExceptionRef {
|
||||
self.new_exception(exc_type, vec![self.ctx.new_str(msg).into()])
|
||||
}
|
||||
|
||||
pub fn new_lookup_error(&self, msg: String) -> PyBaseExceptionRef {
|
||||
let lookup_error = self.ctx.exceptions.lookup_error.clone();
|
||||
self.new_exception_msg(lookup_error, msg)
|
||||
}
|
||||
|
||||
pub fn new_attribute_error(&self, msg: String) -> PyBaseExceptionRef {
|
||||
let attribute_error = self.ctx.exceptions.attribute_error.clone();
|
||||
self.new_exception_msg(attribute_error, msg)
|
||||
}
|
||||
|
||||
pub fn new_type_error(&self, msg: String) -> PyBaseExceptionRef {
|
||||
let type_error = self.ctx.exceptions.type_error.clone();
|
||||
self.new_exception_msg(type_error, msg)
|
||||
}
|
||||
|
||||
pub fn new_name_error(&self, msg: String, name: &PyStrRef) -> PyBaseExceptionRef {
|
||||
let name_error_type = self.ctx.exceptions.name_error.clone();
|
||||
let name_error = self.new_exception_msg(name_error_type, msg);
|
||||
name_error
|
||||
.as_object()
|
||||
.set_attr("name", name.clone(), self)
|
||||
.unwrap();
|
||||
name_error
|
||||
}
|
||||
|
||||
pub fn new_unsupported_unary_error(&self, a: &PyObject, op: &str) -> PyBaseExceptionRef {
|
||||
self.new_type_error(format!(
|
||||
"bad operand type for {}: '{}'",
|
||||
op,
|
||||
a.class().name()
|
||||
))
|
||||
}
|
||||
|
||||
pub fn new_unsupported_binop_error(
|
||||
&self,
|
||||
a: &PyObject,
|
||||
b: &PyObject,
|
||||
op: &str,
|
||||
) -> PyBaseExceptionRef {
|
||||
self.new_type_error(format!(
|
||||
"'{}' not supported between instances of '{}' and '{}'",
|
||||
op,
|
||||
a.class().name(),
|
||||
b.class().name()
|
||||
))
|
||||
}
|
||||
|
||||
pub fn new_unsupported_ternop_error(
|
||||
&self,
|
||||
a: &PyObject,
|
||||
b: &PyObject,
|
||||
c: &PyObject,
|
||||
op: &str,
|
||||
) -> PyBaseExceptionRef {
|
||||
self.new_type_error(format!(
|
||||
"Unsupported operand types for '{}': '{}', '{}', and '{}'",
|
||||
op,
|
||||
a.class().name(),
|
||||
b.class().name(),
|
||||
c.class().name()
|
||||
))
|
||||
}
|
||||
|
||||
pub fn new_os_error(&self, msg: String) -> PyBaseExceptionRef {
|
||||
let os_error = self.ctx.exceptions.os_error.clone();
|
||||
self.new_exception_msg(os_error, msg)
|
||||
}
|
||||
|
||||
pub fn new_unicode_decode_error(&self, msg: String) -> PyBaseExceptionRef {
|
||||
let unicode_decode_error = self.ctx.exceptions.unicode_decode_error.clone();
|
||||
self.new_exception_msg(unicode_decode_error, msg)
|
||||
}
|
||||
|
||||
pub fn new_unicode_encode_error(&self, msg: String) -> PyBaseExceptionRef {
|
||||
let unicode_encode_error = self.ctx.exceptions.unicode_encode_error.clone();
|
||||
self.new_exception_msg(unicode_encode_error, msg)
|
||||
}
|
||||
|
||||
/// Create a new python ValueError object. Useful for raising errors from
|
||||
/// python functions implemented in rust.
|
||||
pub fn new_value_error(&self, msg: String) -> PyBaseExceptionRef {
|
||||
let value_error = self.ctx.exceptions.value_error.clone();
|
||||
self.new_exception_msg(value_error, msg)
|
||||
}
|
||||
|
||||
pub fn new_buffer_error(&self, msg: String) -> PyBaseExceptionRef {
|
||||
let buffer_error = self.ctx.exceptions.buffer_error.clone();
|
||||
self.new_exception_msg(buffer_error, msg)
|
||||
}
|
||||
|
||||
// TODO: don't take ownership should make the success path faster
|
||||
pub fn new_key_error(&self, obj: PyObjectRef) -> PyBaseExceptionRef {
|
||||
let key_error = self.ctx.exceptions.key_error.clone();
|
||||
self.new_exception(key_error, vec![obj])
|
||||
}
|
||||
|
||||
pub fn new_index_error(&self, msg: String) -> PyBaseExceptionRef {
|
||||
let index_error = self.ctx.exceptions.index_error.clone();
|
||||
self.new_exception_msg(index_error, msg)
|
||||
}
|
||||
|
||||
pub fn new_not_implemented_error(&self, msg: String) -> PyBaseExceptionRef {
|
||||
let not_implemented_error = self.ctx.exceptions.not_implemented_error.clone();
|
||||
self.new_exception_msg(not_implemented_error, msg)
|
||||
}
|
||||
|
||||
pub fn new_recursion_error(&self, msg: String) -> PyBaseExceptionRef {
|
||||
let recursion_error = self.ctx.exceptions.recursion_error.clone();
|
||||
self.new_exception_msg(recursion_error, msg)
|
||||
}
|
||||
|
||||
pub fn new_zero_division_error(&self, msg: String) -> PyBaseExceptionRef {
|
||||
let zero_division_error = self.ctx.exceptions.zero_division_error.clone();
|
||||
self.new_exception_msg(zero_division_error, msg)
|
||||
}
|
||||
|
||||
pub fn new_overflow_error(&self, msg: String) -> PyBaseExceptionRef {
|
||||
let overflow_error = self.ctx.exceptions.overflow_error.clone();
|
||||
self.new_exception_msg(overflow_error, msg)
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustpython-compiler")]
|
||||
pub fn new_syntax_error(&self, error: &CompileError) -> PyBaseExceptionRef {
|
||||
let syntax_error_type = match &error.error {
|
||||
CompileErrorType::Parse(p) if p.is_indentation_error() => {
|
||||
self.ctx.exceptions.indentation_error.clone()
|
||||
}
|
||||
CompileErrorType::Parse(p) if p.is_tab_error() => self.ctx.exceptions.tab_error.clone(),
|
||||
_ => self.ctx.exceptions.syntax_error.clone(),
|
||||
};
|
||||
let syntax_error = self.new_exception_msg(syntax_error_type, error.to_string());
|
||||
let lineno = self.ctx.new_int(error.location.row());
|
||||
let offset = self.ctx.new_int(error.location.column());
|
||||
syntax_error
|
||||
.as_object()
|
||||
.set_attr("lineno", lineno, self)
|
||||
.unwrap();
|
||||
syntax_error
|
||||
.as_object()
|
||||
.set_attr("offset", offset, self)
|
||||
.unwrap();
|
||||
syntax_error
|
||||
.as_object()
|
||||
.set_attr("text", error.statement.clone().into_pyobject(self), self)
|
||||
.unwrap();
|
||||
syntax_error
|
||||
.as_object()
|
||||
.set_attr(
|
||||
"filename",
|
||||
self.ctx.new_str(error.source_path.clone()),
|
||||
self,
|
||||
)
|
||||
.unwrap();
|
||||
syntax_error
|
||||
}
|
||||
|
||||
pub fn new_import_error(&self, msg: String, name: impl IntoPyStrRef) -> PyBaseExceptionRef {
|
||||
let import_error = self.ctx.exceptions.import_error.clone();
|
||||
let exc = self.new_exception_msg(import_error, msg);
|
||||
exc.as_object()
|
||||
.set_attr("name", name.into_pystr_ref(self), self)
|
||||
.unwrap();
|
||||
exc
|
||||
}
|
||||
|
||||
pub fn new_runtime_error(&self, msg: String) -> PyBaseExceptionRef {
|
||||
let runtime_error = self.ctx.exceptions.runtime_error.clone();
|
||||
self.new_exception_msg(runtime_error, msg)
|
||||
}
|
||||
|
||||
pub fn new_memory_error(&self, msg: String) -> PyBaseExceptionRef {
|
||||
let memory_error_type = self.ctx.exceptions.memory_error.clone();
|
||||
self.new_exception_msg(memory_error_type, msg)
|
||||
}
|
||||
|
||||
pub fn new_stop_iteration(&self, value: Option<PyObjectRef>) -> PyBaseExceptionRef {
|
||||
let args = if let Some(value) = value {
|
||||
vec![value]
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
self.new_exception(self.ctx.exceptions.stop_iteration.clone(), args)
|
||||
}
|
||||
|
||||
fn new_downcast_error(
|
||||
&self,
|
||||
msg: &'static str,
|
||||
error_type: &PyTypeRef,
|
||||
class: &PyTypeRef,
|
||||
obj: impl std::borrow::Borrow<PyObject>, // the impl Borrow allows to pass PyObjectRef or &PyObject
|
||||
) -> PyBaseExceptionRef {
|
||||
let actual_class = obj.borrow().class();
|
||||
let actual_type = &*actual_class.name();
|
||||
let expected_type = &*class.name();
|
||||
let msg = format!("Expected {msg} '{expected_type}' but '{actual_type}' found");
|
||||
self.new_exception_msg(error_type.clone(), msg)
|
||||
}
|
||||
|
||||
pub(crate) fn new_downcast_runtime_error(
|
||||
&self,
|
||||
class: &PyTypeRef,
|
||||
obj: impl std::borrow::Borrow<PyObject>,
|
||||
) -> PyBaseExceptionRef {
|
||||
self.new_downcast_error("payload", &self.ctx.exceptions.runtime_error, class, obj)
|
||||
}
|
||||
|
||||
pub(crate) fn new_downcast_type_error(
|
||||
&self,
|
||||
class: &PyTypeRef,
|
||||
obj: impl std::borrow::Borrow<PyObject>,
|
||||
) -> PyBaseExceptionRef {
|
||||
self.new_downcast_error("type", &self.ctx.exceptions.type_error, class, obj)
|
||||
}
|
||||
}
|
||||
232
vm/src/vm_object.rs
Normal file
232
vm/src/vm_object.rs
Normal file
@@ -0,0 +1,232 @@
|
||||
use crate::{
|
||||
builtins::{PyBaseExceptionRef, PyList, PyStr},
|
||||
function::{FuncArgs, IntoFuncArgs},
|
||||
vm::VirtualMachine,
|
||||
IdProtocol, PyMethod, PyObject, PyObjectRef, PyObjectWrap, PyResult, PyValue, TypeProtocol,
|
||||
};
|
||||
|
||||
/// Trace events for sys.settrace and sys.setprofile.
|
||||
enum TraceEvent {
|
||||
Call,
|
||||
Return,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for TraceEvent {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
use TraceEvent::*;
|
||||
match self {
|
||||
Call => write!(f, "call"),
|
||||
Return => write!(f, "return"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// PyObject support
|
||||
impl VirtualMachine {
|
||||
#[track_caller]
|
||||
#[cold]
|
||||
fn _py_panic_failed(&self, exc: PyBaseExceptionRef, msg: &str) -> ! {
|
||||
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))]
|
||||
{
|
||||
let show_backtrace = std::env::var_os("RUST_BACKTRACE").map_or(false, |v| &v != "0");
|
||||
let after = if show_backtrace {
|
||||
self.print_exception(exc);
|
||||
"exception backtrace above"
|
||||
} else {
|
||||
"run with RUST_BACKTRACE=1 to see Python backtrace"
|
||||
};
|
||||
panic!("{}; {}", msg, after)
|
||||
}
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
{
|
||||
use wasm_bindgen::prelude::*;
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
fn error(s: &str);
|
||||
}
|
||||
let mut s = String::new();
|
||||
self.write_exception(&mut s, &exc).unwrap();
|
||||
error(&s);
|
||||
panic!("{}; exception backtrace above", msg)
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn unwrap_pyresult<T>(&self, result: PyResult<T>) -> T {
|
||||
match result {
|
||||
Ok(x) => x,
|
||||
Err(exc) => {
|
||||
self._py_panic_failed(exc, "called `vm.unwrap_pyresult()` on an `Err` value")
|
||||
}
|
||||
}
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn expect_pyresult<T>(&self, result: PyResult<T>, msg: &str) -> T {
|
||||
match result {
|
||||
Ok(x) => x,
|
||||
Err(exc) => self._py_panic_failed(exc, msg),
|
||||
}
|
||||
}
|
||||
|
||||
/// Test whether a python object is `None`.
|
||||
pub fn is_none(&self, obj: &PyObject) -> bool {
|
||||
obj.is(&self.ctx.none)
|
||||
}
|
||||
pub fn option_if_none(&self, obj: PyObjectRef) -> Option<PyObjectRef> {
|
||||
if self.is_none(&obj) {
|
||||
None
|
||||
} else {
|
||||
Some(obj)
|
||||
}
|
||||
}
|
||||
pub fn unwrap_or_none(&self, obj: Option<PyObjectRef>) -> PyObjectRef {
|
||||
obj.unwrap_or_else(|| self.ctx.none())
|
||||
}
|
||||
|
||||
pub fn call_get_descriptor_specific(
|
||||
&self,
|
||||
descr: PyObjectRef,
|
||||
obj: Option<PyObjectRef>,
|
||||
cls: Option<PyObjectRef>,
|
||||
) -> Result<PyResult, PyObjectRef> {
|
||||
let descr_get = descr.class().mro_find_map(|cls| cls.slots.descr_get.load());
|
||||
match descr_get {
|
||||
Some(descr_get) => Ok(descr_get(descr, obj, cls, self)),
|
||||
None => Err(descr),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_get_descriptor(
|
||||
&self,
|
||||
descr: PyObjectRef,
|
||||
obj: PyObjectRef,
|
||||
) -> Result<PyResult, PyObjectRef> {
|
||||
let cls = obj.clone_class().into();
|
||||
self.call_get_descriptor_specific(descr, Some(obj), Some(cls))
|
||||
}
|
||||
|
||||
pub fn call_if_get_descriptor(&self, attr: PyObjectRef, obj: PyObjectRef) -> PyResult {
|
||||
self.call_get_descriptor(attr, obj).unwrap_or_else(Ok)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn call_method<T>(&self, obj: &PyObject, method_name: &str, args: T) -> PyResult
|
||||
where
|
||||
T: IntoFuncArgs,
|
||||
{
|
||||
flame_guard!(format!("call_method({:?})", method_name));
|
||||
|
||||
PyMethod::get(
|
||||
obj.to_owned(),
|
||||
PyStr::from(method_name).into_ref(self),
|
||||
self,
|
||||
)?
|
||||
.invoke(args, self)
|
||||
}
|
||||
|
||||
pub fn dir(&self, obj: Option<PyObjectRef>) -> PyResult<PyList> {
|
||||
let seq = match obj {
|
||||
Some(obj) => self
|
||||
.get_special_method(obj, "__dir__")?
|
||||
.map_err(|_obj| self.new_type_error("object does not provide __dir__".to_owned()))?
|
||||
.invoke((), self)?,
|
||||
None => self.call_method(self.current_locals()?.as_object(), "keys", ())?,
|
||||
};
|
||||
let items = self.extract_elements(&seq)?;
|
||||
let lst = PyList::from(items);
|
||||
lst.sort(Default::default(), self)?;
|
||||
Ok(lst)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn get_special_method(
|
||||
&self,
|
||||
obj: PyObjectRef,
|
||||
method: &str,
|
||||
) -> PyResult<Result<PyMethod, PyObjectRef>> {
|
||||
PyMethod::get_special(obj, method, self)
|
||||
}
|
||||
|
||||
/// NOT PUBLIC API
|
||||
#[doc(hidden)]
|
||||
pub fn call_special_method(
|
||||
&self,
|
||||
obj: PyObjectRef,
|
||||
method: &str,
|
||||
args: impl IntoFuncArgs,
|
||||
) -> PyResult {
|
||||
self.get_special_method(obj, method)?
|
||||
.map_err(|_obj| self.new_attribute_error(method.to_owned()))?
|
||||
.invoke(args, self)
|
||||
}
|
||||
|
||||
fn _invoke(&self, callable: &PyObject, args: FuncArgs) -> PyResult {
|
||||
vm_trace!("Invoke: {:?} {:?}", callable, args);
|
||||
let slot_call = callable.class().mro_find_map(|cls| cls.slots.call.load());
|
||||
match slot_call {
|
||||
Some(slot_call) => {
|
||||
self.trace_event(TraceEvent::Call)?;
|
||||
let result = slot_call(callable, args, self);
|
||||
self.trace_event(TraceEvent::Return)?;
|
||||
result
|
||||
}
|
||||
None => Err(self.new_type_error(format!(
|
||||
"'{}' object is not callable",
|
||||
callable.class().name()
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn invoke<O, A>(&self, func: &O, args: A) -> PyResult
|
||||
where
|
||||
O: AsRef<PyObject>,
|
||||
A: IntoFuncArgs,
|
||||
{
|
||||
self._invoke(func.as_ref(), args.into_args(self))
|
||||
}
|
||||
|
||||
/// Call registered trace function.
|
||||
#[inline]
|
||||
fn trace_event(&self, event: TraceEvent) -> PyResult<()> {
|
||||
if self.use_tracing.get() {
|
||||
self._trace_event_inner(event)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
fn _trace_event_inner(&self, event: TraceEvent) -> PyResult<()> {
|
||||
let trace_func = self.trace_func.borrow().to_owned();
|
||||
let profile_func = self.profile_func.borrow().to_owned();
|
||||
if self.is_none(&trace_func) && self.is_none(&profile_func) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let frame_ref = self.current_frame();
|
||||
if frame_ref.is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let frame = frame_ref.unwrap().as_object().to_owned();
|
||||
let event = self.ctx.new_str(event.to_string()).into();
|
||||
let args = vec![frame, event, self.ctx.none()];
|
||||
|
||||
// temporarily disable tracing, during the call to the
|
||||
// tracing function itself.
|
||||
if !self.is_none(&trace_func) {
|
||||
self.use_tracing.set(false);
|
||||
let res = self.invoke(&trace_func, args.clone());
|
||||
self.use_tracing.set(true);
|
||||
res?;
|
||||
}
|
||||
|
||||
if !self.is_none(&profile_func) {
|
||||
self.use_tracing.set(false);
|
||||
let res = self.invoke(&profile_func, args);
|
||||
self.use_tracing.set(true);
|
||||
res?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
440
vm/src/vm_ops.rs
Normal file
440
vm/src/vm_ops.rs
Normal file
@@ -0,0 +1,440 @@
|
||||
use crate::{
|
||||
builtins::{PyInt, PyIntRef},
|
||||
function::PyArithmeticValue,
|
||||
protocol::PyIterReturn,
|
||||
types::PyComparisonOp,
|
||||
vm::VirtualMachine,
|
||||
IdProtocol, PyMethod, PyObject, PyObjectRef, PyResult, TypeProtocol,
|
||||
};
|
||||
|
||||
/// Collection of operators
|
||||
impl VirtualMachine {
|
||||
pub fn to_index_opt(&self, obj: PyObjectRef) -> Option<PyResult<PyIntRef>> {
|
||||
match obj.downcast() {
|
||||
Ok(val) => Some(Ok(val)),
|
||||
Err(obj) => self.get_method(obj, "__index__").map(|index| {
|
||||
// TODO: returning strict subclasses of int in __index__ is deprecated
|
||||
self.invoke(&index?, ())?.downcast().map_err(|bad| {
|
||||
self.new_type_error(format!(
|
||||
"__index__ returned non-int (type {})",
|
||||
bad.class().name()
|
||||
))
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_index(&self, obj: &PyObject) -> PyResult<PyIntRef> {
|
||||
self.to_index_opt(obj.to_owned()).unwrap_or_else(|| {
|
||||
Err(self.new_type_error(format!(
|
||||
"'{}' object cannot be interpreted as an integer",
|
||||
obj.class().name()
|
||||
)))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn bool_eq(&self, a: &PyObject, b: &PyObject) -> PyResult<bool> {
|
||||
a.rich_compare_bool(b, PyComparisonOp::Eq, self)
|
||||
}
|
||||
|
||||
pub fn identical_or_equal(&self, a: &PyObject, b: &PyObject) -> PyResult<bool> {
|
||||
if a.is(b) {
|
||||
Ok(true)
|
||||
} else {
|
||||
self.bool_eq(a, b)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bool_seq_lt(&self, a: &PyObject, b: &PyObject) -> PyResult<Option<bool>> {
|
||||
let value = if a.rich_compare_bool(b, PyComparisonOp::Lt, self)? {
|
||||
Some(true)
|
||||
} else if !self.bool_eq(a, b)? {
|
||||
Some(false)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
pub fn bool_seq_gt(&self, a: &PyObject, b: &PyObject) -> PyResult<Option<bool>> {
|
||||
let value = if a.rich_compare_bool(b, PyComparisonOp::Gt, self)? {
|
||||
Some(true)
|
||||
} else if !self.bool_eq(a, b)? {
|
||||
Some(false)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
pub fn length_hint_opt(&self, iter: PyObjectRef) -> PyResult<Option<usize>> {
|
||||
match iter.length(self) {
|
||||
Ok(len) => return Ok(Some(len)),
|
||||
Err(e) => {
|
||||
if !e.isinstance(&self.ctx.exceptions.type_error) {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
let hint = match self.get_method(iter, "__length_hint__") {
|
||||
Some(hint) => hint?,
|
||||
None => return Ok(None),
|
||||
};
|
||||
let result = match self.invoke(&hint, ()) {
|
||||
Ok(res) => {
|
||||
if res.is(&self.ctx.not_implemented) {
|
||||
return Ok(None);
|
||||
}
|
||||
res
|
||||
}
|
||||
Err(e) => {
|
||||
return if e.isinstance(&self.ctx.exceptions.type_error) {
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
};
|
||||
let hint = result
|
||||
.payload_if_subclass::<PyInt>(self)
|
||||
.ok_or_else(|| {
|
||||
self.new_type_error(format!(
|
||||
"'{}' object cannot be interpreted as an integer",
|
||||
result.class().name()
|
||||
))
|
||||
})?
|
||||
.try_to_primitive::<isize>(self)?;
|
||||
if hint.is_negative() {
|
||||
Err(self.new_value_error("__length_hint__() should return >= 0".to_owned()))
|
||||
} else {
|
||||
Ok(Some(hint as usize))
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks that the multiplication is able to be performed. On Ok returns the
|
||||
/// index as a usize for sequences to be able to use immediately.
|
||||
pub fn check_repeat_or_overflow_error(&self, length: usize, n: isize) -> PyResult<usize> {
|
||||
if n <= 0 {
|
||||
Ok(0)
|
||||
} else {
|
||||
let n = n as usize;
|
||||
if length > crate::stdlib::sys::MAXSIZE as usize / n {
|
||||
Err(self.new_overflow_error("repeated value are too long".to_owned()))
|
||||
} else {
|
||||
Ok(n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls a method on `obj` passing `arg`, if the method exists.
|
||||
///
|
||||
/// Otherwise, or if the result is the special `NotImplemented` built-in constant,
|
||||
/// calls `unsupported` to determine fallback value.
|
||||
pub fn call_or_unsupported<F>(
|
||||
&self,
|
||||
obj: &PyObject,
|
||||
arg: &PyObject,
|
||||
method: &str,
|
||||
unsupported: F,
|
||||
) -> PyResult
|
||||
where
|
||||
F: Fn(&VirtualMachine, &PyObject, &PyObject) -> PyResult,
|
||||
{
|
||||
if let Some(method_or_err) = self.get_method(obj.to_owned(), method) {
|
||||
let method = method_or_err?;
|
||||
let result = self.invoke(&method, (arg.to_owned(),))?;
|
||||
if let PyArithmeticValue::Implemented(x) = PyArithmeticValue::from_object(self, result)
|
||||
{
|
||||
return Ok(x);
|
||||
}
|
||||
}
|
||||
unsupported(self, obj, arg)
|
||||
}
|
||||
|
||||
/// Calls a method, falling back to its reflection with the operands
|
||||
/// reversed, and then to the value provided by `unsupported`.
|
||||
///
|
||||
/// For example: the following:
|
||||
///
|
||||
/// `call_or_reflection(lhs, rhs, "__and__", "__rand__", unsupported)`
|
||||
///
|
||||
/// 1. Calls `__and__` with `lhs` and `rhs`.
|
||||
/// 2. If above is not implemented, calls `__rand__` with `rhs` and `lhs`.
|
||||
/// 3. If above is not implemented, invokes `unsupported` for the result.
|
||||
pub fn call_or_reflection(
|
||||
&self,
|
||||
lhs: &PyObject,
|
||||
rhs: &PyObject,
|
||||
default: &str,
|
||||
reflection: &str,
|
||||
unsupported: fn(&VirtualMachine, &PyObject, &PyObject) -> PyResult,
|
||||
) -> PyResult {
|
||||
if rhs.isinstance(&lhs.clone_class()) {
|
||||
let lop = lhs.get_class_attr(reflection);
|
||||
let rop = rhs.get_class_attr(reflection);
|
||||
if let Some((lop, rop)) = lop.zip(rop) {
|
||||
if !lop.is(&rop) {
|
||||
if let Ok(r) = self.call_or_unsupported(rhs, lhs, reflection, |vm, _, _| {
|
||||
Err(vm.new_exception_empty(vm.ctx.exceptions.exception_type.clone()))
|
||||
}) {
|
||||
return Ok(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Try to call the default method
|
||||
self.call_or_unsupported(lhs, rhs, default, move |vm, lhs, rhs| {
|
||||
// Try to call the reflection method
|
||||
// don't call reflection method if operands are of the same type
|
||||
if !lhs.class().is(&rhs.class()) {
|
||||
vm.call_or_unsupported(rhs, lhs, reflection, |_, rhs, lhs| {
|
||||
// switch them around again
|
||||
unsupported(vm, lhs, rhs)
|
||||
})
|
||||
} else {
|
||||
unsupported(vm, lhs, rhs)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _sub(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__sub__", "__rsub__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "-"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _isub(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__isub__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__sub__", "__rsub__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "-="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _add(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__add__", "__radd__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "+"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _iadd(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__iadd__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__add__", "__radd__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "+="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _mul(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__mul__", "__rmul__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "*"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _imul(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__imul__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__mul__", "__rmul__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "*="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _matmul(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__matmul__", "__rmatmul__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "@"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _imatmul(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__imatmul__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__matmul__", "__rmatmul__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "@="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _truediv(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__truediv__", "__rtruediv__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "/"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _itruediv(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__itruediv__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__truediv__", "__rtruediv__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "/="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _floordiv(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__floordiv__", "__rfloordiv__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "//"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _ifloordiv(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__ifloordiv__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__floordiv__", "__rfloordiv__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "//="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _pow(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__pow__", "__rpow__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "**"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _ipow(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__ipow__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__pow__", "__rpow__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "**="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _mod(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__mod__", "__rmod__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "%"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _imod(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__imod__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__mod__", "__rmod__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "%="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _divmod(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__divmod__", "__rdivmod__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "divmod"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _lshift(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__lshift__", "__rlshift__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "<<"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _ilshift(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__ilshift__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__lshift__", "__rlshift__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "<<="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _rshift(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__rshift__", "__rrshift__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, ">>"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _irshift(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__irshift__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__rshift__", "__rrshift__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, ">>="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _xor(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__xor__", "__rxor__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "^"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _ixor(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__ixor__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__xor__", "__rxor__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "^="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _or(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__or__", "__ror__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "|"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _ior(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__ior__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__or__", "__ror__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "|="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _and(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__and__", "__rand__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "&"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _iand(&self, a: &PyObject, b: &PyObject) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__iand__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__and__", "__rand__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_binop_error(a, b, "&="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _abs(&self, a: &PyObject) -> PyResult<PyObjectRef> {
|
||||
self.get_special_method(a.to_owned(), "__abs__")?
|
||||
.map_err(|_| self.new_unsupported_unary_error(a, "abs()"))?
|
||||
.invoke((), self)
|
||||
}
|
||||
|
||||
pub fn _pos(&self, a: &PyObject) -> PyResult {
|
||||
self.get_special_method(a.to_owned(), "__pos__")?
|
||||
.map_err(|_| self.new_unsupported_unary_error(a, "unary +"))?
|
||||
.invoke((), self)
|
||||
}
|
||||
|
||||
pub fn _neg(&self, a: &PyObject) -> PyResult {
|
||||
self.get_special_method(a.to_owned(), "__neg__")?
|
||||
.map_err(|_| self.new_unsupported_unary_error(a, "unary -"))?
|
||||
.invoke((), self)
|
||||
}
|
||||
|
||||
pub fn _invert(&self, a: &PyObject) -> PyResult {
|
||||
self.get_special_method(a.to_owned(), "__invert__")?
|
||||
.map_err(|_| self.new_unsupported_unary_error(a, "unary ~"))?
|
||||
.invoke((), self)
|
||||
}
|
||||
|
||||
// https://docs.python.org/3/reference/expressions.html#membership-test-operations
|
||||
fn _membership_iter_search(
|
||||
&self,
|
||||
haystack: PyObjectRef,
|
||||
needle: PyObjectRef,
|
||||
) -> PyResult<PyIntRef> {
|
||||
let iter = haystack.get_iter(self)?;
|
||||
loop {
|
||||
if let PyIterReturn::Return(element) = iter.next(self)? {
|
||||
if self.bool_eq(&needle, &element)? {
|
||||
return Ok(self.ctx.new_bool(true));
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
return Ok(self.ctx.new_bool(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn _contains(&self, haystack: PyObjectRef, needle: PyObjectRef) -> PyResult {
|
||||
match PyMethod::get_special(haystack, "__contains__", self)? {
|
||||
Ok(method) => method.invoke((needle,), self),
|
||||
Err(haystack) => self
|
||||
._membership_iter_search(haystack, needle)
|
||||
.map(Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user