diff --git a/tests/snippets/bools.py b/tests/snippets/bools.py index 2aa817ca46..0c277143bc 100644 --- a/tests/snippets/bools.py +++ b/tests/snippets/bools.py @@ -16,6 +16,9 @@ assert bool(1) == True assert bool({}) == False +assert bool(NotImplemented) == True +assert bool(...) == True + if not 1: raise BaseException diff --git a/vm/src/frame.rs b/vm/src/frame.rs index c76552b76e..c7aeccf419 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -285,10 +285,14 @@ impl Frame { let mut out: Vec> = elements .into_iter() - .map(|x| match x.payload { - PyObjectPayload::Integer { ref value } => Some(value.clone()), - PyObjectPayload::None => None, - _ => panic!("Expect Int or None as BUILD_SLICE arguments, got {:?}", x), + .map(|x| { + if x.is(&vm.get_none()) { + None + } else if let PyObjectPayload::Integer { ref value } = x.payload { + Some(value.clone()) + } else { + panic!("Expect Int or None as BUILD_SLICE arguments, got {:?}", x); + } }) .collect(); @@ -574,18 +578,15 @@ impl Frame { } bytecode::Instruction::PrintExpr => { let expr = self.pop_value(); - match expr.payload { - PyObjectPayload::None => (), - _ => { - let repr = vm.to_repr(&expr)?; - builtins::builtin_print( - vm, - PyFuncArgs { - args: vec![repr], - kwargs: vec![], - }, - )?; - } + if !expr.is(&vm.get_none()) { + let repr = vm.to_repr(&expr)?; + builtins::builtin_print( + vm, + PyFuncArgs { + args: vec![repr], + kwargs: vec![], + }, + )?; } Ok(None) } diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index 84b048e3ae..32ec9f20f1 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -13,7 +13,7 @@ impl IntoPyObject for bool { } } -pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result { +pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { if let Some(s) = obj.payload::() { return Ok(!s.value.is_empty()); } @@ -24,7 +24,6 @@ pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result !value.is_zero(), PyObjectPayload::Sequence { ref elements } => !elements.borrow().is_empty(), PyObjectPayload::Dict { ref elements } => !elements.borrow().is_empty(), - PyObjectPayload::None { .. } => false, _ => { if let Ok(f) = vm.get_method(obj.clone(), "__bool__") { let bool_res = vm.invoke(f, PyFuncArgs::default())?; diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 03a307b4c3..c3aada002f 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -311,9 +311,9 @@ pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: unsafe { (*ptr).payload = PyObjectPayload::Class { name: String::from("dict"), - dict: RefCell::new(HashMap::new()), mro: vec![object_type], }; + (*ptr).dict = Some(RefCell::new(HashMap::new())); (*ptr).typ = Some(type_type.clone()); } } diff --git a/vm/src/obj/objnone.rs b/vm/src/obj/objnone.rs index 2aceea9151..bc94f9cb92 100644 --- a/vm/src/obj/objnone.rs +++ b/vm/src/obj/objnone.rs @@ -5,6 +5,7 @@ pub fn init(context: &PyContext) { let none_type = &context.none.typ(); context.set_attr(&none_type, "__new__", context.new_rustfunc(none_new)); context.set_attr(&none_type, "__repr__", context.new_rustfunc(none_repr)); + context.set_attr(&none_type, "__bool__", context.new_rustfunc(none_bool)); } fn none_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -20,3 +21,8 @@ fn none_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(_zelf, Some(vm.ctx.none().typ()))]); Ok(vm.ctx.new_str("None".to_string())) } + +fn none_bool(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(_zelf, Some(vm.ctx.none().typ()))]); + Ok(vm.ctx.new_bool(false)) +} diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 73a4df608f..7f3e70ac3b 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -21,9 +21,9 @@ pub fn create_object(type_type: PyObjectRef, object_type: PyObjectRef, _dict_typ unsafe { (*ptr).payload = PyObjectPayload::Class { name: String::from("object"), - dict: RefCell::new(HashMap::new()), mro: vec![], }; + (*ptr).dict = Some(RefCell::new(HashMap::new())); (*ptr).typ = Some(type_type.clone()); } } @@ -105,13 +105,13 @@ fn object_delattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ] ); - match zelf.payload { - PyObjectPayload::Class { ref dict, .. } | PyObjectPayload::Instance { ref dict, .. } => { + match zelf.dict { + Some(ref dict) => { let attr_name = objstr::get_value(attr); dict.borrow_mut().remove(&attr_name); Ok(vm.get_none()) } - _ => Err(vm.new_type_error("TypeError: no dictionary.".to_string())), + None => Err(vm.new_type_error("TypeError: no dictionary.".to_string())), } } @@ -178,15 +178,14 @@ fn object_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { } fn object_dict(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - match args.args[0].payload { - PyObjectPayload::Class { ref dict, .. } | PyObjectPayload::Instance { ref dict, .. } => { - let new_dict = vm.new_dict(); - for (attr, value) in dict.borrow().iter() { - new_dict.set_item(&vm.ctx, &attr, value.clone()); - } - Ok(new_dict) + if let Some(ref dict) = args.args[0].dict { + let new_dict = vm.new_dict(); + for (attr, value) in dict.borrow().iter() { + new_dict.set_item(&vm.ctx, &attr, value.clone()); } - _ => Err(vm.new_type_error("TypeError: no dictionary.".to_string())), + Ok(new_dict) + } else { + Err(vm.new_type_error("TypeError: no dictionary.".to_string())) } } diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 086f7f28f0..5eb481435c 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -19,9 +19,9 @@ pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, _dict_type: unsafe { (*ptr).payload = PyObjectPayload::Class { name: String::from("type"), - dict: RefCell::new(PyAttributes::new()), mro: vec![object_type], }; + (*ptr).dict = Some(RefCell::new(PyAttributes::new())); (*ptr).typ = Some(type_type); } } @@ -264,26 +264,26 @@ pub fn get_attributes(obj: &PyObjectRef) -> PyAttributes { let mut base_classes = objtype::base_classes(obj); base_classes.reverse(); for bc in base_classes { - if let PyObjectPayload::Class { dict, .. } = &bc.payload { + if let Some(ref dict) = &bc.dict { for (name, value) in dict.borrow().iter() { attributes.insert(name.to_string(), value.clone()); } } } - // Get instance attributes: - if let PyObjectPayload::Instance { dict } = &obj.payload { - for (name, value) in dict.borrow().iter() { - attributes.insert(name.to_string(), value.clone()); - } - } - // Get module attributes: if let PyObjectPayload::Module { ref scope, .. } = &obj.payload { for (name, value) in scope.locals.get_key_value_pairs().iter() { attributes.insert(objstr::get_value(name).to_string(), value.clone()); } + } else { + if let Some(ref dict) = &obj.dict { + for (name, value) in dict.borrow().iter() { + attributes.insert(name.to_string(), value.clone()); + } + } } + attributes } @@ -342,14 +342,15 @@ pub fn new( ) -> PyResult { let mros = bases.into_iter().map(|x| _mro(x).unwrap()).collect(); let mro = linearise_mro(mros).unwrap(); - Ok(PyObject::new( - PyObjectPayload::Class { + Ok(PyObject { + payload: PyObjectPayload::Class { name: String::from(name), - dict: RefCell::new(dict), mro, }, - typ, - )) + dict: Some(RefCell::new(dict)), + typ: Some(typ), + } + .into_ref()) } fn type_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 58976c2d21..0d29b865d5 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -154,11 +154,8 @@ pub struct PyContext { } fn _nothing() -> PyObjectRef { - PyObject { - payload: PyObjectPayload::None, - typ: None, - } - .into_ref() + let obj: PyObject = Default::default(); + obj.into_ref() } pub fn create_type( @@ -223,14 +220,14 @@ impl PyContext { let exceptions = exceptions::ExceptionZoo::new(&type_type, &object_type, &dict_type); let none = PyObject::new( - PyObjectPayload::None, + PyObjectPayload::NoPayload, create_type("NoneType", &type_type, &object_type, &dict_type), ); - let ellipsis = PyObject::new(PyObjectPayload::None, ellipsis_type.clone()); + let ellipsis = PyObject::new(PyObjectPayload::NoPayload, ellipsis_type.clone()); let not_implemented = PyObject::new( - PyObjectPayload::NotImplemented, + PyObjectPayload::NoPayload, create_type("NotImplementedType", &type_type, &object_type, &dict_type), ); @@ -658,12 +655,12 @@ impl PyContext { } else { PyAttributes::new() }; - PyObject::new( - PyObjectPayload::Instance { - dict: RefCell::new(dict), - }, - class, - ) + PyObject { + payload: PyObjectPayload::NoPayload, + typ: Some(class), + dict: Some(RefCell::new(dict)), + } + .into_ref() } // Item set/get: @@ -685,14 +682,12 @@ impl PyContext { } pub fn set_attr(&self, obj: &PyObjectRef, attr_name: &str, value: PyObjectRef) { - match obj.payload { - PyObjectPayload::Module { ref scope, .. } => { - scope.locals.set_item(self, attr_name, value) - } - PyObjectPayload::Instance { ref dict } | PyObjectPayload::Class { ref dict, .. } => { - dict.borrow_mut().insert(attr_name.to_string(), value); - } - ref payload => unimplemented!("set_attr unimplemented for: {:?}", payload), + if let PyObjectPayload::Module { ref scope, .. } = obj.payload { + scope.locals.set_item(self, attr_name, value) + } else if let Some(ref dict) = obj.dict { + dict.borrow_mut().insert(attr_name.to_string(), value); + } else { + unimplemented!("set_attr unimplemented for: {:?}", obj); }; } @@ -727,10 +722,11 @@ impl Default for PyContext { /// This is an actual python object. It consists of a `typ` which is the /// python class, and carries some rust payload optionally. This rust /// payload can be a rust float or rust int in case of float and int objects. +#[derive(Default)] pub struct PyObject { pub payload: PyObjectPayload, pub typ: Option, - // pub dict: HashMap, // __dict__ member + pub dict: Option>, // __dict__ member } pub trait IdProtocol { @@ -777,16 +773,18 @@ pub trait AttributeProtocol { } fn class_get_item(class: &PyObjectRef, attr_name: &str) -> Option { - match class.payload { - PyObjectPayload::Class { ref dict, .. } => dict.borrow().get(attr_name).cloned(), - _ => panic!("Only classes should be in MRO!"), + if let Some(ref dict) = class.dict { + dict.borrow().get(attr_name).cloned() + } else { + panic!("Only classes should be in MRO!"); } } fn class_has_item(class: &PyObjectRef, attr_name: &str) -> bool { - match class.payload { - PyObjectPayload::Class { ref dict, .. } => dict.borrow().contains_key(attr_name), - _ => panic!("Only classes should be in MRO!"), + if let Some(ref dict) = class.dict { + dict.borrow().contains_key(attr_name) + } else { + panic!("Only classes should be in MRO!"); } } @@ -805,8 +803,13 @@ impl AttributeProtocol for PyObjectRef { } None } - PyObjectPayload::Instance { ref dict } => dict.borrow().get(attr_name).cloned(), - _ => None, + _ => { + if let Some(ref dict) = self.dict { + dict.borrow().get(attr_name).cloned() + } else { + None + } + } } } @@ -816,8 +819,13 @@ impl AttributeProtocol for PyObjectRef { PyObjectPayload::Class { ref mro, .. } => { class_has_item(self, attr_name) || mro.iter().any(|d| class_has_item(d, attr_name)) } - PyObjectPayload::Instance { ref dict } => dict.borrow().contains_key(attr_name), - _ => false, + _ => { + if let Some(ref dict) = self.dict { + dict.borrow().contains_key(attr_name) + } else { + false + } + } } } } @@ -1484,11 +1492,9 @@ pub enum PyObjectPayload { name: String, scope: ScopeRef, }, - None, - NotImplemented, + NoPayload, Class { name: String, - dict: RefCell, mro: Vec, }, Set { @@ -1497,9 +1503,6 @@ pub enum PyObjectPayload { WeakRef { referent: PyObjectWeakRef, }, - Instance { - dict: RefCell, - }, RustFunction { function: PyNativeFunc, }, @@ -1508,6 +1511,12 @@ pub enum PyObjectPayload { }, } +impl Default for PyObjectPayload { + fn default() -> Self { + PyObjectPayload::NoPayload + } +} + impl fmt::Debug for PyObjectPayload { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { @@ -1531,10 +1540,8 @@ impl fmt::Debug for PyObjectPayload { ref object, } => write!(f, "bound-method: {:?} of {:?}", function, object), PyObjectPayload::Module { .. } => write!(f, "module"), - PyObjectPayload::None => write!(f, "None"), - PyObjectPayload::NotImplemented => write!(f, "NotImplemented"), + PyObjectPayload::NoPayload => write!(f, "NoPayload"), PyObjectPayload::Class { ref name, .. } => write!(f, "class {:?}", name), - PyObjectPayload::Instance { .. } => write!(f, "instance"), PyObjectPayload::RustFunction { .. } => write!(f, "rust function"), PyObjectPayload::Frame { .. } => write!(f, "frame"), PyObjectPayload::AnyRustValue { .. } => write!(f, "some rust value"), @@ -1543,14 +1550,11 @@ impl fmt::Debug for PyObjectPayload { } impl PyObject { - pub fn new( - payload: PyObjectPayload, - /* dict: PyObjectRef,*/ typ: PyObjectRef, - ) -> PyObjectRef { + pub fn new(payload: PyObjectPayload, typ: PyObjectRef) -> PyObjectRef { PyObject { payload, typ: Some(typ), - // dict: HashMap::new(), // dict, + dict: None, } .into_ref() } diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index 7403c8d8c0..0d0bc23dc5 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -11,7 +11,7 @@ use crate::obj::{ objtype, }; use crate::pyobject::{ - create_type, DictProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, + create_type, DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol, }; use crate::VirtualMachine; @@ -69,7 +69,7 @@ impl<'s> serde::Serialize for PyObjectSerializer<'s> { map.serialize_entry(&key, &self.clone_with_object(&e.1))?; } map.end() - } else if let PyObjectPayload::None = self.pyobject.payload { + } else if self.pyobject.is(&self.vm.get_none()) { serializer.serialize_none() } else { Err(serde::ser::Error::custom(format!( diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 6c6659d4db..ccce833fa9 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -330,9 +330,6 @@ impl VirtualMachine { ref function, ref object, } => self.invoke(function.clone(), args.insert(object.clone())), - PyObjectPayload::Instance { .. } => { - self.call_method_pyargs(&func_ref, "__call__", args) - } ref payload => { // TODO: is it safe to just invoke __call__ otherwise? trace!("invoke __call__ for: {:?}", payload); @@ -473,10 +470,12 @@ impl VirtualMachine { // Add missing positional arguments, if we have fewer positional arguments than the // function definition calls for if nargs < nexpected_args { - let available_defaults = match defaults.payload { - PyObjectPayload::Sequence { ref elements } => elements.borrow().clone(), - PyObjectPayload::None => vec![], - _ => panic!("function defaults not tuple or None"), + let available_defaults = if defaults.is(&self.get_none()) { + vec![] + } else if let PyObjectPayload::Sequence { ref elements } = defaults.payload { + elements.borrow().clone() + } else { + panic!("function defaults not tuple or None"); }; // Given the number of defaults available, check all the arguments for which we