Split vm.rs into segments

This commit is contained in:
Jeong Yunwon
2022-04-17 05:01:24 +09:00
parent d7daf42756
commit c2f9498ca6
7 changed files with 1010 additions and 975 deletions

View File

@@ -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 {

View File

@@ -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::*;

View File

@@ -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.

File diff suppressed because it is too large Load Diff

295
vm/src/vm_new.rs Normal file
View 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
View 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
View 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),
}
}
}