Skip to content

data-only exc #3718

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
13 changes: 10 additions & 3 deletions stdlib/src/binascii.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub(super) use decl::crc32;
mod decl {
use crate::vm::{
builtins::{PyIntRef, PyTypeRef},
exceptions::SimpleException,
function::{ArgAsciiBuffer, ArgBytesLike, OptionalArg},
PyResult, VirtualMachine,
};
Expand Down Expand Up @@ -57,18 +58,24 @@ mod decl {

#[pyfunction(name = "a2b_hex")]
#[pyfunction]
fn unhexlify(data: ArgAsciiBuffer, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
fn unhexlify(data: ArgAsciiBuffer, vm: &VirtualMachine) -> Result<Vec<u8>, SimpleException> {
data.with_ref(|hex_bytes| {
if hex_bytes.len() % 2 != 0 {
return Err(vm.new_value_error("Odd-length string".to_owned()));
return Err(SimpleException::with_message(
vm.ctx.exceptions.value_error,
"Odd-length string",
));
}

let mut unhex = Vec::<u8>::with_capacity(hex_bytes.len() / 2);
for (n1, n2) in hex_bytes.iter().tuples() {
if let (Some(n1), Some(n2)) = (unhex_nibble(*n1), unhex_nibble(*n2)) {
unhex.push(n1 << 4 | n2);
} else {
return Err(vm.new_value_error("Non-hexadecimal digit found".to_owned()));
return Err(SimpleException::with_message(
vm.ctx.exceptions.value_error,
"Non-hexadecimal digit found",
));
}
}

Expand Down
8 changes: 5 additions & 3 deletions vm/src/builtins/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ use super::{PyDict, PyDictRef, PyList, PyStr, PyStrRef, PyType, PyTypeRef};
use crate::common::hash::PyHash;
use crate::{
class::PyClassImpl,
convert::ToPyException,
function::Either,
function::{FuncArgs, PyArithmeticValue, PyComparisonValue},
types::PyComparisonOp,
object::PyToResult,
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine,
};

Expand Down Expand Up @@ -292,13 +294,13 @@ impl PyBaseObject {

/// Return getattr(self, name).
#[pyslot]
pub(crate) fn getattro(obj: &PyObject, name: PyStrRef, vm: &VirtualMachine) -> PyResult {
pub(crate) fn getattro(obj: &PyObject, name: PyStrRef, vm: &VirtualMachine) -> PyToResult {
vm_trace!("object.__getattribute__({:?}, {:?})", obj, name);
obj.as_object().generic_getattr(name, vm)
obj.generic_getattr(name, vm).map_err(|e| -> Box<dyn ToPyException> {Box::new(e) })
}

#[pymethod(magic)]
fn getattribute(obj: PyObjectRef, name: PyStrRef, vm: &VirtualMachine) -> PyResult {
fn getattribute(obj: PyObjectRef, name: PyStrRef, vm: &VirtualMachine) -> PyToResult {
Self::getattro(&obj, name, vm)
}

Expand Down
18 changes: 17 additions & 1 deletion vm/src/convert/to_pyobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,21 @@ pub trait ToPyResult {
}

pub trait ToPyException {
fn to_pyexception(self, vm: &VirtualMachine) -> PyBaseExceptionRef;
fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef;
}

pub trait IntoPyException {
fn into_pyexception(self, vm: &VirtualMachine) -> PyBaseExceptionRef;
}

// impl ToPyException for Box<dyn ToPyException> {
// fn to_pyexception(self, vm: &VirtualMachine) -> PyBaseExceptionRef {
// self.as_ref().to_pyexception(vm)
// }
// }

impl<F> ToPyException for F where F: FnOnce(&VirtualMachine) -> PyBaseExceptionRef {
fn to_pyexception(self, vm: &VirtualMachine) -> PyBaseExceptionRef {
self(vm)
}
}
47 changes: 47 additions & 0 deletions vm/src/exceptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,53 @@ impl TryFromObject for ExceptionCtor {
}
}

pub struct SimpleException {
typ: &'static Py<PyType>,
message: Option<&'static str>,
}

impl SimpleException {
#[inline]
pub fn new(typ: &'static Py<PyType>) -> Self {
Self { typ, message: None }
}
#[inline]
pub fn with_message(typ: &'static Py<PyType>, message: &'static str) -> Self {
let message = Some(message);
Self { typ, message }
}
}

impl ToPyException for SimpleException {
#[inline]
fn to_pyexception(self, vm: &VirtualMachine) -> PyBaseExceptionRef {
let Self { typ, message } = self;
match message {
Some(message) => vm.new_exception_msg(typ.to_owned(), message.to_owned()),
None => vm.new_exception_empty(typ.to_owned()),
}
}
}

pub struct DeferredException {
typ: &'static Py<PyType>,
build_message: fn(&VirtualMachine) -> String,
}

impl DeferredException {
#[inline]
pub fn new(typ: &'static Py<PyType>, build_message: fn(&VirtualMachine) -> String) -> Self {
Self { typ, build_message }
}
}

impl ToPyException for DeferredException {
fn to_pyexception(self, vm: &VirtualMachine) -> PyBaseExceptionRef {
let Self { typ, build_message } = self;
vm.new_exception_msg(typ.to_owned(), build_message(vm))
}
}

impl ExceptionCtor {
pub fn instantiate(self, vm: &VirtualMachine) -> PyResult<PyBaseExceptionRef> {
match self {
Expand Down
1 change: 1 addition & 0 deletions vm/src/object/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Basically reference counting, but then done by rust.
/// Both the python object and the python exception are `PyObjectRef` types
/// since exceptions are also python objects.
pub type PyResult<T = PyObjectRef> = Result<T, PyBaseExceptionRef>; // A valid value, or an exception
pub type PyToResult<T = PyObjectRef> = Result<T, Box<dyn ToPyException>>;

// TODO: remove these 2 impls
impl fmt::Display for PyObjectRef {
Expand Down
1 change: 1 addition & 0 deletions vm/src/protocol/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ impl PyObject {
.mro_find_map(|cls| cls.slots.getattro.load())
.unwrap();
getattro(self, attr_name.clone(), vm).map_err(|exc| {
let exc = exc.to_pyexception(vm);
vm.set_attribute_error_context(&exc, self.to_owned(), attr_name);
exc
})
Expand Down
20 changes: 12 additions & 8 deletions vm/src/types/slot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ use crate::common::{hash::PyHash, lock::PyRwLock};
use crate::{
builtins::{PyInt, PyStrInterned, PyStrRef, PyType, PyTypeRef},
bytecode::ComparisonOperator,
convert::ToPyResult,
convert::{ToPyException, ToPyResult},
exceptions::DeferredException,
function::Either,
function::{FromArgs, FuncArgs, OptionalArg, PyComparisonValue},
identifier,
Expand All @@ -11,6 +12,7 @@ use crate::{
PySequenceMethods,
},
vm::Context,
object::PyToResult,
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
};
use crossbeam_utils::atomic::AtomicCell;
Expand Down Expand Up @@ -142,7 +144,7 @@ pub(crate) type AsMappingFunc = fn(&PyObject, &VirtualMachine) -> &'static PyMap
pub(crate) type AsNumberFunc = fn(&PyObject, &VirtualMachine) -> &'static PyNumberMethods;
pub(crate) type HashFunc = fn(&PyObject, &VirtualMachine) -> PyResult<PyHash>;
// CallFunc = GenericMethod
pub(crate) type GetattroFunc = fn(&PyObject, PyStrRef, &VirtualMachine) -> PyResult;
pub(crate) type GetattroFunc = fn(&PyObject, PyStrRef, &VirtualMachine) -> PyToResult;
pub(crate) type SetattroFunc =
fn(&PyObject, PyStrRef, Option<PyObjectRef>, &VirtualMachine) -> PyResult<()>;
pub(crate) type AsBufferFunc = fn(&PyObject, &VirtualMachine) -> PyResult<PyBuffer>;
Expand Down Expand Up @@ -226,15 +228,15 @@ fn call_wrapper(zelf: &PyObject, args: FuncArgs, vm: &VirtualMachine) -> PyResul
vm.call_special_method(zelf.to_owned(), identifier!(vm, __call__), args)
}

fn getattro_wrapper(zelf: &PyObject, name: PyStrRef, vm: &VirtualMachine) -> PyResult {
fn getattro_wrapper(zelf: &PyObject, name: PyStrRef, vm: &VirtualMachine) -> PyToResult {
let __getattribute__ = identifier!(vm, __getattribute__);
let __getattr__ = identifier!(vm, __getattr__);
match vm.call_special_method(zelf.to_owned(), __getattribute__, (name.clone(),)) {
Ok(r) => Ok(r),
Err(_) if zelf.class().has_attr(__getattr__) => {
vm.call_special_method(zelf.to_owned(), __getattr__, (name,))
vm.call_special_method(zelf.to_owned(), __getattr__, (name,)).map_err(|e| -> Box<dyn ToPyException> { Box::new(e)})
}
Err(e) => Err(e),
Err(e) => Err(Box::new(e)),
}
}

Expand Down Expand Up @@ -773,11 +775,13 @@ impl PyComparisonOp {
#[pyimpl]
pub trait GetAttr: PyPayload {
#[pyslot]
fn slot_getattro(obj: &PyObject, name: PyStrRef, vm: &VirtualMachine) -> PyResult {
fn slot_getattro(obj: &PyObject, name: PyStrRef, vm: &VirtualMachine) -> PyToResult {
if let Some(zelf) = obj.downcast_ref::<Self>() {
Self::getattro(zelf, name, vm)
Self::getattro(zelf, name, vm).map_err(|e| -> Box<dyn ToPyException> {
Box::new(e)
})
} else {
Err(vm.new_type_error("unexpected payload for __getattribute__".to_owned()))
Err(Box::new(vm.new_type_error("unexpected payload for __getattribute__".to_owned())))
}
}

Expand Down
8 changes: 8 additions & 0 deletions vm/src/vm/vm_new.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::{
PyBaseException, PyBaseExceptionRef, PyDictRef, PyModule, PyStrRef, PyType, PyTypeRef,
},
convert::ToPyObject,
exceptions::DeferredException,
scope::Scope,
vm::VirtualMachine,
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef,
Expand Down Expand Up @@ -96,6 +97,13 @@ impl VirtualMachine {
self.new_exception_msg(attribute_error, msg)
}

pub fn new_deferred_attribute_error(
&self,
msg: fn(&VirtualMachine) -> String,
) -> DeferredException {
DeferredException::new(self.ctx.exceptions.attribute_error, msg)
}

pub fn new_type_error(&self, msg: String) -> PyBaseExceptionRef {
let type_error = self.ctx.exceptions.type_error.to_owned();
self.new_exception_msg(type_error, msg)
Expand Down