diff --git a/tests/snippets/try_exceptions.py b/tests/snippets/try_exceptions.py index c865c0afa..a121241a8 100644 --- a/tests/snippets/try_exceptions.py +++ b/tests/snippets/try_exceptions.py @@ -72,3 +72,13 @@ except NameError as ex: l.append(3) print('boom', type(ex)) assert l == [1, 3] + + +l = [] +try: + l.append(1) + raise 1 +except TypeError as ex: + l.append(3) + print('boom', type(ex)) +assert l == [1, 3] diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index ab6b4bd01..6276a24f5 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -74,7 +74,7 @@ fn builtin_any(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn builtin_chr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if args.args.len() != 1 { - return Err(vm.new_exception("Expected one arguments".to_string())); + return Err(vm.new_type_error("Expected one arguments".to_string())); } let code_point_obj = args.args[0].borrow(); @@ -96,7 +96,7 @@ fn builtin_chr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn builtin_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if args.args.len() < 1 { - return Err(vm.new_exception("Expected more arguments".to_string())); + return Err(vm.new_type_error("Expected more arguments".to_string())); } // TODO: let mode = compile::Mode::Eval; @@ -104,7 +104,7 @@ fn builtin_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match compile::compile(vm, &source, mode) { Ok(value) => Ok(value), - Err(msg) => Err(vm.new_exception(msg)), + Err(msg) => Err(vm.new_type_error(msg)), } } @@ -126,11 +126,11 @@ fn builtin_dir(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let args = args.args; if args.len() > 3 { - return Err(vm.new_exception("Expected at maximum of 3 arguments".to_string())); + return Err(vm.new_type_error("Expected at maximum of 3 arguments".to_string())); } else if args.len() > 2 { // TODO: handle optional global and locals } else { - return Err(vm.new_exception("Expected at least one argument".to_string())); + return Err(vm.new_type_error("Expected at least one argument".to_string())); } let source = args[0].clone(); let _globals = args[1].clone(); @@ -166,10 +166,10 @@ fn builtin_getattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if let PyObjectKind::String { ref value } = attr.kind { vm.get_attribute(obj, value) } else { - Err(vm.new_exception("Attr can only be str for now".to_string())) + Err(vm.new_type_error("Attr can only be str for now".to_string())) } } else { - Err(vm.new_exception("Expected 2 arguments".to_string())) + Err(vm.new_type_error("Expected 2 arguments".to_string())) } } @@ -187,10 +187,10 @@ fn builtin_hasattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; Ok(vm.context().new_bool(has_attr)) } else { - Err(vm.new_exception("Attr can only be str for now".to_string())) + Err(vm.new_type_error("Attr can only be str for now".to_string())) } } else { - Err(vm.new_exception("Expected 2 arguments".to_string())) + Err(vm.new_type_error("Expected 2 arguments".to_string())) } } @@ -200,7 +200,7 @@ fn builtin_hasattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn builtin_id(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if args.args.len() != 1 { - return Err(vm.new_exception("Expected only one argument".to_string())); + return Err(vm.new_type_error("Expected only one argument".to_string())); } Ok(vm.context().new_int(args.args[0].get_id() as i32)) @@ -217,7 +217,7 @@ fn builtin_isinstance(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let obj = args.args[0].clone(); let typ = args.args[1].clone(); - let isinstance = vm.isinstance(obj, typ); + let isinstance = objtype::isinstance(obj, typ); Ok(vm.context().new_bool(isinstance)) } @@ -308,10 +308,10 @@ fn builtin_setattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { obj.set_attr(name, value); Ok(vm.get_none()) } else { - Err(vm.new_exception("Attr can only be str for now".to_string())) + Err(vm.new_type_error("Attr can only be str for now".to_string())) } } else { - Err(vm.new_exception("Expected 3 arguments".to_string())) + Err(vm.new_type_error("Expected 3 arguments".to_string())) } } diff --git a/vm/src/import.rs b/vm/src/import.rs index cafdcaa02..ff6f5fced 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -20,11 +20,13 @@ fn import_module(vm: &mut VirtualMachine, module: &String) -> PyResult { return Ok(module); } + // TODO: introduce import error: + let import_error = vm.context().exceptions.exception_type.clone(); // Time to search for module in any place: - let filepath = - find_source(vm, module).map_err(|e| vm.new_exception(format!("Error: {:?}", e)))?; + let filepath = find_source(vm, module) + .map_err(|e| vm.new_exception(import_error.clone(), format!("Error: {:?}", e)))?; let source = parser::read_file(filepath.as_path()) - .map_err(|e| vm.new_exception(format!("Error: {:?}", e)))?; + .map_err(|e| vm.new_exception(import_error.clone(), format!("Error: {:?}", e)))?; let code_obj = match compile::compile(vm, &source, compile::Mode::Exec) { Ok(bytecode) => { diff --git a/vm/src/objbool.rs b/vm/src/objbool.rs index 4e18fd61d..b0252dd7d 100644 --- a/vm/src/objbool.rs +++ b/vm/src/objbool.rs @@ -18,7 +18,7 @@ pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result match result.borrow().kind { PyObjectKind::Boolean { value } => value, - _ => return Err(vm.new_exception(String::from("TypeError"))), + _ => return Err(vm.new_type_error(String::from("TypeError"))), }, Err(err) => return Err(err), } diff --git a/vm/src/objfunction.rs b/vm/src/objfunction.rs index 589054b0a..355754ec3 100644 --- a/vm/src/objfunction.rs +++ b/vm/src/objfunction.rs @@ -16,6 +16,9 @@ fn bind_method(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn member_get(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { match args.shift().get_attr("function") { Some(function) => vm.invoke(function, args), - None => Err(vm.new_exception(String::from("Attribute Error"))), + None => { + let attribute_error = vm.context().exceptions.attribute_error.clone(); + Err(vm.new_exception(attribute_error, String::from("Attribute Error"))) + } } } diff --git a/vm/src/objlist.rs b/vm/src/objlist.rs index 0723a4218..3d8d21c8b 100644 --- a/vm/src/objlist.rs +++ b/vm/src/objlist.rs @@ -34,10 +34,10 @@ fn append(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { elements.push(o); Ok(vm.get_none()) } else { - Err(vm.new_exception("list.append is called with no list".to_string())) + Err(vm.new_type_error("list.append is called with no list".to_string())) } } else { - Err(vm.new_exception("list.append requires two arguments".to_string())) + Err(vm.new_type_error("list.append requires two arguments".to_string())) } } @@ -50,10 +50,10 @@ fn clear(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { elements.clear(); Ok(vm.get_none()) } else { - Err(vm.new_exception("list.clear is called with no list".to_string())) + Err(vm.new_type_error("list.clear is called with no list".to_string())) } } else { - Err(vm.new_exception("list.clear requires one arguments".to_string())) + Err(vm.new_type_error("list.clear requires one arguments".to_string())) } } @@ -66,10 +66,10 @@ fn len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if let PyObjectKind::List { ref elements } = list_obj.kind { Ok(vm.context().new_int(elements.len() as i32)) } else { - Err(vm.new_exception("list.len is called with no list".to_string())) + Err(vm.new_type_error("list.len is called with no list".to_string())) } } else { - Err(vm.new_exception("list.len requires one arguments".to_string())) + Err(vm.new_type_error("list.len requires one arguments".to_string())) } } @@ -82,10 +82,10 @@ fn reverse(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { elements.reverse(); Ok(vm.get_none()) } else { - Err(vm.new_exception("list.reverse is called with no list".to_string())) + Err(vm.new_type_error("list.reverse is called with no list".to_string())) } } else { - Err(vm.new_exception("list.reverse requires one arguments".to_string())) + Err(vm.new_type_error("list.reverse requires one arguments".to_string())) } } diff --git a/vm/src/objobject.rs b/vm/src/objobject.rs index 3fc94e261..56f985de9 100644 --- a/vm/src/objobject.rs +++ b/vm/src/objobject.rs @@ -39,7 +39,7 @@ fn object_dict(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match args.args[0].borrow().kind { PyObjectKind::Class { ref dict, .. } => Ok(dict.clone()), PyObjectKind::Instance { ref dict, .. } => Ok(dict.clone()), - _ => Err(vm.new_exception("TypeError: no dictionary.".to_string())), + _ => Err(vm.new_type_error("TypeError: no dictionary.".to_string())), } } diff --git a/vm/src/objsequence.rs b/vm/src/objsequence.rs index acc2abfa5..b4c87d8ac 100644 --- a/vm/src/objsequence.rs +++ b/vm/src/objsequence.rs @@ -72,7 +72,8 @@ pub fn get_item( let obj = elements[pos_index].clone(); Ok(obj) } else { - Err(vm.new_exception("Index out of bounds!".to_string())) + let value_error = vm.context().exceptions.value_error.clone(); + Err(vm.new_exception(value_error, "Index out of bounds!".to_string())) } } PyObjectKind::Slice { @@ -91,7 +92,7 @@ pub fn get_item( }, vm.get_type(), )), - _ => Err(vm.new_exception(format!( + _ => Err(vm.new_type_error(format!( "TypeError: indexing type {:?} with index {:?} is not supported (yet?)", sequence, subscript ))), diff --git a/vm/src/objtype.rs b/vm/src/objtype.rs index d919042ff..0a621c3af 100644 --- a/vm/src/objtype.rs +++ b/vm/src/objtype.rs @@ -29,7 +29,7 @@ pub fn init(context: &PyContext) { fn type_mro(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match _mro(args.args[0].clone()) { Some(mro) => Ok(vm.context().new_tuple(mro)), - None => Err(vm.new_exception("Only classes have an MRO.".to_string())), + None => Err(vm.new_type_error("Only classes have an MRO.".to_string())), } } @@ -44,6 +44,11 @@ fn _mro(cls: PyObjectRef) -> Option> { } } +pub fn isinstance(obj: PyObjectRef, cls: PyObjectRef) -> bool { + let mro = _mro(obj.typ()).unwrap(); + mro.into_iter().any(|c| c.is(&cls)) +} + pub fn type_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { debug!("type.__new__{:?}", args); if args.args.len() == 2 { @@ -56,7 +61,7 @@ pub fn type_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let dict = args.args[3].clone(); new(typ, &name, bases, dict) } else { - Err(vm.new_exception(format!("TypeError: type_new: {:?}", args))) + Err(vm.new_type_error(format!(": type_new: {:?}", args))) } } @@ -92,10 +97,11 @@ pub fn get_attribute(vm: &mut VirtualMachine, obj: PyObjectRef, name: &String) - } else if let Some(cls_attr) = cls.get_attr(name) { Ok(cls_attr) } else { - Err(vm.new_exception(format!( - "AttributeError: {:?} object has no attribute {}", - cls, name - ))) + let attribute_error = vm.context().exceptions.attribute_error.clone(); + Err(vm.new_exception( + attribute_error, + format!("{:?} object has no attribute {}", cls, name), + )) } } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 18af9b8b3..9563dc3fe 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -264,12 +264,17 @@ pub struct PyObject { pub trait IdProtocol { fn get_id(&self) -> usize; + fn is(&self, other: &PyObjectRef) -> bool; } impl IdProtocol for PyObjectRef { fn get_id(&self) -> usize { self.as_ptr() as usize } + + fn is(&self, other: &PyObjectRef) -> bool { + self.get_id() == other.get_id() + } } pub trait TypeProtocol { diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 781cb3315..22e62fd0e 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -19,7 +19,7 @@ use super::objstr; use super::objtype; use super::pyobject::{ AttributeProtocol, DictProtocol, IdProtocol, ParentProtocol, PyContext, PyFuncArgs, PyObject, - PyObjectKind, PyObjectRef, PyResult, TypeProtocol, + PyObjectKind, PyObjectRef, PyResult, }; use super::sysmodule; @@ -52,8 +52,20 @@ impl VirtualMachine { self.ctx.new_dict() } - pub fn new_exception(&self, msg: String) -> PyObjectRef { - self.new_str(msg) + pub fn new_exception(&mut self, exc_type: PyObjectRef, msg: String) -> PyObjectRef { + // TODO: maybe there is a clearer way to create an instance: + info!("New exception created: {}", msg); + let args: Vec = Vec::new(); + let args = PyFuncArgs { args: args }; + + // Call function: + let exception = self.invoke(exc_type, args).unwrap(); + exception + } + + pub fn new_type_error(&mut self, msg: String) -> PyObjectRef { + let type_error = self.context().exceptions.type_error.clone(); + self.new_exception(type_error, msg) } pub fn new_scope(&mut self) -> PyObjectRef { @@ -61,10 +73,6 @@ impl VirtualMachine { self.ctx.new_scope(Some(parent_scope)) } - pub fn isinstance(&self, obj: PyObjectRef, typ: PyObjectRef) -> bool { - self._is(obj.typ(), typ) - } - pub fn get_none(&self) -> PyObjectRef { self.ctx.none.clone() } @@ -252,7 +260,7 @@ impl VirtualMachine { PyObjectKind::List { ref elements } | PyObjectKind::Tuple { ref elements } => { super::objsequence::get_item(self, &a, elements, b) } - _ => Err(self.new_exception(format!( + _ => Err(self.new_type_error(format!( "TypeError: indexing type {:?} with index {:?} is not supported (yet?)", a, b ))), @@ -268,7 +276,7 @@ impl VirtualMachine { PyObjectKind::List { ref mut elements } => { objlist::set_item(self, elements, idx, value) } - _ => Err(self.new_exception(format!( + _ => Err(self.new_type_error(format!( "TypeError: __setitem__ assign type {:?} with index {:?} is not supported (yet?)", obj, idx ))), @@ -435,16 +443,11 @@ impl VirtualMachine { fn _is(&self, a: PyObjectRef, b: PyObjectRef) -> bool { // Pointer equal: - let id_a = self._id(a); - let id_b = self._id(b); - id_a == id_b + a.is(&b) } fn _is_not(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - // Pointer equal: - let id_a = self._id(a); - let id_b = self._id(b); - let result_bool = id_a != id_b; + let result_bool = !a.is(&b); let result = self.ctx.new_bool(result_bool); Ok(result) } @@ -800,23 +803,21 @@ impl VirtualMachine { 0 | 2 | 3 => panic!("Not implemented!"), _ => panic!("Invalid paramter for RAISE_VARARGS, must be between 0 to 3"), }; - if self.isinstance( + if objtype::isinstance( exception.clone(), self.context().exceptions.base_exception_type.clone(), ) { info!("Exception raised: {:?}", exception); Some(Err(exception)) } else { - Some(Err(exception)) - // TODO: enable this when isinstance works properly: - /* - info!( - "Can only raise BaseException derived types: {:?}", + let msg = format!( + "Can only raise BaseException derived types, not {:?}", exception ); - let type_error = self.context().exceptions.type_error.clone(); + let type_error_type = self.context().exceptions.type_error.clone(); + let type_error = self.new_exception(type_error_type, msg); Some(Err(type_error)) - */ } + } } bytecode::Instruction::Break => {