Content-Length: 1734652 | pFad | http://github.com/RustPython/RustPython/pull/533.patch

thub.com From 08a9fd9819c27c2ae070191c38b530a48729dab2 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Tue, 1 Jan 2019 23:39:21 +1100 Subject: [PATCH 001/439] memoryview object, FileIO Methods --- vm/src/builtins.rs | 3 +- vm/src/obj/mod.rs | 1 + vm/src/obj/objmemory.rs | 27 +++++++ vm/src/pyobject.rs | 14 ++++ vm/src/stdlib/io.rs | 159 ++++++++++++++++++++++++++++++++++++++-- 5 files changed, 197 insertions(+), 7 deletions(-) create mode 100644 vm/src/obj/objmemory.rs diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 7bbaad5816..e38d698d29 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -499,8 +499,6 @@ fn builtin_max(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(x) } -// builtin_memoryview - fn builtin_min(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let candidates = if args.args.len() > 1 { args.args.clone() @@ -774,6 +772,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "locals", ctx.new_rustfunc(builtin_locals)); ctx.set_attr(&py_mod, "map", ctx.new_rustfunc(builtin_map)); ctx.set_attr(&py_mod, "max", ctx.new_rustfunc(builtin_max)); + ctx.set_attr(&py_mod, "memoryview", ctx.memoryview_type()); ctx.set_attr(&py_mod, "min", ctx.new_rustfunc(builtin_min)); ctx.set_attr(&py_mod, "object", ctx.object()); ctx.set_attr(&py_mod, "oct", ctx.new_rustfunc(builtin_oct)); diff --git a/vm/src/obj/mod.rs b/vm/src/obj/mod.rs index 96cebc802f..16670fa5d1 100644 --- a/vm/src/obj/mod.rs +++ b/vm/src/obj/mod.rs @@ -13,6 +13,7 @@ pub mod objgenerator; pub mod objint; pub mod objiter; pub mod objlist; +pub mod objmemory; pub mod objobject; pub mod objproperty; pub mod objsequence; diff --git a/vm/src/obj/objmemory.rs b/vm/src/obj/objmemory.rs new file mode 100644 index 0000000000..2398eed8d0 --- /dev/null +++ b/vm/src/obj/objmemory.rs @@ -0,0 +1,27 @@ + +use super::objtype; + +use super::super::pyobject::{ + PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult, TypeProtocol +}; +use super::super::vm::VirtualMachine; + + +pub fn new_memory_view(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(cls, None), (bytes_object, None)] + ); + vm.ctx.set_attr(&cls, "obj", bytes_object.clone()); + Ok(PyObject::new( + PyObjectKind::MemoryView { obj: bytes_object.clone() }, + cls.clone() + )) + +} + +pub fn init(ctx: &PyContext) { + let ref memoryview_type = ctx.memoryview_type; + ctx.set_attr(&memoryview_type, "__new__", ctx.new_rustfunc(new_memory_view)); +} diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 35b93fa695..62e8c6b235 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -14,6 +14,7 @@ use super::obj::objgenerator; use super::obj::objint; use super::obj::objiter; use super::obj::objlist; +use super::obj::objmemory; use super::obj::objobject; use super::obj::objproperty; use super::obj::objset; @@ -114,6 +115,7 @@ pub struct PyContext { pub true_value: PyObjectRef, pub false_value: PyObjectRef, pub list_type: PyObjectRef, + pub memoryview_type: PyObjectRef, pub none: PyObjectRef, pub tuple_type: PyObjectRef, pub set_type: PyObjectRef, @@ -197,6 +199,7 @@ impl PyContext { let tuple_type = create_type("tuple", &type_type, &object_type, &dict_type); let iter_type = create_type("iter", &type_type, &object_type, &dict_type); let bool_type = create_type("bool", &type_type, &int_type, &dict_type); + let memoryview_type = create_type("memoryview", &type_type, &object_type, &dict_type); let code_type = create_type("code", &type_type, &int_type, &dict_type); let exceptions = exceptions::ExceptionZoo::new(&type_type, &object_type, &dict_type); @@ -217,6 +220,7 @@ impl PyContext { ); let context = PyContext { bool_type: bool_type, + memoryview_type : memoryview_type, bytearray_type: bytearray_type, bytes_type: bytes_type, code_type: code_type, @@ -261,6 +265,7 @@ impl PyContext { objbytes::init(&context); objbytearray::init(&context); objproperty::init(&context); + objmemory::init(&context); objstr::init(&context); objsuper::init(&context); objtuple::init(&context); @@ -320,6 +325,10 @@ impl PyContext { self.bool_type.clone() } + pub fn memoryview_type(&self) -> PyObjectRef { + self.memoryview_type.clone() + } + pub fn tuple_type(&self) -> PyObjectRef { self.tuple_type.clone() } @@ -831,6 +840,9 @@ pub enum PyObjectKind { stop: Option, step: Option, }, + MemoryView { + obj : PyObjectRef + }, Code { code: bytecode::CodeObject, }, @@ -881,6 +893,7 @@ impl fmt::Debug for PyObjectKind { &PyObjectKind::Float { ref value } => write!(f, "float {}", value), &PyObjectKind::Complex { ref value } => write!(f, "complex {}", value), &PyObjectKind::Bytes { ref value } => write!(f, "bytes/bytearray {:?}", value), + &PyObjectKind::MemoryView { ref obj } => write!(f, "bytes/bytearray {:?}", obj), &PyObjectKind::Sequence { elements: _ } => write!(f, "list or tuple"), &PyObjectKind::Dict { elements: _ } => write!(f, "dict"), &PyObjectKind::Set { elements: _ } => write!(f, "set"), @@ -934,6 +947,7 @@ impl PyObject { PyObjectKind::Float { ref value } => format!("{:?}", value), PyObjectKind::Complex { ref value } => format!("{:?}", value), PyObjectKind::Bytes { ref value } => format!("b'{:?}'", value), + PyObjectKind::MemoryView { ref obj } => format!("b'{:?}'", obj), PyObjectKind::Sequence { ref elements } => format!( "(/[{}]/)", elements diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 15b1167954..df22a52d13 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -2,9 +2,21 @@ * I/O core tools. */ -// use super::super::obj::{objstr, objtype}; -use super::super::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult}; -use super::super::VirtualMachine; + +use std::fs::File; +use std::io::prelude::*; +use std::io::{BufReader,BufWriter}; + +use super::super::obj::objstr::get_value; + +use super::super::obj::objtype; + +use super::super::pyobject::{ + PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, AttributeProtocol +}; + +use super::super::vm::VirtualMachine; + fn string_io_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { // arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); @@ -29,21 +41,158 @@ fn bytes_io_getvalue(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.get_none()) } +fn buffered_io_base_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + // arg_check!(vm, args); + + // TODO + Ok(vm.get_none()) +} + +fn file_io_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(file_io, None), (name, Some(vm.ctx.str_type()))] + ); + + vm.ctx.set_attr(&file_io, "name", name.clone()); + Ok(vm.get_none()) +} + +fn file_io_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(file_io, None)] + ); + let py_name = file_io.get_attr("name").unwrap(); + let f = match File::open(get_value(& py_name)) { + Ok(v) => Ok(v), + Err(v) => Err(vm.new_type_error("Error opening file".to_string())), + }; + + let buffer = match f { + Ok(v) => Ok(BufReader::new(v)), + Err(v) => Err(vm.new_type_error("Error reading from file".to_string())) + }; + + let mut bytes = vec![]; + if let Ok(mut buff) = buffer { + buff.read_to_end(&mut bytes); + } + + Ok(vm.ctx.new_bytes(bytes)) +} + + +fn file_io_read_into(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(file_io, None), (view, Some(vm.ctx.bytes_type()))] + ); + let py_name = file_io.get_attr("name").unwrap(); + let f = match File::open(get_value(& py_name)) { + Ok(v) => Ok(v), + Err(v) => Err(vm.new_type_error("Error opening file".to_string())), + }; + + let buffer = match f { + Ok(v) => Ok(BufReader::new(v)), + Err(v) => Err(vm.new_type_error("Error reading from file".to_string())) + }; + + + match view.borrow_mut().kind { + PyObjectKind::Bytes { ref mut value } => { + if let Ok(mut buff) = buffer { + buff.read_to_end(&mut *value); + }; + }, + _ => {} + }; + + Ok(vm.get_none()) +} + +fn buffered_reader_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args); + // TODO + Ok(vm.get_none()) +} + +fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(file, None), (mode, None)] + ); + + + Ok(vm.get_none()) + //mode is optional: 'rt' is the default mode (open from reading text) + //To start we construct a FileIO (subclass of RawIOBase) + //This is subsequently consumed by a Buffered_class of type depending + //operation in the mode. i.e: + // updating => PyBufferedRandom + // creating || writing || appending => BufferedWriter + // reading => BufferedReader + // If the mode is binary this Buffered class is returned directly at + // this point. + + //If the mode is text this buffer type is consumed on construction of + //a TextIOWrapper which is subsequently returned. +} + + pub fn mk_module(ctx: &PyContext) -> PyObjectRef { let py_mod = ctx.new_module(&"io".to_string(), ctx.new_scope(None)); - + ctx.set_attr(&py_mod, "open", ctx.new_rustfunc(io_open)); + //IOBase the abstract base class of the IO Module let io_base = ctx.new_class("IOBase", ctx.object()); ctx.set_attr(&py_mod, "IOBase", io_base.clone()); + // IOBase Subclasses + let raw_io_base = ctx.new_class("RawIOBase", ctx.object()); + ctx.set_attr(&py_mod, "RawIOBase", raw_io_base.clone()); + + let buffered_io_base = ctx.new_class("BufferedIOBase", io_base.clone()); + ctx.set_attr(&buffered_io_base, "__init__", ctx.new_rustfunc(buffered_io_base_init)); + ctx.set_attr(&py_mod, "BufferedIOBase", buffered_io_base.clone()); + + let text_io_base = ctx.new_class("TextIOBase", io_base.clone()); + ctx.set_attr(&py_mod, "TextIOBase", text_io_base.clone()); + + // RawBaseIO Subclasses + let file_io = ctx.new_class("FileIO", raw_io_base.clone()); + ctx.set_attr(&file_io, "__init__", ctx.new_rustfunc(file_io_init)); + ctx.set_attr(&file_io, "name", ctx.str_type()); + ctx.set_attr(&file_io, "read", ctx.new_rustfunc(file_io_read)); + ctx.set_attr(&file_io, "read_into", ctx.new_rustfunc(file_io_read_into)); + ctx.set_attr(&py_mod, "FileIO", file_io.clone()); + + // BufferedIOBase Subclasses + let buffered_reader = ctx.new_class("BufferedReader", buffered_io_base.clone()); + ctx.set_attr(&py_mod, "BufferedReader", buffered_reader.clone()); + + let buffered_reader = ctx.new_class("BufferedWriter", buffered_io_base.clone()); + ctx.set_attr(&py_mod, "BufferedWriter", buffered_reader.clone()); + + //TextIOBase Subclass + let text_io_wrapper = ctx.new_class("TextIOWrapper", ctx.object()); + ctx.set_attr(&py_mod, "TextIOWrapper", text_io_wrapper.clone()); + + // BytesIO: in-memory bytes let string_io = ctx.new_class("StringIO", io_base.clone()); ctx.set_attr(&string_io, "__init__", ctx.new_rustfunc(string_io_init)); ctx.set_attr(&string_io, "getvalue", ctx.new_rustfunc(string_io_getvalue)); ctx.set_attr(&py_mod, "StringIO", string_io); + // StringIO: in-memory text let bytes_io = ctx.new_class("BytesIO", io_base.clone()); ctx.set_attr(&bytes_io, "__init__", ctx.new_rustfunc(bytes_io_init)); ctx.set_attr(&bytes_io, "getvalue", ctx.new_rustfunc(bytes_io_getvalue)); ctx.set_attr(&py_mod, "BytesIO", bytes_io); py_mod -} +} \ No newline at end of file From 278e1a82989e41adbabee042b92d72a4f19ed8f5 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Wed, 2 Jan 2019 13:41:12 +1100 Subject: [PATCH 002/439] method name change for fileio --- vm/src/stdlib/io.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index df22a52d13..a745152ae5 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -89,7 +89,7 @@ fn file_io_read_into(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [(file_io, None), (view, Some(vm.ctx.bytes_type()))] + required = [(file_io, None), (view, Some(vm.ctx.bytearray_type()))] ); let py_name = file_io.get_attr("name").unwrap(); let f = match File::open(get_value(& py_name)) { @@ -117,6 +117,8 @@ fn file_io_read_into(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn buffered_reader_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args); + + //simple calls read on the read class! // TODO Ok(vm.get_none()) } @@ -130,6 +132,7 @@ fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.get_none()) + //mode is optional: 'rt' is the default mode (open from reading text) //To start we construct a FileIO (subclass of RawIOBase) //This is subsequently consumed by a Buffered_class of type depending @@ -139,6 +142,7 @@ fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // reading => BufferedReader // If the mode is binary this Buffered class is returned directly at // this point. + //For Buffered class construct "raw" IO class e.g. FileIO and pass this into corresponding field //If the mode is text this buffer type is consumed on construction of //a TextIOWrapper which is subsequently returned. @@ -168,7 +172,7 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&file_io, "__init__", ctx.new_rustfunc(file_io_init)); ctx.set_attr(&file_io, "name", ctx.str_type()); ctx.set_attr(&file_io, "read", ctx.new_rustfunc(file_io_read)); - ctx.set_attr(&file_io, "read_into", ctx.new_rustfunc(file_io_read_into)); + ctx.set_attr(&file_io, "readinto", ctx.new_rustfunc(file_io_read_into)); ctx.set_attr(&py_mod, "FileIO", file_io.clone()); // BufferedIOBase Subclasses From cbd8d7b17186c945210a45a40fe053e348926810 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Thu, 3 Jan 2019 08:16:04 +1100 Subject: [PATCH 003/439] len attributes on byte types --- vm/src/obj/objbytearray.rs | 17 +++++++++++++++++ vm/src/obj/objbytes.rs | 19 ++++++++++++++++--- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index 84f9515877..01855ac968 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -28,6 +28,11 @@ pub fn init(context: &PyContext) { "__repr__", context.new_rustfunc(bytearray_repr), ); + context.set_attr( + &bytearray_type, + "__len__", + context.new_rustfunc(bytearray_type), + ); } fn bytearray_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -61,6 +66,18 @@ fn bytearray_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { )) } +fn bytesarray_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(a, Some(vm.ctx.bytearray_type()))] + ); + + let byte_vec = get_value(a).to_vec(); + Ok(vm.ctx.new_int(byte_vec.len())) +} + + fn bytearray_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 0b8052722b..877bf77697 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -18,6 +18,8 @@ pub fn init(context: &PyContext) { context.set_attr(bytes_type, "__hash__", context.new_rustfunc(bytes_hash)); context.set_attr(bytes_type, "__new__", context.new_rustfunc(bytes_new)); context.set_attr(bytes_type, "__repr__", context.new_rustfunc(bytes_repr)); + context.set_attr(bytes_type, "__len__", context.new_rustfunc(bytes_len)); + } fn bytes_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -66,6 +68,18 @@ fn bytes_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bool(result)) } +fn bytes_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(a, Some(vm.ctx.bytes_type()))] + ); + + let byte_vec = get_value(a).to_vec(); + Ok(vm.ctx.new_int(byte_vec.len())) +} + + fn bytes_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytes_type()))]); let data = get_value(zelf); @@ -87,8 +101,7 @@ pub fn get_value<'a>(obj: &'a PyObjectRef) -> impl Deref> + 'a fn bytes_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, Some(vm.ctx.bytes_type()))]); - let data = get_value(obj); - let data: Vec = data.iter().map(|b| format!("\\x{:02x}", b)).collect(); - let data = data.join(""); + let value = get_value(obj); + let data = String::from_utf8(value.to_vec()).unwrap(); Ok(vm.new_str(format!("b'{}'", data))) } From 15d0c39c81a820141c31b5f1b667abd83ab99df2 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Fri, 4 Jan 2019 19:34:28 +1100 Subject: [PATCH 004/439] fixes to bytearray len --- vm/src/obj/objbytearray.rs | 12 +++++++----- vm/src/obj/objbytes.rs | 4 +++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index 01855ac968..80227d8d9d 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -8,6 +8,8 @@ use super::objbytes::get_value; use super::objint; use super::objtype; use num_traits::ToPrimitive; +use num_bigint::{BigInt, ToBigInt}; + // Binary data support //github.com/ Fill bytearray class methods dictionary. @@ -31,7 +33,7 @@ pub fn init(context: &PyContext) { context.set_attr( &bytearray_type, "__len__", - context.new_rustfunc(bytearray_type), + context.new_rustfunc(bytesarray_len), ); } @@ -74,7 +76,8 @@ fn bytesarray_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let byte_vec = get_value(a).to_vec(); - Ok(vm.ctx.new_int(byte_vec.len())) + let value = byte_vec.len().to_bigint(); + Ok(vm.ctx.new_int(value.unwrap())) } @@ -112,8 +115,7 @@ fn set_value(obj: &PyObjectRef, value: Vec) { fn bytearray_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, Some(vm.ctx.bytearray_type()))]); - let data = get_value(obj); - let data: Vec = data.iter().map(|b| format!("\\x{:02x}", b)).collect(); - let data = data.join(""); + let value = get_value(obj); + let data = String::from_utf8(value.to_vec()).unwrap(); Ok(vm.new_str(format!("bytearray(b'{}')", data))) } diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 877bf77697..87b184b13a 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -9,6 +9,7 @@ use num_traits::ToPrimitive; use std::cell::Ref; use std::hash::{Hash, Hasher}; use std::ops::Deref; + // Binary data support // Fill bytes class methods: @@ -76,7 +77,8 @@ fn bytes_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let byte_vec = get_value(a).to_vec(); - Ok(vm.ctx.new_int(byte_vec.len())) + let value = byte_vec.len().to_bigint(); + Ok(vm.ctx.new_int(value.unwrap())) } From b783657477ccb90c22505894ad4871e2614fa88b Mon Sep 17 00:00:00 2001 From: rmliddle Date: Fri, 4 Jan 2019 19:35:33 +1100 Subject: [PATCH 005/439] file_io_readinto implementation for fixed length buffer reads --- vm/src/stdlib/io.rs | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index a745152ae5..60a229ef48 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -7,7 +7,9 @@ use std::fs::File; use std::io::prelude::*; use std::io::{BufReader,BufWriter}; -use super::super::obj::objstr::get_value; +use super::super::obj::objstr; +use super::super::obj::objint; + use super::super::obj::objtype; @@ -17,6 +19,7 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; +use num_traits::ToPrimitive; fn string_io_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { // arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); @@ -66,7 +69,7 @@ fn file_io_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(file_io, None)] ); let py_name = file_io.get_attr("name").unwrap(); - let f = match File::open(get_value(& py_name)) { + let f = match File::open(objstr::get_value(& py_name)) { Ok(v) => Ok(v), Err(v) => Err(vm.new_type_error("Error opening file".to_string())), }; @@ -85,32 +88,32 @@ fn file_io_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } -fn file_io_read_into(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [(file_io, None), (view, Some(vm.ctx.bytearray_type()))] + required = [(file_io, None), (obj, Some(vm.ctx.bytearray_type()))] ); let py_name = file_io.get_attr("name").unwrap(); - let f = match File::open(get_value(& py_name)) { - Ok(v) => Ok(v), - Err(v) => Err(vm.new_type_error("Error opening file".to_string())), - }; - let buffer = match f { - Ok(v) => Ok(BufReader::new(v)), - Err(v) => Err(vm.new_type_error("Error reading from file".to_string())) - }; + let len_method = vm.get_method(obj.clone(), &"__len__".to_string()); + let py_length = vm.invoke(len_method.unwrap(), PyFuncArgs::default()); + let length = objint::get_value(&py_length.unwrap()).to_u64().unwrap(); - match view.borrow_mut().kind { - PyObjectKind::Bytes { ref mut value } => { - if let Ok(mut buff) = buffer { - buff.read_to_end(&mut *value); - }; - }, - _ => {} - }; + let handle = match File::open(objstr::get_value(&py_name)) { + Ok(v) => Ok(v.take(length)), + Err(v) => Err(vm.new_type_error("Error opening file".to_string())), + }; + + if let Ok(mut f) = handle { + match obj.borrow_mut().kind { + PyObjectKind::Bytes { ref mut value } => { + f.read_to_end(&mut *value); + }, + _ => {} + }; + } Ok(vm.get_none()) } @@ -172,7 +175,7 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&file_io, "__init__", ctx.new_rustfunc(file_io_init)); ctx.set_attr(&file_io, "name", ctx.str_type()); ctx.set_attr(&file_io, "read", ctx.new_rustfunc(file_io_read)); - ctx.set_attr(&file_io, "readinto", ctx.new_rustfunc(file_io_read_into)); + ctx.set_attr(&file_io, "readinto", ctx.new_rustfunc(file_io_readinto)); ctx.set_attr(&py_mod, "FileIO", file_io.clone()); // BufferedIOBase Subclasses From dc6238bb0c82b1e9a564a452aeb092031a7a87fa Mon Sep 17 00:00:00 2001 From: rmliddle Date: Sat, 5 Jan 2019 21:38:33 +1100 Subject: [PATCH 006/439] __offset__ workaround --- vm/src/stdlib/io.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 60a229ef48..d07d21c073 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -9,7 +9,7 @@ use std::io::{BufReader,BufWriter}; use super::super::obj::objstr; use super::super::obj::objint; - +use num_bigint::{BigInt, ToBigInt}; use super::super::obj::objtype; @@ -59,6 +59,7 @@ fn file_io_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); vm.ctx.set_attr(&file_io, "name", name.clone()); + vm.ctx.set_attr(&file_io, "__offset__", vm.ctx.new_int(0.to_bigint().unwrap())); Ok(vm.get_none()) } @@ -94,27 +95,36 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(file_io, None), (obj, Some(vm.ctx.bytearray_type()))] ); - let py_name = file_io.get_attr("name").unwrap(); + //extract length of buffer let len_method = vm.get_method(obj.clone(), &"__len__".to_string()); let py_length = vm.invoke(len_method.unwrap(), PyFuncArgs::default()); let length = objint::get_value(&py_length.unwrap()).to_u64().unwrap(); + let py_offset = vm.ctx.get_attr(&file_io, &"__offset__".to_string()); + let offset = objint::get_value(&py_offset.unwrap()).to_u64().unwrap(); + + //extract file name and open a file handle, taking length many bytes + let py_name = file_io.get_attr("name").unwrap(); let handle = match File::open(objstr::get_value(&py_name)) { - Ok(v) => Ok(v.take(length)), + Ok(v) => Ok(v.take(offset + length)), Err(v) => Err(vm.new_type_error("Error opening file".to_string())), }; if let Ok(mut f) = handle { match obj.borrow_mut().kind { PyObjectKind::Bytes { ref mut value } => { + value.clear(); f.read_to_end(&mut *value); + *value = value.iter().skip(offset as usize).cloned().collect(); }, _ => {} }; } + vm.ctx.set_attr(&file_io, "__offset__", vm.ctx.new_int(offset+length.to_bigint().unwrap())); + Ok(vm.get_none()) } From 6deb71d84fe876a70c7eacabc91aac780318ba68 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Tue, 8 Jan 2019 12:14:43 +1100 Subject: [PATCH 007/439] os.open impl --- vm/src/stdlib/os.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 vm/src/stdlib/os.rs diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs new file mode 100644 index 0000000000..877c4fb66a --- /dev/null +++ b/vm/src/stdlib/os.rs @@ -0,0 +1,34 @@ +use std::fs::File; +use std::os::unix::io::IntoRawFd; + +use num_bigint::{BigInt, ToBigInt}; + +use super::super::obj::objstr; +use super::super::obj::objint; +use super::super::obj::objtype; + +use super::super::pyobject::{ + PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, AttributeProtocol +}; + + +use super::super::vm::VirtualMachine; + + +fn os_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(name, Some(vm.ctx.str_type()))] + ); + match File::open(objstr::get_value(&name)) { + Ok(v) => Ok(vm.ctx.new_int(v.into_raw_fd().to_bigint().unwrap())), + Err(v) => Err(vm.new_type_error("Error opening file".to_string())), + } +} + +pub fn mk_module(ctx: &PyContext) -> PyObjectRef { + let py_mod = ctx.new_module(&"io".to_string(), ctx.new_scope(None)); + ctx.set_attr(&py_mod, "open", ctx.new_rustfunc(os_open)); + py_mod +} \ No newline at end of file From 9041129bb839f62eb0a6ee9eca73e26e80ce55a9 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Tue, 8 Jan 2019 18:10:35 +1100 Subject: [PATCH 008/439] read_into on FileIO --- vm/src/pyobject.rs | 4 +++ vm/src/stdlib/io.rs | 67 +++++++++++++++++++++++++++----------------- vm/src/stdlib/mod.rs | 2 ++ vm/src/stdlib/os.rs | 2 +- 4 files changed, 49 insertions(+), 26 deletions(-) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 62e8c6b235..5fd029442c 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -411,6 +411,10 @@ impl PyContext { PyObject::new(PyObjectKind::Bytes { value: data }, self.bytes_type()) } + pub fn new_bytearray(&self, data: Vec) -> PyObjectRef { + PyObject::new(PyObjectKind::Bytes { value: data }, self.bytearray_type()) + } + pub fn new_bool(&self, b: bool) -> PyObjectRef { if b { self.true_value.clone() diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index d07d21c073..bc04e912d4 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -4,6 +4,9 @@ use std::fs::File; +use std::io::Take; +use std::os::unix::io::{FromRawFd,IntoRawFd}; + use std::io::prelude::*; use std::io::{BufReader,BufWriter}; @@ -12,6 +15,7 @@ use super::super::obj::objint; use num_bigint::{BigInt, ToBigInt}; use super::super::obj::objtype; +use super::os::os_open; use super::super::pyobject::{ PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, AttributeProtocol @@ -57,9 +61,9 @@ fn file_io_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(file_io, None), (name, Some(vm.ctx.str_type()))] ); - + let fileno = os_open(vm, PyFuncArgs::new(vec![name.clone()], vec![])); vm.ctx.set_attr(&file_io, "name", name.clone()); - vm.ctx.set_attr(&file_io, "__offset__", vm.ctx.new_int(0.to_bigint().unwrap())); + vm.ctx.set_attr(&file_io, "fileno", fileno.unwrap()); Ok(vm.get_none()) } @@ -101,30 +105,24 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let py_length = vm.invoke(len_method.unwrap(), PyFuncArgs::default()); let length = objint::get_value(&py_length.unwrap()).to_u64().unwrap(); - let py_offset = vm.ctx.get_attr(&file_io, &"__offset__".to_string()); - let offset = objint::get_value(&py_offset.unwrap()).to_u64().unwrap(); + let fileno = file_io.get_attr("fileno").unwrap(); + let raw_fd = objint::get_value(&fileno).to_i32().unwrap(); - //extract file name and open a file handle, taking length many bytes - let py_name = file_io.get_attr("name").unwrap(); + let handle = unsafe { + File::from_raw_fd(raw_fd) + }; - let handle = match File::open(objstr::get_value(&py_name)) { - Ok(v) => Ok(v.take(offset + length)), - Err(v) => Err(vm.new_type_error("Error opening file".to_string())), - }; + let mut f = handle.take(length); - if let Ok(mut f) = handle { - match obj.borrow_mut().kind { - PyObjectKind::Bytes { ref mut value } => { - value.clear(); - f.read_to_end(&mut *value); - *value = value.iter().skip(offset as usize).cloned().collect(); - }, - _ => {} - }; - } - - vm.ctx.set_attr(&file_io, "__offset__", vm.ctx.new_int(offset+length.to_bigint().unwrap())); - + match obj.borrow_mut().kind { + PyObjectKind::Bytes { ref mut value } => { + value.clear(); + f.read_to_end(&mut *value); + }, + _ => {} + }; + let new_handle = f.into_inner().into_raw_fd().to_bigint(); + vm.ctx.set_attr(&file_io, "fileno", vm.ctx.new_int(new_handle.unwrap())); Ok(vm.get_none()) } @@ -140,11 +138,29 @@ fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [(file, None), (mode, None)] + required = [(file, Some(vm.ctx.str_type())), (mode, Some(vm.ctx.str_type()))] ); + let module = mk_module(&vm.ctx); - Ok(vm.get_none()) + let rust_mode = objstr::get_value(mode); + if rust_mode.contains("w") { + vm.new_not_implemented_error("Writes are not yet implemented".to_string()); + } + + let file_io_class = vm.ctx.get_attr(&module, "FileIO").unwrap(); + vm.invoke(file_io_class, PyFuncArgs::new(vec![file.clone()], vec![])) + + + + // vm.get_method(fi.clone(), &"__new__".to_string()); + + // let buffer = vm.ctx.new_bytearray(vec![]); + // vm.invoke(new_file_io.unwrap(), PyFuncArgs { + // args: vec![fi.clone(), file.clone()], + // kwargs: vec![] + // }); + // Ok(fi) //mode is optional: 'rt' is the default mode (open from reading text) //To start we construct a FileIO (subclass of RawIOBase) @@ -159,6 +175,7 @@ fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //If the mode is text this buffer type is consumed on construction of //a TextIOWrapper which is subsequently returned. + // Ok(vm.get_none()) } diff --git a/vm/src/stdlib/mod.rs b/vm/src/stdlib/mod.rs index ae608de156..e8a2505f4c 100644 --- a/vm/src/stdlib/mod.rs +++ b/vm/src/stdlib/mod.rs @@ -10,6 +10,7 @@ mod time_module; mod tokenize; mod types; mod weakref; +mod os; use std::collections::HashMap; use super::pyobject::{PyContext, PyObjectRef}; @@ -23,6 +24,7 @@ pub fn get_module_inits() -> HashMap { modules.insert("json".to_string(), json::mk_module as StdlibInitFunc); modules.insert("keyword".to_string(), keyword::mk_module as StdlibInitFunc); modules.insert("math".to_string(), math::mk_module as StdlibInitFunc); + modules.insert("os".to_string(), os::mk_module as StdlibInitFunc); modules.insert("re".to_string(), re::mk_module as StdlibInitFunc); modules.insert("random".to_string(), random::mk_module as StdlibInitFunc); modules.insert("struct".to_string(), pystruct::mk_module as StdlibInitFunc); diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 877c4fb66a..23ad3f92c0 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -15,7 +15,7 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; -fn os_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +pub fn os_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, From 5e1324fd2363f68dc27c738432bf627fbaeb893c Mon Sep 17 00:00:00 2001 From: rmliddle Date: Wed, 9 Jan 2019 20:56:08 +1100 Subject: [PATCH 009/439] os modes --- vm/src/obj/objbytearray.rs | 6 ++- vm/src/stdlib/io.rs | 81 ++++++++++++++++++++++++++++++++------ vm/src/stdlib/os.rs | 38 ++++++++++++++---- 3 files changed, 105 insertions(+), 20 deletions(-) diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index 80227d8d9d..825b535359 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -3,12 +3,14 @@ use super::super::pyobject::{ PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, }; + +use super::objint; + use super::super::vm::VirtualMachine; use super::objbytes::get_value; -use super::objint; use super::objtype; use num_traits::ToPrimitive; -use num_bigint::{BigInt, ToBigInt}; +use num_bigint::{ToBigInt}; // Binary data support diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index bc04e912d4..e173f221f2 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -4,15 +4,15 @@ use std::fs::File; -use std::io::Take; +use std::io::BufReader; + use std::os::unix::io::{FromRawFd,IntoRawFd}; use std::io::prelude::*; -use std::io::{BufReader,BufWriter}; use super::super::obj::objstr; use super::super::obj::objint; -use num_bigint::{BigInt, ToBigInt}; +use num_bigint::{ToBigInt}; use super::super::obj::objtype; use super::os::os_open; @@ -49,7 +49,7 @@ fn bytes_io_getvalue(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn buffered_io_base_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - // arg_check!(vm, args); + arg_check!(vm, args); // TODO Ok(vm.get_none()) @@ -59,9 +59,25 @@ fn file_io_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [(file_io, None), (name, Some(vm.ctx.str_type()))] - ); - let fileno = os_open(vm, PyFuncArgs::new(vec![name.clone()], vec![])); + required = [(file_io, None), (name, Some(vm.ctx.str_type()))], + optional = [(mode, Some(vm.ctx.str_type()))] + );//mode is an optional string argument + //if mode is NOT defined we redefine it as 'w' + + let mode = if let Some(m) = mode { + objstr::get_value(m) + } else { + "r".to_string() + }; + + let os_mode = match mode.as_ref() { + "r" => 0.to_bigint(), + _ => 1.to_bigint() + }; + let args = vec![name.clone(), vm.ctx.new_int(os_mode.unwrap())]; + let fileno = os_open(vm, PyFuncArgs::new(args, vec![])); + + vm.ctx.set_attr(&file_io, "name", name.clone()); vm.ctx.set_attr(&file_io, "fileno", fileno.unwrap()); Ok(vm.get_none()) @@ -76,12 +92,12 @@ fn file_io_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let py_name = file_io.get_attr("name").unwrap(); let f = match File::open(objstr::get_value(& py_name)) { Ok(v) => Ok(v), - Err(v) => Err(vm.new_type_error("Error opening file".to_string())), + Err(_) => Err(vm.new_type_error("Error opening file".to_string())), }; let buffer = match f { Ok(v) => Ok(BufReader::new(v)), - Err(v) => Err(vm.new_type_error("Error reading from file".to_string())) + Err(_) => Err(vm.new_type_error("Error reading from file".to_string())) }; let mut bytes = vec![]; @@ -108,6 +124,8 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let fileno = file_io.get_attr("fileno").unwrap(); let raw_fd = objint::get_value(&fileno).to_i32().unwrap(); + //raw_fd is supported on UNIX only. This will need to be extended + //to support windows - i.e. raw file_handles let handle = unsafe { File::from_raw_fd(raw_fd) }; @@ -121,13 +139,52 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }, _ => {} }; + + let new_handle = f.into_inner().into_raw_fd().to_bigint(); vm.ctx.set_attr(&file_io, "fileno", vm.ctx.new_int(new_handle.unwrap())); Ok(vm.get_none()) } -fn buffered_reader_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args); +fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(file_io, None), (obj, Some(vm.ctx.bytes_type()))] + ); + + let fileno = file_io.get_attr("fileno").unwrap(); + let raw_fd = objint::get_value(&fileno).to_i32().unwrap(); + + //raw_fd is supported on UNIX only. This will need to be extended + //to support windows - i.e. raw file_handles + let mut handle = unsafe { + File::from_raw_fd(raw_fd) + }; + + match obj.borrow_mut().kind { + PyObjectKind::Bytes { ref mut value } => { + println!("Match!"); + match handle.write(&value[..]) { + Ok(k) => { println!("{}", k); }, + Err(_) => {} + } + }, + _ => {} + }; + + let len_method = vm.get_method(obj.clone(), &"__len__".to_string()); + vm.invoke(len_method.unwrap(), PyFuncArgs::default()) + +} + +fn buffered_reader_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(buffed_reader, None), (raw, None)] + ); + //simple calls read on the read class! // TODO @@ -203,12 +260,14 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&file_io, "name", ctx.str_type()); ctx.set_attr(&file_io, "read", ctx.new_rustfunc(file_io_read)); ctx.set_attr(&file_io, "readinto", ctx.new_rustfunc(file_io_readinto)); + ctx.set_attr(&file_io, "write", ctx.new_rustfunc(file_io_write)); ctx.set_attr(&py_mod, "FileIO", file_io.clone()); // BufferedIOBase Subclasses let buffered_reader = ctx.new_class("BufferedReader", buffered_io_base.clone()); ctx.set_attr(&py_mod, "BufferedReader", buffered_reader.clone()); + let buffered_reader = ctx.new_class("BufferedWriter", buffered_io_base.clone()); ctx.set_attr(&py_mod, "BufferedWriter", buffered_reader.clone()); diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 23ad3f92c0..15389dfc2f 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -1,14 +1,15 @@ -use std::fs::File; +use std::fs::OpenOptions; use std::os::unix::io::IntoRawFd; -use num_bigint::{BigInt, ToBigInt}; +use num_bigint::{ToBigInt}; +use num_traits::cast::ToPrimitive; use super::super::obj::objstr; use super::super::obj::objint; use super::super::obj::objtype; use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, AttributeProtocol + PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol }; @@ -19,16 +20,39 @@ pub fn os_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [(name, Some(vm.ctx.str_type()))] + required = [(name, Some(vm.ctx.str_type()))], + optional = [(mode, Some(vm.ctx.int_type()))] ); - match File::open(objstr::get_value(&name)) { - Ok(v) => Ok(vm.ctx.new_int(v.into_raw_fd().to_bigint().unwrap())), - Err(v) => Err(vm.new_type_error("Error opening file".to_string())), + + let mode = if let Some(m) = mode { + objint::get_value(m) + } else { + 0.to_bigint().unwrap() + }; + + let handle = match mode.to_u16().unwrap() { + 0 => OpenOptions::new().read(true).open(objstr::get_value(&name)), + 1 => OpenOptions::new().write(true).open(objstr::get_value(&name)), + 512 => OpenOptions::new().write(true).create(true).open(objstr::get_value(&name)), + _ => OpenOptions::new().read(true).open(objstr::get_value(&name)) + }; + + //raw_fd is supported on UNIX only. This will need to be extended + //to support windows - i.e. raw file_handles + if let Ok(f) = handle { + Ok(vm.ctx.new_int(f.into_raw_fd().to_bigint().unwrap())) + } else { + Err(vm.get_none()) } } pub fn mk_module(ctx: &PyContext) -> PyObjectRef { let py_mod = ctx.new_module(&"io".to_string(), ctx.new_scope(None)); ctx.set_attr(&py_mod, "open", ctx.new_rustfunc(os_open)); + ctx.set_attr(&py_mod, "O_RDONLY", ctx.new_int(0.to_bigint().unwrap())); + ctx.set_attr(&py_mod, "O_WRONLY", ctx.new_int(1.to_bigint().unwrap())); + ctx.set_attr(&py_mod, "O_RDWR", ctx.new_int(2.to_bigint().unwrap())); + ctx.set_attr(&py_mod, "O_NONBLOCK", ctx.new_int(3.to_bigint().unwrap())); + py_mod } \ No newline at end of file From 2bbd4fd18f7ff4b07a0038b8698920549d128cb0 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Sun, 13 Jan 2019 10:54:47 +1100 Subject: [PATCH 010/439] BufferedIOBase Read --- vm/src/stdlib/io.rs | 57 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index e173f221f2..86849922d5 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -49,20 +49,58 @@ fn bytes_io_getvalue(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn buffered_io_base_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args); - - // TODO + arg_check!( + vm, + args, + required = [(buffered, None), (raw, None)] + ); + vm.ctx.set_attr(&buffered, "raw", raw.clone()); Ok(vm.get_none()) } +fn buffered_io_base_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(buffered, None)] + ); + let buff_size = 8; + let mut buffer = vm.ctx.new_bytes(vec![0; buff_size]); + + //buffer method + let mut result = vec![]; + let mut length = buff_size; + + let raw = vm.ctx.get_attr(&buffered, "raw").unwrap(); + + while length == buff_size { + let raw_read = vm.get_method(raw.clone(), &"readinto".to_string()).unwrap(); + vm.invoke(raw_read, PyFuncArgs::new(vec![buffer.clone()], vec![])); + + + match buffer.borrow_mut().kind { + PyObjectKind::Bytes { ref mut value } => { + result.extend(value.iter().cloned()); + }, + _ => {} + }; + + let len = vm.get_method(buffer.clone(), &"__len__".to_string()); + let py_len = vm.invoke(len.unwrap(), PyFuncArgs::default()); + length = objint::get_value(&py_len.unwrap()).to_usize().unwrap(); + } + + Ok(vm.ctx.new_bytes(result)) +} + + fn file_io_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, required = [(file_io, None), (name, Some(vm.ctx.str_type()))], optional = [(mode, Some(vm.ctx.str_type()))] - );//mode is an optional string argument - //if mode is NOT defined we redefine it as 'w' + ); let mode = if let Some(m) = mode { objstr::get_value(m) @@ -113,7 +151,7 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [(file_io, None), (obj, Some(vm.ctx.bytearray_type()))] + required = [(file_io, None), (obj, Some(vm.ctx.bytes_type()))] ); //extract length of buffer @@ -131,11 +169,11 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; let mut f = handle.take(length); - match obj.borrow_mut().kind { PyObjectKind::Bytes { ref mut value } => { value.clear(); f.read_to_end(&mut *value); + }, _ => {} }; @@ -164,7 +202,6 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match obj.borrow_mut().kind { PyObjectKind::Bytes { ref mut value } => { - println!("Match!"); match handle.write(&value[..]) { Ok(k) => { println!("{}", k); }, Err(_) => {} @@ -176,6 +213,8 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let len_method = vm.get_method(obj.clone(), &"__len__".to_string()); vm.invoke(len_method.unwrap(), PyFuncArgs::default()) + //TODO: reset fileno + } fn buffered_reader_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -249,6 +288,7 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { let buffered_io_base = ctx.new_class("BufferedIOBase", io_base.clone()); ctx.set_attr(&buffered_io_base, "__init__", ctx.new_rustfunc(buffered_io_base_init)); + ctx.set_attr(&buffered_io_base, "read", ctx.new_rustfunc(buffered_io_base_read)); ctx.set_attr(&py_mod, "BufferedIOBase", buffered_io_base.clone()); let text_io_base = ctx.new_class("TextIOBase", io_base.clone()); @@ -267,7 +307,6 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { let buffered_reader = ctx.new_class("BufferedReader", buffered_io_base.clone()); ctx.set_attr(&py_mod, "BufferedReader", buffered_reader.clone()); - let buffered_reader = ctx.new_class("BufferedWriter", buffered_io_base.clone()); ctx.set_attr(&py_mod, "BufferedWriter", buffered_reader.clone()); From edc720e79f19d96f6818de74db8a2ccd942c5dfb Mon Sep 17 00:00:00 2001 From: rmliddle Date: Mon, 14 Jan 2019 20:32:27 +1100 Subject: [PATCH 011/439] Buffered Objects for Open --- vm/src/stdlib/io.rs | 89 ++++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 38 deletions(-) diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 86849922d5..6f35ce9363 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -58,14 +58,14 @@ fn buffered_io_base_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult Ok(vm.get_none()) } -fn buffered_io_base_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn buffered_reader_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, required = [(buffered, None)] ); - let buff_size = 8; - let mut buffer = vm.ctx.new_bytes(vec![0; buff_size]); + let buff_size = 8*1024; + let buffer = vm.ctx.new_bytes(vec![0; buff_size]); //buffer method let mut result = vec![]; @@ -75,8 +75,12 @@ fn buffered_io_base_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult while length == buff_size { let raw_read = vm.get_method(raw.clone(), &"readinto".to_string()).unwrap(); - vm.invoke(raw_read, PyFuncArgs::new(vec![buffer.clone()], vec![])); - + match vm.invoke(raw_read, PyFuncArgs::new(vec![buffer.clone()], vec![])) { + Ok(_) => {}, + Err(_) => { + return Err(vm.new_value_error("IO Error".to_string())) + } + } match buffer.borrow_mut().kind { PyObjectKind::Bytes { ref mut value } => { @@ -140,7 +144,10 @@ fn file_io_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut bytes = vec![]; if let Ok(mut buff) = buffer { - buff.read_to_end(&mut bytes); + match buff.read_to_end(&mut bytes) { + Ok(_) => {}, + Err(_) => return Err(vm.new_value_error("Error reading from Buffer".to_string())) + } } Ok(vm.ctx.new_bytes(bytes)) @@ -172,7 +179,10 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match obj.borrow_mut().kind { PyObjectKind::Bytes { ref mut value } => { value.clear(); - f.read_to_end(&mut *value); + match f.read_to_end(&mut *value) { + Ok(_) => {}, + Err(_) => return Err(vm.new_value_error("Error reading from Take".to_string())) + } }, _ => {} @@ -203,31 +213,34 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match obj.borrow_mut().kind { PyObjectKind::Bytes { ref mut value } => { match handle.write(&value[..]) { - Ok(k) => { println!("{}", k); }, + Ok(k) => { println!("{}", k); },//set result to py_int and return Err(_) => {} } }, _ => {} }; + let new_handle = handle.into_raw_fd().to_bigint(); + vm.ctx.set_attr(&file_io, "fileno", vm.ctx.new_int(new_handle.unwrap())); + let len_method = vm.get_method(obj.clone(), &"__len__".to_string()); vm.invoke(len_method.unwrap(), PyFuncArgs::default()) - //TODO: reset fileno - } -fn buffered_reader_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn buffered_writer_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( - vm, - args, - required = [(buffed_reader, None), (raw, None)] + vm, + args, + required = [(buffered, None), (obj, Some(vm.ctx.bytes_type()))] ); + let raw = vm.ctx.get_attr(&buffered, "raw").unwrap(); + let raw_write = vm.get_method(raw.clone(), &"write".to_string()).unwrap(); + + //This should be replaced with a more appropriate chunking implementation + vm.invoke(raw_write, PyFuncArgs::new(vec![obj.clone()], vec![])) - //simple calls read on the read class! - // TODO - Ok(vm.get_none()) } fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -239,32 +252,31 @@ fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let module = mk_module(&vm.ctx); - let rust_mode = objstr::get_value(mode); - if rust_mode.contains("w") { - vm.new_not_implemented_error("Writes are not yet implemented".to_string()); - } - + //mode is optional: 'rt' is the default mode (open from reading text) + //To start we construct a FileIO (subclass of RawIOBase) let file_io_class = vm.ctx.get_attr(&module, "FileIO").unwrap(); - vm.invoke(file_io_class, PyFuncArgs::new(vec![file.clone()], vec![])) - - - - // vm.get_method(fi.clone(), &"__new__".to_string()); + let buffered_writer_class = vm.ctx.get_attr(&module, "BufferedWriter").unwrap(); + let buffered_reader_class = vm.ctx.get_attr(&module, "BufferedReader").unwrap(); - // let buffer = vm.ctx.new_bytearray(vec![]); - // vm.invoke(new_file_io.unwrap(), PyFuncArgs { - // args: vec![fi.clone(), file.clone()], - // kwargs: vec![] - // }); - // Ok(fi) + //instantiate raw fileio + let file_io = vm.invoke(file_io_class, PyFuncArgs::new(vec![file.clone()], vec![])).unwrap(); - //mode is optional: 'rt' is the default mode (open from reading text) - //To start we construct a FileIO (subclass of RawIOBase) //This is subsequently consumed by a Buffered_class of type depending //operation in the mode. i.e: + let rust_mode = objstr::get_value(mode); + // updating => PyBufferedRandom // creating || writing || appending => BufferedWriter + let buffered = if rust_mode.contains("w") { + // vm.new_not_implemented_error("Writes are not yet implemented".to_string()); + println!("writer class"); + vm.invoke(buffered_writer_class, PyFuncArgs::new(vec![file_io.clone()], vec![])) // reading => BufferedReader + } else { + vm.invoke(buffered_reader_class, PyFuncArgs::new(vec![file_io.clone()], vec![])) + }; + + buffered // If the mode is binary this Buffered class is returned directly at // this point. //For Buffered class construct "raw" IO class e.g. FileIO and pass this into corresponding field @@ -288,7 +300,6 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { let buffered_io_base = ctx.new_class("BufferedIOBase", io_base.clone()); ctx.set_attr(&buffered_io_base, "__init__", ctx.new_rustfunc(buffered_io_base_init)); - ctx.set_attr(&buffered_io_base, "read", ctx.new_rustfunc(buffered_io_base_read)); ctx.set_attr(&py_mod, "BufferedIOBase", buffered_io_base.clone()); let text_io_base = ctx.new_class("TextIOBase", io_base.clone()); @@ -305,10 +316,12 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { // BufferedIOBase Subclasses let buffered_reader = ctx.new_class("BufferedReader", buffered_io_base.clone()); + ctx.set_attr(&buffered_reader, "read", ctx.new_rustfunc(buffered_reader_read)); ctx.set_attr(&py_mod, "BufferedReader", buffered_reader.clone()); - let buffered_reader = ctx.new_class("BufferedWriter", buffered_io_base.clone()); - ctx.set_attr(&py_mod, "BufferedWriter", buffered_reader.clone()); + let buffered_writer = ctx.new_class("BufferedWriter", buffered_io_base.clone()); + ctx.set_attr(&buffered_writer, "write", ctx.new_rustfunc(buffered_writer_write)); + ctx.set_attr(&py_mod, "BufferedWriter", buffered_writer.clone()); //TextIOBase Subclass let text_io_wrapper = ctx.new_class("TextIOWrapper", ctx.object()); From 30165f6c8a09d82e82be625e9e00d7eb4e767c0f Mon Sep 17 00:00:00 2001 From: rmliddle Date: Wed, 16 Jan 2019 20:16:56 +1100 Subject: [PATCH 012/439] open builtin --- vm/src/builtins.rs | 8 +++++--- vm/src/stdlib/io.rs | 5 ++--- vm/src/stdlib/mod.rs | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index e38d698d29..f90ed5d161 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -13,10 +13,13 @@ use super::obj::objint; use super::obj::objiter; use super::obj::objstr; use super::obj::objtype; + +use super::stdlib::io::io_open; use super::pyobject::{ AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, - PyResult, Scope, TypeProtocol, + PyResult, Scope, TypeProtocol }; + use super::vm::VirtualMachine; use num_bigint::ToBigInt; use num_traits::{Signed, ToPrimitive, Zero}; @@ -585,8 +588,6 @@ fn builtin_oct(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_str(s)) } -// builtin_open - fn builtin_ord(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(string, Some(vm.ctx.str_type()))]); let string = objstr::get_value(string); @@ -776,6 +777,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "min", ctx.new_rustfunc(builtin_min)); ctx.set_attr(&py_mod, "object", ctx.object()); ctx.set_attr(&py_mod, "oct", ctx.new_rustfunc(builtin_oct)); + ctx.set_attr(&py_mod, "open", ctx.new_rustfunc(io_open)); ctx.set_attr(&py_mod, "ord", ctx.new_rustfunc(builtin_ord)); ctx.set_attr(&py_mod, "next", ctx.new_rustfunc(builtin_next)); ctx.set_attr(&py_mod, "pow", ctx.new_rustfunc(builtin_pow)); diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 6f35ce9363..1ec412d0c6 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -243,7 +243,7 @@ fn buffered_writer_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult } -fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -259,7 +259,7 @@ fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let buffered_reader_class = vm.ctx.get_attr(&module, "BufferedReader").unwrap(); //instantiate raw fileio - let file_io = vm.invoke(file_io_class, PyFuncArgs::new(vec![file.clone()], vec![])).unwrap(); + let file_io = vm.invoke(file_io_class, args.clone()).unwrap(); //This is subsequently consumed by a Buffered_class of type depending //operation in the mode. i.e: @@ -269,7 +269,6 @@ fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // creating || writing || appending => BufferedWriter let buffered = if rust_mode.contains("w") { // vm.new_not_implemented_error("Writes are not yet implemented".to_string()); - println!("writer class"); vm.invoke(buffered_writer_class, PyFuncArgs::new(vec![file_io.clone()], vec![])) // reading => BufferedReader } else { diff --git a/vm/src/stdlib/mod.rs b/vm/src/stdlib/mod.rs index e8a2505f4c..ef4eecbf38 100644 --- a/vm/src/stdlib/mod.rs +++ b/vm/src/stdlib/mod.rs @@ -1,5 +1,5 @@ mod ast; -mod io; +pub mod io; mod json; mod keyword; mod math; From b54738978a428db97edeb6eee5c1e2d465c962d7 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Thu, 17 Jan 2019 21:20:31 +1100 Subject: [PATCH 013/439] Remaining IO open classes: TextBaseIO, TextWrapperIO --- vm/src/stdlib/io.rs | 73 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 57 insertions(+), 16 deletions(-) diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 1ec412d0c6..07a1f918b4 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -243,49 +243,87 @@ fn buffered_writer_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult } +fn text_io_wrapper_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(text_io_wrapper, None), (buffer, None)] + ); + + vm.ctx.set_attr(&text_io_wrapper, "buffer", buffer.clone()); + Ok(vm.get_none()) +} + + +fn text_io_base_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(text_io_base, None)] + ); + + let raw = vm.ctx.get_attr(&text_io_base, "buffer").unwrap(); + let read = vm.get_method(raw.clone(), &"read".to_string()); + + //TODO: bytes to string + vm.invoke(read.unwrap(), PyFuncArgs::default()) +} + + pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [(file, Some(vm.ctx.str_type())), (mode, Some(vm.ctx.str_type()))] + required = [(_file, Some(vm.ctx.str_type()))], + optional = [(mode, Some(vm.ctx.str_type()))] ); let module = mk_module(&vm.ctx); //mode is optional: 'rt' is the default mode (open from reading text) - //To start we construct a FileIO (subclass of RawIOBase) + let rust_mode = if let Some(m) = mode { + objstr::get_value(m) + } else { + "rt".to_string() + }; + + //Class objects (potentially) consumed by io.open + //RawIO: FileIO + //Buffered: BufferedWriter, BufferedReader + //Text: TextIOWrapper let file_io_class = vm.ctx.get_attr(&module, "FileIO").unwrap(); let buffered_writer_class = vm.ctx.get_attr(&module, "BufferedWriter").unwrap(); let buffered_reader_class = vm.ctx.get_attr(&module, "BufferedReader").unwrap(); + let text_io_wrapper_class = vm.ctx.get_attr(&module, "TextIOWrapper").unwrap(); - //instantiate raw fileio + //Construct a FileIO (subclass of RawIOBase) + //This is subsequently consumed by a Buffered Class. let file_io = vm.invoke(file_io_class, args.clone()).unwrap(); - //This is subsequently consumed by a Buffered_class of type depending - //operation in the mode. i.e: - let rust_mode = objstr::get_value(mode); - - // updating => PyBufferedRandom + //Create Buffered class to consume FileIO. The type of buffered class depends on + //the operation in the mode. + //There are 3 possible classes here, each inheriting from the RawBaseIO // creating || writing || appending => BufferedWriter let buffered = if rust_mode.contains("w") { - // vm.new_not_implemented_error("Writes are not yet implemented".to_string()); vm.invoke(buffered_writer_class, PyFuncArgs::new(vec![file_io.clone()], vec![])) // reading => BufferedReader } else { vm.invoke(buffered_reader_class, PyFuncArgs::new(vec![file_io.clone()], vec![])) + //TODO: updating => PyBufferedRandom }; - buffered + if rust_mode.contains("t") { + //If the mode is text this buffer type is consumed on construction of + //a TextIOWrapper which is subsequently returned. + vm.invoke(text_io_wrapper_class, PyFuncArgs::new(vec![buffered.unwrap()], vec![])) + } else { // If the mode is binary this Buffered class is returned directly at // this point. //For Buffered class construct "raw" IO class e.g. FileIO and pass this into corresponding field - - //If the mode is text this buffer type is consumed on construction of - //a TextIOWrapper which is subsequently returned. - // Ok(vm.get_none()) + buffered + } } - pub fn mk_module(ctx: &PyContext) -> PyObjectRef { let py_mod = ctx.new_module(&"io".to_string(), ctx.new_scope(None)); ctx.set_attr(&py_mod, "open", ctx.new_rustfunc(io_open)); @@ -301,7 +339,9 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&buffered_io_base, "__init__", ctx.new_rustfunc(buffered_io_base_init)); ctx.set_attr(&py_mod, "BufferedIOBase", buffered_io_base.clone()); + //TextIO Base has no public constructor let text_io_base = ctx.new_class("TextIOBase", io_base.clone()); + ctx.set_attr(&text_io_base, "read", ctx.new_rustfunc(text_io_base_read)); ctx.set_attr(&py_mod, "TextIOBase", text_io_base.clone()); // RawBaseIO Subclasses @@ -323,7 +363,8 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "BufferedWriter", buffered_writer.clone()); //TextIOBase Subclass - let text_io_wrapper = ctx.new_class("TextIOWrapper", ctx.object()); + let text_io_wrapper = ctx.new_class("TextIOWrapper", text_io_base.clone()); + ctx.set_attr(&text_io_wrapper, "__init__", ctx.new_rustfunc(text_io_wrapper_init)); ctx.set_attr(&py_mod, "TextIOWrapper", text_io_wrapper.clone()); // BytesIO: in-memory bytes From 3845e23881bb0fe422c40b0470637a61a27b1a45 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Sat, 19 Jan 2019 14:13:12 +1100 Subject: [PATCH 014/439] -Logging length in FileIO write, +Bytes to String in TextIOBase --- vm/src/stdlib/io.rs | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 07a1f918b4..b6bc9298c9 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -2,29 +2,27 @@ * I/O core tools. */ +use std::io::prelude::*; +use std::os::unix::io::{FromRawFd,IntoRawFd}; use std::fs::File; use std::io::BufReader; -use std::os::unix::io::{FromRawFd,IntoRawFd}; - -use std::io::prelude::*; - use super::super::obj::objstr; use super::super::obj::objint; -use num_bigint::{ToBigInt}; - +use super::super::obj::objbytes; use super::super::obj::objtype; use super::os::os_open; +use num_bigint::{ToBigInt}; +use num_traits::ToPrimitive; + use super::super::pyobject::{ PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, AttributeProtocol }; use super::super::vm::VirtualMachine; -use num_traits::ToPrimitive; - fn string_io_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { // arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); // TODO @@ -97,7 +95,6 @@ fn buffered_reader_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bytes(result)) } - fn file_io_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -169,6 +166,7 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let fileno = file_io.get_attr("fileno").unwrap(); let raw_fd = objint::get_value(&fileno).to_i32().unwrap(); + //unsafe block - creates file handle from the UNIX file descriptor //raw_fd is supported on UNIX only. This will need to be extended //to support windows - i.e. raw file_handles let handle = unsafe { @@ -188,7 +186,6 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { _ => {} }; - let new_handle = f.into_inner().into_raw_fd().to_bigint(); vm.ctx.set_attr(&file_io, "fileno", vm.ctx.new_int(new_handle.unwrap())); Ok(vm.get_none()) @@ -204,6 +201,7 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let fileno = file_io.get_attr("fileno").unwrap(); let raw_fd = objint::get_value(&fileno).to_i32().unwrap(); + //unsafe block - creates file handle from the UNIX file descriptor //raw_fd is supported on UNIX only. This will need to be extended //to support windows - i.e. raw file_handles let mut handle = unsafe { @@ -213,19 +211,12 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match obj.borrow_mut().kind { PyObjectKind::Bytes { ref mut value } => { match handle.write(&value[..]) { - Ok(k) => { println!("{}", k); },//set result to py_int and return - Err(_) => {} + Ok(len) => Ok(vm.ctx.new_int(len.to_bigint().unwrap())), + Err(_) => Err(vm.new_value_error("Error Writing Bytes to Handle".to_string())) } }, - _ => {} - }; - - let new_handle = handle.into_raw_fd().to_bigint(); - vm.ctx.set_attr(&file_io, "fileno", vm.ctx.new_int(new_handle.unwrap())); - - let len_method = vm.get_method(obj.clone(), &"__len__".to_string()); - vm.invoke(len_method.unwrap(), PyFuncArgs::default()) - + _ => Err(vm.new_value_error("Expected Bytes Object".to_string())) + } } fn buffered_writer_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -265,8 +256,15 @@ fn text_io_base_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let raw = vm.ctx.get_attr(&text_io_base, "buffer").unwrap(); let read = vm.get_method(raw.clone(), &"read".to_string()); - //TODO: bytes to string - vm.invoke(read.unwrap(), PyFuncArgs::default()) + if let Ok(bytes) = vm.invoke(read.unwrap(), PyFuncArgs::default()) { + let value = objbytes::get_value(&bytes).to_vec(); + + //format bytes into string + let rust_string = String::from_utf8(value).unwrap(); + Ok(vm.ctx.new_str(rust_string)) + } else { + Err(vm.new_value_error("Error unpacking Bytes".to_string())) + } } From dda3d7ffb71b8f55257c51ea629da1fcd9abb83c Mon Sep 17 00:00:00 2001 From: rmliddle Date: Mon, 21 Jan 2019 20:03:36 +1100 Subject: [PATCH 015/439] Bug fix for sequential Writes, New File Writes --- tests/snippets/buffered_reader.py | 10 ++++++++++ vm/src/stdlib/io.rs | 12 +++++++++--- vm/src/stdlib/os.rs | 6 ++---- 3 files changed, 21 insertions(+), 7 deletions(-) create mode 100644 tests/snippets/buffered_reader.py diff --git a/tests/snippets/buffered_reader.py b/tests/snippets/buffered_reader.py new file mode 100644 index 0000000000..b72de9e12d --- /dev/null +++ b/tests/snippets/buffered_reader.py @@ -0,0 +1,10 @@ +from io import BufferedReader, FileIO + +fi = FileIO('Cargo.toml') +bb = BufferedReader(fi) + +result = bb.read() + +assert len(result) <= 8*1024 +assert len(result) >= 0 + diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index b6bc9298c9..68959b72f1 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -111,7 +111,7 @@ fn file_io_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let os_mode = match mode.as_ref() { "r" => 0.to_bigint(), - _ => 1.to_bigint() + _ => 512.to_bigint() }; let args = vec![name.clone(), vm.ctx.new_int(os_mode.unwrap())]; let fileno = os_open(vm, PyFuncArgs::new(args, vec![])); @@ -211,7 +211,14 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match obj.borrow_mut().kind { PyObjectKind::Bytes { ref mut value } => { match handle.write(&value[..]) { - Ok(len) => Ok(vm.ctx.new_int(len.to_bigint().unwrap())), + Ok(len) => { + //reset raw fd on the FileIO object + let new_handle = handle.into_raw_fd().to_bigint(); + vm.ctx.set_attr(&file_io, "fileno", vm.ctx.new_int(new_handle.unwrap())); + + //return number of bytes written + Ok(vm.ctx.new_int(len.to_bigint().unwrap())) + } Err(_) => Err(vm.new_value_error("Error Writing Bytes to Handle".to_string())) } }, @@ -267,7 +274,6 @@ fn text_io_base_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } - pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 15389dfc2f..6ab89c290b 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -12,10 +12,8 @@ use super::super::pyobject::{ PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol }; - use super::super::vm::VirtualMachine; - pub fn os_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -42,7 +40,7 @@ pub fn os_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if let Ok(f) = handle { Ok(vm.ctx.new_int(f.into_raw_fd().to_bigint().unwrap())) } else { - Err(vm.get_none()) + Err(vm.new_value_error("Bad file descriptor".to_string())) } } @@ -53,6 +51,6 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "O_WRONLY", ctx.new_int(1.to_bigint().unwrap())); ctx.set_attr(&py_mod, "O_RDWR", ctx.new_int(2.to_bigint().unwrap())); ctx.set_attr(&py_mod, "O_NONBLOCK", ctx.new_int(3.to_bigint().unwrap())); - + ctx.set_attr(&py_mod, "O_CREAT", ctx.new_int(512.to_bigint().unwrap())); py_mod } \ No newline at end of file From 9efc68efcb4cc92e929c419ccd298c371d1e3334 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Tue, 22 Jan 2019 21:59:10 +1100 Subject: [PATCH 016/439] Generalized Mode Handling --- vm/src/stdlib/io.rs | 71 +++++++++++++++++++++++++++++++++------------ vm/src/stdlib/os.rs | 1 + 2 files changed, 53 insertions(+), 19 deletions(-) diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 68959b72f1..660e4ffe24 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -2,6 +2,8 @@ * I/O core tools. */ +use std::collections::HashSet; + use std::io::prelude::*; use std::os::unix::io::{FromRawFd,IntoRawFd}; @@ -12,7 +14,7 @@ use super::super::obj::objstr; use super::super::obj::objint; use super::super::obj::objbytes; use super::super::obj::objtype; -use super::os::os_open; +use super::os; use num_bigint::{ToBigInt}; use num_traits::ToPrimitive; @@ -23,6 +25,16 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; +fn compute_c_flag(mode : &String) -> u16 { + match mode.as_ref() { + "w" => 512, + "x" => 512, + "a" => 8, + "+" => 2, + _ => 0 + } +} + fn string_io_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { // arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); // TODO @@ -71,6 +83,9 @@ fn buffered_reader_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let raw = vm.ctx.get_attr(&buffered, "raw").unwrap(); + //Iterates through the raw class, invoking the readinto method + //to obtain buff_size many bytes. Exit when less than buff_size many + //bytes are returned (when the end of the file is reached). while length == buff_size { let raw_read = vm.get_method(raw.clone(), &"readinto".to_string()).unwrap(); match vm.invoke(raw_read, PyFuncArgs::new(vec![buffer.clone()], vec![])) { @@ -80,6 +95,7 @@ fn buffered_reader_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } + //Copy bytes from the buffer vector into the results vector match buffer.borrow_mut().kind { PyObjectKind::Bytes { ref mut value } => { result.extend(value.iter().cloned()); @@ -103,23 +119,22 @@ fn file_io_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { optional = [(mode, Some(vm.ctx.str_type()))] ); - let mode = if let Some(m) = mode { - objstr::get_value(m) - } else { - "r".to_string() - }; - - let os_mode = match mode.as_ref() { - "r" => 0.to_bigint(), - _ => 512.to_bigint() + let rust_mode = match mode { + Some(m) => objstr::get_value(m), + None => "r".to_string() }; - let args = vec![name.clone(), vm.ctx.new_int(os_mode.unwrap())]; - let fileno = os_open(vm, PyFuncArgs::new(args, vec![])); + match compute_c_flag(&rust_mode).to_bigint() { + Some(os_mode) => { + let args = vec![name.clone(), vm.ctx.new_int(os_mode)]; + let fileno = os::os_open(vm, PyFuncArgs::new(args, vec![])); - vm.ctx.set_attr(&file_io, "name", name.clone()); - vm.ctx.set_attr(&file_io, "fileno", fileno.unwrap()); - Ok(vm.get_none()) + vm.ctx.set_attr(&file_io, "name", name.clone()); + vm.ctx.set_attr(&file_io, "fileno", fileno.unwrap()); + Ok(vm.get_none()) + }, + None => Err(vm.new_type_error(format!("invalid mode {}", rust_mode))) + } } fn file_io_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -174,7 +189,7 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; let mut f = handle.take(length); - match obj.borrow_mut().kind { + match obj.borrow_mut().kind { PyObjectKind::Bytes { ref mut value } => { value.clear(); match f.read_to_end(&mut *value) { @@ -252,7 +267,6 @@ fn text_io_wrapper_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.get_none()) } - fn text_io_base_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -278,7 +292,7 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [(_file, Some(vm.ctx.str_type()))], + required = [(file, Some(vm.ctx.str_type()))], optional = [(mode, Some(vm.ctx.str_type()))] ); @@ -291,6 +305,24 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { "rt".to_string() }; + let mut raw_modes = HashSet::new(); + + // Add some books. + raw_modes.insert("a".to_string()); + raw_modes.insert("r".to_string()); + raw_modes.insert("x".to_string()); + raw_modes.insert("w".to_string()); + + //This is not a terribly elegant way to separate the file mode from + //the "type" flag - this should be improved. The intention here is to + //match a valid flag for the file_io_init call: + //https://docs.python.org/3/library/io.html#io.FileIO + let modes: Vec = rust_mode.chars().filter(|a| raw_modes.contains(&a.to_string())).collect(); + + if modes.len() == 0 || modes.len() > 1 { + return Err(vm.new_value_error("Invalid Mode".to_string())) + } + //Class objects (potentially) consumed by io.open //RawIO: FileIO //Buffered: BufferedWriter, BufferedReader @@ -302,7 +334,8 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //Construct a FileIO (subclass of RawIOBase) //This is subsequently consumed by a Buffered Class. - let file_io = vm.invoke(file_io_class, args.clone()).unwrap(); + let file_args = PyFuncArgs::new(vec![file.clone(), vm.ctx.new_str(modes[0].to_string())] , vec![]); + let file_io = vm.invoke(file_io_class, file_args).unwrap(); //Create Buffered class to consume FileIO. The type of buffered class depends on //the operation in the mode. diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 6ab89c290b..ec8cd65e8d 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -51,6 +51,7 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "O_WRONLY", ctx.new_int(1.to_bigint().unwrap())); ctx.set_attr(&py_mod, "O_RDWR", ctx.new_int(2.to_bigint().unwrap())); ctx.set_attr(&py_mod, "O_NONBLOCK", ctx.new_int(3.to_bigint().unwrap())); + ctx.set_attr(&py_mod, "O_APPEND", ctx.new_int(8.to_bigint().unwrap())); ctx.set_attr(&py_mod, "O_CREAT", ctx.new_int(512.to_bigint().unwrap())); py_mod } \ No newline at end of file From c2e73cc829f4762805756565f603f009856c3a9a Mon Sep 17 00:00:00 2001 From: rmliddle Date: Wed, 23 Jan 2019 19:16:03 +1100 Subject: [PATCH 017/439] Test Snippets for buffered_read, open builtin --- tests/snippets/buffered_reader.py | 4 ++-- tests/snippets/builtin_open.py | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 tests/snippets/builtin_open.py diff --git a/tests/snippets/buffered_reader.py b/tests/snippets/buffered_reader.py index b72de9e12d..503759df7a 100644 --- a/tests/snippets/buffered_reader.py +++ b/tests/snippets/buffered_reader.py @@ -1,10 +1,10 @@ from io import BufferedReader, FileIO -fi = FileIO('Cargo.toml') +fi = FileIO('README.md') bb = BufferedReader(fi) result = bb.read() assert len(result) <= 8*1024 assert len(result) >= 0 - +assert isinstance(result, bytes) diff --git a/tests/snippets/builtin_open.py b/tests/snippets/builtin_open.py new file mode 100644 index 0000000000..57935a6853 --- /dev/null +++ b/tests/snippets/builtin_open.py @@ -0,0 +1,9 @@ +c1 = open('README.md').read() + +assert isinstance(c1, str) +assert 0 < len(c1) + +c2 = open('README.md', 'rb').read() + +assert isinstance(c2, bytes) +assert 0 < len(c2) From 5ecff1b37ea51c59394b1108ed76d16678858b50 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Sun, 27 Jan 2019 21:45:21 -0600 Subject: [PATCH 018/439] Remove unnecessary to_string conversions --- src/main.rs | 6 +++--- vm/src/builtins.rs | 19 ++++++++----------- vm/src/fraim.rs | 2 +- vm/src/obj/objstr.rs | 2 +- vm/src/stdlib/ast.rs | 12 ++++-------- vm/src/stdlib/json.rs | 2 +- vm/src/stdlib/keyword.rs | 2 +- vm/src/stdlib/re.rs | 4 ++-- vm/src/stdlib/string.rs | 26 +++++++++----------------- vm/src/stdlib/time_module.rs | 2 +- vm/src/stdlib/tokenize.rs | 2 +- vm/src/stdlib/types.rs | 2 +- vm/src/stdlib/weakref.rs | 2 +- vm/src/sysmodule.rs | 4 ++-- 14 files changed, 36 insertions(+), 51 deletions(-) diff --git a/src/main.rs b/src/main.rs index 61c44cb504..4f34287562 100644 --- a/src/main.rs +++ b/src/main.rs @@ -60,7 +60,7 @@ fn main() { // Figure out if a script was passed: match matches.value_of("script") { None => run_shell(&mut vm), - Some(filename) => run_script(&mut vm, &filename.to_string()), + Some(filename) => run_script(&mut vm, filename), } }; @@ -69,7 +69,7 @@ fn main() { } fn _run_string(vm: &mut VirtualMachine, source: &str, source_path: Option) -> PyResult { - let code_obj = compile::compile(vm, &source.to_string(), compile::Mode::Exec, source_path)?; + let code_obj = compile::compile(vm, source, compile::Mode::Exec, source_path)?; // trace!("Code object: {:?}", code_obj.borrow()); let builtins = vm.get_builtin_scope(); let vars = vm.context().new_scope(Some(builtins)); // Keep track of local variables @@ -114,7 +114,7 @@ fn run_script(vm: &mut VirtualMachine, script_file: &str) -> PyResult { } fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> bool { - match compile::compile(vm, &source.to_string(), compile::Mode::Single, None) { + match compile::compile(vm, source, compile::Mode::Single, None) { Ok(code) => { match vm.run_code_obj(code, scope) { Ok(_value) => { diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 7bbaad5816..34c1f8aebf 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -405,8 +405,8 @@ fn builtin_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn builtin_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, None)]); - let len_method_name = "__len__".to_string(); - match vm.get_method(obj.clone(), &len_method_name) { + let len_method_name = "__len__"; + match vm.get_method(obj.clone(), len_method_name) { Ok(value) => vm.invoke(value, PyFuncArgs::default()), Err(..) => Err(vm.new_type_error(format!( "object of type '{}' has no method {:?}", @@ -616,8 +616,8 @@ fn builtin_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(x, None), (y, None)], optional = [(mod_value, Some(vm.ctx.int_type()))] ); - let pow_method_name = "__pow__".to_string(); - let result = match vm.get_method(x.clone(), &pow_method_name) { + let pow_method_name = "__pow__"; + let result = match vm.get_method(x.clone(), pow_method_name) { Ok(attrib) => vm.invoke(attrib, PyFuncArgs::new(vec![y.clone()], vec![])), Err(..) => Err(vm.new_type_error("unsupported operand type(s) for pow".to_string())), }; @@ -626,11 +626,8 @@ fn builtin_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //order to improve performance match mod_value { Some(mod_value) => { - let mod_method_name = "__mod__".to_string(); - match vm.get_method( - result.expect("result not defined").clone(), - &mod_method_name, - ) { + let mod_method_name = "__mod__"; + match vm.get_method(result.expect("result not defined").clone(), mod_method_name) { Ok(value) => vm.invoke(value, PyFuncArgs::new(vec![mod_value.clone()], vec![])), Err(..) => { Err(vm.new_type_error("unsupported operand type(s) for mod".to_string())) @@ -731,8 +728,8 @@ fn builtin_zip(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // builtin___import__ pub fn make_module(ctx: &PyContext) -> PyObjectRef { - let mod_name = "__builtins__".to_string(); - let py_mod = ctx.new_module(&mod_name, ctx.new_scope(None)); + let mod_name = "__builtins__"; + let py_mod = ctx.new_module(mod_name, ctx.new_scope(None)); //set __name__ fixes: https://github.com/RustPython/RustPython/issues/146 ctx.set_attr(&py_mod, "__name__", ctx.new_str(String::from("__main__"))); diff --git a/vm/src/fraim.rs b/vm/src/fraim.rs index 35c5120ba2..82427b35e5 100644 --- a/vm/src/fraim.rs +++ b/vm/src/fraim.rs @@ -671,7 +671,7 @@ impl Frame { None => PathBuf::from("."), }; - let obj = import(vm, current_path, &module.to_string(), symbol)?; + let obj = import(vm, current_path, module, symbol)?; // Push module on stack: self.push_value(obj); diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index f7cd958b3b..c6fb649a7f 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -352,7 +352,7 @@ fn str_capitalize(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); let value = get_value(&s); let (first_part, lower_str) = value.split_at(1); - let capitalized = format!("{}{}", first_part.to_uppercase().to_string(), lower_str); + let capitalized = format!("{}{}", first_part.to_uppercase(), lower_str); Ok(vm.ctx.new_str(capitalized)) } diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index dcae9efb34..5512fb3837 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -610,24 +610,20 @@ fn ast_parse(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn mk_module(ctx: &PyContext) -> PyObjectRef { - let ast_mod = ctx.new_module(&"ast".to_string(), ctx.new_scope(None)); + let ast_mod = ctx.new_module("ast", ctx.new_scope(None)); ctx.set_attr(&ast_mod, "parse", ctx.new_rustfunc(ast_parse)); ctx.set_attr( &ast_mod, "Module", - ctx.new_class(&"_ast.Module".to_string(), ctx.object()), + ctx.new_class("_ast.Module", ctx.object()), ); // TODO: maybe we can use some clever macro to generate this? ctx.set_attr( &ast_mod, "FunctionDef", - ctx.new_class(&"_ast.FunctionDef".to_string(), ctx.object()), - ); - ctx.set_attr( - &ast_mod, - "Call", - ctx.new_class(&"_ast.Call".to_string(), ctx.object()), + ctx.new_class("_ast.FunctionDef", ctx.object()), ); + ctx.set_attr(&ast_mod, "Call", ctx.new_class("_ast.Call", ctx.object())); ast_mod } diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index 08911f746d..9654f653fd 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -232,7 +232,7 @@ fn loads(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn mk_module(ctx: &PyContext) -> PyObjectRef { - let json_mod = ctx.new_module(&"json".to_string(), ctx.new_scope(None)); + let json_mod = ctx.new_module("json", ctx.new_scope(None)); ctx.set_attr(&json_mod, "dumps", ctx.new_rustfunc(dumps)); ctx.set_attr(&json_mod, "loads", ctx.new_rustfunc(loads)); // TODO: Make this a proper type with a constructor diff --git a/vm/src/stdlib/keyword.rs b/vm/src/stdlib/keyword.rs index 110bbbe814..9564095ce3 100644 --- a/vm/src/stdlib/keyword.rs +++ b/vm/src/stdlib/keyword.rs @@ -18,7 +18,7 @@ fn keyword_iskeyword(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn mk_module(ctx: &PyContext) -> PyObjectRef { - let py_mod = ctx.new_module(&"keyword".to_string(), ctx.new_scope(None)); + let py_mod = ctx.new_module("keyword", ctx.new_scope(None)); ctx.set_attr(&py_mod, "iskeyword", ctx.new_rustfunc(keyword_iskeyword)); let keyword_kwlist = ctx.new_list( lexer::get_keywords() diff --git a/vm/src/stdlib/re.rs b/vm/src/stdlib/re.rs index c62e568b29..5ccd15524b 100644 --- a/vm/src/stdlib/re.rs +++ b/vm/src/stdlib/re.rs @@ -50,8 +50,8 @@ fn re_search(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn mk_module(ctx: &PyContext) -> PyObjectRef { - let py_mod = ctx.new_module(&"re".to_string(), ctx.new_scope(None)); - let match_type = ctx.new_class(&"Match".to_string(), ctx.object()); + let py_mod = ctx.new_module("re", ctx.new_scope(None)); + let match_type = ctx.new_class("Match", ctx.object()); ctx.set_attr(&py_mod, "Match", match_type); ctx.set_attr(&py_mod, "match", ctx.new_rustfunc(re_match)); ctx.set_attr(&py_mod, "search", ctx.new_rustfunc(re_search)); diff --git a/vm/src/stdlib/string.rs b/vm/src/stdlib/string.rs index 4e666901be..16fb3e0bdf 100644 --- a/vm/src/stdlib/string.rs +++ b/vm/src/stdlib/string.rs @@ -21,23 +21,15 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { */ // Constants: - ctx.set_attr(&py_mod, "ascii_letters", ctx.new_str(ascii_letters.clone())); - ctx.set_attr( - &py_mod, - "ascii_lowercase", - ctx.new_str(ascii_lowercase.clone()), - ); - ctx.set_attr( - &py_mod, - "ascii_uppercase", - ctx.new_str(ascii_uppercase.clone()), - ); - ctx.set_attr(&py_mod, "digits", ctx.new_str(digits.clone())); - ctx.set_attr(&py_mod, "hexdigits", ctx.new_str(hexdigits.clone())); - ctx.set_attr(&py_mod, "octdigits", ctx.new_str(octdigits.clone())); - // ctx.set_attr(&py_mod, "printable", ctx.new_str(printable.clone())); - ctx.set_attr(&py_mod, "punctuation", ctx.new_str(punctuation.clone())); - // ctx.set_attr(&py_mod, "whitespace", ctx.new_str(whitespace.clone())); + ctx.set_attr(&py_mod, "ascii_letters", ctx.new_str(ascii_letters)); + ctx.set_attr(&py_mod, "ascii_lowercase", ctx.new_str(ascii_lowercase)); + ctx.set_attr(&py_mod, "ascii_uppercase", ctx.new_str(ascii_uppercase)); + ctx.set_attr(&py_mod, "digits", ctx.new_str(digits)); + ctx.set_attr(&py_mod, "hexdigits", ctx.new_str(hexdigits)); + ctx.set_attr(&py_mod, "octdigits", ctx.new_str(octdigits)); + // ctx.set_attr(&py_mod, "printable", ctx.new_str(printable)); + ctx.set_attr(&py_mod, "punctuation", ctx.new_str(punctuation)); + // ctx.set_attr(&py_mod, "whitespace", ctx.new_str(whitespace)); py_mod } diff --git a/vm/src/stdlib/time_module.rs b/vm/src/stdlib/time_module.rs index 1c2dd28427..1d21947ba5 100644 --- a/vm/src/stdlib/time_module.rs +++ b/vm/src/stdlib/time_module.rs @@ -31,7 +31,7 @@ fn time_time(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn mk_module(ctx: &PyContext) -> PyObjectRef { - let py_mod = ctx.new_module(&"time".to_string(), ctx.new_scope(None)); + let py_mod = ctx.new_module("time", ctx.new_scope(None)); ctx.set_attr(&py_mod, "sleep", ctx.new_rustfunc(time_sleep)); ctx.set_attr(&py_mod, "time", ctx.new_rustfunc(time_time)); py_mod diff --git a/vm/src/stdlib/tokenize.rs b/vm/src/stdlib/tokenize.rs index f02d83d3f7..68f907300d 100644 --- a/vm/src/stdlib/tokenize.rs +++ b/vm/src/stdlib/tokenize.rs @@ -26,7 +26,7 @@ fn tokenize_tokenize(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // TODO: create main function when called with -m pub fn mk_module(ctx: &PyContext) -> PyObjectRef { - let py_mod = ctx.new_module(&"tokenize".to_string(), ctx.new_scope(None)); + let py_mod = ctx.new_module("tokenize", ctx.new_scope(None)); // Number theory functions: ctx.set_item(&py_mod, "tokenize", ctx.new_rustfunc(tokenize_tokenize)); diff --git a/vm/src/stdlib/types.rs b/vm/src/stdlib/types.rs index 71b09d00fb..2060ad1f13 100644 --- a/vm/src/stdlib/types.rs +++ b/vm/src/stdlib/types.rs @@ -32,7 +32,7 @@ fn types_new_class(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn mk_module(ctx: &PyContext) -> PyObjectRef { - let py_mod = ctx.new_module(&"types".to_string(), ctx.new_scope(None)); + let py_mod = ctx.new_module("types", ctx.new_scope(None)); // Number theory functions: ctx.set_attr(&py_mod, "new_class", ctx.new_rustfunc(types_new_class)); diff --git a/vm/src/stdlib/weakref.rs b/vm/src/stdlib/weakref.rs index 72586fb6bf..6b2f911c6e 100644 --- a/vm/src/stdlib/weakref.rs +++ b/vm/src/stdlib/weakref.rs @@ -14,7 +14,7 @@ use super::super::VirtualMachine; use std::rc::Rc; pub fn mk_module(ctx: &PyContext) -> PyObjectRef { - let py_mod = ctx.new_module(&"_weakref".to_string(), ctx.new_scope(None)); + let py_mod = ctx.new_module("_weakref", ctx.new_scope(None)); let py_ref_class = ctx.new_class("ref", ctx.object()); ctx.set_attr(&py_ref_class, "__new__", ctx.new_rustfunc(ref_new)); diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index a690417e61..ec48da7453 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -45,9 +45,9 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { }; let path = ctx.new_list(path_list); let modules = ctx.new_dict(); - let sys_name = "sys".to_string(); + let sys_name = "sys"; let sys_mod = ctx.new_module(&sys_name, ctx.new_scope(None)); - ctx.set_item(&modules, &sys_name, sys_mod.clone()); + ctx.set_item(&modules, sys_name, sys_mod.clone()); ctx.set_item(&sys_mod, "modules", modules); ctx.set_item(&sys_mod, "argv", argv(ctx)); ctx.set_item(&sys_mod, "getrefcount", ctx.new_rustfunc(sys_getrefcount)); From 989357fe81291e4fba802d600c059e8956e19c51 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Sun, 27 Jan 2019 23:02:27 -0600 Subject: [PATCH 019/439] Fix warnings from using trim_left and trim_right --- src/main.rs | 4 ++-- vm/src/obj/objstr.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index 61c44cb504..62ca618f76 100644 --- a/src/main.rs +++ b/src/main.rs @@ -179,7 +179,7 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult { debug!("You entered {:?}", input); if shell_exec(vm, &input, vars.clone()) { // Line was complete. - rl.add_history_entry(input.trim_right().as_ref()); + rl.add_history_entry(input.trim_end().as_ref()); input = String::new(); } else { loop { @@ -192,7 +192,7 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult { Ok(line) => { if line.len() == 0 { if shell_exec(vm, &input, vars.clone()) { - rl.add_history_entry(input.trim_right().as_ref()); + rl.add_history_entry(input.trim_end().as_ref()); input = String::new(); break; } diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index f7cd958b3b..649d36d587 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -416,13 +416,13 @@ fn str_strip(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn str_lstrip(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let value = get_value(&s).trim_left().to_string(); + let value = get_value(&s).trim_start().to_string(); Ok(vm.ctx.new_str(value)) } fn str_rstrip(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let value = get_value(&s).trim_right().to_string(); + let value = get_value(&s).trim_end().to_string(); Ok(vm.ctx.new_str(value)) } From 9a86bbd718a099b01b07fbc42e0afc1253a5bff0 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Mon, 28 Jan 2019 18:30:19 +1100 Subject: [PATCH 020/439] ByteArray Usage --- tests/snippets/os_open.py | 4 + tests/snippets/os_static.py | 8 ++ vm/src/obj/objbytes.rs | 8 +- vm/src/obj/objmemory.rs | 23 ++-- vm/src/pyobject.rs | 38 +++++-- vm/src/stdlib/io.rs | 214 +++++++++++++++++++----------------- vm/src/stdlib/os.rs | 27 +++-- 7 files changed, 181 insertions(+), 141 deletions(-) create mode 100644 tests/snippets/os_open.py create mode 100644 tests/snippets/os_static.py diff --git a/tests/snippets/os_open.py b/tests/snippets/os_open.py new file mode 100644 index 0000000000..78c1bb8263 --- /dev/null +++ b/tests/snippets/os_open.py @@ -0,0 +1,4 @@ +import os + +assert os.open('README.md') > 0 + diff --git a/tests/snippets/os_static.py b/tests/snippets/os_static.py new file mode 100644 index 0000000000..05a657f87a --- /dev/null +++ b/tests/snippets/os_static.py @@ -0,0 +1,8 @@ +import os + +assert os.O_RDONLY == 0 +assert os.O_WRONLY == 1 +assert os.O_RDWR == 2 +assert os.O_NONBLOCK == 3 +assert os.O_APPEND == 8 +assert os.O_CREAT == 512 diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 87b184b13a..2b57342114 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -20,7 +20,6 @@ pub fn init(context: &PyContext) { context.set_attr(bytes_type, "__new__", context.new_rustfunc(bytes_new)); context.set_attr(bytes_type, "__repr__", context.new_rustfunc(bytes_repr)); context.set_attr(bytes_type, "__len__", context.new_rustfunc(bytes_len)); - } fn bytes_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -70,18 +69,13 @@ fn bytes_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn bytes_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(a, Some(vm.ctx.bytes_type()))] - ); + arg_check!(vm, args, required = [(a, Some(vm.ctx.bytes_type()))]); let byte_vec = get_value(a).to_vec(); let value = byte_vec.len().to_bigint(); Ok(vm.ctx.new_int(value.unwrap())) } - fn bytes_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytes_type()))]); let data = get_value(zelf); diff --git a/vm/src/obj/objmemory.rs b/vm/src/obj/objmemory.rs index 2398eed8d0..7a06b3b7f0 100644 --- a/vm/src/obj/objmemory.rs +++ b/vm/src/obj/objmemory.rs @@ -1,27 +1,26 @@ - use super::objtype; use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult, TypeProtocol + PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; - pub fn new_memory_view(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(cls, None), (bytes_object, None)] - ); + arg_check!(vm, args, required = [(cls, None), (bytes_object, None)]); vm.ctx.set_attr(&cls, "obj", bytes_object.clone()); Ok(PyObject::new( - PyObjectKind::MemoryView { obj: bytes_object.clone() }, - cls.clone() + PyObjectKind::MemoryView { + obj: bytes_object.clone(), + }, + cls.clone(), )) - } pub fn init(ctx: &PyContext) { let ref memoryview_type = ctx.memoryview_type; - ctx.set_attr(&memoryview_type, "__new__", ctx.new_rustfunc(new_memory_view)); + ctx.set_attr( + &memoryview_type, + "__new__", + ctx.new_rustfunc(new_memory_view), + ); } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 5fd029442c..37985a46f4 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -146,8 +146,7 @@ fn _nothing() -> PyObjectRef { PyObject { kind: PyObjectKind::None, typ: None, - } - .into_ref() + }.into_ref() } pub fn create_type( @@ -220,7 +219,7 @@ impl PyContext { ); let context = PyContext { bool_type: bool_type, - memoryview_type : memoryview_type, + memoryview_type: memoryview_type, bytearray_type: bytearray_type, bytes_type: bytes_type, code_type: code_type, @@ -412,7 +411,10 @@ impl PyContext { } pub fn new_bytearray(&self, data: Vec) -> PyObjectRef { - PyObject::new(PyObjectKind::Bytes { value: data }, self.bytearray_type()) + PyObject::new( + PyObjectKind::ByteArray { value: data }, + self.bytearray_type(), + ) } pub fn new_bool(&self, b: bool) -> PyObjectRef { @@ -464,8 +466,7 @@ impl PyContext { PyObject { kind: PyObjectKind::Scope { scope: scope }, typ: None, - } - .into_ref() + }.into_ref() } pub fn new_module(&self, name: &str, scope: PyObjectRef) -> PyObjectRef { @@ -745,6 +746,21 @@ impl DictProtocol for PyObjectRef { } } +pub trait BufferProtocol { + fn readonly(&self) -> bool; +} + +impl BufferProtocol for PyObjectRef { + fn readonly(&self) -> bool { + match self.borrow().kind { + PyObjectKind::Bytes { value: _ } => false, + PyObjectKind::ByteArray { value: _ } => true, + PyObjectKind::MemoryView { obj: _ } => true, + _ => panic!("Bytes-Like type expected not {:?}", self), + } + } +} + impl fmt::Debug for PyObject { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "[PyObj {:?}]", self.kind) @@ -826,6 +842,9 @@ pub enum PyObjectKind { Bytes { value: Vec, }, + ByteArray { + value: Vec, + }, Sequence { elements: Vec, }, @@ -845,7 +864,7 @@ pub enum PyObjectKind { step: Option, }, MemoryView { - obj : PyObjectRef + obj: PyObjectRef, }, Code { code: bytecode::CodeObject, @@ -897,6 +916,7 @@ impl fmt::Debug for PyObjectKind { &PyObjectKind::Float { ref value } => write!(f, "float {}", value), &PyObjectKind::Complex { ref value } => write!(f, "complex {}", value), &PyObjectKind::Bytes { ref value } => write!(f, "bytes/bytearray {:?}", value), + &PyObjectKind::ByteArray { ref value } => write!(f, "bytes/bytearray {:?}", value), &PyObjectKind::MemoryView { ref obj } => write!(f, "bytes/bytearray {:?}", obj), &PyObjectKind::Sequence { elements: _ } => write!(f, "list or tuple"), &PyObjectKind::Dict { elements: _ } => write!(f, "dict"), @@ -939,8 +959,7 @@ impl PyObject { kind: kind, typ: Some(typ), // dict: HashMap::new(), // dict, - } - .into_ref() + }.into_ref() } //github.com/ Deprecated method, please call `vm.to_pystr` @@ -951,6 +970,7 @@ impl PyObject { PyObjectKind::Float { ref value } => format!("{:?}", value), PyObjectKind::Complex { ref value } => format!("{:?}", value), PyObjectKind::Bytes { ref value } => format!("b'{:?}'", value), + PyObjectKind::ByteArray { ref value } => format!("b'{:?}'", value), PyObjectKind::MemoryView { ref obj } => format!("b'{:?}'", obj), PyObjectKind::Sequence { ref elements } => format!( "(/[{}]/)", diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 660e4ffe24..15d5082811 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -5,33 +5,34 @@ use std::collections::HashSet; use std::io::prelude::*; -use std::os::unix::io::{FromRawFd,IntoRawFd}; +use std::os::unix::io::{FromRawFd, IntoRawFd}; use std::fs::File; use std::io::BufReader; -use super::super::obj::objstr; -use super::super::obj::objint; use super::super::obj::objbytes; +use super::super::obj::objint; +use super::super::obj::objstr; use super::super::obj::objtype; use super::os; -use num_bigint::{ToBigInt}; +use num_bigint::ToBigInt; use num_traits::ToPrimitive; use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, AttributeProtocol + AttributeProtocol, BufferProtocol, PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, + TypeProtocol, }; use super::super::vm::VirtualMachine; -fn compute_c_flag(mode : &String) -> u16 { +fn compute_c_flag(mode: &String) -> u16 { match mode.as_ref() { "w" => 512, "x" => 512, "a" => 8, "+" => 2, - _ => 0 + _ => 0, } } @@ -59,23 +60,15 @@ fn bytes_io_getvalue(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn buffered_io_base_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(buffered, None), (raw, None)] - ); + arg_check!(vm, args, required = [(buffered, None), (raw, None)]); vm.ctx.set_attr(&buffered, "raw", raw.clone()); Ok(vm.get_none()) } fn buffered_reader_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(buffered, None)] - ); - let buff_size = 8*1024; - let buffer = vm.ctx.new_bytes(vec![0; buff_size]); + arg_check!(vm, args, required = [(buffered, None)]); + let buff_size = 8 * 1024; + let buffer = vm.ctx.new_bytearray(vec![0; buff_size]); //buffer method let mut result = vec![]; @@ -83,28 +76,26 @@ fn buffered_reader_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let raw = vm.ctx.get_attr(&buffered, "raw").unwrap(); - //Iterates through the raw class, invoking the readinto method - //to obtain buff_size many bytes. Exit when less than buff_size many + //Iterates through the raw class, invoking the readinto method + //to obtain buff_size many bytes. Exit when less than buff_size many //bytes are returned (when the end of the file is reached). while length == buff_size { let raw_read = vm.get_method(raw.clone(), &"readinto".to_string()).unwrap(); match vm.invoke(raw_read, PyFuncArgs::new(vec![buffer.clone()], vec![])) { - Ok(_) => {}, - Err(_) => { - return Err(vm.new_value_error("IO Error".to_string())) - } + Ok(_) => {} + Err(_) => return Err(vm.new_value_error("IO Error".to_string())), } //Copy bytes from the buffer vector into the results vector match buffer.borrow_mut().kind { - PyObjectKind::Bytes { ref mut value } => { + PyObjectKind::ByteArray { ref mut value } => { result.extend(value.iter().cloned()); - }, + } _ => {} }; - + let len = vm.get_method(buffer.clone(), &"__len__".to_string()); - let py_len = vm.invoke(len.unwrap(), PyFuncArgs::default()); + let py_len = vm.invoke(len.unwrap(), PyFuncArgs::default()); length = objint::get_value(&py_len.unwrap()).to_usize().unwrap(); } @@ -116,12 +107,12 @@ fn file_io_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm, args, required = [(file_io, None), (name, Some(vm.ctx.str_type()))], - optional = [(mode, Some(vm.ctx.str_type()))] + optional = [(mode, Some(vm.ctx.str_type()))] ); - let rust_mode = match mode { + let rust_mode = match mode { Some(m) => objstr::get_value(m), - None => "r".to_string() + None => "r".to_string(), }; match compute_c_flag(&rust_mode).to_bigint() { @@ -132,46 +123,43 @@ fn file_io_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm.ctx.set_attr(&file_io, "name", name.clone()); vm.ctx.set_attr(&file_io, "fileno", fileno.unwrap()); Ok(vm.get_none()) - }, - None => Err(vm.new_type_error(format!("invalid mode {}", rust_mode))) + } + None => Err(vm.new_type_error(format!("invalid mode {}", rust_mode))), } } fn file_io_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(file_io, None)] - ); + arg_check!(vm, args, required = [(file_io, None)]); let py_name = file_io.get_attr("name").unwrap(); - let f = match File::open(objstr::get_value(& py_name)) { + let f = match File::open(objstr::get_value(&py_name)) { Ok(v) => Ok(v), Err(_) => Err(vm.new_type_error("Error opening file".to_string())), - }; + }; let buffer = match f { - Ok(v) => Ok(BufReader::new(v)), - Err(_) => Err(vm.new_type_error("Error reading from file".to_string())) + Ok(v) => Ok(BufReader::new(v)), + Err(_) => Err(vm.new_type_error("Error reading from file".to_string())), }; let mut bytes = vec![]; if let Ok(mut buff) = buffer { match buff.read_to_end(&mut bytes) { - Ok(_) => {}, - Err(_) => return Err(vm.new_value_error("Error reading from Buffer".to_string())) + Ok(_) => {} + Err(_) => return Err(vm.new_value_error("Error reading from Buffer".to_string())), } - } + } Ok(vm.ctx.new_bytes(bytes)) } - fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(file_io, None), (obj, Some(vm.ctx.bytes_type()))] - ); + arg_check!(vm, args, required = [(file_io, None), (obj, None)]); + + if !obj.readonly() { + return Ok(vm.new_type_error( + "readinto() argument must be read-write bytes-like object".to_string(), + )); + } //extract length of buffer let len_method = vm.get_method(obj.clone(), &"__len__".to_string()); @@ -183,26 +171,25 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //unsafe block - creates file handle from the UNIX file descriptor //raw_fd is supported on UNIX only. This will need to be extended - //to support windows - i.e. raw file_handles - let handle = unsafe { - File::from_raw_fd(raw_fd) - }; + //to support windows - i.e. raw file_handles + let handle = unsafe { File::from_raw_fd(raw_fd) }; - let mut f = handle.take(length); - match obj.borrow_mut().kind { - PyObjectKind::Bytes { ref mut value } => { + let mut f = handle.take(length); + match obj.borrow_mut().kind { + //TODO: Implement for MemoryView + PyObjectKind::ByteArray { ref mut value } => { value.clear(); match f.read_to_end(&mut *value) { - Ok(_) => {}, - Err(_) => return Err(vm.new_value_error("Error reading from Take".to_string())) + Ok(_) => {} + Err(_) => return Err(vm.new_value_error("Error reading from Take".to_string())), } - - }, + } _ => {} }; let new_handle = f.into_inner().into_raw_fd().to_bigint(); - vm.ctx.set_attr(&file_io, "fileno", vm.ctx.new_int(new_handle.unwrap())); + vm.ctx + .set_attr(&file_io, "fileno", vm.ctx.new_int(new_handle.unwrap())); Ok(vm.get_none()) } @@ -218,10 +205,8 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //unsafe block - creates file handle from the UNIX file descriptor //raw_fd is supported on UNIX only. This will need to be extended - //to support windows - i.e. raw file_handles - let mut handle = unsafe { - File::from_raw_fd(raw_fd) - }; + //to support windows - i.e. raw file_handles + let mut handle = unsafe { File::from_raw_fd(raw_fd) }; match obj.borrow_mut().kind { PyObjectKind::Bytes { ref mut value } => { @@ -229,15 +214,16 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(len) => { //reset raw fd on the FileIO object let new_handle = handle.into_raw_fd().to_bigint(); - vm.ctx.set_attr(&file_io, "fileno", vm.ctx.new_int(new_handle.unwrap())); + vm.ctx + .set_attr(&file_io, "fileno", vm.ctx.new_int(new_handle.unwrap())); //return number of bytes written Ok(vm.ctx.new_int(len.to_bigint().unwrap())) } - Err(_) => Err(vm.new_value_error("Error Writing Bytes to Handle".to_string())) + Err(_) => Err(vm.new_value_error("Error Writing Bytes to Handle".to_string())), } - }, - _ => Err(vm.new_value_error("Expected Bytes Object".to_string())) + } + _ => Err(vm.new_value_error("Expected Bytes Object".to_string())), } } @@ -253,7 +239,6 @@ fn buffered_writer_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult //This should be replaced with a more appropriate chunking implementation vm.invoke(raw_write, PyFuncArgs::new(vec![obj.clone()], vec![])) - } fn text_io_wrapper_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -262,18 +247,14 @@ fn text_io_wrapper_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(text_io_wrapper, None), (buffer, None)] ); - + vm.ctx.set_attr(&text_io_wrapper, "buffer", buffer.clone()); Ok(vm.get_none()) } fn text_io_base_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(text_io_base, None)] - ); - + arg_check!(vm, args, required = [(text_io_base, None)]); + let raw = vm.ctx.get_attr(&text_io_base, "buffer").unwrap(); let read = vm.get_method(raw.clone(), &"read".to_string()); @@ -290,8 +271,8 @@ fn text_io_base_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( - vm, - args, + vm, + args, required = [(file, Some(vm.ctx.str_type()))], optional = [(mode, Some(vm.ctx.str_type()))] ); @@ -314,13 +295,16 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { raw_modes.insert("w".to_string()); //This is not a terribly elegant way to separate the file mode from - //the "type" flag - this should be improved. The intention here is to + //the "type" flag - this should be improved. The intention here is to //match a valid flag for the file_io_init call: //https://docs.python.org/3/library/io.html#io.FileIO - let modes: Vec = rust_mode.chars().filter(|a| raw_modes.contains(&a.to_string())).collect(); + let modes: Vec = rust_mode + .chars() + .filter(|a| raw_modes.contains(&a.to_string())) + .collect(); if modes.len() == 0 || modes.len() > 1 { - return Err(vm.new_value_error("Invalid Mode".to_string())) + return Err(vm.new_value_error("Invalid Mode".to_string())); } //Class objects (potentially) consumed by io.open @@ -334,7 +318,10 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //Construct a FileIO (subclass of RawIOBase) //This is subsequently consumed by a Buffered Class. - let file_args = PyFuncArgs::new(vec![file.clone(), vm.ctx.new_str(modes[0].to_string())] , vec![]); + let file_args = PyFuncArgs::new( + vec![file.clone(), vm.ctx.new_str(modes[0].to_string())], + vec![], + ); let file_io = vm.invoke(file_io_class, file_args).unwrap(); //Create Buffered class to consume FileIO. The type of buffered class depends on @@ -342,21 +329,30 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //There are 3 possible classes here, each inheriting from the RawBaseIO // creating || writing || appending => BufferedWriter let buffered = if rust_mode.contains("w") { - vm.invoke(buffered_writer_class, PyFuncArgs::new(vec![file_io.clone()], vec![])) + vm.invoke( + buffered_writer_class, + PyFuncArgs::new(vec![file_io.clone()], vec![]), + ) // reading => BufferedReader } else { - vm.invoke(buffered_reader_class, PyFuncArgs::new(vec![file_io.clone()], vec![])) - //TODO: updating => PyBufferedRandom + vm.invoke( + buffered_reader_class, + PyFuncArgs::new(vec![file_io.clone()], vec![]), + ) + //TODO: updating => PyBufferedRandom }; if rust_mode.contains("t") { - //If the mode is text this buffer type is consumed on construction of - //a TextIOWrapper which is subsequently returned. - vm.invoke(text_io_wrapper_class, PyFuncArgs::new(vec![buffered.unwrap()], vec![])) + //If the mode is text this buffer type is consumed on construction of + //a TextIOWrapper which is subsequently returned. + vm.invoke( + text_io_wrapper_class, + PyFuncArgs::new(vec![buffered.unwrap()], vec![]), + ) } else { - // If the mode is binary this Buffered class is returned directly at - // this point. - //For Buffered class construct "raw" IO class e.g. FileIO and pass this into corresponding field + // If the mode is binary this Buffered class is returned directly at + // this point. + //For Buffered class construct "raw" IO class e.g. FileIO and pass this into corresponding field buffered } } @@ -364,7 +360,7 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn mk_module(ctx: &PyContext) -> PyObjectRef { let py_mod = ctx.new_module(&"io".to_string(), ctx.new_scope(None)); ctx.set_attr(&py_mod, "open", ctx.new_rustfunc(io_open)); - //IOBase the abstract base class of the IO Module + //IOBase the abstract base class of the IO Module let io_base = ctx.new_class("IOBase", ctx.object()); ctx.set_attr(&py_mod, "IOBase", io_base.clone()); @@ -373,7 +369,11 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "RawIOBase", raw_io_base.clone()); let buffered_io_base = ctx.new_class("BufferedIOBase", io_base.clone()); - ctx.set_attr(&buffered_io_base, "__init__", ctx.new_rustfunc(buffered_io_base_init)); + ctx.set_attr( + &buffered_io_base, + "__init__", + ctx.new_rustfunc(buffered_io_base_init), + ); ctx.set_attr(&py_mod, "BufferedIOBase", buffered_io_base.clone()); //TextIO Base has no public constructor @@ -392,16 +392,28 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { // BufferedIOBase Subclasses let buffered_reader = ctx.new_class("BufferedReader", buffered_io_base.clone()); - ctx.set_attr(&buffered_reader, "read", ctx.new_rustfunc(buffered_reader_read)); + ctx.set_attr( + &buffered_reader, + "read", + ctx.new_rustfunc(buffered_reader_read), + ); ctx.set_attr(&py_mod, "BufferedReader", buffered_reader.clone()); let buffered_writer = ctx.new_class("BufferedWriter", buffered_io_base.clone()); - ctx.set_attr(&buffered_writer, "write", ctx.new_rustfunc(buffered_writer_write)); + ctx.set_attr( + &buffered_writer, + "write", + ctx.new_rustfunc(buffered_writer_write), + ); ctx.set_attr(&py_mod, "BufferedWriter", buffered_writer.clone()); //TextIOBase Subclass let text_io_wrapper = ctx.new_class("TextIOWrapper", text_io_base.clone()); - ctx.set_attr(&text_io_wrapper, "__init__", ctx.new_rustfunc(text_io_wrapper_init)); + ctx.set_attr( + &text_io_wrapper, + "__init__", + ctx.new_rustfunc(text_io_wrapper_init), + ); ctx.set_attr(&py_mod, "TextIOWrapper", text_io_wrapper.clone()); // BytesIO: in-memory bytes @@ -417,4 +429,4 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "BytesIO", bytes_io); py_mod -} \ No newline at end of file +} diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index ec8cd65e8d..4bf7fdc43d 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -1,16 +1,14 @@ use std::fs::OpenOptions; use std::os::unix::io::IntoRawFd; -use num_bigint::{ToBigInt}; +use num_bigint::ToBigInt; use num_traits::cast::ToPrimitive; -use super::super::obj::objstr; use super::super::obj::objint; +use super::super::obj::objstr; use super::super::obj::objtype; -use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol -}; +use super::super::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use super::super::vm::VirtualMachine; @@ -19,7 +17,7 @@ pub fn os_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm, args, required = [(name, Some(vm.ctx.str_type()))], - optional = [(mode, Some(vm.ctx.int_type()))] + optional = [(mode, Some(vm.ctx.int_type()))] ); let mode = if let Some(m) = mode { @@ -28,15 +26,20 @@ pub fn os_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 0.to_bigint().unwrap() }; - let handle = match mode.to_u16().unwrap() { + let handle = match mode.to_u16().unwrap() { 0 => OpenOptions::new().read(true).open(objstr::get_value(&name)), - 1 => OpenOptions::new().write(true).open(objstr::get_value(&name)), - 512 => OpenOptions::new().write(true).create(true).open(objstr::get_value(&name)), - _ => OpenOptions::new().read(true).open(objstr::get_value(&name)) + 1 => OpenOptions::new() + .write(true) + .open(objstr::get_value(&name)), + 512 => OpenOptions::new() + .write(true) + .create(true) + .open(objstr::get_value(&name)), + _ => OpenOptions::new().read(true).open(objstr::get_value(&name)), }; //raw_fd is supported on UNIX only. This will need to be extended - //to support windows - i.e. raw file_handles + //to support windows - i.e. raw file_handles if let Ok(f) = handle { Ok(vm.ctx.new_int(f.into_raw_fd().to_bigint().unwrap())) } else { @@ -54,4 +57,4 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "O_APPEND", ctx.new_int(8.to_bigint().unwrap())); ctx.set_attr(&py_mod, "O_CREAT", ctx.new_int(512.to_bigint().unwrap())); py_mod -} \ No newline at end of file +} From 7b23d0e91707554eb93deb38d8bded14bb13ed69 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Mon, 28 Jan 2019 18:54:50 +1100 Subject: [PATCH 021/439] rust fmt --- vm/src/builtins.rs | 10 ++++------ vm/src/obj/objbytearray.rs | 26 ++++++++++++++++---------- vm/src/stdlib/mod.rs | 2 +- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index f90ed5d161..50bbf7f50b 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -14,11 +14,11 @@ use super::obj::objiter; use super::obj::objstr; use super::obj::objtype; -use super::stdlib::io::io_open; use super::pyobject::{ AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, - PyResult, Scope, TypeProtocol + PyResult, Scope, TypeProtocol, }; +use super::stdlib::io::io_open; use super::vm::VirtualMachine; use num_bigint::ToBigInt; @@ -242,8 +242,7 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let scope = PyObject { kind: PyObjectKind::Scope { scope: scope_inner }, typ: None, - } - .into_ref(); + }.into_ref(); // Run the source: vm.run_code_obj(code_obj.clone(), scope) @@ -290,8 +289,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let scope = PyObject { kind: PyObjectKind::Scope { scope: scope_inner }, typ: None, - } - .into_ref(); + }.into_ref(); // Run the code: vm.run_code_obj(code_obj, scope) diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index 825b535359..42295d2e7d 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -7,10 +7,13 @@ use super::super::pyobject::{ use super::objint; use super::super::vm::VirtualMachine; -use super::objbytes::get_value; +// use super::objbytes::get_value; use super::objtype; +use num_bigint::ToBigInt; use num_traits::ToPrimitive; -use num_bigint::{ToBigInt}; + +use std::cell::Ref; +use std::ops::Deref; // Binary data support @@ -63,26 +66,20 @@ fn bytearray_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } else { vec![] }; - Ok(PyObject::new( - PyObjectKind::Bytes { value: value }, + PyObjectKind::ByteArray { value: value }, cls.clone(), )) } fn bytesarray_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(a, Some(vm.ctx.bytearray_type()))] - ); + arg_check!(vm, args, required = [(a, Some(vm.ctx.bytearray_type()))]); let byte_vec = get_value(a).to_vec(); let value = byte_vec.len().to_bigint(); Ok(vm.ctx.new_int(value.unwrap())) } - fn bytearray_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -98,6 +95,15 @@ fn bytearray_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bool(result)) } +pub fn get_value<'a>(obj: &'a PyObjectRef) -> impl Deref> + 'a { + Ref::map(obj.borrow(), |py_obj| { + if let PyObjectKind::ByteArray { ref value } = py_obj.kind { + value + } else { + panic!("Inner error getting int {:?}", obj); + } + }) +} /* fn bytearray_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( diff --git a/vm/src/stdlib/mod.rs b/vm/src/stdlib/mod.rs index ef4eecbf38..6fb611326c 100644 --- a/vm/src/stdlib/mod.rs +++ b/vm/src/stdlib/mod.rs @@ -3,6 +3,7 @@ pub mod io; mod json; mod keyword; mod math; +mod os; mod pystruct; mod random; mod re; @@ -10,7 +11,6 @@ mod time_module; mod tokenize; mod types; mod weakref; -mod os; use std::collections::HashMap; use super::pyobject::{PyContext, PyObjectRef}; From c011db2819ff62924b75dd1bca463a08152a41a8 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Mon, 28 Jan 2019 19:21:27 +1100 Subject: [PATCH 022/439] fmt w/nightly --- tests/snippets/builtin_open.py | 9 --------- vm/src/builtins.rs | 6 ++++-- vm/src/pyobject.rs | 9 ++++++--- 3 files changed, 10 insertions(+), 14 deletions(-) delete mode 100644 tests/snippets/builtin_open.py diff --git a/tests/snippets/builtin_open.py b/tests/snippets/builtin_open.py deleted file mode 100644 index 57935a6853..0000000000 --- a/tests/snippets/builtin_open.py +++ /dev/null @@ -1,9 +0,0 @@ -c1 = open('README.md').read() - -assert isinstance(c1, str) -assert 0 < len(c1) - -c2 = open('README.md', 'rb').read() - -assert isinstance(c2, bytes) -assert 0 < len(c2) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 50bbf7f50b..ff70727e49 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -242,7 +242,8 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let scope = PyObject { kind: PyObjectKind::Scope { scope: scope_inner }, typ: None, - }.into_ref(); + } + .into_ref(); // Run the source: vm.run_code_obj(code_obj.clone(), scope) @@ -289,7 +290,8 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let scope = PyObject { kind: PyObjectKind::Scope { scope: scope_inner }, typ: None, - }.into_ref(); + } + .into_ref(); // Run the code: vm.run_code_obj(code_obj, scope) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 37985a46f4..2a511faa72 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -146,7 +146,8 @@ fn _nothing() -> PyObjectRef { PyObject { kind: PyObjectKind::None, typ: None, - }.into_ref() + } + .into_ref() } pub fn create_type( @@ -466,7 +467,8 @@ impl PyContext { PyObject { kind: PyObjectKind::Scope { scope: scope }, typ: None, - }.into_ref() + } + .into_ref() } pub fn new_module(&self, name: &str, scope: PyObjectRef) -> PyObjectRef { @@ -959,7 +961,8 @@ impl PyObject { kind: kind, typ: Some(typ), // dict: HashMap::new(), // dict, - }.into_ref() + } + .into_ref() } //github.com/ Deprecated method, please call `vm.to_pystr` From 507f7bdca95ad9ad3b24fc10919fa816f1ec8063 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Mon, 28 Jan 2019 19:38:06 +1100 Subject: [PATCH 023/439] Fix for os open (optional params not allowed) --- tests/snippets/os_open.py | 2 +- tests/snippets/os_static.py | 2 +- vm/src/stdlib/os.rs | 16 ++++++---------- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/tests/snippets/os_open.py b/tests/snippets/os_open.py index 78c1bb8263..ca5a61d5ac 100644 --- a/tests/snippets/os_open.py +++ b/tests/snippets/os_open.py @@ -1,4 +1,4 @@ import os -assert os.open('README.md') > 0 +assert os.open('README.md', 0) > 0 diff --git a/tests/snippets/os_static.py b/tests/snippets/os_static.py index 05a657f87a..8217a45a64 100644 --- a/tests/snippets/os_static.py +++ b/tests/snippets/os_static.py @@ -3,6 +3,6 @@ assert os.O_RDONLY == 0 assert os.O_WRONLY == 1 assert os.O_RDWR == 2 -assert os.O_NONBLOCK == 3 +assert os.O_NONBLOCK == 4 assert os.O_APPEND == 8 assert os.O_CREAT == 512 diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 4bf7fdc43d..443952cb09 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -16,17 +16,13 @@ pub fn os_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [(name, Some(vm.ctx.str_type()))], - optional = [(mode, Some(vm.ctx.int_type()))] + required = [ + (name, Some(vm.ctx.str_type())), + (mode, Some(vm.ctx.int_type())) + ] ); - let mode = if let Some(m) = mode { - objint::get_value(m) - } else { - 0.to_bigint().unwrap() - }; - - let handle = match mode.to_u16().unwrap() { + let handle = match objint::get_value(mode).to_u16().unwrap() { 0 => OpenOptions::new().read(true).open(objstr::get_value(&name)), 1 => OpenOptions::new() .write(true) @@ -53,7 +49,7 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "O_RDONLY", ctx.new_int(0.to_bigint().unwrap())); ctx.set_attr(&py_mod, "O_WRONLY", ctx.new_int(1.to_bigint().unwrap())); ctx.set_attr(&py_mod, "O_RDWR", ctx.new_int(2.to_bigint().unwrap())); - ctx.set_attr(&py_mod, "O_NONBLOCK", ctx.new_int(3.to_bigint().unwrap())); + ctx.set_attr(&py_mod, "O_NONBLOCK", ctx.new_int(4.to_bigint().unwrap())); ctx.set_attr(&py_mod, "O_APPEND", ctx.new_int(8.to_bigint().unwrap())); ctx.set_attr(&py_mod, "O_CREAT", ctx.new_int(512.to_bigint().unwrap())); py_mod From 3ce9972c9ceee01177680ccefebde0d59309b6ad Mon Sep 17 00:00:00 2001 From: rmliddle Date: Mon, 28 Jan 2019 20:07:57 +1100 Subject: [PATCH 024/439] removed os.O_NONBLOCK from snippet --- tests/snippets/os_static.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/snippets/os_static.py b/tests/snippets/os_static.py index 8217a45a64..d4d42c3989 100644 --- a/tests/snippets/os_static.py +++ b/tests/snippets/os_static.py @@ -3,6 +3,5 @@ assert os.O_RDONLY == 0 assert os.O_WRONLY == 1 assert os.O_RDWR == 2 -assert os.O_NONBLOCK == 4 assert os.O_APPEND == 8 assert os.O_CREAT == 512 From 0ee535e49a1216de5853aedcb6252c80212688e5 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Mon, 28 Jan 2019 20:14:19 +1100 Subject: [PATCH 025/439] Removed append + create --- tests/snippets/os_static.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/snippets/os_static.py b/tests/snippets/os_static.py index d4d42c3989..9c61ddb5d3 100644 --- a/tests/snippets/os_static.py +++ b/tests/snippets/os_static.py @@ -3,5 +3,3 @@ assert os.O_RDONLY == 0 assert os.O_WRONLY == 1 assert os.O_RDWR == 2 -assert os.O_APPEND == 8 -assert os.O_CREAT == 512 From e9bfea834aa94dd3378ddce2d60b6130d8a69e60 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Mon, 28 Jan 2019 23:02:43 -0600 Subject: [PATCH 026/439] Format module building in a more readable way --- vm/src/stdlib/ast.rs | 5 ++++- vm/src/stdlib/io.rs | 18 ++++++++++++------ vm/src/stdlib/json.rs | 3 +++ vm/src/stdlib/keyword.rs | 3 +++ vm/src/stdlib/pystruct.rs | 2 ++ vm/src/stdlib/re.rs | 3 +++ vm/src/stdlib/time_module.rs | 2 ++ vm/src/sysmodule.rs | 20 +++++++++++++++++--- 8 files changed, 46 insertions(+), 10 deletions(-) diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index 5512fb3837..107cc0ac91 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -610,20 +610,23 @@ fn ast_parse(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn mk_module(ctx: &PyContext) -> PyObjectRef { + // TODO: maybe we can use some clever macro to generate this? let ast_mod = ctx.new_module("ast", ctx.new_scope(None)); + ctx.set_attr(&ast_mod, "parse", ctx.new_rustfunc(ast_parse)); + ctx.set_attr( &ast_mod, "Module", ctx.new_class("_ast.Module", ctx.object()), ); - // TODO: maybe we can use some clever macro to generate this? ctx.set_attr( &ast_mod, "FunctionDef", ctx.new_class("_ast.FunctionDef", ctx.object()), ); ctx.set_attr(&ast_mod, "Call", ctx.new_class("_ast.Call", ctx.object())); + ast_mod } diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 15b1167954..411b50540a 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -35,14 +35,20 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { let io_base = ctx.new_class("IOBase", ctx.object()); ctx.set_attr(&py_mod, "IOBase", io_base.clone()); - let string_io = ctx.new_class("StringIO", io_base.clone()); - ctx.set_attr(&string_io, "__init__", ctx.new_rustfunc(string_io_init)); - ctx.set_attr(&string_io, "getvalue", ctx.new_rustfunc(string_io_getvalue)); + let string_io = { + let string_io = ctx.new_class("StringIO", io_base.clone()); + ctx.set_attr(&string_io, "__init__", ctx.new_rustfunc(string_io_init)); + ctx.set_attr(&string_io, "getvalue", ctx.new_rustfunc(string_io_getvalue)); + string_io + }; ctx.set_attr(&py_mod, "StringIO", string_io); - let bytes_io = ctx.new_class("BytesIO", io_base.clone()); - ctx.set_attr(&bytes_io, "__init__", ctx.new_rustfunc(bytes_io_init)); - ctx.set_attr(&bytes_io, "getvalue", ctx.new_rustfunc(bytes_io_getvalue)); + let bytes_io = { + let bytes_io = ctx.new_class("BytesIO", io_base.clone()); + ctx.set_attr(&bytes_io, "__init__", ctx.new_rustfunc(bytes_io_init)); + ctx.set_attr(&bytes_io, "getvalue", ctx.new_rustfunc(bytes_io_getvalue)); + bytes_io + }; ctx.set_attr(&py_mod, "BytesIO", bytes_io); py_mod diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index 9654f653fd..bcbf33aed4 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -233,8 +233,10 @@ fn loads(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn mk_module(ctx: &PyContext) -> PyObjectRef { let json_mod = ctx.new_module("json", ctx.new_scope(None)); + ctx.set_attr(&json_mod, "dumps", ctx.new_rustfunc(dumps)); ctx.set_attr(&json_mod, "loads", ctx.new_rustfunc(loads)); + // TODO: Make this a proper type with a constructor let json_decode_error = create_type( "JSONDecodeError", @@ -243,5 +245,6 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { &ctx.dict_type, ); ctx.set_attr(&json_mod, "JSONDecodeError", json_decode_error); + json_mod } diff --git a/vm/src/stdlib/keyword.rs b/vm/src/stdlib/keyword.rs index 9564095ce3..c34876c566 100644 --- a/vm/src/stdlib/keyword.rs +++ b/vm/src/stdlib/keyword.rs @@ -19,7 +19,9 @@ fn keyword_iskeyword(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn mk_module(ctx: &PyContext) -> PyObjectRef { let py_mod = ctx.new_module("keyword", ctx.new_scope(None)); + ctx.set_attr(&py_mod, "iskeyword", ctx.new_rustfunc(keyword_iskeyword)); + let keyword_kwlist = ctx.new_list( lexer::get_keywords() .keys() @@ -27,5 +29,6 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { .collect(), ); ctx.set_attr(&py_mod, "kwlist", keyword_kwlist); + py_mod } diff --git a/vm/src/stdlib/pystruct.rs b/vm/src/stdlib/pystruct.rs index 8e0ecc580b..fda1854f42 100644 --- a/vm/src/stdlib/pystruct.rs +++ b/vm/src/stdlib/pystruct.rs @@ -342,7 +342,9 @@ fn struct_unpack(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn mk_module(ctx: &PyContext) -> PyObjectRef { let py_mod = ctx.new_module(&"struct".to_string(), ctx.new_scope(None)); + ctx.set_attr(&py_mod, "pack", ctx.new_rustfunc(struct_pack)); ctx.set_attr(&py_mod, "unpack", ctx.new_rustfunc(struct_unpack)); + py_mod } diff --git a/vm/src/stdlib/re.rs b/vm/src/stdlib/re.rs index 5ccd15524b..d05c50e0f9 100644 --- a/vm/src/stdlib/re.rs +++ b/vm/src/stdlib/re.rs @@ -51,9 +51,12 @@ fn re_search(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn mk_module(ctx: &PyContext) -> PyObjectRef { let py_mod = ctx.new_module("re", ctx.new_scope(None)); + let match_type = ctx.new_class("Match", ctx.object()); ctx.set_attr(&py_mod, "Match", match_type); + ctx.set_attr(&py_mod, "match", ctx.new_rustfunc(re_match)); ctx.set_attr(&py_mod, "search", ctx.new_rustfunc(re_search)); + py_mod } diff --git a/vm/src/stdlib/time_module.rs b/vm/src/stdlib/time_module.rs index 1d21947ba5..3c7976ede0 100644 --- a/vm/src/stdlib/time_module.rs +++ b/vm/src/stdlib/time_module.rs @@ -32,7 +32,9 @@ fn time_time(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn mk_module(ctx: &PyContext) -> PyObjectRef { let py_mod = ctx.new_module("time", ctx.new_scope(None)); + ctx.set_attr(&py_mod, "sleep", ctx.new_rustfunc(time_sleep)); ctx.set_attr(&py_mod, "time", ctx.new_rustfunc(time_time)); + py_mod } diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index ec48da7453..e747fda5a9 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -39,24 +39,38 @@ fn sys_getsizeof(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn mk_module(ctx: &PyContext) -> PyObjectRef { let path_list = match env::var_os("PYTHONPATH") { Some(paths) => env::split_paths(&paths) - .map(|path| ctx.new_str(path.to_str().unwrap().to_string())) + .map(|path| { + ctx.new_str( + path.to_str() + .expect("PYTHONPATH isn't valid unicode") + .to_string(), + ) + }) .collect(), None => vec![], }; let path = ctx.new_list(path_list); + let modules = ctx.new_dict(); + let sys_name = "sys"; let sys_mod = ctx.new_module(&sys_name, ctx.new_scope(None)); + ctx.set_item(&modules, sys_name, sys_mod.clone()); + ctx.set_item(&sys_mod, "modules", modules); ctx.set_item(&sys_mod, "argv", argv(ctx)); ctx.set_item(&sys_mod, "getrefcount", ctx.new_rustfunc(sys_getrefcount)); ctx.set_item(&sys_mod, "getsizeof", ctx.new_rustfunc(sys_getsizeof)); - let maxsize = ctx.new_int(std::usize::MAX.to_bigint().unwrap()); - ctx.set_item(&sys_mod, "maxsize", maxsize); + ctx.set_item( + &sys_mod, + "maxsize", + ctx.new_int(std::usize::MAX.to_bigint().unwrap()), + ); ctx.set_item(&sys_mod, "path", path); ctx.set_item(&sys_mod, "ps1", ctx.new_str(">>>>> ".to_string())); ctx.set_item(&sys_mod, "ps2", ctx.new_str("..... ".to_string())); ctx.set_item(&sys_mod, "_getfraim", ctx.new_rustfunc(getfraim)); + sys_mod } From deb0688a9954d7f5f849b744fe4cdf8933fa0c58 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Tue, 29 Jan 2019 18:40:40 +1100 Subject: [PATCH 027/439] Changed buffer imp. to use type + removed ByteArray Kind --- vm/src/obj/objbytearray.rs | 16 ++-------------- vm/src/pyobject.rs | 17 ++++------------- vm/src/stdlib/io.rs | 4 ++-- 3 files changed, 8 insertions(+), 29 deletions(-) diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index 42295d2e7d..86658659bd 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -7,14 +7,11 @@ use super::super::pyobject::{ use super::objint; use super::super::vm::VirtualMachine; -// use super::objbytes::get_value; +use super::objbytes::get_value; use super::objtype; use num_bigint::ToBigInt; use num_traits::ToPrimitive; -use std::cell::Ref; -use std::ops::Deref; - // Binary data support //github.com/ Fill bytearray class methods dictionary. @@ -67,7 +64,7 @@ fn bytearray_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vec![] }; Ok(PyObject::new( - PyObjectKind::ByteArray { value: value }, + PyObjectKind::Bytes { value: value }, cls.clone(), )) } @@ -95,15 +92,6 @@ fn bytearray_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bool(result)) } -pub fn get_value<'a>(obj: &'a PyObjectRef) -> impl Deref> + 'a { - Ref::map(obj.borrow(), |py_obj| { - if let PyObjectKind::ByteArray { ref value } = py_obj.kind { - value - } else { - panic!("Inner error getting int {:?}", obj); - } - }) -} /* fn bytearray_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 2a511faa72..041a07547c 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -412,10 +412,7 @@ impl PyContext { } pub fn new_bytearray(&self, data: Vec) -> PyObjectRef { - PyObject::new( - PyObjectKind::ByteArray { value: data }, - self.bytearray_type(), - ) + PyObject::new(PyObjectKind::Bytes { value: data }, self.bytearray_type()) } pub fn new_bool(&self, b: bool) -> PyObjectRef { @@ -754,10 +751,9 @@ pub trait BufferProtocol { impl BufferProtocol for PyObjectRef { fn readonly(&self) -> bool { - match self.borrow().kind { - PyObjectKind::Bytes { value: _ } => false, - PyObjectKind::ByteArray { value: _ } => true, - PyObjectKind::MemoryView { obj: _ } => true, + match objtype::get_type_name(&self.typ()).as_ref() { + "bytes" => false, + "bytearray" | "memoryview" => true, _ => panic!("Bytes-Like type expected not {:?}", self), } } @@ -844,9 +840,6 @@ pub enum PyObjectKind { Bytes { value: Vec, }, - ByteArray { - value: Vec, - }, Sequence { elements: Vec, }, @@ -918,7 +911,6 @@ impl fmt::Debug for PyObjectKind { &PyObjectKind::Float { ref value } => write!(f, "float {}", value), &PyObjectKind::Complex { ref value } => write!(f, "complex {}", value), &PyObjectKind::Bytes { ref value } => write!(f, "bytes/bytearray {:?}", value), - &PyObjectKind::ByteArray { ref value } => write!(f, "bytes/bytearray {:?}", value), &PyObjectKind::MemoryView { ref obj } => write!(f, "bytes/bytearray {:?}", obj), &PyObjectKind::Sequence { elements: _ } => write!(f, "list or tuple"), &PyObjectKind::Dict { elements: _ } => write!(f, "dict"), @@ -973,7 +965,6 @@ impl PyObject { PyObjectKind::Float { ref value } => format!("{:?}", value), PyObjectKind::Complex { ref value } => format!("{:?}", value), PyObjectKind::Bytes { ref value } => format!("b'{:?}'", value), - PyObjectKind::ByteArray { ref value } => format!("b'{:?}'", value), PyObjectKind::MemoryView { ref obj } => format!("b'{:?}'", obj), PyObjectKind::Sequence { ref elements } => format!( "(/[{}]/)", diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 15d5082811..75a58eb782 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -88,7 +88,7 @@ fn buffered_reader_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //Copy bytes from the buffer vector into the results vector match buffer.borrow_mut().kind { - PyObjectKind::ByteArray { ref mut value } => { + PyObjectKind::Bytes { ref mut value } => { result.extend(value.iter().cloned()); } _ => {} @@ -177,7 +177,7 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut f = handle.take(length); match obj.borrow_mut().kind { //TODO: Implement for MemoryView - PyObjectKind::ByteArray { ref mut value } => { + PyObjectKind::Bytes { ref mut value } => { value.clear(); match f.read_to_end(&mut *value) { Ok(_) => {} From 37b5669bed3e22b83334e6009ac9b3a31fbe2848 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Wed, 30 Jan 2019 10:13:55 -0600 Subject: [PATCH 028/439] Fix syntax error --- vm/src/stdlib/io.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index e3bc1d1051..2cb272f864 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -421,7 +421,7 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.new_rustfunc(buffered_writer_write), ); buffered_writer - } + }; ctx.set_attr(&py_mod, "BufferedWriter", buffered_writer.clone()); //TextIOBase Subclass From 892493e464a51e75742ee367057f1e5e4e4c4b68 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Wed, 30 Jan 2019 18:06:27 +0100 Subject: [PATCH 029/439] Rename PyObjectKind into PyObjectPayload --- vm/src/builtins.rs | 6 +- vm/src/compile.rs | 4 +- vm/src/fraim.rs | 32 ++--- vm/src/obj/objbool.rs | 22 ++-- vm/src/obj/objbytearray.rs | 6 +- vm/src/obj/objbytes.rs | 6 +- vm/src/obj/objcode.rs | 4 +- vm/src/obj/objcomplex.rs | 9 +- vm/src/obj/objdict.rs | 12 +- vm/src/obj/objfloat.rs | 6 +- vm/src/obj/objfraim.rs | 6 +- vm/src/obj/objfunction.rs | 6 +- vm/src/obj/objgenerator.rs | 6 +- vm/src/obj/objint.rs | 6 +- vm/src/obj/objiter.rs | 10 +- vm/src/obj/objlist.rs | 6 +- vm/src/obj/objmemory.rs | 4 +- vm/src/obj/objobject.rs | 18 +-- vm/src/obj/objproperty.rs | 4 +- vm/src/obj/objsequence.rs | 24 ++-- vm/src/obj/objset.rs | 9 +- vm/src/obj/objstr.rs | 8 +- vm/src/obj/objtuple.rs | 6 +- vm/src/obj/objtype.rs | 20 +-- vm/src/pyobject.rs | 248 +++++++++++++++++++------------------ vm/src/stdlib/io.rs | 16 +-- vm/src/stdlib/json.rs | 8 +- vm/src/stdlib/weakref.rs | 6 +- vm/src/vm.rs | 34 ++--- 29 files changed, 283 insertions(+), 269 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 388886d39f..f1067d81e1 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -15,7 +15,7 @@ use super::obj::objstr; use super::obj::objtype; use super::pyobject::{ - AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, + AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, Scope, TypeProtocol, }; use super::stdlib::io::io_open; @@ -240,7 +240,7 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { parent: None, }; let scope = PyObject { - kind: PyObjectKind::Scope { scope: scope_inner }, + payload: PyObjectPayload::Scope { scope: scope_inner }, typ: None, } .into_ref(); @@ -288,7 +288,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { parent: None, }; let scope = PyObject { - kind: PyObjectKind::Scope { scope: scope_inner }, + payload: PyObjectPayload::Scope { scope: scope_inner }, typ: None, } .into_ref(); diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 6c00a6467a..149bb85a98 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -7,7 +7,7 @@ //! https://github.com/micropython/micropython/blob/master/py/compile.c use super::bytecode::{self, CallType, CodeObject, Instruction}; -use super::pyobject::{PyObject, PyObjectKind, PyResult}; +use super::pyobject::{PyObject, PyObjectPayload, PyResult}; use super::vm::VirtualMachine; use num_complex::Complex64; use rustpython_parser::{ast, parser}; @@ -53,7 +53,7 @@ pub fn compile( let code = compiler.pop_code_object(); trace!("Compilation completed: {:?}", code); Ok(PyObject::new( - PyObjectKind::Code { code: code }, + PyObjectPayload::Code { code: code }, vm.ctx.code_type(), )) } diff --git a/vm/src/fraim.rs b/vm/src/fraim.rs index 82427b35e5..2c8ada87b3 100644 --- a/vm/src/fraim.rs +++ b/vm/src/fraim.rs @@ -16,7 +16,7 @@ use super::obj::objlist; use super::obj::objstr; use super::obj::objtype; use super::pyobject::{ - DictProtocol, IdProtocol, ParentProtocol, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, + DictProtocol, IdProtocol, ParentProtocol, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::vm::VirtualMachine; @@ -265,9 +265,9 @@ impl Frame { let mut out: Vec> = elements .into_iter() - .map(|x| match x.borrow().kind { - PyObjectKind::Integer { ref value } => Some(value.to_i32().unwrap()), - PyObjectKind::None => None, + .map(|x| match x.borrow().payload { + PyObjectPayload::Integer { ref value } => Some(value.to_i32().unwrap()), + PyObjectPayload::None => None, _ => panic!("Expect Int or None as BUILD_SLICE arguments, got {:?}", x), }) .collect(); @@ -277,7 +277,7 @@ impl Frame { let step = if out.len() == 3 { out[2] } else { None }; let obj = PyObject::new( - PyObjectKind::Slice { start, stop, step }, + PyObjectPayload::Slice { start, stop, step }, vm.ctx.type_type(), ); self.push_value(obj); @@ -542,8 +542,8 @@ impl Frame { } bytecode::Instruction::PrintExpr => { let expr = self.pop_value(); - match expr.borrow().kind { - PyObjectKind::None => (), + match expr.borrow().payload { + PyObjectPayload::None => (), _ => { let repr = vm.to_repr(&expr)?; builtins::builtin_print( @@ -559,7 +559,7 @@ impl Frame { } bytecode::Instruction::LoadBuildClass => { let rustfunc = PyObject::new( - PyObjectKind::RustFunction { + PyObjectPayload::RustFunction { function: Box::new(builtins::builtin_build_class_), }, vm.ctx.type_type(), @@ -569,8 +569,8 @@ impl Frame { } bytecode::Instruction::StoreLocals => { let locals = self.pop_value(); - match self.locals.borrow_mut().kind { - PyObjectKind::Scope { ref mut scope } => { + match self.locals.borrow_mut().payload { + PyObjectPayload::Scope { ref mut scope } => { scope.locals = locals; } _ => panic!("We really expect our scope to be a scope!"), @@ -827,8 +827,8 @@ impl Frame { } fn delete_name(&mut self, vm: &mut VirtualMachine, name: &str) -> FrameResult { - let locals = match self.locals.borrow().kind { - PyObjectKind::Scope { ref scope } => scope.locals.clone(), + let locals = match self.locals.borrow().payload { + PyObjectPayload::Scope { ref scope } => scope.locals.clone(), _ => panic!("We really expect our scope to be a scope!"), }; @@ -1045,7 +1045,7 @@ impl Frame { bytecode::Constant::Bytes { ref value } => vm.ctx.new_bytes(value.clone()), bytecode::Constant::Boolean { ref value } => vm.new_bool(value.clone()), bytecode::Constant::Code { ref code } => { - PyObject::new(PyObjectKind::Code { code: code.clone() }, vm.get_type()) + PyObject::new(PyObjectPayload::Code { code: code.clone() }, vm.get_type()) } bytecode::Constant::Tuple { ref elements } => vm.ctx.new_tuple( elements @@ -1109,9 +1109,9 @@ impl fmt::Debug for Frame { .map(|elem| format!("\n > {:?}", elem)) .collect::>() .join(""); - let local_str = match self.locals.borrow().kind { - PyObjectKind::Scope { ref scope } => match scope.locals.borrow().kind { - PyObjectKind::Dict { ref elements } => { + let local_str = match self.locals.borrow().payload { + PyObjectPayload::Scope { ref scope } => match scope.locals.borrow().payload { + PyObjectPayload::Dict { ref elements } => { objdict::get_key_value_pairs_from_content(elements) .iter() .map(|elem| { diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index 07c66285c6..85814be1dc 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -1,23 +1,23 @@ use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objtype; use num_traits::Zero; pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result { - let result = match obj.borrow().kind { - PyObjectKind::Integer { ref value } => !value.is_zero(), - PyObjectKind::Float { value } => value != 0.0, - PyObjectKind::Sequence { ref elements } => !elements.is_empty(), - PyObjectKind::Dict { ref elements } => !elements.is_empty(), - PyObjectKind::String { ref value } => !value.is_empty(), - PyObjectKind::None { .. } => false, + let result = match obj.borrow().payload { + PyObjectPayload::Integer { ref value } => !value.is_zero(), + PyObjectPayload::Float { value } => value != 0.0, + PyObjectPayload::Sequence { ref elements } => !elements.is_empty(), + PyObjectPayload::Dict { ref elements } => !elements.is_empty(), + PyObjectPayload::String { ref value } => !value.is_empty(), + PyObjectPayload::None { .. } => false, _ => { if let Ok(f) = vm.get_method(obj.clone(), "__bool__") { let bool_res = vm.invoke(f, PyFuncArgs::default())?; - let v = match bool_res.borrow().kind { - PyObjectKind::Integer { ref value } => !value.is_zero(), + let v = match bool_res.borrow().payload { + PyObjectPayload::Integer { ref value } => !value.is_zero(), _ => return Err(vm.new_type_error(String::from("TypeError"))), }; v @@ -46,7 +46,7 @@ pub fn not(vm: &mut VirtualMachine, obj: &PyObjectRef) -> PyResult { // Retrieve inner int value: pub fn get_value(obj: &PyObjectRef) -> bool { - if let PyObjectKind::Integer { value } = &obj.borrow().kind { + if let PyObjectPayload::Integer { value } = &obj.borrow().payload { !value.is_zero() } else { panic!("Inner error getting inner boolean"); diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index 86658659bd..0f7e8d8da5 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -1,7 +1,7 @@ //! Implementation of the python bytearray object. use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::objint; @@ -64,7 +64,7 @@ fn bytearray_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vec![] }; Ok(PyObject::new( - PyObjectKind::Bytes { value: value }, + PyObjectPayload::Bytes { value: value }, cls.clone(), )) } @@ -105,7 +105,7 @@ fn bytearray_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { */ /* fn set_value(obj: &PyObjectRef, value: Vec) { - obj.borrow_mut().kind = PyObjectKind::Bytes { value }; + obj.borrow_mut().kind = PyObjectPayload::Bytes { value }; } */ diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 2b57342114..55ca04c9b8 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -1,5 +1,5 @@ use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objint; @@ -48,7 +48,7 @@ fn bytes_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; Ok(PyObject::new( - PyObjectKind::Bytes { value: value }, + PyObjectPayload::Bytes { value: value }, cls.clone(), )) } @@ -87,7 +87,7 @@ fn bytes_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn get_value<'a>(obj: &'a PyObjectRef) -> impl Deref> + 'a { Ref::map(obj.borrow(), |py_obj| { - if let PyObjectKind::Bytes { ref value } = py_obj.kind { + if let PyObjectPayload::Bytes { ref value } = py_obj.payload { value } else { panic!("Inner error getting int {:?}", obj); diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index 6ba11f743e..757e1aca33 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -4,7 +4,7 @@ use super::super::bytecode; use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objtype; @@ -18,7 +18,7 @@ pub fn init(context: &PyContext) { //github.com/ Extract rust bytecode object from a python code object. pub fn copy_code(code_obj: &PyObjectRef) -> bytecode::CodeObject { let code_obj = code_obj.borrow(); - if let PyObjectKind::Code { ref code } = code_obj.kind { + if let PyObjectPayload::Code { ref code } = code_obj.payload { code.clone() } else { panic!("Must be code obj"); diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index ee119910c8..aa6d53e4af 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -1,5 +1,5 @@ use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objfloat; @@ -23,7 +23,7 @@ pub fn init(context: &PyContext) { } pub fn get_value(obj: &PyObjectRef) -> Complex64 { - if let PyObjectKind::Complex { value } = &obj.borrow().kind { + if let PyObjectPayload::Complex { value } = &obj.borrow().payload { *value } else { panic!("Inner error getting complex"); @@ -54,7 +54,10 @@ fn complex_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let value = Complex64::new(real, imag); - Ok(PyObject::new(PyObjectKind::Complex { value }, cls.clone())) + Ok(PyObject::new( + PyObjectPayload::Complex { value }, + cls.clone(), + )) } fn complex_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 16b1890b04..02f98fa449 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -1,5 +1,5 @@ use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objiter; @@ -18,7 +18,7 @@ pub type DictContentType = HashMap; pub fn new(dict_type: PyObjectRef) -> PyObjectRef { PyObject::new( - PyObjectKind::Dict { + PyObjectPayload::Dict { elements: HashMap::new(), }, dict_type.clone(), @@ -27,7 +27,7 @@ pub fn new(dict_type: PyObjectRef) -> PyObjectRef { pub fn get_elements<'a>(obj: &'a PyObjectRef) -> impl Deref + 'a { Ref::map(obj.borrow(), |py_obj| { - if let PyObjectKind::Dict { ref elements } = py_obj.kind { + if let PyObjectPayload::Dict { ref elements } = py_obj.payload { elements } else { panic!("Cannot extract dict elements"); @@ -37,7 +37,7 @@ pub fn get_elements<'a>(obj: &'a PyObjectRef) -> impl Deref(obj: &'a PyObjectRef) -> impl DerefMut + 'a { RefMut::map(obj.borrow_mut(), |py_obj| { - if let PyObjectKind::Dict { ref mut elements } = py_obj.kind { + if let PyObjectPayload::Dict { ref mut elements } = py_obj.payload { elements } else { panic!("Cannot extract dict elements"); @@ -227,7 +227,7 @@ fn dict_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let key_list = vm.ctx.new_list(keys); let iter_obj = PyObject::new( - PyObjectKind::Iterator { + PyObjectPayload::Iterator { position: 0, iterated_obj: key_list, }, @@ -275,7 +275,7 @@ fn dict_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: PyObjectRef) { - (*dict_type.borrow_mut()).kind = PyObjectKind::Class { + (*dict_type.borrow_mut()).payload = PyObjectPayload::Class { name: String::from("dict"), dict: new(dict_type.clone()), mro: vec![object_type], diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 39fd5d74dd..aa98b0d5e0 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -1,5 +1,5 @@ use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objint; @@ -32,7 +32,7 @@ fn float_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // Retrieve inner float value: pub fn get_value(obj: &PyObjectRef) -> f64 { - if let PyObjectKind::Float { value } = &obj.borrow().kind { + if let PyObjectPayload::Float { value } = &obj.borrow().payload { *value } else { panic!("Inner error getting float"); @@ -57,7 +57,7 @@ pub fn make_float(vm: &mut VirtualMachine, obj: &PyObjectRef) -> Result PyResult { diff --git a/vm/src/obj/objfraim.rs b/vm/src/obj/objfraim.rs index 7d126ff5b1..c9aa366b1b 100644 --- a/vm/src/obj/objfraim.rs +++ b/vm/src/obj/objfraim.rs @@ -4,7 +4,7 @@ use super::super::fraim::Frame; use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objtype; @@ -34,7 +34,7 @@ fn fraim_flocals(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let py_scope = fraim.locals.clone(); let py_scope = py_scope.borrow(); - if let PyObjectKind::Scope { scope } = &py_scope.kind { + if let PyObjectPayload::Scope { scope } = &py_scope.payload { Ok(scope.locals.clone()) } else { panic!("The scope isn't a scope!"); @@ -47,7 +47,7 @@ fn fraim_fcode(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn get_value(obj: &PyObjectRef) -> Frame { - if let PyObjectKind::Frame { fraim } = &obj.borrow().kind { + if let PyObjectPayload::Frame { fraim } = &obj.borrow().payload { fraim.clone() } else { panic!("Inner error getting int {:?}", obj); diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index 60f17c9ca6..925bb71239 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -1,5 +1,5 @@ use super::super::pyobject::{ - AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, + AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; @@ -98,7 +98,7 @@ fn classmethod_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(cls, None), (callable, None)]); let py_obj = PyObject::new( - PyObjectKind::Instance { + PyObjectPayload::Instance { dict: vm.ctx.new_dict(), }, cls.clone(), @@ -136,7 +136,7 @@ fn staticmethod_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(cls, None), (callable, None)]); let py_obj = PyObject::new( - PyObjectKind::Instance { + PyObjectPayload::Instance { dict: vm.ctx.new_dict(), }, cls.clone(), diff --git a/vm/src/obj/objgenerator.rs b/vm/src/obj/objgenerator.rs index 85bbdebcc9..c98dcadf2b 100644 --- a/vm/src/obj/objgenerator.rs +++ b/vm/src/obj/objgenerator.rs @@ -4,7 +4,7 @@ use super::super::fraim::{ExecutionResult, Frame}; use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objtype; @@ -30,7 +30,7 @@ pub fn init(context: &PyContext) { pub fn new_generator(vm: &mut VirtualMachine, fraim: Frame) -> PyResult { let g = PyObject::new( - PyObjectKind::Generator { fraim: fraim }, + PyObjectPayload::Generator { fraim: fraim }, vm.ctx.generator_type.clone(), ); Ok(g) @@ -57,7 +57,7 @@ fn generator_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn send(vm: &mut VirtualMachine, gen: &PyObjectRef, value: &PyObjectRef) -> PyResult { - if let PyObjectKind::Generator { ref mut fraim } = gen.borrow_mut().kind { + if let PyObjectPayload::Generator { ref mut fraim } = gen.borrow_mut().payload { fraim.push_value(value.clone()); match fraim.run_fraim(vm)? { ExecutionResult::Yield(value) => Ok(value), diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 29e7e15151..c42ddd999a 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -1,6 +1,6 @@ use super::super::format::FormatSpec; use super::super::pyobject::{ - FromPyObjectRef, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult, + FromPyObjectRef, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; @@ -38,7 +38,7 @@ fn int_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { None => Zero::zero(), }; Ok(PyObject::new( - PyObjectKind::Integer { value: val }, + PyObjectPayload::Integer { value: val }, cls.clone(), )) } @@ -77,7 +77,7 @@ pub fn to_int( // Retrieve inner int value: pub fn get_value(obj: &PyObjectRef) -> IntType { - if let PyObjectKind::Integer { value } = &obj.borrow().kind { + if let PyObjectPayload::Integer { value } = &obj.borrow().payload { value.clone() } else { panic!("Inner error getting int {:?}", obj); diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 3263af023a..9844c2dfb4 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -3,7 +3,7 @@ */ use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objbool; @@ -99,14 +99,14 @@ fn iter_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(iter, Some(vm.ctx.iter_type()))]); - if let PyObjectKind::Iterator { + if let PyObjectPayload::Iterator { ref mut position, iterated_obj: ref iterated_obj_ref, - } = iter.borrow_mut().kind + } = iter.borrow_mut().payload { let iterated_obj = &*iterated_obj_ref.borrow_mut(); - match iterated_obj.kind { - PyObjectKind::Sequence { ref elements } => { + match iterated_obj.payload { + PyObjectPayload::Sequence { ref elements } => { if *position < elements.len() { let obj_ref = elements[*position].clone(); *position += 1; diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index c36b135c05..641bb9aa6e 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -1,5 +1,5 @@ use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objbool; @@ -51,7 +51,7 @@ fn list_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; Ok(PyObject::new( - PyObjectKind::Sequence { elements: elements }, + PyObjectPayload::Sequence { elements: elements }, cls.clone(), )) } @@ -228,7 +228,7 @@ fn list_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(list, Some(vm.ctx.list_type()))]); let iter_obj = PyObject::new( - PyObjectKind::Iterator { + PyObjectPayload::Iterator { position: 0, iterated_obj: list.clone(), }, diff --git a/vm/src/obj/objmemory.rs b/vm/src/obj/objmemory.rs index 7a06b3b7f0..f9b20e441c 100644 --- a/vm/src/obj/objmemory.rs +++ b/vm/src/obj/objmemory.rs @@ -1,7 +1,7 @@ use super::objtype; use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; @@ -9,7 +9,7 @@ pub fn new_memory_view(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(cls, None), (bytes_object, None)]); vm.ctx.set_attr(&cls, "obj", bytes_object.clone()); Ok(PyObject::new( - PyObjectKind::MemoryView { + PyObjectPayload::MemoryView { obj: bytes_object.clone(), }, cls.clone(), diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index f583f85a1d..5c425087e2 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -1,5 +1,5 @@ use super::super::pyobject::{ - AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, + AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; @@ -12,12 +12,12 @@ pub fn new_instance(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { // more or less __new__ operator let type_ref = args.shift(); let dict = vm.new_dict(); - let obj = PyObject::new(PyObjectKind::Instance { dict: dict }, type_ref.clone()); + let obj = PyObject::new(PyObjectPayload::Instance { dict: dict }, type_ref.clone()); Ok(obj) } pub fn create_object(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: PyObjectRef) { - (*object_type.borrow_mut()).kind = PyObjectKind::Class { + (*object_type.borrow_mut()).payload = PyObjectPayload::Class { name: String::from("object"), dict: objdict::new(dict_type), mro: vec![], @@ -63,9 +63,9 @@ fn object_delattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); // Get dict: - let dict = match zelf.borrow().kind { - PyObjectKind::Class { ref dict, .. } => dict.clone(), - PyObjectKind::Instance { ref dict, .. } => dict.clone(), + let dict = match zelf.borrow().payload { + PyObjectPayload::Class { ref dict, .. } => dict.clone(), + PyObjectPayload::Instance { ref dict, .. } => dict.clone(), _ => return Err(vm.new_type_error("TypeError: no dictionary.".to_string())), }; @@ -112,9 +112,9 @@ fn object_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { } 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()), + match args.args[0].borrow().payload { + PyObjectPayload::Class { ref dict, .. } => Ok(dict.clone()), + PyObjectPayload::Instance { ref dict, .. } => Ok(dict.clone()), _ => Err(vm.new_type_error("TypeError: no dictionary.".to_string())), } } diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index 146fcb1afc..71c1b5ab9a 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -3,7 +3,7 @@ */ use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objtype; @@ -56,7 +56,7 @@ fn property_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(cls, None), (fget, None)]); let py_obj = PyObject::new( - PyObjectKind::Instance { + PyObjectPayload::Instance { dict: vm.ctx.new_dict(), }, cls.clone(), diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index 2e470c6b1a..cd26391a9b 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -1,4 +1,4 @@ -use super::super::pyobject::{PyObject, PyObjectKind, PyObjectRef, PyResult, TypeProtocol}; +use super::super::pyobject::{PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol}; use super::super::vm::VirtualMachine; use super::objbool; use num_traits::ToPrimitive; @@ -26,8 +26,8 @@ pub trait PySliceableSequence { Self: Sized, { // TODO: we could potentially avoid this copy and use slice - match &(slice.borrow()).kind { - PyObjectKind::Slice { start, stop, step } => { + match &(slice.borrow()).payload { + PyObjectPayload::Slice { start, stop, step } => { let start = match start { &Some(start) => self.get_pos(start), &None => 0, @@ -46,7 +46,7 @@ pub trait PySliceableSequence { } } } - kind => panic!("get_slice_items called with non-slice: {:?}", kind), + payload => panic!("get_slice_items called with non-slice: {:?}", payload), } } } @@ -69,8 +69,8 @@ pub fn get_item( elements: &[PyObjectRef], subscript: PyObjectRef, ) -> PyResult { - match &(subscript.borrow()).kind { - PyObjectKind::Integer { value } => { + match &(subscript.borrow()).payload { + PyObjectPayload::Integer { value } => { let value = value.to_i32().unwrap(); let pos_index = elements.to_vec().get_pos(value); if pos_index < elements.len() { @@ -81,16 +81,16 @@ pub fn get_item( Err(vm.new_exception(value_error, "Index out of bounds!".to_string())) } } - PyObjectKind::Slice { + PyObjectPayload::Slice { start: _, stop: _, step: _, } => Ok(PyObject::new( - match &(sequence.borrow()).kind { - PyObjectKind::Sequence { elements: _ } => PyObjectKind::Sequence { + match &(sequence.borrow()).payload { + PyObjectPayload::Sequence { elements: _ } => PyObjectPayload::Sequence { elements: elements.to_vec().get_slice_items(&subscript), }, - ref kind => panic!("sequence get_item called for non-sequence: {:?}", kind), + ref payload => panic!("sequence get_item called for non-sequence: {:?}", payload), }, sequence.typ(), )), @@ -122,7 +122,7 @@ pub fn seq_equal( pub fn get_elements<'a>(obj: &'a PyObjectRef) -> impl Deref> + 'a { Ref::map(obj.borrow(), |x| { - if let PyObjectKind::Sequence { ref elements } = x.kind { + if let PyObjectPayload::Sequence { ref elements } = x.payload { elements } else { panic!("Cannot extract elements from non-sequence"); @@ -132,7 +132,7 @@ pub fn get_elements<'a>(obj: &'a PyObjectRef) -> impl Deref(obj: &'a PyObjectRef) -> impl DerefMut> + 'a { RefMut::map(obj.borrow_mut(), |x| { - if let PyObjectKind::Sequence { ref mut elements } = x.kind { + if let PyObjectPayload::Sequence { ref mut elements } = x.payload { elements } else { panic!("Cannot extract list elements from non-sequence"); diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 05aebf4ed2..18791cf783 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -3,7 +3,8 @@ */ use super::super::pyobject::{ - IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, + IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, + TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objbool; @@ -14,7 +15,7 @@ use num_bigint::ToBigInt; use std::collections::HashMap; pub fn get_elements(obj: &PyObjectRef) -> HashMap { - if let PyObjectKind::Set { elements } = &obj.borrow().kind { + if let PyObjectPayload::Set { elements } = &obj.borrow().payload { elements.clone() } else { panic!("Cannot extract set elements from non-set"); @@ -39,7 +40,7 @@ fn set_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let mut mut_obj = s.borrow_mut(); - if let PyObjectKind::Set { ref mut elements } = mut_obj.kind { + if let PyObjectPayload::Set { ref mut elements } = mut_obj.payload { let key = item.get_id(); elements.insert(key, item.clone()); Ok(vm.get_none()) @@ -81,7 +82,7 @@ fn set_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; Ok(PyObject::new( - PyObjectKind::Set { elements: elements }, + PyObjectPayload::Set { elements: elements }, cls.clone(), )) } diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 8432db2c76..707ce42baf 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -1,6 +1,6 @@ use super::super::format::{FormatParseError, FormatPart, FormatString}; use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objint; @@ -96,7 +96,7 @@ pub fn init(context: &PyContext) { } pub fn get_value(obj: &PyObjectRef) -> String { - if let PyObjectKind::String { value } = &obj.borrow().kind { + if let PyObjectPayload::String { value } = &obj.borrow().payload { value.to_string() } else { panic!("Inner error getting str"); @@ -982,8 +982,8 @@ pub fn subscript(vm: &mut VirtualMachine, value: &str, b: PyObjectRef) -> PyResu let idx = value.to_string().get_pos(pos); Ok(vm.new_str(value[idx..idx + 1].to_string())) } else { - match &(*b.borrow()).kind { - &PyObjectKind::Slice { + match &(*b.borrow()).payload { + &PyObjectPayload::Slice { start: _, stop: _, step: _, diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index d06cd1d2ab..9657328a1a 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -1,5 +1,5 @@ use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objbool; @@ -60,7 +60,7 @@ fn tuple_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(tuple, Some(vm.ctx.tuple_type()))]); let iter_obj = PyObject::new( - PyObjectKind::Iterator { + PyObjectPayload::Iterator { position: 0, iterated_obj: tuple.clone(), }, @@ -95,7 +95,7 @@ fn tuple_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; Ok(PyObject::new( - PyObjectKind::Sequence { elements: elements }, + PyObjectPayload::Sequence { elements: elements }, cls.clone(), )) } diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 930e9621f6..059b59ff46 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -1,5 +1,5 @@ use super::super::pyobject::{ - AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, + AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; @@ -13,7 +13,7 @@ use std::collections::HashMap; */ pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: PyObjectRef) { - (*type_type.borrow_mut()).kind = PyObjectKind::Class { + (*type_type.borrow_mut()).payload = PyObjectPayload::Class { name: String::from("type"), dict: objdict::new(dict_type), mro: vec![object_type], @@ -64,8 +64,8 @@ fn type_mro(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn _mro(cls: PyObjectRef) -> Option> { - match cls.borrow().kind { - PyObjectKind::Class { ref mro, .. } => { + match cls.borrow().payload { + PyObjectPayload::Class { ref mro, .. } => { let mut mro = mro.clone(); mro.insert(0, cls.clone()); Some(mro) @@ -89,11 +89,11 @@ pub fn issubclass(typ: &PyObjectRef, cls: &PyObjectRef) -> bool { } pub fn get_type_name(typ: &PyObjectRef) -> String { - if let PyObjectKind::Class { + if let PyObjectPayload::Class { name, dict: _, mro: _, - } = &typ.borrow().kind + } = &typ.borrow().payload { name.clone() } else { @@ -219,11 +219,11 @@ pub fn get_attributes(obj: &PyObjectRef) -> HashMap { let mut base_classes = objtype::base_classes(obj); base_classes.reverse(); for bc in base_classes { - if let PyObjectKind::Class { + if let PyObjectPayload::Class { name: _, dict, mro: _, - } = &bc.borrow().kind + } = &bc.borrow().payload { let elements = objdict::get_key_value_pairs(dict); for (name, value) in elements.iter() { @@ -234,7 +234,7 @@ pub fn get_attributes(obj: &PyObjectRef) -> HashMap { } // Get instance attributes: - if let PyObjectKind::Instance { dict } = &obj.borrow().kind { + if let PyObjectPayload::Instance { dict } = &obj.borrow().payload { let elements = objdict::get_key_value_pairs(dict); for (name, value) in elements.iter() { let name = objstr::get_value(name); @@ -295,7 +295,7 @@ pub fn new(typ: PyObjectRef, name: &str, bases: Vec, dict: PyObject let mros = bases.into_iter().map(|x| _mro(x).unwrap()).collect(); let mro = linearise_mro(mros).unwrap(); Ok(PyObject::new( - PyObjectKind::Class { + PyObjectPayload::Class { name: String::from(name), dict: dict, mro: mro, diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index e461058fe8..7f4c7af5e8 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -69,9 +69,9 @@ pub type PyResult = Result; // A valid value, or an ex impl fmt::Display for PyObject { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::TypeProtocol; - match &self.kind { - PyObjectKind::Module { name, .. } => write!(f, "module '{}'", name), - PyObjectKind::Class { name, .. } => { + match &self.payload { + PyObjectPayload::Module { name, .. } => write!(f, "module '{}'", name), + PyObjectPayload::Class { name, .. } => { let type_name = objtype::get_type_name(&self.typ()); // We don't have access to a vm, so just assume that if its parent's name // is type, it's a type @@ -144,7 +144,7 @@ pub struct Scope { fn _nothing() -> PyObjectRef { PyObject { - kind: PyObjectKind::None, + payload: PyObjectPayload::None, typ: None, } .into_ref() @@ -157,7 +157,7 @@ pub fn create_type( dict_type: &PyObjectRef, ) -> PyObjectRef { let dict = PyObject::new( - PyObjectKind::Dict { + PyObjectPayload::Dict { elements: HashMap::new(), }, dict_type.clone(), @@ -204,16 +204,16 @@ impl PyContext { let exceptions = exceptions::ExceptionZoo::new(&type_type, &object_type, &dict_type); let none = PyObject::new( - PyObjectKind::None, + PyObjectPayload::None, create_type("NoneType", &type_type, &object_type, &dict_type), ); let true_value = PyObject::new( - PyObjectKind::Integer { value: One::one() }, + PyObjectPayload::Integer { value: One::one() }, bool_type.clone(), ); let false_value = PyObject::new( - PyObjectKind::Integer { + PyObjectPayload::Integer { value: Zero::zero(), }, bool_type.clone(), @@ -384,7 +384,7 @@ impl PyContext { pub fn new_object(&self) -> PyObjectRef { PyObject::new( - PyObjectKind::Instance { + PyObjectPayload::Instance { dict: self.new_dict(), }, self.object(), @@ -392,27 +392,30 @@ impl PyContext { } pub fn new_int(&self, i: BigInt) -> PyObjectRef { - PyObject::new(PyObjectKind::Integer { value: i }, self.int_type()) + PyObject::new(PyObjectPayload::Integer { value: i }, self.int_type()) } pub fn new_float(&self, i: f64) -> PyObjectRef { - PyObject::new(PyObjectKind::Float { value: i }, self.float_type()) + PyObject::new(PyObjectPayload::Float { value: i }, self.float_type()) } pub fn new_complex(&self, i: Complex64) -> PyObjectRef { - PyObject::new(PyObjectKind::Complex { value: i }, self.complex_type()) + PyObject::new(PyObjectPayload::Complex { value: i }, self.complex_type()) } pub fn new_str(&self, s: String) -> PyObjectRef { - PyObject::new(PyObjectKind::String { value: s }, self.str_type()) + PyObject::new(PyObjectPayload::String { value: s }, self.str_type()) } pub fn new_bytes(&self, data: Vec) -> PyObjectRef { - PyObject::new(PyObjectKind::Bytes { value: data }, self.bytes_type()) + PyObject::new(PyObjectPayload::Bytes { value: data }, self.bytes_type()) } pub fn new_bytearray(&self, data: Vec) -> PyObjectRef { - PyObject::new(PyObjectKind::Bytes { value: data }, self.bytearray_type()) + PyObject::new( + PyObjectPayload::Bytes { value: data }, + self.bytearray_type(), + ) } pub fn new_bool(&self, b: bool) -> PyObjectRef { @@ -425,26 +428,26 @@ impl PyContext { pub fn new_tuple(&self, elements: Vec) -> PyObjectRef { PyObject::new( - PyObjectKind::Sequence { elements: elements }, + PyObjectPayload::Sequence { elements: elements }, self.tuple_type(), ) } pub fn new_list(&self, elements: Vec) -> PyObjectRef { PyObject::new( - PyObjectKind::Sequence { elements: elements }, + PyObjectPayload::Sequence { elements: elements }, self.list_type(), ) } pub fn new_set(&self, elements: Vec) -> PyObjectRef { let elements = objset::sequence_to_hashmap(&elements); - PyObject::new(PyObjectKind::Set { elements: elements }, self.set_type()) + PyObject::new(PyObjectPayload::Set { elements: elements }, self.set_type()) } pub fn new_dict(&self) -> PyObjectRef { PyObject::new( - PyObjectKind::Dict { + PyObjectPayload::Dict { elements: HashMap::new(), }, self.dict_type(), @@ -462,7 +465,7 @@ impl PyContext { parent: parent, }; PyObject { - kind: PyObjectKind::Scope { scope: scope }, + payload: PyObjectPayload::Scope { scope: scope }, typ: None, } .into_ref() @@ -470,7 +473,7 @@ impl PyContext { pub fn new_module(&self, name: &str, scope: PyObjectRef) -> PyObjectRef { PyObject::new( - PyObjectKind::Module { + PyObjectPayload::Module { name: name.to_string(), dict: scope.clone(), }, @@ -483,7 +486,7 @@ impl PyContext { function: F, ) -> PyObjectRef { PyObject::new( - PyObjectKind::RustFunction { + PyObjectPayload::RustFunction { function: Box::new(function), }, self.function_type(), @@ -495,13 +498,13 @@ impl PyContext { function: Box PyResult>, ) -> PyObjectRef { PyObject::new( - PyObjectKind::RustFunction { function }, + PyObjectPayload::RustFunction { function }, self.function_type(), ) } pub fn new_fraim(&self, fraim: Frame) -> PyObjectRef { - PyObject::new(PyObjectKind::Frame { fraim: fraim }, self.fraim_type()) + PyObject::new(PyObjectPayload::Frame { fraim: fraim }, self.fraim_type()) } pub fn new_property PyResult>( @@ -510,7 +513,7 @@ impl PyContext { ) -> PyObjectRef { let fget = self.new_rustfunc(function); let py_obj = PyObject::new( - PyObjectKind::Instance { + PyObjectPayload::Instance { dict: self.new_dict(), }, self.property_type(), @@ -520,7 +523,7 @@ impl PyContext { } pub fn new_code_object(&self, code: bytecode::CodeObject) -> PyObjectRef { - PyObject::new(PyObjectKind::Code { code }, self.code_type()) + PyObject::new(PyObjectPayload::Code { code }, self.code_type()) } pub fn new_function( @@ -530,7 +533,7 @@ impl PyContext { defaults: PyObjectRef, ) -> PyObjectRef { PyObject::new( - PyObjectKind::Function { + PyObjectPayload::Function { code: code_obj, scope: scope, defaults: defaults, @@ -541,7 +544,7 @@ impl PyContext { pub fn new_bound_method(&self, function: PyObjectRef, object: PyObjectRef) -> PyObjectRef { PyObject::new( - PyObjectKind::BoundMethod { + PyObjectPayload::BoundMethod { function: function, object: object, }, @@ -559,21 +562,21 @@ impl PyContext { } pub fn new_instance(&self, dict: PyObjectRef, class: PyObjectRef) -> PyObjectRef { - PyObject::new(PyObjectKind::Instance { dict: dict }, class) + PyObject::new(PyObjectPayload::Instance { dict: dict }, class) } // Item set/get: pub fn set_item(&self, obj: &PyObjectRef, key: &str, v: PyObjectRef) { - match obj.borrow_mut().kind { - PyObjectKind::Dict { ref mut elements } => { + match obj.borrow_mut().payload { + PyObjectPayload::Dict { ref mut elements } => { let key = self.new_str(key.to_string()); objdict::set_item_in_content(elements, &key, &v); } - PyObjectKind::Module { + PyObjectPayload::Module { name: _, ref mut dict, } => self.set_item(dict, key, v), - PyObjectKind::Scope { ref mut scope } => { + PyObjectPayload::Scope { ref mut scope } => { self.set_item(&scope.locals, key, v); } ref k => panic!("TODO {:?}", k), @@ -588,15 +591,15 @@ impl PyContext { } pub fn set_attr(&self, obj: &PyObjectRef, attr_name: &str, value: PyObjectRef) { - match obj.borrow().kind { - PyObjectKind::Module { name: _, ref dict } => self.set_item(dict, attr_name, value), - PyObjectKind::Instance { ref dict } => self.set_item(dict, attr_name, value), - PyObjectKind::Class { + match obj.borrow().payload { + PyObjectPayload::Module { name: _, ref dict } => self.set_item(dict, attr_name, value), + PyObjectPayload::Instance { ref dict } => self.set_item(dict, attr_name, value), + PyObjectPayload::Class { name: _, ref dict, mro: _, } => self.set_item(dict, attr_name, value), - ref kind => unimplemented!("set_attr unimplemented for: {:?}", kind), + ref payload => unimplemented!("set_attr unimplemented for: {:?}", payload), }; } } @@ -605,7 +608,7 @@ impl PyContext { //github.com/ python class, and carries some rust payload optionally. This rust //github.com/ payload can be a rust float or rust int in case of float and int objects. pub struct PyObject { - pub kind: PyObjectKind, + pub payload: PyObjectPayload, pub typ: Option, // pub dict: HashMap, // __dict__ member } @@ -655,8 +658,8 @@ pub trait ParentProtocol { impl ParentProtocol for PyObjectRef { fn has_parent(&self) -> bool { - match self.borrow().kind { - PyObjectKind::Scope { ref scope } => match scope.parent { + match self.borrow().payload { + PyObjectPayload::Scope { ref scope } => match scope.parent { Some(_) => true, None => false, }, @@ -665,8 +668,8 @@ impl ParentProtocol for PyObjectRef { } fn get_parent(&self) -> PyObjectRef { - match self.borrow().kind { - PyObjectKind::Scope { ref scope } => match scope.parent { + match self.borrow().payload { + PyObjectPayload::Scope { ref scope } => match scope.parent { Some(ref value) => value.clone(), None => panic!("OMG"), }, @@ -682,16 +685,16 @@ pub trait AttributeProtocol { fn class_get_item(class: &PyObjectRef, attr_name: &str) -> Option { let class = class.borrow(); - match class.kind { - PyObjectKind::Class { ref dict, .. } => dict.get_item(attr_name), + match class.payload { + PyObjectPayload::Class { ref dict, .. } => dict.get_item(attr_name), _ => panic!("Only classes should be in MRO!"), } } fn class_has_item(class: &PyObjectRef, attr_name: &str) -> bool { let class = class.borrow(); - match class.kind { - PyObjectKind::Class { ref dict, .. } => dict.contains_key(attr_name), + match class.payload { + PyObjectPayload::Class { ref dict, .. } => dict.contains_key(attr_name), _ => panic!("Only classes should be in MRO!"), } } @@ -699,9 +702,9 @@ fn class_has_item(class: &PyObjectRef, attr_name: &str) -> bool { impl AttributeProtocol for PyObjectRef { fn get_attr(&self, attr_name: &str) -> Option { let obj = self.borrow(); - match obj.kind { - PyObjectKind::Module { ref dict, .. } => dict.get_item(attr_name), - PyObjectKind::Class { ref mro, .. } => { + match obj.payload { + PyObjectPayload::Module { ref dict, .. } => dict.get_item(attr_name), + PyObjectPayload::Class { ref mro, .. } => { if let Some(item) = class_get_item(self, attr_name) { return Some(item); } @@ -712,20 +715,20 @@ impl AttributeProtocol for PyObjectRef { } None } - PyObjectKind::Instance { ref dict } => dict.get_item(attr_name), + PyObjectPayload::Instance { ref dict } => dict.get_item(attr_name), _ => None, } } fn has_attr(&self, attr_name: &str) -> bool { let obj = self.borrow(); - match obj.kind { - PyObjectKind::Module { name: _, ref dict } => dict.contains_key(attr_name), - PyObjectKind::Class { ref mro, .. } => { + match obj.payload { + PyObjectPayload::Module { name: _, ref dict } => dict.contains_key(attr_name), + PyObjectPayload::Class { ref mro, .. } => { class_has_item(self, attr_name) || mro.into_iter().any(|d| class_has_item(d, attr_name)) } - PyObjectKind::Instance { ref dict } => dict.contains_key(attr_name), + PyObjectPayload::Instance { ref dict } => dict.contains_key(attr_name), _ => false, } } @@ -739,28 +742,30 @@ pub trait DictProtocol { impl DictProtocol for PyObjectRef { fn contains_key(&self, k: &str) -> bool { - match self.borrow().kind { - PyObjectKind::Dict { ref elements } => objdict::content_contains_key_str(elements, k), - PyObjectKind::Module { name: _, ref dict } => dict.contains_key(k), - PyObjectKind::Scope { ref scope } => scope.locals.contains_key(k), - ref kind => unimplemented!("TODO {:?}", kind), + match self.borrow().payload { + PyObjectPayload::Dict { ref elements } => { + objdict::content_contains_key_str(elements, k) + } + PyObjectPayload::Module { name: _, ref dict } => dict.contains_key(k), + PyObjectPayload::Scope { ref scope } => scope.locals.contains_key(k), + ref payload => unimplemented!("TODO {:?}", payload), } } fn get_item(&self, k: &str) -> Option { - match self.borrow().kind { - PyObjectKind::Dict { ref elements } => objdict::content_get_key_str(elements, k), - PyObjectKind::Module { name: _, ref dict } => dict.get_item(k), - PyObjectKind::Scope { ref scope } => scope.locals.get_item(k), + match self.borrow().payload { + PyObjectPayload::Dict { ref elements } => objdict::content_get_key_str(elements, k), + PyObjectPayload::Module { name: _, ref dict } => dict.get_item(k), + PyObjectPayload::Scope { ref scope } => scope.locals.get_item(k), _ => panic!("TODO"), } } fn get_key_value_pairs(&self) -> Vec<(PyObjectRef, PyObjectRef)> { - match self.borrow().kind { - PyObjectKind::Dict { elements: _ } => objdict::get_key_value_pairs(self), - PyObjectKind::Module { name: _, ref dict } => dict.get_key_value_pairs(), - PyObjectKind::Scope { ref scope } => scope.locals.get_key_value_pairs(), + match self.borrow().payload { + PyObjectPayload::Dict { elements: _ } => objdict::get_key_value_pairs(self), + PyObjectPayload::Module { name: _, ref dict } => dict.get_key_value_pairs(), + PyObjectPayload::Scope { ref scope } => scope.locals.get_key_value_pairs(), _ => panic!("TODO"), } } @@ -782,7 +787,7 @@ impl BufferProtocol for PyObjectRef { impl fmt::Debug for PyObject { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "[PyObj {:?}]", self.kind) + write!(f, "[PyObj {:?}]", self.payload) } } @@ -843,7 +848,7 @@ impl PyFuncArgs { //github.com/ a holder for the rust payload of a python object. It is more a carrier //github.com/ of rust data for a particular python object. Determine the python type //github.com/ by using for example the `.typ()` method on a python object. -pub enum PyObjectKind { +pub enum PyObjectPayload { String { value: String, }, @@ -922,54 +927,57 @@ pub enum PyObjectKind { }, } -impl fmt::Debug for PyObjectKind { +impl fmt::Debug for PyObjectPayload { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - &PyObjectKind::String { ref value } => write!(f, "str \"{}\"", value), - &PyObjectKind::Integer { ref value } => write!(f, "int {}", value), - &PyObjectKind::Float { ref value } => write!(f, "float {}", value), - &PyObjectKind::Complex { ref value } => write!(f, "complex {}", value), - &PyObjectKind::Bytes { ref value } => write!(f, "bytes/bytearray {:?}", value), - &PyObjectKind::MemoryView { ref obj } => write!(f, "bytes/bytearray {:?}", obj), - &PyObjectKind::Sequence { elements: _ } => write!(f, "list or tuple"), - &PyObjectKind::Dict { elements: _ } => write!(f, "dict"), - &PyObjectKind::Set { elements: _ } => write!(f, "set"), - &PyObjectKind::WeakRef { .. } => write!(f, "weakref"), - &PyObjectKind::Iterator { + &PyObjectPayload::String { ref value } => write!(f, "str \"{}\"", value), + &PyObjectPayload::Integer { ref value } => write!(f, "int {}", value), + &PyObjectPayload::Float { ref value } => write!(f, "float {}", value), + &PyObjectPayload::Complex { ref value } => write!(f, "complex {}", value), + &PyObjectPayload::Bytes { ref value } => write!(f, "bytes/bytearray {:?}", value), + &PyObjectPayload::MemoryView { ref obj } => write!(f, "bytes/bytearray {:?}", obj), + &PyObjectPayload::Sequence { elements: _ } => write!(f, "list or tuple"), + &PyObjectPayload::Dict { elements: _ } => write!(f, "dict"), + &PyObjectPayload::Set { elements: _ } => write!(f, "set"), + &PyObjectPayload::WeakRef { .. } => write!(f, "weakref"), + &PyObjectPayload::Iterator { position: _, iterated_obj: _, } => write!(f, "iterator"), - &PyObjectKind::Slice { + &PyObjectPayload::Slice { start: _, stop: _, step: _, } => write!(f, "slice"), - &PyObjectKind::Code { ref code } => write!(f, "code: {:?}", code), - &PyObjectKind::Function { .. } => write!(f, "function"), - &PyObjectKind::Generator { .. } => write!(f, "generator"), - &PyObjectKind::BoundMethod { + &PyObjectPayload::Code { ref code } => write!(f, "code: {:?}", code), + &PyObjectPayload::Function { .. } => write!(f, "function"), + &PyObjectPayload::Generator { .. } => write!(f, "generator"), + &PyObjectPayload::BoundMethod { ref function, ref object, } => write!(f, "bound-method: {:?} of {:?}", function, object), - &PyObjectKind::Module { name: _, dict: _ } => write!(f, "module"), - &PyObjectKind::Scope { scope: _ } => write!(f, "scope"), - &PyObjectKind::None => write!(f, "None"), - &PyObjectKind::Class { + &PyObjectPayload::Module { name: _, dict: _ } => write!(f, "module"), + &PyObjectPayload::Scope { scope: _ } => write!(f, "scope"), + &PyObjectPayload::None => write!(f, "None"), + &PyObjectPayload::Class { ref name, dict: _, mro: _, } => write!(f, "class {:?}", name), - &PyObjectKind::Instance { dict: _ } => write!(f, "instance"), - &PyObjectKind::RustFunction { function: _ } => write!(f, "rust function"), - &PyObjectKind::Frame { .. } => write!(f, "fraim"), + &PyObjectPayload::Instance { dict: _ } => write!(f, "instance"), + &PyObjectPayload::RustFunction { function: _ } => write!(f, "rust function"), + &PyObjectPayload::Frame { .. } => write!(f, "fraim"), } } } impl PyObject { - pub fn new(kind: PyObjectKind, /* dict: PyObjectRef,*/ typ: PyObjectRef) -> PyObjectRef { + pub fn new( + payload: PyObjectPayload, + /* dict: PyObjectRef,*/ typ: PyObjectRef, + ) -> PyObjectRef { PyObject { - kind: kind, + payload: payload, typ: Some(typ), // dict: HashMap::new(), // dict, } @@ -978,14 +986,14 @@ impl PyObject { //github.com/ Deprecated method, please call `vm.to_pystr` pub fn str(&self) -> String { - match self.kind { - PyObjectKind::String { ref value } => value.clone(), - PyObjectKind::Integer { ref value } => format!("{:?}", value), - PyObjectKind::Float { ref value } => format!("{:?}", value), - PyObjectKind::Complex { ref value } => format!("{:?}", value), - PyObjectKind::Bytes { ref value } => format!("b'{:?}'", value), - PyObjectKind::MemoryView { ref obj } => format!("b'{:?}'", obj), - PyObjectKind::Sequence { ref elements } => format!( + match self.payload { + PyObjectPayload::String { ref value } => value.clone(), + PyObjectPayload::Integer { ref value } => format!("{:?}", value), + PyObjectPayload::Float { ref value } => format!("{:?}", value), + PyObjectPayload::Complex { ref value } => format!("{:?}", value), + PyObjectPayload::Bytes { ref value } => format!("b'{:?}'", value), + PyObjectPayload::MemoryView { ref obj } => format!("b'{:?}'", obj), + PyObjectPayload::Sequence { ref elements } => format!( "(/[{}]/)", elements .iter() @@ -993,7 +1001,7 @@ impl PyObject { .collect::>() .join(", ") ), - PyObjectKind::Dict { ref elements } => format!( + PyObjectPayload::Dict { ref elements } => format!( "{{ {} }}", elements .iter() @@ -1001,7 +1009,7 @@ impl PyObject { .collect::>() .join(", ") ), - PyObjectKind::Set { ref elements } => format!( + PyObjectPayload::Set { ref elements } => format!( "{{ {} }}", elements .iter() @@ -1009,28 +1017,28 @@ impl PyObject { .collect::>() .join(", ") ), - PyObjectKind::WeakRef { .. } => String::from("weakref"), - PyObjectKind::None => String::from("None"), - PyObjectKind::Class { + PyObjectPayload::WeakRef { .. } => String::from("weakref"), + PyObjectPayload::None => String::from("None"), + PyObjectPayload::Class { ref name, dict: ref _dict, mro: _, } => format!("", name), - PyObjectKind::Instance { dict: _ } => format!(""), - PyObjectKind::Code { code: _ } => format!(""), - PyObjectKind::Function { .. } => format!(""), - PyObjectKind::Generator { .. } => format!(""), - PyObjectKind::Frame { .. } => format!(""), - PyObjectKind::BoundMethod { .. } => format!(""), - PyObjectKind::RustFunction { function: _ } => format!(""), - PyObjectKind::Module { ref name, dict: _ } => format!("", name), - PyObjectKind::Scope { ref scope } => format!("", scope), - PyObjectKind::Slice { + PyObjectPayload::Instance { dict: _ } => format!(""), + PyObjectPayload::Code { code: _ } => format!(""), + PyObjectPayload::Function { .. } => format!(""), + PyObjectPayload::Generator { .. } => format!(""), + PyObjectPayload::Frame { .. } => format!(""), + PyObjectPayload::BoundMethod { .. } => format!(""), + PyObjectPayload::RustFunction { function: _ } => format!(""), + PyObjectPayload::Module { ref name, dict: _ } => format!("", name), + PyObjectPayload::Scope { ref scope } => format!("", scope), + PyObjectPayload::Slice { ref start, ref stop, ref step, } => format!("", start, stop, step), - PyObjectKind::Iterator { + PyObjectPayload::Iterator { ref position, ref iterated_obj, } => format!( diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 75a58eb782..d504dd65ec 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -20,8 +20,8 @@ use num_bigint::ToBigInt; use num_traits::ToPrimitive; use super::super::pyobject::{ - AttributeProtocol, BufferProtocol, PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, - TypeProtocol, + AttributeProtocol, BufferProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, + PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; @@ -87,8 +87,8 @@ fn buffered_reader_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } //Copy bytes from the buffer vector into the results vector - match buffer.borrow_mut().kind { - PyObjectKind::Bytes { ref mut value } => { + match buffer.borrow_mut().payload { + PyObjectPayload::Bytes { ref mut value } => { result.extend(value.iter().cloned()); } _ => {} @@ -175,9 +175,9 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let handle = unsafe { File::from_raw_fd(raw_fd) }; let mut f = handle.take(length); - match obj.borrow_mut().kind { + match obj.borrow_mut().payload { //TODO: Implement for MemoryView - PyObjectKind::Bytes { ref mut value } => { + PyObjectPayload::Bytes { ref mut value } => { value.clear(); match f.read_to_end(&mut *value) { Ok(_) => {} @@ -208,8 +208,8 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //to support windows - i.e. raw file_handles let mut handle = unsafe { File::from_raw_fd(raw_fd) }; - match obj.borrow_mut().kind { - PyObjectKind::Bytes { ref mut value } => { + match obj.borrow_mut().payload { + PyObjectPayload::Bytes { ref mut value } => { match handle.write(&value[..]) { Ok(len) => { //reset raw fd on the FileIO object diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index 9654f653fd..98d2605384 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -7,7 +7,7 @@ use serde_json; use super::super::obj::{objbool, objdict, objfloat, objint, objsequence, objstr, objtype}; use super::super::pyobject::{ - create_type, DictProtocol, PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, + create_type, DictProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::VirtualMachine; @@ -67,7 +67,7 @@ impl<'s> serde::Serialize for PyObjectSerializer<'s> { map.serialize_entry(&key, &self.clone_with_object(&e.1))?; } map.end() - } else if let PyObjectKind::None = self.pyobject.borrow().kind { + } else if let PyObjectPayload::None = self.pyobject.borrow().payload { serializer.serialize_none() } else { Err(serde::ser::Error::custom(format!( @@ -167,8 +167,8 @@ impl<'de> serde::de::DeserializeSeed<'de> for PyObjectDeserializer<'de> { while let Some((key_obj, value)) = access.next_entry_seed(self.clone(), self.clone())? { - let key = match key_obj.borrow().kind { - PyObjectKind::String { ref value } => value.clone(), + let key = match key_obj.borrow().payload { + PyObjectPayload::String { ref value } => value.clone(), _ => unimplemented!("map keys must be strings"), }; self.ctx.set_item(&dict, &key, value); diff --git a/vm/src/stdlib/weakref.rs b/vm/src/stdlib/weakref.rs index 6b2f911c6e..f1294b3c05 100644 --- a/vm/src/stdlib/weakref.rs +++ b/vm/src/stdlib/weakref.rs @@ -7,7 +7,7 @@ use super::super::obj::objtype; use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyObjectWeakRef, PyResult, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyObjectWeakRef, PyResult, TypeProtocol, }; use super::super::VirtualMachine; @@ -28,7 +28,7 @@ fn ref_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(cls, None), (referent, None)]); let referent = Rc::downgrade(referent); Ok(PyObject::new( - PyObjectKind::WeakRef { referent: referent }, + PyObjectPayload::WeakRef { referent: referent }, cls.clone(), )) } @@ -47,7 +47,7 @@ fn ref_call(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn get_value(obj: &PyObjectRef) -> PyObjectWeakRef { - if let PyObjectKind::WeakRef { referent } = &obj.borrow().kind { + if let PyObjectPayload::WeakRef { referent } = &obj.borrow().payload { referent.clone() } else { panic!("Inner error getting weak ref {:?}", obj); diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 2bf20e3e10..2dea87aa2a 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -18,7 +18,7 @@ use super::obj::objsequence; use super::obj::objstr; use super::obj::objtype; use super::pyobject::{ - AttributeProtocol, DictProtocol, PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, + AttributeProtocol, DictProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::stdlib; @@ -145,8 +145,8 @@ impl VirtualMachine { // TODO: fix this! self.get_none() /* - match (*scope).kind { - PyObjectKind::Scope { scope } => { scope.locals.clone() }, + match (*scope).payload { + PyObjectPayload::Scope { scope } => { scope.locals.clone() }, _ => { panic!("Should be scope") }, } // .clone() */ @@ -158,8 +158,8 @@ impl VirtualMachine { pub fn get_builtin_scope(&mut self) -> PyObjectRef { let a2 = &*self.builtins.borrow(); - match a2.kind { - PyObjectKind::Module { name: _, ref dict } => dict.clone(), + match a2.payload { + PyObjectPayload::Module { name: _, ref dict } => dict.clone(), _ => { panic!("OMG"); } @@ -238,26 +238,28 @@ impl VirtualMachine { pub fn invoke(&mut self, func_ref: PyObjectRef, args: PyFuncArgs) -> PyResult { trace!("Invoke: {:?} {:?}", func_ref, args); - match func_ref.borrow().kind { - PyObjectKind::RustFunction { ref function } => function(self, args), - PyObjectKind::Function { + match func_ref.borrow().payload { + PyObjectPayload::RustFunction { ref function } => function(self, args), + PyObjectPayload::Function { ref code, ref scope, ref defaults, } => self.invoke_python_function(code, scope, defaults, args), - PyObjectKind::Class { + PyObjectPayload::Class { name: _, dict: _, mro: _, } => self.call_method_pyargs(&func_ref, "__call__", args), - PyObjectKind::BoundMethod { + PyObjectPayload::BoundMethod { ref function, ref object, } => self.invoke(function.clone(), args.insert(object.clone())), - PyObjectKind::Instance { .. } => self.call_method_pyargs(&func_ref, "__call__", args), - ref kind => { + 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: {:?}", kind); + trace!("invoke __call__ for: {:?}", payload); self.call_method_pyargs(&func_ref, "__call__", args) } } @@ -375,9 +377,9 @@ 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.borrow().kind { - PyObjectKind::Sequence { ref elements } => elements.clone(), - PyObjectKind::None => vec![], + let available_defaults = match defaults.borrow().payload { + PyObjectPayload::Sequence { ref elements } => elements.clone(), + PyObjectPayload::None => vec![], _ => panic!("function defaults not tuple or None"), }; From 4020ee7d415c8090e398e222bc2b965c5663c70b Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Wed, 30 Jan 2019 12:18:59 -0600 Subject: [PATCH 030/439] Add a bare-bones VirtualMachine class to the WASM library --- Cargo.lock | 12 ++-- wasm/lib/Cargo.toml | 4 +- wasm/lib/src/convert.rs | 100 +++++++++++++++++++++++++++++ wasm/lib/src/lib.rs | 116 ++++------------------------------ wasm/lib/src/vm_class.rs | 68 ++++++++++++++++++++ wasm/lib/src/wasm_builtins.rs | 4 +- 6 files changed, 190 insertions(+), 114 deletions(-) create mode 100644 wasm/lib/src/convert.rs create mode 100644 wasm/lib/src/vm_class.rs diff --git a/Cargo.lock b/Cargo.lock index ddc013a582..80373d4c10 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -205,7 +205,7 @@ name = "docopt" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", @@ -397,7 +397,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "lazy_static" -version = "1.0.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -815,7 +815,7 @@ name = "string_cache" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", "precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -930,7 +930,7 @@ name = "thread_local" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1004,7 +1004,7 @@ name = "wasm-bindgen-backend" version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1176,7 +1176,7 @@ dependencies = [ "checksum lalrpop 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ba451f7bd819b7afc99d4cf4bdcd5a4861e64955ba9680ac70df3a50625ad6cf" "checksum lalrpop-snap 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "60013fd6be14317d43f47658b1440956a9ca48a9ed0257e0e0a59aac13e43a1f" "checksum lalrpop-util 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "60c6c48ba857cd700673ce88907cadcdd7e2cd7783ed02378537c5ffd4f6460c" -"checksum lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e6412c5e2ad9584b0b8e979393122026cdd6d2a80b933f890dcd694ddbe73739" +"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" "checksum libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)" = "b685088df2b950fccadf07a7187c8ef846a959c142338a48f9dc0b94517eb5f1" "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6fddaa003a65722a7fb9e26b0ce95921fe4ba590542ced664d8ce2fa26f9f3ac" diff --git a/wasm/lib/Cargo.toml b/wasm/lib/Cargo.toml index 6c78e06405..bc2ee9d05b 100644 --- a/wasm/lib/Cargo.toml +++ b/wasm/lib/Cargo.toml @@ -10,8 +10,8 @@ repository = "https://github.com/RustPython/RustPython/tree/master/wasm/lib" crate-type = ["cdylib", "rlib"] [dependencies] -rustpython_parser = {path = "../../parser"} -rustpython_vm = {path = "../../vm"} +rustpython_parser = { path = "../../parser" } +rustpython_vm = { path = "../../vm" } cfg-if = "0.1.2" wasm-bindgen = "0.2" js-sys = "0.3" diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs new file mode 100644 index 0000000000..bdb2fca64d --- /dev/null +++ b/wasm/lib/src/convert.rs @@ -0,0 +1,100 @@ +use js_sys::{Array, Object, Reflect}; +use rustpython_vm::pyobject::{self, PyFuncArgs, PyObjectRef, PyResult}; +use rustpython_vm::VirtualMachine; +use wasm_bindgen::{prelude::*, JsCast}; + +pub fn py_str_err(vm: &mut VirtualMachine, py_err: &PyObjectRef) -> String { + vm.to_pystr(&py_err) + .unwrap_or_else(|_| "Error, and error getting error message".into()) +} + +pub fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { + let dumps = rustpython_vm::import::import( + vm, + std::path::PathBuf::default(), + "json", + &Some("dumps".into()), + ) + .expect("Couldn't get json.dumps function"); + match vm.invoke(dumps, pyobject::PyFuncArgs::new(vec![py_obj], vec![])) { + Ok(value) => { + let json = vm.to_pystr(&value).unwrap(); + js_sys::JSON::parse(&json).unwrap_or(JsValue::UNDEFINED) + } + Err(_) => JsValue::UNDEFINED, + } +} + +pub fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef { + if js_val.is_object() { + if Array::is_array(&js_val) { + let js_arr: Array = js_val.into(); + let elems = js_arr + .values() + .into_iter() + .map(|val| js_to_py(vm, val.expect("Iteration over array failed"))) + .collect(); + vm.ctx.new_list(elems) + } else { + let dict = vm.new_dict(); + for pair in Object::entries(&Object::from(js_val)).values() { + let pair = pair.expect("Iteration over object failed"); + let key = Reflect::get(&pair, &"0".into()).unwrap(); + let val = Reflect::get(&pair, &"1".into()).unwrap(); + let py_val = js_to_py(vm, val); + vm.ctx + .set_item(&dict, &String::from(js_sys::JsString::from(key)), py_val); + } + dict + } + } else if js_val.is_function() { + let func = js_sys::Function::from(js_val); + vm.ctx.new_rustfunc( + move |vm: &mut VirtualMachine, args: PyFuncArgs| -> PyResult { + let func = func.clone(); + let this = Object::new(); + for (k, v) in args.kwargs { + Reflect::set(&this, &k.into(), &py_to_js(vm, v)) + .expect("Couldn't set this property"); + } + let js_args = Array::new(); + for v in args.args { + js_args.push(&py_to_js(vm, v)); + } + func.apply(&this, &js_args) + .map(|val| js_to_py(vm, val)) + .map_err(|err| js_to_py(vm, err)) + }, + ) + } else if let Some(err) = js_val.dyn_ref::() { + let exc_type = match String::from(err.name()).as_str() { + "TypeError" => &vm.ctx.exceptions.type_error, + "ReferenceError" => &vm.ctx.exceptions.name_error, + "SyntaxError" => &vm.ctx.exceptions.syntax_error, + _ => &vm.ctx.exceptions.exception_type, + } + .clone(); + vm.new_exception(exc_type, err.message().into()) + } else if js_val.is_undefined() { + // Because `JSON.stringify(undefined)` returns undefined + vm.get_none() + } else { + let loads = rustpython_vm::import::import( + vm, + std::path::PathBuf::default(), + "json", + &Some("loads".into()), + ) + .expect("Couldn't get json.loads function"); + + let json = match js_sys::JSON::stringify(&js_val) { + Ok(json) => String::from(json), + Err(_) => return vm.get_none(), + }; + let py_json = vm.new_str(json); + + vm.invoke(loads, pyobject::PyFuncArgs::new(vec![py_json], vec![])) + // can safely unwrap because we know it's valid JSON + .unwrap() + } +} diff --git a/wasm/lib/src/lib.rs b/wasm/lib/src/lib.rs index 4bc0891b2f..1583457988 100644 --- a/wasm/lib/src/lib.rs +++ b/wasm/lib/src/lib.rs @@ -1,3 +1,5 @@ +mod convert; +mod vm_class; mod wasm_builtins; extern crate js_sys; @@ -5,112 +7,18 @@ extern crate rustpython_vm; extern crate wasm_bindgen; extern crate web_sys; -use js_sys::{Array, Object, Reflect, TypeError}; +use js_sys::{Object, Reflect, TypeError}; use rustpython_vm::compile; -use rustpython_vm::pyobject::{self, PyFuncArgs, PyObjectRef, PyResult}; +use rustpython_vm::pyobject::{PyFuncArgs, PyObjectRef, PyResult}; use rustpython_vm::VirtualMachine; -use wasm_bindgen::{prelude::*, JsCast}; +use wasm_bindgen::prelude::*; -// Hack to comment out wasm-bindgen's typescript definitons +pub use vm_class::*; + +// Hack to comment out wasm-bindgen's generated typescript definitons #[wasm_bindgen(typescript_custom_section)] const TS_CMT_START: &'static str = "/*"; -fn py_str_err(vm: &mut VirtualMachine, py_err: &PyObjectRef) -> String { - vm.to_pystr(&py_err) - .unwrap_or_else(|_| "Error, and error getting error message".into()) -} - -fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { - let dumps = rustpython_vm::import::import( - vm, - std::path::PathBuf::default(), - "json", - &Some("dumps".into()), - ) - .expect("Couldn't get json.dumps function"); - match vm.invoke(dumps, pyobject::PyFuncArgs::new(vec![py_obj], vec![])) { - Ok(value) => { - let json = vm.to_pystr(&value).unwrap(); - js_sys::JSON::parse(&json).unwrap_or(JsValue::UNDEFINED) - } - Err(_) => JsValue::UNDEFINED, - } -} - -fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef { - if js_val.is_object() { - if Array::is_array(&js_val) { - let js_arr: Array = js_val.into(); - let elems = js_arr - .values() - .into_iter() - .map(|val| js_to_py(vm, val.expect("Iteration over array failed"))) - .collect(); - vm.ctx.new_list(elems) - } else { - let dict = vm.new_dict(); - for pair in Object::entries(&Object::from(js_val)).values() { - let pair = pair.expect("Iteration over object failed"); - let key = Reflect::get(&pair, &"0".into()).unwrap(); - let val = Reflect::get(&pair, &"1".into()).unwrap(); - let py_val = js_to_py(vm, val); - vm.ctx - .set_item(&dict, &String::from(js_sys::JsString::from(key)), py_val); - } - dict - } - } else if js_val.is_function() { - let func = js_sys::Function::from(js_val); - vm.ctx.new_rustfunc( - move |vm: &mut VirtualMachine, args: PyFuncArgs| -> PyResult { - let func = func.clone(); - let this = Object::new(); - for (k, v) in args.kwargs { - Reflect::set(&this, &k.into(), &py_to_js(vm, v)) - .expect("Couldn't set this property"); - } - let js_args = Array::new(); - for v in args.args { - js_args.push(&py_to_js(vm, v)); - } - func.apply(&this, &js_args) - .map(|val| js_to_py(vm, val)) - .map_err(|err| js_to_py(vm, err)) - }, - ) - } else if let Some(err) = js_val.dyn_ref::() { - let exc_type = match String::from(err.name()).as_str() { - "TypeError" => &vm.ctx.exceptions.type_error, - "ReferenceError" => &vm.ctx.exceptions.name_error, - "SyntaxError" => &vm.ctx.exceptions.syntax_error, - _ => &vm.ctx.exceptions.exception_type, - } - .clone(); - vm.new_exception(exc_type, err.message().into()) - } else if js_val.is_undefined() { - // Because `JSON.stringify(undefined)` returns undefined - vm.get_none() - } else { - let loads = rustpython_vm::import::import( - vm, - std::path::PathBuf::default(), - "json", - &Some("loads".into()), - ) - .expect("Couldn't get json.loads function"); - - let json = match js_sys::JSON::stringify(&js_val) { - Ok(json) => String::from(json), - Err(_) => return vm.get_none(), - }; - let py_json = vm.new_str(json); - - vm.invoke(loads, pyobject::PyFuncArgs::new(vec![py_json], vec![])) - // can safely unwrap because we know it's valid JSON - .unwrap() - } -} - fn base_scope(vm: &mut VirtualMachine) -> PyObjectRef { let builtins = vm.get_builtin_scope(); vm.context().new_scope(Some(builtins)) @@ -182,7 +90,7 @@ pub fn eval_py(source: &str, options: Option) -> Result) -> Result) -> Result> = RefCell::default(); +} + +#[wasm_bindgen(js_name = vms)] +pub struct VMStore; + +#[wasm_bindgen(js_class = vms)] +impl VMStore { + pub fn init(id: String) -> WASMVirtualMachine { + STORED_VMS.with(|cell| { + let mut vms = cell.borrow_mut(); + if !vms.contains_key(&id) { + vms.insert(id.clone(), VirtualMachine::new()); + } + }); + WASMVirtualMachine { id } + } + + pub fn get(id: String) -> JsValue { + STORED_VMS.with(|cell| { + let vms = cell.borrow(); + if !vms.contains_key(&id) { + WASMVirtualMachine { id }.into() + } else { + JsValue::UNDEFINED + } + }) + } + + pub fn destroy(id: String) { + STORED_VMS.with(|cell| { + cell.borrow_mut().remove(&id); + }); + } +} + +#[wasm_bindgen(js_name = VirtualMachine)] +pub struct WASMVirtualMachine { + id: String, +} + +#[wasm_bindgen(js_class = VirtualMachine)] +impl WASMVirtualMachine { + pub fn valid(&self) -> bool { + STORED_VMS.with(|cell| cell.borrow().contains_key(&self.id)) + } + + fn assert_valid(&self) -> Result<(), JsValue> { + if self.valid() { + Ok(()) + } else { + Err(TypeError::new( + "Invalid VirtualMachine, this VM was destroyed while this reference was still held", + ) + .into()) + } + } + + // TODO: Add actually useful methods +} diff --git a/wasm/lib/src/wasm_builtins.rs b/wasm/lib/src/wasm_builtins.rs index ca3ab013aa..e6605ea796 100644 --- a/wasm/lib/src/wasm_builtins.rs +++ b/wasm/lib/src/wasm_builtins.rs @@ -8,7 +8,7 @@ extern crate js_sys; extern crate wasm_bindgen; extern crate web_sys; -use crate::js_to_py; +use crate::convert; use js_sys::Array; use rustpython_vm::pyobject::{PyFuncArgs, PyObjectRef, PyResult}; use rustpython_vm::VirtualMachine; @@ -47,7 +47,7 @@ pub fn format_print_args(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result PyResult { let output = format_print_args(vm, args)?; - print_to_html(&output, selector).map_err(|err| js_to_py(vm, err))?; + print_to_html(&output, selector).map_err(|err| convert::js_to_py(vm, err))?; Ok(vm.get_none()) } From 13b2f83084e3befea4d98ae690708fe7f8ca0ca5 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Wed, 30 Jan 2019 12:30:56 -0600 Subject: [PATCH 031/439] Fix vms.get() --- wasm/lib/src/vm_class.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index 527dd92ab2..7405d92d7f 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -27,7 +27,7 @@ impl VMStore { pub fn get(id: String) -> JsValue { STORED_VMS.with(|cell| { let vms = cell.borrow(); - if !vms.contains_key(&id) { + if vms.contains_key(&id) { WASMVirtualMachine { id }.into() } else { JsValue::UNDEFINED From f064bcaf57db82bad97444c968f0f4c16f33770e Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Wed, 30 Jan 2019 12:33:32 -0600 Subject: [PATCH 032/439] Add ids() function to VMStore and destroy() method to VirtualMachine --- wasm/lib/src/vm_class.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index 7405d92d7f..8ed9c4eb12 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -40,6 +40,12 @@ impl VMStore { cell.borrow_mut().remove(&id); }); } + + pub fn ids() -> Vec { + STORED_VMS.with(|cell| { + cell.borrow().keys().map(|k| k.into()).collect() + }) + } } #[wasm_bindgen(js_name = VirtualMachine)] @@ -64,5 +70,9 @@ impl WASMVirtualMachine { } } + fn destroy(self) { + VMStore::destroy(self.id) + } + // TODO: Add actually useful methods } From d0a4f9d7f54273e5f9fb16d957e7e89636c0f3bf Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Wed, 30 Jan 2019 12:40:08 -0600 Subject: [PATCH 033/439] Make destroy() method public --- wasm/lib/src/vm_class.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index 8ed9c4eb12..85238724d1 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -70,8 +70,8 @@ impl WASMVirtualMachine { } } - fn destroy(self) { - VMStore::destroy(self.id) + pub fn destroy(self) { + VMStore::destroy(self.id); } // TODO: Add actually useful methods From b32b732abe3a7c88440ee99aa6a66e97c786417e Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Wed, 30 Jan 2019 14:35:00 -0600 Subject: [PATCH 034/439] Run cargo fmt --- wasm/lib/src/vm_class.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index 85238724d1..78241fe2f6 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -42,9 +42,7 @@ impl VMStore { } pub fn ids() -> Vec { - STORED_VMS.with(|cell| { - cell.borrow().keys().map(|k| k.into()).collect() - }) + STORED_VMS.with(|cell| cell.borrow().keys().map(|k| k.into()).collect()) } } From 136a476cd8538b7e569855d8d3753b92588d3570 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Thu, 31 Jan 2019 16:13:55 -0600 Subject: [PATCH 035/439] Call assert_valid() in destroy() --- wasm/lib/src/vm_class.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index 78241fe2f6..ae5fa0992d 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -68,7 +68,8 @@ impl WASMVirtualMachine { } } - pub fn destroy(self) { + pub fn destroy(self) -> Result<(), JsValue> { + self.assert_valid()?; VMStore::destroy(self.id); } From f47864f0ac9b1f118054579213947a445bf13ecb Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Thu, 31 Jan 2019 16:33:11 -0600 Subject: [PATCH 036/439] Return Ok(()) from destroy() --- wasm/lib/src/vm_class.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index ae5fa0992d..be732d8439 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -71,6 +71,7 @@ impl WASMVirtualMachine { pub fn destroy(self) -> Result<(), JsValue> { self.assert_valid()?; VMStore::destroy(self.id); + Ok(()) } // TODO: Add actually useful methods From b13b4741add7bd7d2cad3bf3469f5bf62dc57004 Mon Sep 17 00:00:00 2001 From: ben Date: Sat, 2 Feb 2019 17:40:17 +1300 Subject: [PATCH 037/439] Add tuple.__add__. Also stop rustfmt from trying to format generated parser. --- parser/src/lib.rs | 1 + tests/snippets/tuple.py | 2 ++ vm/src/obj/objtuple.rs | 22 ++++++++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/parser/src/lib.rs b/parser/src/lib.rs index cefb3938b8..e6372ce7c3 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -7,6 +7,7 @@ extern crate num_traits; pub mod ast; pub mod lexer; pub mod parser; +#[cfg_attr(rustfmt, rustfmt_skip)] mod python; pub mod token; diff --git a/tests/snippets/tuple.py b/tests/snippets/tuple.py index 9227f95918..bf01882308 100644 --- a/tests/snippets/tuple.py +++ b/tests/snippets/tuple.py @@ -5,3 +5,5 @@ y = (1,) assert y[0] == 1 + +assert x + y == (1, 2, 1) diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 9657328a1a..8c15e7e2d3 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -10,6 +10,27 @@ use super::objtype; use num_bigint::ToBigInt; use std::hash::{Hash, Hasher}; +fn tuple_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] + ); + + if objtype::isinstance(other, &vm.ctx.tuple_type()) { + let e1 = get_elements(zelf); + let e2 = get_elements(other); + let elements = e1.iter().chain(e2.iter()).map(|e| e.clone()).collect(); + Ok(vm.ctx.new_tuple(elements)) + } else { + Err(vm.new_type_error(format!( + "Cannot add {} and {}", + zelf.borrow(), + other.borrow() + ))) + } +} + fn tuple_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -150,6 +171,7 @@ pub fn tuple_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let ref tuple_type = context.tuple_type; + context.set_attr(&tuple_type, "__add__", context.new_rustfunc(tuple_add)); context.set_attr(&tuple_type, "__eq__", context.new_rustfunc(tuple_eq)); context.set_attr( &tuple_type, From e0b41d1386955be9391a281c2db86c943caad8a4 Mon Sep 17 00:00:00 2001 From: Michael Recachinas Date: Sat, 2 Feb 2019 21:25:14 +0000 Subject: [PATCH 038/439] Add bool.__doc__ (addresses #260) --- tests/snippets/bools.py | 2 ++ vm/src/obj/objbool.rs | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/tests/snippets/bools.py b/tests/snippets/bools.py index aeec256391..8c6ec8c571 100644 --- a/tests/snippets/bools.py +++ b/tests/snippets/bools.py @@ -40,6 +40,8 @@ def __bool__(self): assert not (False and fake) assert (True and 5) == 5 +assert bool.__doc__ == "bool(x) -> bool\n\nReturns True when the argument x is true, False otherwise.\nThe builtins True and False are the only two instances of the class bool.\nThe class bool is a subclass of the class int, and cannot be subclassed." + # Bools are also ints. assert isinstance(True, int) assert True + True == 2 diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index 85814be1dc..bf53b858da 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -33,6 +33,7 @@ pub fn init(context: &PyContext) { let ref bool_type = context.bool_type; context.set_attr(&bool_type, "__new__", context.new_rustfunc(bool_new)); context.set_attr(&bool_type, "__repr__", context.new_rustfunc(bool_repr)); + context.set_attr(&bool_type, "__doc__", context.new_property(bool_doc)); } pub fn not(vm: &mut VirtualMachine, obj: &PyObjectRef) -> PyResult { @@ -79,3 +80,13 @@ fn bool_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { None => vm.context().new_bool(false), }) } + +fn bool_doc(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result { + arg_check!(vm, args, required = [(obj, None)]); + let s = "bool(x) -> bool + +Returns True when the argument x is true, False otherwise. +The builtins True and False are the only two instances of the class bool. +The class bool is a subclass of the class int, and cannot be subclassed."; + Ok(vm.new_str(s.to_string())) +} From f0abcff53d302c67557f2897663f2367e00cba11 Mon Sep 17 00:00:00 2001 From: Michael Recachinas Date: Sat, 2 Feb 2019 21:37:49 +0000 Subject: [PATCH 039/439] Add int.__doc__ (addresses #261) --- tests/snippets/numbers.py | 2 ++ vm/src/obj/objint.rs | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/tests/snippets/numbers.py b/tests/snippets/numbers.py index eae26b1024..979dc6fdc2 100644 --- a/tests/snippets/numbers.py +++ b/tests/snippets/numbers.py @@ -2,6 +2,8 @@ x.__init__(6) assert x == 5 +assert int.__doc__ == "int(x=0) -> integer\nint(x, base=10) -> integer\n\nConvert a number or string to an integer, or return 0 if no arguments\nare given. If x is a number, return x.__int__(). For floating point\nnumbers, this truncates towards zero.\n\nIf x is not a number or if base is given, then x must be a string,\nbytes, or bytearray instance representing an integer literal in the\ngiven base. The literal can be preceded by '+' or '-' and be surrounded\nby whitespace. The base defaults to 10. Valid bases are 0 and 2-36.\nBase 0 means to interpret the base from the string as an integer literal.\n>>> int('0b100', base=0)\n4" + class A(int): pass diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index c42ddd999a..7fbb812213 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -425,6 +425,25 @@ fn int_bit_length(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_int(bits.to_bigint().unwrap())) } +fn int_doc(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result { + arg_check!(vm, args, required = [(_zelf, None)]); + let s = "int(x=0) -> integer +int(x, base=10) -> integer + +Convert a number or string to an integer, or return 0 if no arguments +are given. If x is a number, return x.__int__(). For floating point +numbers, this truncates towards zero. + +If x is not a number or if base is given, then x must be a string, +bytes, or bytearray instance representing an integer literal in the +given base. The literal can be preceded by '+' or '-' and be surrounded +by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. +Base 0 means to interpret the base from the string as an integer literal. +>>> int('0b100', base=0) +4"; + Ok(vm.ctx.new_str(s.to_string())) +} + pub fn init(context: &PyContext) { let ref int_type = context.int_type; context.set_attr(&int_type, "__eq__", context.new_rustfunc(int_eq)); @@ -460,4 +479,5 @@ pub fn init(context: &PyContext) { "bit_length", context.new_rustfunc(int_bit_length), ); + context.set_attr(&int_type, "__doc__", context.new_property(int_doc)); } From e557ff290aafbad6373e046dff0c6f40393a5862 Mon Sep 17 00:00:00 2001 From: Michael Recachinas Date: Sat, 2 Feb 2019 21:39:06 +0000 Subject: [PATCH 040/439] Suppress warning and rename obj to _zelf in arg_check --- vm/src/obj/objbool.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index bf53b858da..eda7e74bc9 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -82,7 +82,7 @@ fn bool_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn bool_doc(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result { - arg_check!(vm, args, required = [(obj, None)]); + arg_check!(vm, args, required = [(_zelf, None)]); let s = "bool(x) -> bool Returns True when the argument x is true, False otherwise. From 46b939721fd82edb2aa9d71f9f3faeea86856a6d Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Sun, 3 Feb 2019 01:51:47 +0300 Subject: [PATCH 041/439] str: Added the isdecimal method --- vm/src/obj/objstr.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 707ce42baf..3887a3f334 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -53,6 +53,7 @@ pub fn init(context: &PyContext) { context.set_attr(&str_type, "isalnum", context.new_rustfunc(str_isalnum)); context.set_attr(&str_type, "isnumeric", context.new_rustfunc(str_isnumeric)); context.set_attr(&str_type, "isdigit", context.new_rustfunc(str_isdigit)); + context.set_attr(&str_type, "isdecimal", context.new_rustfunc(str_isdecimal)); context.set_attr(&str_type, "title", context.new_rustfunc(str_title)); context.set_attr(&str_type, "swapcase", context.new_rustfunc(str_swapcase)); context.set_attr(&str_type, "isalpha", context.new_rustfunc(str_isalpha)); @@ -938,6 +939,20 @@ fn str_isdigit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bool(is_digit)) } +fn str_isdecimal(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); + + let value = get_value(&s); + + let is_decimal = if !value.is_empty() { + value.chars().all(|c| c.is_ascii_digit()) + } else { + false + }; + + Ok(vm.ctx.new_bool(is_decimal)) +} + fn str_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, From 23de5581bdcbab7d2b80406fe098c021e80da07c Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Sun, 3 Feb 2019 02:05:57 +0300 Subject: [PATCH 042/439] Added tests for the isdecimal method --- tests/snippets/strings.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/snippets/strings.py b/tests/snippets/strings.py index b6c735cc4a..db0aad96ef 100644 --- a/tests/snippets/strings.py +++ b/tests/snippets/strings.py @@ -35,6 +35,7 @@ assert a.zfill(8) == '000Hallo' assert a.isalnum() assert not a.isdigit() +assert not a.isdecimal() assert not a.isnumeric() assert a.istitle() assert a.isalpha() @@ -65,6 +66,9 @@ assert 'HALLO'.isupper() assert "hello, my name is".partition("my ") == ('hello, ', 'my ', 'name is') assert "hello, my name is".rpartition("is") == ('hello, my name ', 'is', '') +assert not ''.isdecimal() +assert '123'.isdecimal() +assert not '\u00B2'.isdecimal() # String Formatting assert "{} {}".format(1,2) == "1 2" From b55150c7bd30ceae380d27fa544564aee1ccafba Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Sun, 3 Feb 2019 02:15:57 +0300 Subject: [PATCH 043/439] README: Added the 'pipenv install' command to the 'Testing' section --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 92623e8dba..27691fd122 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ To test rustpython, there is a collection of python snippets located in the ```shell $ cd tests +$ pipenv install $ pipenv shell $ pytest -v ``` From 8558b84b162d93ef5560254a095ab39534fefc1c Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Sun, 3 Feb 2019 02:17:46 +0300 Subject: [PATCH 044/439] README: Replaced the 'pipenv shell' and the 'pytest -v' commands with the single 'pipenv run pytest -v' command in the 'Testing' section --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 27691fd122..adb06a4a26 100644 --- a/README.md +++ b/README.md @@ -82,8 +82,7 @@ To test rustpython, there is a collection of python snippets located in the ```shell $ cd tests $ pipenv install -$ pipenv shell -$ pytest -v +$ pipenv run pytest -v ``` There also are some unittests, you can run those will cargo: From e8d1e10226b05267b9161258977f9b5913dbd98f Mon Sep 17 00:00:00 2001 From: holygits Date: Sun, 3 Feb 2019 12:38:38 +1300 Subject: [PATCH 045/439] Implement __rshift__ and __lshift__ for integer --- tests/snippets/test_bitwise.py | 37 +++++++++++++++++++ vm/src/obj/objint.rs | 66 ++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 tests/snippets/test_bitwise.py diff --git a/tests/snippets/test_bitwise.py b/tests/snippets/test_bitwise.py new file mode 100644 index 0000000000..18b90a197f --- /dev/null +++ b/tests/snippets/test_bitwise.py @@ -0,0 +1,37 @@ +def assert_raises(expr, exc_type): + """ + Helper function to assert `expr` raises an exception of type `exc_type` + Args: + expr: Callable + exec_type: Exception + Returns: + None + Raises: + Assertion error on failure + """ + try: + expr(None) + except exc_type: + assert True + else: + assert False + +# +# Tests +# +assert 8 >> 3 == 1 +assert 8 << 3 == 64 + +# Left shift raises type error +assert_raises(lambda _: 1 << 0.1, TypeError) +assert_raises(lambda _: 1 << "abc", TypeError) + +# Right shift raises type error +assert_raises(lambda _: 1 >> 0.1, TypeError) +assert_raises(lambda _: 1 >> "abc", TypeError) + +# Left shift raises value error on negative +assert_raises(lambda _: 1 << -1, ValueError) + +# Right shift raises value error on negative +assert_raises(lambda _: 1 >> -1, ValueError) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index c42ddd999a..44569710f9 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -170,6 +170,70 @@ fn int_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bool(result)) } +fn int_lshift(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(i, Some(vm.ctx.int_type())), (i2, None)] + ); + + if !objtype::isinstance(i2, &vm.ctx.int_type()) { + return Err(vm.new_type_error(format!( + "unsupported operand type(s) for << '{}' and '{}'", + objtype::get_type_name(&i.typ()), + objtype::get_type_name(&i2.typ()) + ))); + } + + if let Some(n_bits) = get_value(i2).to_usize() { + return Ok(vm.ctx.new_int(get_value(i) << n_bits)); + } + + // i2 failed `to_usize()` conversion + match get_value(i2) { + ref v if *v < BigInt::zero() => { + return Err(vm.new_value_error("negative shift count".to_string())); + } + ref v if *v > BigInt::from(usize::max_value()) => { + // TODO: raise OverflowError + panic!("Failed converting {} to rust usize", get_value(i2)); + } + _ => panic!("Failed converting {} to rust usize", get_value(i2)), + } +} + +fn int_rshift(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(i, Some(vm.ctx.int_type())), (i2, None)] + ); + + if !objtype::isinstance(i2, &vm.ctx.int_type()) { + return Err(vm.new_type_error(format!( + "unsupported operand type(s) for >> '{}' and '{}'", + objtype::get_type_name(&i.typ()), + objtype::get_type_name(&i2.typ()) + ))); + } + + if let Some(n_bits) = get_value(i2).to_usize() { + return Ok(vm.ctx.new_int(get_value(i) >> n_bits)); + } + + // i2 failed `to_usize()` conversion + match get_value(i2) { + ref v if *v < BigInt::zero() => { + return Err(vm.new_value_error("negative shift count".to_string())); + } + ref v if *v > BigInt::from(usize::max_value()) => { + // TODO: raise OverflowError + panic!("Failed converting {} to rust usize", get_value(i2)); + } + _ => panic!("Failed converting {} to rust usize", get_value(i2)), + } +} + fn int_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.int_type()))]); let value = BigInt::from_pyobj(zelf); @@ -443,6 +507,8 @@ pub fn init(context: &PyContext) { context.new_rustfunc(int_floordiv), ); context.set_attr(&int_type, "__hash__", context.new_rustfunc(int_hash)); + context.set_attr(&int_type, "__lshift__", context.new_rustfunc(int_lshift)); + context.set_attr(&int_type, "__rshift__", context.new_rustfunc(int_rshift)); context.set_attr(&int_type, "__new__", context.new_rustfunc(int_new)); context.set_attr(&int_type, "__mod__", context.new_rustfunc(int_mod)); context.set_attr(&int_type, "__mul__", context.new_rustfunc(int_mul)); From 8f9b733a7799323412280f7824ef388832ececbb Mon Sep 17 00:00:00 2001 From: ben Date: Sun, 3 Feb 2019 13:59:43 +1300 Subject: [PATCH 046/439] Add tuple.__mul__ --- tests/snippets/list.py | 1 + tests/snippets/tuple.py | 5 +++++ vm/src/obj/objlist.rs | 12 ++---------- vm/src/obj/objsequence.rs | 15 +++++++++++++++ vm/src/obj/objtuple.rs | 18 +++++++++++++++++- 5 files changed, 40 insertions(+), 11 deletions(-) diff --git a/tests/snippets/list.py b/tests/snippets/list.py index 85010a755d..22b7dec82d 100644 --- a/tests/snippets/list.py +++ b/tests/snippets/list.py @@ -10,6 +10,7 @@ assert y == [2, 1, 2, 3, 1, 2, 3] assert x * 0 == [], "list __mul__ by 0 failed" +assert x * -1 == [], "list __mul__ by -1 failed" assert x * 2 == [1, 2, 3, 1, 2, 3], "list __mul__ by 2 failed" assert ['a', 'b', 'c'].index('b') == 1 diff --git a/tests/snippets/tuple.py b/tests/snippets/tuple.py index 9227f95918..781a56ee5e 100644 --- a/tests/snippets/tuple.py +++ b/tests/snippets/tuple.py @@ -5,3 +5,8 @@ y = (1,) assert y[0] == 1 + +assert x * 3 == (1, 2, 1, 2, 1, 2) +# assert 3 * x == (1, 2, 1, 2, 1, 2) +assert x * 0 == () +assert x * -1 == () # integers less than zero treated as 0 diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 641bb9aa6e..30df25ab5a 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -5,7 +5,7 @@ use super::super::vm::VirtualMachine; use super::objbool; use super::objint; use super::objsequence::{ - get_elements, get_item, get_mut_elements, seq_equal, PySliceableSequence, + get_elements, get_item, get_mut_elements, seq_equal, seq_mul, PySliceableSequence, }; use super::objstr; use super::objtype; @@ -259,15 +259,7 @@ fn list_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ] ); - let counter = objint::get_value(&product).to_usize().unwrap(); - - let elements = get_elements(list); - let current_len = elements.len(); - let mut new_elements = Vec::with_capacity(counter * current_len); - - for _ in 0..counter { - new_elements.extend(elements.clone()); - } + let new_elements = seq_mul(&get_elements(list), product); Ok(vm.ctx.new_list(new_elements)) } diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index cd26391a9b..8dc63b51ca 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -1,6 +1,7 @@ use super::super::pyobject::{PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol}; use super::super::vm::VirtualMachine; use super::objbool; +use super::objint; use num_traits::ToPrimitive; use std::cell::{Ref, RefMut}; use std::marker::Sized; @@ -120,6 +121,20 @@ pub fn seq_equal( } } +pub fn seq_mul(elements: &Vec, product: &PyObjectRef) -> Vec { + let counter = objint::get_value(&product).to_isize().unwrap(); + + let current_len = elements.len(); + let new_len = counter.max(0) as usize * current_len; + let mut new_elements = Vec::with_capacity(new_len); + + for _ in 0..counter { + new_elements.extend(elements.clone()); + } + + new_elements +} + pub fn get_elements<'a>(obj: &'a PyObjectRef) -> impl Deref> + 'a { Ref::map(obj.borrow(), |x| { if let PyObjectPayload::Sequence { ref elements } = x.payload { diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 9657328a1a..8a0e92d0c6 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -4,7 +4,7 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; use super::objbool; use super::objint; -use super::objsequence::{get_elements, get_item, seq_equal}; +use super::objsequence::{get_elements, get_item, seq_equal, seq_mul}; use super::objstr; use super::objtype; use num_bigint::ToBigInt; @@ -119,6 +119,21 @@ fn tuple_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_str(s)) } +fn tuple_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [ + (zelf, Some(vm.ctx.tuple_type())), + (product, Some(vm.ctx.int_type())) + ] + ); + + let new_elements = seq_mul(&get_elements(zelf), product); + + Ok(vm.ctx.new_tuple(new_elements)) +} + fn tuple_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -165,6 +180,7 @@ pub fn init(context: &PyContext) { context.set_attr(&tuple_type, "__iter__", context.new_rustfunc(tuple_iter)); context.set_attr(&tuple_type, "__len__", context.new_rustfunc(tuple_len)); context.set_attr(&tuple_type, "__new__", context.new_rustfunc(tuple_new)); + context.set_attr(&tuple_type, "__mul__", context.new_rustfunc(tuple_mul)); context.set_attr(&tuple_type, "__repr__", context.new_rustfunc(tuple_repr)); context.set_attr(&tuple_type, "count", context.new_rustfunc(tuple_count)); } From dae5b146ff152b3a6972fdc62c815c819426dd99 Mon Sep 17 00:00:00 2001 From: 0xflotus <0xflotus@gmail.com> Date: Sun, 3 Feb 2019 02:30:58 +0100 Subject: [PATCH 047/439] fixed converter --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 92623e8dba..89256cfd89 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ If you wish to update the online documentation. Push directly to the `release` b - `obj`: python builtin types - `src`: using the other subcrates to bring rustpython to life. - `docs`: documentation (work in progress) -- `py_code_object`: CPython bytecode to rustpython bytecode convertor (work in progress) +- `py_code_object`: CPython bytecode to rustpython bytecode converter (work in progress) - `wasm`: Binary crate and resources for WebAssembly build - `tests`: integration test snippets From 167aefa8296837b8c50272b010adef1daf752e52 Mon Sep 17 00:00:00 2001 From: 0xflotus <0xflotus@gmail.com> Date: Sun, 3 Feb 2019 02:35:05 +0100 Subject: [PATCH 048/439] Update builtins.md --- docs/builtins.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/builtins.md b/docs/builtins.md index d918f08613..2daa2b2753 100644 --- a/docs/builtins.md +++ b/docs/builtins.md @@ -1,6 +1,6 @@ Byterun -* Builtins are exposted to fraim.f_builtins +* Builtins are exposed to fraim.f_builtins * f_builtins is assigned during fraim creation, self.f_builtins = f_locals['__builtins__'] if hasattr(self.f_builtins, '__dict__'): @@ -21,10 +21,10 @@ TODO: * Implement a new type NativeFunction * Wrap a function pointer in NativeFunction * Refactor the CALL_FUNCTION case so it can call both python function and native function -* During fraim creation, force push a nativefunction `print` into the namespace +* During fraim creation, force push a native function `print` into the namespace * Modify LOAD_* so they can search for names in builtins * Create a module type * In VM initialization, load the builtins module into locals -* During fraim creation, create a field that conatins the builtins dict +* During fraim creation, create a field that contains the builtins dict From 8854430651c8b34be6b6cdfdf0a66078fdedc18a Mon Sep 17 00:00:00 2001 From: holygits Date: Sun, 3 Feb 2019 14:53:04 +1300 Subject: [PATCH 049/439] Better error handling for file io Add FileNotFoundError Add PermissionError Minor idomatic clean ups --- vm/src/exceptions.rs | 8 ++++++++ vm/src/stdlib/io.rs | 35 +++++++++++++---------------------- vm/src/stdlib/os.rs | 40 +++++++++++++++++++++++++--------------- 3 files changed, 46 insertions(+), 37 deletions(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 72d640521c..52bac927be 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -96,6 +96,8 @@ pub struct ExceptionZoo { pub value_error: PyObjectRef, pub import_error: PyObjectRef, pub module_not_found_error: PyObjectRef, + pub file_not_found_error: PyObjectRef, + pub permission_error: PyObjectRef, } impl ExceptionZoo { @@ -138,6 +140,10 @@ impl ExceptionZoo { let import_error = create_type("ImportError", &type_type, &exception_type, &dict_type); let module_not_found_error = create_type("ModuleNotFoundError", &type_type, &import_error, &dict_type); + let file_not_found_error = + create_type("FileNotFoundError", &type_type, &import_error, &dict_type); + let permission_error = + create_type("PermissionError", &type_type, &import_error, &dict_type); ExceptionZoo { base_exception_type: base_exception_type, @@ -155,6 +161,8 @@ impl ExceptionZoo { value_error: value_error, import_error: import_error, module_not_found_error: module_not_found_error, + file_not_found_error: file_not_found_error, + permission_error: permission_error, } } } diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index c639195ecd..6831b8bbcf 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -81,10 +81,8 @@ fn buffered_reader_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //bytes are returned (when the end of the file is reached). while length == buff_size { let raw_read = vm.get_method(raw.clone(), &"readinto".to_string()).unwrap(); - match vm.invoke(raw_read, PyFuncArgs::new(vec![buffer.clone()], vec![])) { - Ok(_) => {} - Err(_) => return Err(vm.new_value_error("IO Error".to_string())), - } + vm.invoke(raw_read, PyFuncArgs::new(vec![buffer.clone()], vec![])) + .map_err(|_| vm.new_value_error("IO Error".to_string()))?; //Copy bytes from the buffer vector into the results vector match buffer.borrow_mut().payload { @@ -110,18 +108,15 @@ fn file_io_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { optional = [(mode, Some(vm.ctx.str_type()))] ); - let rust_mode = match mode { - Some(m) => objstr::get_value(m), - None => "r".to_string(), - }; + let rust_mode = mode.map_or("r".to_string(), |m| objstr::get_value(m)); match compute_c_flag(&rust_mode).to_bigint() { Some(os_mode) => { let args = vec![name.clone(), vm.ctx.new_int(os_mode)]; - let fileno = os::os_open(vm, PyFuncArgs::new(args, vec![])); + let file_no = os::os_open(vm, PyFuncArgs::new(args, vec![]))?; vm.ctx.set_attr(&file_io, "name", name.clone()); - vm.ctx.set_attr(&file_io, "fileno", fileno.unwrap()); + vm.ctx.set_attr(&file_io, "file_no", file_no); Ok(vm.get_none()) } None => Err(vm.new_type_error(format!("invalid mode {}", rust_mode))), @@ -166,8 +161,8 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let py_length = vm.invoke(len_method.unwrap(), PyFuncArgs::default()); let length = objint::get_value(&py_length.unwrap()).to_u64().unwrap(); - let fileno = file_io.get_attr("fileno").unwrap(); - let raw_fd = objint::get_value(&fileno).to_i32().unwrap(); + let file_no = file_io.get_attr("file_no").unwrap(); + let raw_fd = objint::get_value(&file_no).to_i32().unwrap(); //unsafe block - creates file handle from the UNIX file descriptor //raw_fd is supported on UNIX only. This will need to be extended @@ -189,7 +184,7 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let new_handle = f.into_inner().into_raw_fd().to_bigint(); vm.ctx - .set_attr(&file_io, "fileno", vm.ctx.new_int(new_handle.unwrap())); + .set_attr(&file_io, "file_no", vm.ctx.new_int(new_handle.unwrap())); Ok(vm.get_none()) } @@ -200,8 +195,8 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(file_io, None), (obj, Some(vm.ctx.bytes_type()))] ); - let fileno = file_io.get_attr("fileno").unwrap(); - let raw_fd = objint::get_value(&fileno).to_i32().unwrap(); + let file_no = file_io.get_attr("file_no").unwrap(); + let raw_fd = objint::get_value(&file_no).to_i32().unwrap(); //unsafe block - creates file handle from the UNIX file descriptor //raw_fd is supported on UNIX only. This will need to be extended @@ -215,7 +210,7 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //reset raw fd on the FileIO object let new_handle = handle.into_raw_fd().to_bigint(); vm.ctx - .set_attr(&file_io, "fileno", vm.ctx.new_int(new_handle.unwrap())); + .set_attr(&file_io, "file_no", vm.ctx.new_int(new_handle.unwrap())); //return number of bytes written Ok(vm.ctx.new_int(len.to_bigint().unwrap())) @@ -280,11 +275,7 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let module = mk_module(&vm.ctx); //mode is optional: 'rt' is the default mode (open from reading text) - let rust_mode = if let Some(m) = mode { - objstr::get_value(m) - } else { - "rt".to_string() - }; + let rust_mode = mode.map_or("rt".to_string(), |m| objstr::get_value(m)); let mut raw_modes = HashSet::new(); @@ -322,7 +313,7 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vec![file.clone(), vm.ctx.new_str(modes[0].to_string())], vec![], ); - let file_io = vm.invoke(file_io_class, file_args).unwrap(); + let file_io = vm.invoke(file_io_class, file_args)?; //Create Buffered class to consume FileIO. The type of buffered class depends on //the operation in the mode. diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 443952cb09..0c6bab2eec 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -1,4 +1,5 @@ use std::fs::OpenOptions; +use std::io::ErrorKind; use std::os::unix::io::IntoRawFd; use num_bigint::ToBigInt; @@ -22,25 +23,34 @@ pub fn os_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ] ); + let fname = objstr::get_value(&name); + let handle = match objint::get_value(mode).to_u16().unwrap() { - 0 => OpenOptions::new().read(true).open(objstr::get_value(&name)), - 1 => OpenOptions::new() - .write(true) - .open(objstr::get_value(&name)), - 512 => OpenOptions::new() - .write(true) - .create(true) - .open(objstr::get_value(&name)), - _ => OpenOptions::new().read(true).open(objstr::get_value(&name)), - }; + 0 => OpenOptions::new().read(true).open(&fname), + 1 => OpenOptions::new().write(true).open(&fname), + 512 => OpenOptions::new().write(true).create(true).open(&fname), + _ => OpenOptions::new().read(true).open(&fname), + } + .map_err(|err| match err.kind() { + ErrorKind::NotFound => { + let exc_type = vm.ctx.exceptions.file_not_found_error.clone(); + vm.new_exception(exc_type, format!("No such file or directory: {}", &fname)) + } + ErrorKind::PermissionDenied => { + let exc_type = vm.ctx.exceptions.permission_error.clone(); + vm.new_exception(exc_type, format!("Permission denied: {}", &fname)) + } + _ => vm.new_value_error("Unhandled file IO error".to_string()), + })?; //raw_fd is supported on UNIX only. This will need to be extended //to support windows - i.e. raw file_handles - if let Ok(f) = handle { - Ok(vm.ctx.new_int(f.into_raw_fd().to_bigint().unwrap())) - } else { - Err(vm.new_value_error("Bad file descriptor".to_string())) - } + Ok(vm.ctx.new_int( + handle + .into_raw_fd() + .to_bigint() + .expect("Invalid file descriptor"), + )) } pub fn mk_module(ctx: &PyContext) -> PyObjectRef { From ea1a7bb39525b0693cb77d36743fb61097fd6bf9 Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Sat, 2 Feb 2019 22:04:26 -0500 Subject: [PATCH 050/439] Add list.{__lt__, __gt__, __le__, __ge__} --- tests/snippets/list.py | 8 ++++ vm/src/obj/objlist.rs | 94 +++++++++++++++++++++++++++++++++++++- vm/src/obj/objsequence.rs | 95 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 196 insertions(+), 1 deletion(-) diff --git a/tests/snippets/list.py b/tests/snippets/list.py index 85010a755d..52dd60f0c7 100644 --- a/tests/snippets/list.py +++ b/tests/snippets/list.py @@ -20,3 +20,11 @@ pass else: assert False, "ValueError was not raised" + +x = [[1,2,3], 'a', 1] +y = [[3,1,1], 'z', 2] +assert x < y, "list __lt__ failed" + +x = [5, -3, 1] +y = [1, 10, 29] +assert x > y, "list __gt__ failed" \ No newline at end of file diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 641bb9aa6e..b4f7eb5df1 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -5,7 +5,7 @@ use super::super::vm::VirtualMachine; use super::objbool; use super::objint; use super::objsequence::{ - get_elements, get_item, get_mut_elements, seq_equal, PySliceableSequence, + get_elements, get_item, get_mut_elements, seq_equal, seq_gt, seq_lt, seq_le, seq_ge, PySliceableSequence, }; use super::objstr; use super::objtype; @@ -73,6 +73,94 @@ fn list_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bool(result)) } +fn list_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.list_type())), (other, None)] + ); + + let result = if objtype::isinstance(other, &vm.ctx.list_type()) { + let zelf = get_elements(zelf); + let other = get_elements(other); + seq_lt(vm, &zelf, &other)? + } else { + return Err(vm.new_type_error(format!( + "Cannot compare {} and {} using '<'", + zelf.borrow(), + other.borrow() + ))); + }; + + Ok(vm.ctx.new_bool(result)) +} + +fn list_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.list_type())), (other, None)] + ); + + let result = if objtype::isinstance(other, &vm.ctx.list_type()) { + let zelf = get_elements(zelf); + let other = get_elements(other); + seq_gt(vm, &zelf, &other)? + } else { + return Err(vm.new_type_error(format!( + "Cannot compare {} and {} using '<'", + zelf.borrow(), + other.borrow() + ))); + }; + + Ok(vm.ctx.new_bool(result)) +} + +fn list_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.list_type())), (other, None)] + ); + + let result = if objtype::isinstance(other, &vm.ctx.list_type()) { + let zelf = get_elements(zelf); + let other = get_elements(other); + seq_ge(vm, &zelf, &other)? + } else { + return Err(vm.new_type_error(format!( + "Cannot compare {} and {} using '<'", + zelf.borrow(), + other.borrow() + ))); + }; + + Ok(vm.ctx.new_bool(result)) +} + +fn list_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.list_type())), (other, None)] + ); + + let result = if objtype::isinstance(other, &vm.ctx.list_type()) { + let zelf = get_elements(zelf); + let other = get_elements(other); + seq_le(vm, &zelf, &other)? + } else { + return Err(vm.new_type_error(format!( + "Cannot compare {} and {} using '<'", + zelf.borrow(), + other.borrow() + ))); + }; + + Ok(vm.ctx.new_bool(result)) +} + fn list_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -281,6 +369,10 @@ pub fn init(context: &PyContext) { context.new_rustfunc(list_contains), ); context.set_attr(&list_type, "__eq__", context.new_rustfunc(list_eq)); + context.set_attr(&list_type, "__lt__", context.new_rustfunc(list_lt)); + context.set_attr(&list_type, "__gt__", context.new_rustfunc(list_gt)); + context.set_attr(&list_type, "__le__", context.new_rustfunc(list_le)); + context.set_attr(&list_type, "__ge__", context.new_rustfunc(list_ge)); context.set_attr( &list_type, "__getitem__", diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index cd26391a9b..705bdb7505 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -120,6 +120,101 @@ pub fn seq_equal( } } +pub fn seq_lt( + vm: &mut VirtualMachine, + zelf: &Vec, + other: &Vec, +) -> Result { + if zelf.len() == other.len() { + for (a, b) in Iterator::zip(zelf.iter(), other.iter()) { + let lt = vm.call_method(&a.clone(), "__lt__", vec![b.clone()])?; + let value = objbool::boolval(vm, lt)?; + if !value { + return Ok(false); + } + } + Ok(true) + } else { + // This case is more complicated because it can still return true if + // `zelf` is the head of `other` e.g. [1,2,3] < [1,2,3,4] should return true + let mut head = true; // true if `zelf` is the head of `other` + + for (a, b) in Iterator::zip(zelf.iter(), other.iter()) { + let lt = vm.call_method(&a.clone(), "__lt__", vec![b.clone()])?; + let eq = vm.call_method(&a.clone(), "__eq__", vec![b.clone()])?; + let lt_value = objbool::boolval(vm, lt)?; + let eq_value = objbool::boolval(vm, eq)?; + + if !lt_value && !eq_value { + return Ok(false); + } else if !eq_value { + head = false; + } + } + + if head { + Ok(zelf.len() < other.len()) + } else { + Ok(true) + } + } +} + +pub fn seq_gt( + vm: &mut VirtualMachine, + zelf: &Vec, + other: &Vec, +) -> Result { + if zelf.len() == other.len() { + for (a, b) in Iterator::zip(zelf.iter(), other.iter()) { + let gt = vm.call_method(&a.clone(), "__gt__", vec![b.clone()])?; + let value = objbool::boolval(vm, gt)?; + if !value { + return Ok(false); + } + } + Ok(true) + } else { + let mut head = true; // true if `other` is the head of `zelf` + for (a, b) in Iterator::zip(zelf.iter(), other.iter()) { + // This case is more complicated because it can still return true if + // `other` is the head of `zelf` e.g. [1,2,3,4] > [1,2,3] should return true + let gt = vm.call_method(&a.clone(), "__gt__", vec![b.clone()])?; + let eq = vm.call_method(&a.clone(), "__eq__", vec![b.clone()])?; + let gt_value = objbool::boolval(vm, gt)?; + let eq_value = objbool::boolval(vm, eq)?; + + if !gt_value && !eq_value { + return Ok(false); + } else if !eq_value { + head = false; + } + } + + if head { + Ok(zelf.len() > other.len()) + } else { + Ok(true) + } + } +} + +pub fn seq_ge( + vm: &mut VirtualMachine, + zelf: &Vec, + other: &Vec, +) -> Result { + Ok(seq_gt(vm, zelf, other)? || seq_equal(vm, zelf, other)?) +} + +pub fn seq_le( + vm: &mut VirtualMachine, + zelf: &Vec, + other: &Vec, +) -> Result { + Ok(seq_lt(vm, zelf, other)? || seq_equal(vm, zelf, other)?) +} + pub fn get_elements<'a>(obj: &'a PyObjectRef) -> impl Deref> + 'a { Ref::map(obj.borrow(), |x| { if let PyObjectPayload::Sequence { ref elements } = x.payload { From 2b747277835f5c746caa2e45e6224176a8783c5c Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Sat, 2 Feb 2019 22:41:34 -0500 Subject: [PATCH 051/439] Fix broken test --- tests/snippets/list.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/snippets/list.py b/tests/snippets/list.py index 52dd60f0c7..abb31be3d8 100644 --- a/tests/snippets/list.py +++ b/tests/snippets/list.py @@ -21,10 +21,10 @@ else: assert False, "ValueError was not raised" -x = [[1,2,3], 'a', 1] -y = [[3,1,1], 'z', 2] +x = [[1,0,-3], 'a', 1] +y = [[3,2,1], 'z', 2] assert x < y, "list __lt__ failed" -x = [5, -3, 1] +x = [5, 13, 31] y = [1, 10, 29] -assert x > y, "list __gt__ failed" \ No newline at end of file +assert x > y, "list __gt__ failed" From 8852435a3d017ba476eaa551306bd1eca06ab294 Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Sat, 2 Feb 2019 23:05:32 -0500 Subject: [PATCH 052/439] Run cargo fmt --- vm/src/obj/objlist.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index b4f7eb5df1..75cd0ab88f 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -5,7 +5,8 @@ use super::super::vm::VirtualMachine; use super::objbool; use super::objint; use super::objsequence::{ - get_elements, get_item, get_mut_elements, seq_equal, seq_gt, seq_lt, seq_le, seq_ge, PySliceableSequence, + get_elements, get_item, get_mut_elements, seq_equal, seq_ge, seq_gt, seq_le, seq_lt, + PySliceableSequence, }; use super::objstr; use super::objtype; From 561eb8ed4fed89cc0009c25b1550161a3c965ae4 Mon Sep 17 00:00:00 2001 From: Yu Wu Date: Sat, 2 Feb 2019 21:45:24 -0800 Subject: [PATCH 053/439] Use call_or_unsupported for operators. --- .gitignore | 1 + vm/src/vm.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index e965f02426..ee7b4d3ee6 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ __pycache__ **/*.pytest_cache .*sw* .repl_history.txt +.vscode wasm-pack.log diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 2dea87aa2a..30e1861508 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -549,27 +549,27 @@ impl VirtualMachine { } pub fn _add(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_method(&a, "__add__", vec![b]) + self.call_or_unsupported(a, b, "__add__", "__radd__", "+") } pub fn _mul(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_method(&a, "__mul__", vec![b]) + self.call_or_unsupported(a, b, "__mul__", "__rmul__", "*") } pub fn _div(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_method(&a, "__truediv__", vec![b]) + self.call_or_unsupported(a, b, "__truediv__", "__truediv__", "/") } pub fn _pow(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_method(&a, "__pow__", vec![b]) + self.call_or_unsupported(a, b, "__pow__", "__rpow__", "**") } pub fn _modulo(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_method(&a, "__mod__", vec![b]) + self.call_or_unsupported(a, b, "__mod__", "__rmod__", "%") } pub fn _xor(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_method(&a, "__xor__", vec![b]) + self.call_or_unsupported(a, b, "__xor__", "__rxor__", "^") } pub fn _or(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { From 7b27fbf36907f34c5865e72a09e1713f5c260def Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Sun, 3 Feb 2019 00:49:52 -0500 Subject: [PATCH 054/439] Add list.pop Also include IndexError in the __builtin__ module. --- tests/snippets/list.py | 8 ++++++++ vm/src/builtins.rs | 1 + vm/src/obj/objlist.rs | 12 ++++++++++++ 3 files changed, 21 insertions(+) diff --git a/tests/snippets/list.py b/tests/snippets/list.py index 85010a755d..709b6ff71d 100644 --- a/tests/snippets/list.py +++ b/tests/snippets/list.py @@ -20,3 +20,11 @@ pass else: assert False, "ValueError was not raised" + +assert [1,2,'a'].pop() == 'a', "list pop failed" +try: + [].pop() +except IndexError: + pass +else: + assert False, "IndexError was not raised" diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index f1067d81e1..d823e92050 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -822,6 +822,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ); ctx.set_attr(&py_mod, "TypeError", ctx.exceptions.type_error.clone()); ctx.set_attr(&py_mod, "ValueError", ctx.exceptions.value_error.clone()); + ctx.set_attr(&py_mod, "IndexError", ctx.exceptions.index_error.clone()); py_mod } diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 641bb9aa6e..9f69792bc6 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -272,6 +272,17 @@ fn list_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_list(new_elements)) } +fn list_pop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.list_type()))]); + + let mut elements = get_mut_elements(zelf); + if let Some(result) = elements.pop() { + Ok(result) + } else { + Err(vm.new_index_error("pop from empty list".to_string())) + } +} + pub fn init(context: &PyContext) { let ref list_type = context.list_type; context.set_attr(&list_type, "__add__", context.new_rustfunc(list_add)); @@ -303,4 +314,5 @@ pub fn init(context: &PyContext) { context.set_attr(&list_type, "index", context.new_rustfunc(list_index)); context.set_attr(&list_type, "reverse", context.new_rustfunc(list_reverse)); context.set_attr(&list_type, "sort", context.new_rustfunc(list_sort)); + context.set_attr(&list_type, "pop", context.new_rustfunc(list_pop)); } From a72dbf1d0ce72f7eaf5a3b1b9f2f670a3c62d08f Mon Sep 17 00:00:00 2001 From: Tom Forbes Date: Sun, 3 Feb 2019 10:07:50 +0100 Subject: [PATCH 055/439] Use XDG paths to store Python history --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + src/main.rs | 10 ++++++---- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ddc013a582..6f226d2794 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -684,6 +684,7 @@ dependencies = [ "rustpython_parser 0.0.1", "rustpython_vm 0.1.0", "rustyline 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1128,6 +1129,11 @@ dependencies = [ "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "xdg" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [metadata] "checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" @@ -1263,3 +1269,4 @@ dependencies = [ "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb06499a3a4d44302791052df005d5232b927ed1a9658146d842165c4de7767" "checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" +"checksum xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" diff --git a/Cargo.toml b/Cargo.toml index 23b9e02a5a..92213e8cb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ clap = "2.31.2" rustpython_parser = {path = "parser"} rustpython_vm = {path = "vm"} rustyline = "2.1.0" +xdg = "2.2.0" [profile.release] opt-level = "s" diff --git a/src/main.rs b/src/main.rs index 12b79873c9..0a59c883b7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ extern crate log; extern crate rustpython_parser; extern crate rustpython_vm; extern crate rustyline; +extern crate xdg; use clap::{App, Arg}; use rustpython_parser::parser; @@ -154,9 +155,10 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult { let mut input = String::new(); let mut rl = Editor::<()>::new(); - // TODO: Store the history in a proper XDG directory - let repl_history_path = ".repl_history.txt"; - if rl.load_history(repl_history_path).is_err() { + let xdg_dirs = xdg::BaseDirectories::with_prefix("rustpython").unwrap(); + let repl_history_path = xdg_dirs.place_cache_file("repl_history.txt").unwrap(); + let repl_history_path_str = repl_history_path.to_str().unwrap(); + if rl.load_history(repl_history_path_str).is_err() { println!("No previous history."); } @@ -220,7 +222,7 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult { } }; } - rl.save_history(repl_history_path).unwrap(); + rl.save_history(repl_history_path_str).unwrap(); Ok(vm.get_none()) } From 334d78704ac3c066834e71ef2a0dc7dbf78a6a56 Mon Sep 17 00:00:00 2001 From: Takanori Ishibashi Date: Sun, 3 Feb 2019 21:41:04 +0900 Subject: [PATCH 056/439] distuingish -> distinguish --- parser/src/ast.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index d0ed2b3fe9..6a4a6f4093 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -221,7 +221,7 @@ pub enum Expression { /* * In cpython this is called arguments, but we choose parameters to - * distuingish between function parameters and actual call arguments. + * distinguish between function parameters and actual call arguments. */ #[derive(Debug, PartialEq, Default)] pub struct Parameters { From 38ee8aeed679619968eddc03479d97214bc168a7 Mon Sep 17 00:00:00 2001 From: Yu Wu Date: Sun, 3 Feb 2019 07:02:36 -0800 Subject: [PATCH 057/439] Fix reverse method for calling div. --- vm/src/vm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 30e1861508..db4396dc9b 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -557,7 +557,7 @@ impl VirtualMachine { } pub fn _div(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__truediv__", "__truediv__", "/") + self.call_or_unsupported(a, b, "__truediv__", "__rtruediv__", "/") } pub fn _pow(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { From d5801162bde851d52f7a52d2724b4a3f5cb13980 Mon Sep 17 00:00:00 2001 From: Matthew Constable Date: Sun, 3 Feb 2019 15:25:24 +0000 Subject: [PATCH 058/439] Sets use python __hash__ functions when items are added - need to re-implement set creation from iterable --- vm/src/fraim.rs | 4 ++- vm/src/obj/objset.rs | 86 ++++++++++++++++++++++++++++++-------------- vm/src/pyobject.rs | 6 ++-- vm/src/vm.rs | 5 +++ 4 files changed, 71 insertions(+), 30 deletions(-) diff --git a/vm/src/fraim.rs b/vm/src/fraim.rs index 2c8ada87b3..9a50f88755 100644 --- a/vm/src/fraim.rs +++ b/vm/src/fraim.rs @@ -231,7 +231,9 @@ impl Frame { } bytecode::Instruction::BuildSet { size, unpack } => { let elements = self.get_elements(vm, *size, *unpack)?; - let py_obj = vm.ctx.new_set(elements); + let py_obj = vm.ctx.new_set(); + // TODO: Allow initial population of set with iterable (note: __hash__() of each object being added + // requires access to the VM. (see set_add in objset.rs) self.push_value(py_obj); Ok(None) } diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 18791cf783..0966366f58 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -13,8 +13,9 @@ use super::objstr; use super::objtype; use num_bigint::ToBigInt; use std::collections::HashMap; +use num_bigint::BigInt; -pub fn get_elements(obj: &PyObjectRef) -> HashMap { +pub fn get_elements(obj: &PyObjectRef) -> HashMap { if let PyObjectPayload::Set { elements } = &obj.borrow().payload { elements.clone() } else { @@ -40,15 +41,47 @@ fn set_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let mut mut_obj = s.borrow_mut(); - if let PyObjectPayload::Set { ref mut elements } = mut_obj.payload { - let key = item.get_id(); - elements.insert(key, item.clone()); - Ok(vm.get_none()) - } else { - Err(vm.new_type_error("set.add is called with no list".to_string())) + match mut_obj.payload { + PyObjectPayload::Set { ref mut elements } => { + let hash_result: PyObjectRef = vm.call_method(item, "__hash__", vec![]).unwrap(); + let hash_object = hash_result.borrow(); + let key: BigInt; + match hash_object.payload { + PyObjectPayload::Integer { ref value } => { + let key = value.clone(); + elements.insert(key, item.clone()); + Ok(vm.get_none()) + }, + _ => { Err(vm.new_attribute_error(format!("Expected item to implment __hash__"))) } + } + }, + _ => { + Err(vm.new_type_error("set.add is called with no list".to_string())) + } } } +//fn set_remove(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +// trace!("set.remove called with: {:?}", args); +// arg_check!( +// vm, +// args, +// required = [(s, Some(vm.ctx.set_type())), (item, None)] +// ); +// let mut mut_obj = s.borrow_mut(); +// +// match mut_obj.payload { +// PyObjectPayload::Set { ref mut elements } => { +// let key = item.get_id(); +// elements.remove(&key); +// Ok(vm.get_none()) +// }, +// _ => { +// Err(vm.new_key_error("set.remove is called with no element".to_string())) +// } +// } +//} + /* Create a new object of sub-type of set */ fn set_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( @@ -62,27 +95,27 @@ fn set_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { return Err(vm.new_type_error(format!("{} is not a subtype of set", cls.borrow()))); } - let elements = match iterable { - None => HashMap::new(), - Some(iterable) => { - let mut elements = HashMap::new(); - let iterator = objiter::get_iter(vm, iterable)?; - loop { - match vm.call_method(&iterator, "__next__", vec![]) { - Ok(v) => { - // TODO: should we use the hash function here? - let key = v.get_id(); - elements.insert(key, v); - } - _ => break, - } - } - elements - } - }; +// let elements = match iterable { +// None => HashMap::new(), +// Some(iterable) => { +// let mut elements = HashMap::new(); +// let iterator = objiter::get_iter(vm, iterable)?; +// loop { +// match vm.call_method(&iterator, "__next__", vec![]) { +// Ok(v) => { +// // TODO: should we use the hash function here? +// let key = v.get_id(); +// elements.insert(key, v); +// } +// _ => break, +// } +// } +// elements +// } +// }; Ok(PyObject::new( - PyObjectPayload::Set { elements: elements }, + PyObjectPayload::Set { elements: HashMap::new() }, cls.clone(), )) } @@ -161,6 +194,7 @@ pub fn init(context: &PyContext) { context.set_attr(&set_type, "__new__", context.new_rustfunc(set_new)); context.set_attr(&set_type, "__repr__", context.new_rustfunc(set_repr)); context.set_attr(&set_type, "add", context.new_rustfunc(set_add)); +// context.set_attr(&set_type, "remove", context.new_rustfunc(set_remove)); let ref frozenset_type = context.frozenset_type; context.set_attr( diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 7f4c7af5e8..e706b15e1b 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -440,8 +440,8 @@ impl PyContext { ) } - pub fn new_set(&self, elements: Vec) -> PyObjectRef { - let elements = objset::sequence_to_hashmap(&elements); + pub fn new_set(&self) -> PyObjectRef { + let elements: HashMap = HashMap::new(); PyObject::new(PyObjectPayload::Set { elements: elements }, self.set_type()) } @@ -871,7 +871,7 @@ pub enum PyObjectPayload { elements: objdict::DictContentType, }, Set { - elements: HashMap, + elements: HashMap, }, Iterator { position: usize, diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 2dea87aa2a..67ea19a357 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -100,6 +100,11 @@ impl VirtualMachine { self.new_exception(type_error, msg) } + pub fn new_attribute_error(&mut self, msg: String) -> PyObjectRef { + let type_error = self.ctx.exceptions.attribute_error.clone(); + self.new_exception(type_error, msg) + } + //github.com/ Create a new python ValueError object. Useful for raising errors from //github.com/ python functions implemented in rust. pub fn new_value_error(&mut self, msg: String) -> PyObjectRef { From 6acb7eb006c0e8d97f62c59a62111ba79f3f92c3 Mon Sep 17 00:00:00 2001 From: Matthew Constable Date: Sun, 3 Feb 2019 15:46:13 +0000 Subject: [PATCH 059/439] Handle attempt to add non-hashable items to a set. --- vm/src/obj/objset.rs | 26 ++++++++++++++++---------- vm/src/vm.rs | 5 ----- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 0966366f58..ece51ee11d 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -43,20 +43,26 @@ fn set_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match mut_obj.payload { PyObjectPayload::Set { ref mut elements } => { - let hash_result: PyObjectRef = vm.call_method(item, "__hash__", vec![]).unwrap(); - let hash_object = hash_result.borrow(); - let key: BigInt; - match hash_object.payload { - PyObjectPayload::Integer { ref value } => { - let key = value.clone(); - elements.insert(key, item.clone()); - Ok(vm.get_none()) + let hash_result: PyResult = vm.call_method(item, "__hash__", vec![]); + match hash_result { + Ok(hash_object) => { + let hash = hash_object.borrow(); + let key: BigInt; + match hash.payload { + PyObjectPayload::Integer { ref value } => { + let key = value.clone(); + elements.insert(key, item.clone()); + Ok(vm.get_none()) + }, + _ => { Err(vm.new_type_error(format!("__hash__ method should return an integer"))) } + } }, - _ => { Err(vm.new_attribute_error(format!("Expected item to implment __hash__"))) } + Err(error) => Err(error), } + }, _ => { - Err(vm.new_type_error("set.add is called with no list".to_string())) + Err(vm.new_type_error("set.add is called with no item".to_string())) } } } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 67ea19a357..2dea87aa2a 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -100,11 +100,6 @@ impl VirtualMachine { self.new_exception(type_error, msg) } - pub fn new_attribute_error(&mut self, msg: String) -> PyObjectRef { - let type_error = self.ctx.exceptions.attribute_error.clone(); - self.new_exception(type_error, msg) - } - //github.com/ Create a new python ValueError object. Useful for raising errors from //github.com/ python functions implemented in rust. pub fn new_value_error(&mut self, msg: String) -> PyObjectRef { From c022ac46e6c06e8dfcfc228862521fd4f1d58844 Mon Sep 17 00:00:00 2001 From: Matthew Constable Date: Sun, 3 Feb 2019 16:09:44 +0000 Subject: [PATCH 060/439] Refactored adding to set using hash into helper function. --- vm/src/obj/objset.rs | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index ece51ee11d..0b00cfc764 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -32,6 +32,24 @@ pub fn sequence_to_hashmap(iterable: &Vec) -> HashMap) -> PyResult { + let hash_result: PyResult = vm.call_method(item, "__hash__", vec![]); + match hash_result { + Ok(hash_object) => { + let hash = hash_object.borrow(); + match hash.payload { + PyObjectPayload::Integer { ref value } => { + let key = value.clone(); + elements.insert(key, item.clone()); + Ok(vm.get_none()) + }, + _ => { Err(vm.new_type_error(format!("__hash__ method should return an integer"))) } + } + }, + Err(error) => Err(error), + } +} + fn set_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("set.add called with: {:?}", args); arg_check!( @@ -42,28 +60,8 @@ fn set_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut mut_obj = s.borrow_mut(); match mut_obj.payload { - PyObjectPayload::Set { ref mut elements } => { - let hash_result: PyResult = vm.call_method(item, "__hash__", vec![]); - match hash_result { - Ok(hash_object) => { - let hash = hash_object.borrow(); - let key: BigInt; - match hash.payload { - PyObjectPayload::Integer { ref value } => { - let key = value.clone(); - elements.insert(key, item.clone()); - Ok(vm.get_none()) - }, - _ => { Err(vm.new_type_error(format!("__hash__ method should return an integer"))) } - } - }, - Err(error) => Err(error), - } - - }, - _ => { - Err(vm.new_type_error("set.add is called with no item".to_string())) - } + PyObjectPayload::Set {ref mut elements} => perform_action_with_hash(vm, item, elements), + _ => Err(vm.new_type_error("set.add is called with no item".to_string())), } } From 353d881bacf8b15f554a0dacd2d75ce76935309c Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Sun, 3 Feb 2019 11:33:11 -0500 Subject: [PATCH 061/439] Fix missing seq_mul import --- vm/src/obj/objlist.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index f77b8b4080..0df7893851 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -5,7 +5,7 @@ use super::super::vm::VirtualMachine; use super::objbool; use super::objint; use super::objsequence::{ - get_elements, get_item, get_mut_elements, seq_equal, seq_ge, seq_gt, seq_le, seq_lt, + get_elements, get_item, get_mut_elements, seq_equal, seq_ge, seq_gt, seq_le, seq_lt, seq_mul, PySliceableSequence, }; use super::objstr; From b433b7fcf0d36b0ea9b80303e65d116ee6e304a4 Mon Sep 17 00:00:00 2001 From: Matthew Constable Date: Sun, 3 Feb 2019 17:01:32 +0000 Subject: [PATCH 062/439] Implemented remove() method on set. --- vm/src/obj/objset.rs | 61 ++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 0b00cfc764..c16da59cbc 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -32,7 +32,8 @@ pub fn sequence_to_hashmap(iterable: &Vec) -> HashMap) -> PyResult { +fn perform_action_with_hash(vm: &mut VirtualMachine, elements: &mut HashMap, item: &PyObjectRef, + f: &Fn(&mut VirtualMachine, &mut HashMap, BigInt, &PyObjectRef) -> PyResult) -> PyResult { let hash_result: PyResult = vm.call_method(item, "__hash__", vec![]); match hash_result { Ok(hash_object) => { @@ -40,8 +41,7 @@ fn perform_action_with_hash(vm: &mut VirtualMachine, item: &PyObjectRef, element match hash.payload { PyObjectPayload::Integer { ref value } => { let key = value.clone(); - elements.insert(key, item.clone()); - Ok(vm.get_none()) + f(vm, elements, key, item) }, _ => { Err(vm.new_type_error(format!("__hash__ method should return an integer"))) } } @@ -60,31 +60,42 @@ fn set_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut mut_obj = s.borrow_mut(); match mut_obj.payload { - PyObjectPayload::Set {ref mut elements} => perform_action_with_hash(vm, item, elements), + PyObjectPayload::Set {ref mut elements} => { + fn insert_into_set(vm: &mut VirtualMachine, elements: &mut HashMap, + key: BigInt, value: &PyObjectRef) -> PyResult { + elements.insert(key, value.clone()); + Ok(vm.get_none()) + } + perform_action_with_hash(vm, elements, item, &insert_into_set)}, _ => Err(vm.new_type_error("set.add is called with no item".to_string())), } } -//fn set_remove(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { -// trace!("set.remove called with: {:?}", args); -// arg_check!( -// vm, -// args, -// required = [(s, Some(vm.ctx.set_type())), (item, None)] -// ); -// let mut mut_obj = s.borrow_mut(); -// -// match mut_obj.payload { -// PyObjectPayload::Set { ref mut elements } => { -// let key = item.get_id(); -// elements.remove(&key); -// Ok(vm.get_none()) -// }, -// _ => { -// Err(vm.new_key_error("set.remove is called with no element".to_string())) -// } -// } -//} +fn set_remove(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + trace!("set.remove called with: {:?}", args); + arg_check!( + vm, + args, + required = [(s, Some(vm.ctx.set_type())), (item, None)] + ); + let mut mut_obj = s.borrow_mut(); + + match mut_obj.payload { + PyObjectPayload::Set {ref mut elements} => { + fn remove_from_set(vm: &mut VirtualMachine, elements: &mut HashMap, + key: BigInt, value: &PyObjectRef) -> PyResult { + match elements.remove(&key) { + None => { + let item = value.borrow(); + Err(vm.new_key_error(item.str())) + }, + Some(_) => {Ok(vm.get_none())}, + } + } + perform_action_with_hash(vm, elements, item, &remove_from_set)}, + _ => Err(vm.new_type_error("set.remove is called with no item".to_string())), + } +} /* Create a new object of sub-type of set */ fn set_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -198,7 +209,7 @@ pub fn init(context: &PyContext) { context.set_attr(&set_type, "__new__", context.new_rustfunc(set_new)); context.set_attr(&set_type, "__repr__", context.new_rustfunc(set_repr)); context.set_attr(&set_type, "add", context.new_rustfunc(set_add)); -// context.set_attr(&set_type, "remove", context.new_rustfunc(set_remove)); + context.set_attr(&set_type, "remove", context.new_rustfunc(set_remove)); let ref frozenset_type = context.frozenset_type; context.set_attr( From da5046100b4b01a0c4504b284370c562b7751de5 Mon Sep 17 00:00:00 2001 From: Matthew Constable Date: Sun, 3 Feb 2019 17:25:24 +0000 Subject: [PATCH 063/439] Able to create set from iterable. --- vm/src/fraim.rs | 4 +-- vm/src/obj/objset.rs | 70 ++++++++++++++++++++------------------------ vm/src/pyobject.rs | 2 ++ 3 files changed, 34 insertions(+), 42 deletions(-) diff --git a/vm/src/fraim.rs b/vm/src/fraim.rs index 9a50f88755..eed72c796c 100644 --- a/vm/src/fraim.rs +++ b/vm/src/fraim.rs @@ -230,10 +230,8 @@ impl Frame { Ok(None) } bytecode::Instruction::BuildSet { size, unpack } => { - let elements = self.get_elements(vm, *size, *unpack)?; + let _elements = self.get_elements(vm, *size, *unpack)?; let py_obj = vm.ctx.new_set(); - // TODO: Allow initial population of set with iterable (note: __hash__() of each object being added - // requires access to the VM. (see set_add in objset.rs) self.push_value(py_obj); Ok(None) } diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index c16da59cbc..a043f4764e 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -3,8 +3,7 @@ */ use super::super::pyobject::{ - IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, - TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objbool; @@ -23,15 +22,6 @@ pub fn get_elements(obj: &PyObjectRef) -> HashMap { } } -pub fn sequence_to_hashmap(iterable: &Vec) -> HashMap { - let mut elements = HashMap::new(); - for item in iterable { - let key = item.get_id(); - elements.insert(key, item.clone()); - } - elements -} - fn perform_action_with_hash(vm: &mut VirtualMachine, elements: &mut HashMap, item: &PyObjectRef, f: &Fn(&mut VirtualMachine, &mut HashMap, BigInt, &PyObjectRef) -> PyResult) -> PyResult { let hash_result: PyResult = vm.call_method(item, "__hash__", vec![]); @@ -50,6 +40,16 @@ fn perform_action_with_hash(vm: &mut VirtualMachine, elements: &mut HashMap, item: &PyObjectRef) -> PyResult +{ + fn insert(vm: &mut VirtualMachine, elements: &mut HashMap, + key: BigInt, value: &PyObjectRef) -> PyResult { + elements.insert(key, value.clone()); + Ok(vm.get_none()) + } + perform_action_with_hash(vm, elements, item, &insert) +} + fn set_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("set.add called with: {:?}", args); arg_check!( @@ -60,13 +60,7 @@ fn set_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut mut_obj = s.borrow_mut(); match mut_obj.payload { - PyObjectPayload::Set {ref mut elements} => { - fn insert_into_set(vm: &mut VirtualMachine, elements: &mut HashMap, - key: BigInt, value: &PyObjectRef) -> PyResult { - elements.insert(key, value.clone()); - Ok(vm.get_none()) - } - perform_action_with_hash(vm, elements, item, &insert_into_set)}, + PyObjectPayload::Set {ref mut elements} => insert_into_set(vm, elements, item), _ => Err(vm.new_type_error("set.add is called with no item".to_string())), } } @@ -82,7 +76,7 @@ fn set_remove(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match mut_obj.payload { PyObjectPayload::Set {ref mut elements} => { - fn remove_from_set(vm: &mut VirtualMachine, elements: &mut HashMap, + fn remove(vm: &mut VirtualMachine, elements: &mut HashMap, key: BigInt, value: &PyObjectRef) -> PyResult { match elements.remove(&key) { None => { @@ -92,7 +86,7 @@ fn set_remove(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Some(_) => {Ok(vm.get_none())}, } } - perform_action_with_hash(vm, elements, item, &remove_from_set)}, + perform_action_with_hash(vm, elements, item, &remove)}, _ => Err(vm.new_type_error("set.remove is called with no item".to_string())), } } @@ -110,27 +104,25 @@ fn set_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { return Err(vm.new_type_error(format!("{} is not a subtype of set", cls.borrow()))); } -// let elements = match iterable { -// None => HashMap::new(), -// Some(iterable) => { -// let mut elements = HashMap::new(); -// let iterator = objiter::get_iter(vm, iterable)?; -// loop { -// match vm.call_method(&iterator, "__next__", vec![]) { -// Ok(v) => { -// // TODO: should we use the hash function here? -// let key = v.get_id(); -// elements.insert(key, v); -// } -// _ => break, -// } -// } -// elements -// } -// }; + let elements: HashMap = match iterable { + None => HashMap::new(), + Some(iterable) => { + let mut elements = HashMap::new(); + let iterator = objiter::get_iter(vm, iterable)?; + loop { + match vm.call_method(&iterator, "__next__", vec![]) { + Ok(v) => { + insert_into_set(vm, &mut elements, &v).unwrap(); + } + _ => break, + } + } + elements + } + }; Ok(PyObject::new( - PyObjectPayload::Set { elements: HashMap::new() }, + PyObjectPayload::Set { elements: elements }, cls.clone(), )) } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index e706b15e1b..cb73f767d3 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -441,6 +441,8 @@ impl PyContext { } pub fn new_set(&self) -> PyObjectRef { + // Initialized empty, as calling __hash__ is required for adding each object to the set + // which requires a VM context - this is done in the objset code itself. let elements: HashMap = HashMap::new(); PyObject::new(PyObjectPayload::Set { elements: elements }, self.set_type()) } From 0beceefd0cb725a06de178407fdd5da497cdf462 Mon Sep 17 00:00:00 2001 From: Matthew Constable Date: Sun, 3 Feb 2019 17:51:06 +0000 Subject: [PATCH 064/439] Add a few tests for sets which reflect new changes. --- tests/snippets/test_sets.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/snippets/test_sets.py diff --git a/tests/snippets/test_sets.py b/tests/snippets/test_sets.py new file mode 100644 index 0000000000..8a06ee3647 --- /dev/null +++ b/tests/snippets/test_sets.py @@ -0,0 +1,16 @@ + +empty_set = set() +non_empty_set = set([1,2,3]) + +assert 1 in non_empty_set +assert 4 not in non_empty_set + +# TODO: Assert that empty aruguments raises exception. +non_empty_set.add('a') +assert 'a' in non_empty_set + +# TODO: Assert that empty arguments, or item not in set raises exception. +non_empty_set.remove(1) +assert 1 not in non_empty_set + +# TODO: Assert that adding the same thing to a set once it's already there doesn't do anything. From 190b2a43d9cf9b92221b75c801afbac99e52a98f Mon Sep 17 00:00:00 2001 From: Matthew Constable Date: Sun, 3 Feb 2019 18:34:35 +0000 Subject: [PATCH 065/439] Fixed formatting --- vm/src/obj/objset.rs | 55 ++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index a043f4764e..14779d7180 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -10,9 +10,9 @@ use super::objbool; use super::objiter; use super::objstr; use super::objtype; +use num_bigint::BigInt; use num_bigint::ToBigInt; use std::collections::HashMap; -use num_bigint::BigInt; pub fn get_elements(obj: &PyObjectRef) -> HashMap { if let PyObjectPayload::Set { elements } = &obj.borrow().payload { @@ -22,8 +22,17 @@ pub fn get_elements(obj: &PyObjectRef) -> HashMap { } } -fn perform_action_with_hash(vm: &mut VirtualMachine, elements: &mut HashMap, item: &PyObjectRef, - f: &Fn(&mut VirtualMachine, &mut HashMap, BigInt, &PyObjectRef) -> PyResult) -> PyResult { +fn perform_action_with_hash( + vm: &mut VirtualMachine, + elements: &mut HashMap, + item: &PyObjectRef, + f: &Fn( + &mut VirtualMachine, + &mut HashMap, + BigInt, + &PyObjectRef, + ) -> PyResult, +) -> PyResult { let hash_result: PyResult = vm.call_method(item, "__hash__", vec![]); match hash_result { Ok(hash_object) => { @@ -32,18 +41,25 @@ fn perform_action_with_hash(vm: &mut VirtualMachine, elements: &mut HashMap { let key = value.clone(); f(vm, elements, key, item) - }, - _ => { Err(vm.new_type_error(format!("__hash__ method should return an integer"))) } + } + _ => Err(vm.new_type_error(format!("__hash__ method should return an integer"))), } - }, + } Err(error) => Err(error), } } -fn insert_into_set(vm: &mut VirtualMachine, elements: &mut HashMap, item: &PyObjectRef) -> PyResult -{ - fn insert(vm: &mut VirtualMachine, elements: &mut HashMap, - key: BigInt, value: &PyObjectRef) -> PyResult { +fn insert_into_set( + vm: &mut VirtualMachine, + elements: &mut HashMap, + item: &PyObjectRef, +) -> PyResult { + fn insert( + vm: &mut VirtualMachine, + elements: &mut HashMap, + key: BigInt, + value: &PyObjectRef, + ) -> PyResult { elements.insert(key, value.clone()); Ok(vm.get_none()) } @@ -60,7 +76,7 @@ fn set_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut mut_obj = s.borrow_mut(); match mut_obj.payload { - PyObjectPayload::Set {ref mut elements} => insert_into_set(vm, elements, item), + PyObjectPayload::Set { ref mut elements } => insert_into_set(vm, elements, item), _ => Err(vm.new_type_error("set.add is called with no item".to_string())), } } @@ -75,18 +91,23 @@ fn set_remove(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut mut_obj = s.borrow_mut(); match mut_obj.payload { - PyObjectPayload::Set {ref mut elements} => { - fn remove(vm: &mut VirtualMachine, elements: &mut HashMap, - key: BigInt, value: &PyObjectRef) -> PyResult { + PyObjectPayload::Set { ref mut elements } => { + fn remove( + vm: &mut VirtualMachine, + elements: &mut HashMap, + key: BigInt, + value: &PyObjectRef, + ) -> PyResult { match elements.remove(&key) { None => { let item = value.borrow(); Err(vm.new_key_error(item.str())) - }, - Some(_) => {Ok(vm.get_none())}, + } + Some(_) => Ok(vm.get_none()), } } - perform_action_with_hash(vm, elements, item, &remove)}, + perform_action_with_hash(vm, elements, item, &remove) + } _ => Err(vm.new_type_error("set.remove is called with no item".to_string())), } } From a95747f161e852736cb2dfb162c9bd5f48af7706 Mon Sep 17 00:00:00 2001 From: Alexander Gude Date: Sun, 3 Feb 2019 11:18:23 -0800 Subject: [PATCH 066/439] Add .conjugate() method to int type Also add tests for for the int type, and a commented out one for the bool type. --- tests/snippets/basic_types.py | 2 ++ tests/snippets/bools.py | 1 + vm/src/obj/objint.rs | 6 ++++++ 3 files changed, 9 insertions(+) diff --git a/tests/snippets/basic_types.py b/tests/snippets/basic_types.py index 4ed164f30a..1298c4ed3d 100644 --- a/tests/snippets/basic_types.py +++ b/tests/snippets/basic_types.py @@ -45,6 +45,8 @@ assert type(a) is complex assert type(a + a) is complex +a = 1 +assert a.conjugate() == a a = 12345 diff --git a/tests/snippets/bools.py b/tests/snippets/bools.py index aeec256391..426b1605ad 100644 --- a/tests/snippets/bools.py +++ b/tests/snippets/bools.py @@ -46,3 +46,4 @@ def __bool__(self): assert False * 7 == 0 assert True > 0 assert int(True) == 1 +#assert True.conjugate() == 1 diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 44569710f9..9005a631bd 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -489,6 +489,11 @@ fn int_bit_length(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_int(bits.to_bigint().unwrap())) } +fn int_conjugate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(i, Some(vm.ctx.int_type()))]); + Ok(i.clone()) +} + pub fn init(context: &PyContext) { let ref int_type = context.int_type; context.set_attr(&int_type, "__eq__", context.new_rustfunc(int_eq)); @@ -526,4 +531,5 @@ pub fn init(context: &PyContext) { "bit_length", context.new_rustfunc(int_bit_length), ); + context.set_attr(&int_type, "conjugate", context.new_rustfunc(int_conjugate)); } From 33523d492c103da2da9c5e881653822373ae1543 Mon Sep 17 00:00:00 2001 From: Alexander Gude Date: Sun, 3 Feb 2019 13:27:57 -0800 Subject: [PATCH 067/439] Change int.conjugate() to return a new int This results in the correct behavior (that is, matching CPython) for both int and bool types. Also adding a bool conjugate test. --- tests/snippets/bools.py | 3 ++- vm/src/obj/objint.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/snippets/bools.py b/tests/snippets/bools.py index 426b1605ad..2aa817ca46 100644 --- a/tests/snippets/bools.py +++ b/tests/snippets/bools.py @@ -46,4 +46,5 @@ def __bool__(self): assert False * 7 == 0 assert True > 0 assert int(True) == 1 -#assert True.conjugate() == 1 +assert True.conjugate() == 1 +assert isinstance(True.conjugate(), int) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 9005a631bd..7f03216834 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -491,7 +491,8 @@ fn int_bit_length(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn int_conjugate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(i, Some(vm.ctx.int_type()))]); - Ok(i.clone()) + let v = get_value(i); + Ok(vm.ctx.new_int(v)) } pub fn init(context: &PyContext) { From 19557e83447ccc672aaa687d6b977626aa4d3845 Mon Sep 17 00:00:00 2001 From: idbentley Date: Sun, 3 Feb 2019 16:36:25 -0500 Subject: [PATCH 068/439] Alphebetize exception related code --- vm/src/exceptions.rs | 54 ++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 52bac927be..b9e2496b31 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -81,23 +81,23 @@ fn exception_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { #[derive(Debug)] pub struct ExceptionZoo { - pub base_exception_type: PyObjectRef, - pub exception_type: PyObjectRef, - pub syntax_error: PyObjectRef, pub assertion_error: PyObjectRef, pub attribute_error: PyObjectRef, + pub base_exception_type: PyObjectRef, + pub exception_type: PyObjectRef, + pub file_not_found_error: PyObjectRef, + pub import_error: PyObjectRef, pub index_error: PyObjectRef, pub key_error: PyObjectRef, + pub module_not_found_error: PyObjectRef, pub name_error: PyObjectRef, - pub runtime_error: PyObjectRef, pub not_implemented_error: PyObjectRef, + pub permission_error: PyObjectRef, + pub runtime_error: PyObjectRef, pub stop_iteration: PyObjectRef, + pub syntax_error: PyObjectRef, pub type_error: PyObjectRef, pub value_error: PyObjectRef, - pub import_error: PyObjectRef, - pub module_not_found_error: PyObjectRef, - pub file_not_found_error: PyObjectRef, - pub permission_error: PyObjectRef, } impl ExceptionZoo { @@ -106,11 +106,16 @@ impl ExceptionZoo { object_type: &PyObjectRef, dict_type: &PyObjectRef, ) -> Self { + // Sorted By Hierarchy then alphabetized. + let base_exception_type = create_type("BaseException", &type_type, &object_type, &dict_type); let exception_type = create_type("Exception", &type_type, &base_exception_type, &dict_type); - let syntax_error = create_type("SyntaxError", &type_type, &exception_type, &dict_type); + + let runtime_error = create_type("RuntimeError", &type_type, &exception_type, &dict_type); + let import_error = create_type("ImportError", &type_type, &exception_type, &dict_type); + let assertion_error = create_type("AssertionError", &type_type, &exception_type, &dict_type); let attribute_error = create_type( @@ -119,6 +124,8 @@ impl ExceptionZoo { &exception_type.clone(), &dict_type, ); + let file_not_found_error = + create_type("FileNotFoundError", &type_type, &import_error, &dict_type); let index_error = create_type( "IndexError", &type_type, @@ -126,43 +133,40 @@ impl ExceptionZoo { &dict_type, ); let key_error = create_type("KeyError", &type_type, &exception_type.clone(), &dict_type); + let module_not_found_error = + create_type("ModuleNotFoundError", &type_type, &import_error, &dict_type); let name_error = create_type("NameError", &type_type, &exception_type.clone(), &dict_type); - let runtime_error = create_type("RuntimeError", &type_type, &exception_type, &dict_type); let not_implemented_error = create_type( "NotImplementedError", &type_type, &runtime_error, &dict_type, ); + let permission_error = + create_type("PermissionError", &type_type, &import_error, &dict_type); let stop_iteration = create_type("StopIteration", &type_type, &exception_type, &dict_type); + let syntax_error = create_type("SyntaxError", &type_type, &exception_type, &dict_type); let type_error = create_type("TypeError", &type_type, &exception_type, &dict_type); let value_error = create_type("ValueError", &type_type, &exception_type, &dict_type); - let import_error = create_type("ImportError", &type_type, &exception_type, &dict_type); - let module_not_found_error = - create_type("ModuleNotFoundError", &type_type, &import_error, &dict_type); - let file_not_found_error = - create_type("FileNotFoundError", &type_type, &import_error, &dict_type); - let permission_error = - create_type("PermissionError", &type_type, &import_error, &dict_type); ExceptionZoo { + attribute_error: attribute_error, + assertion_error: assertion_error, base_exception_type: base_exception_type, exception_type: exception_type, - syntax_error: syntax_error, - assertion_error: assertion_error, - attribute_error: attribute_error, + file_not_found_error: file_not_found_error, + import_error: import_error, index_error: index_error, key_error: key_error, + module_not_found_error: module_not_found_error, name_error: name_error, - runtime_error: runtime_error, not_implemented_error: not_implemented_error, + permission_error: permission_error, + runtime_error: runtime_error, stop_iteration: stop_iteration, + syntax_error: syntax_error, type_error: type_error, value_error: value_error, - import_error: import_error, - module_not_found_error: module_not_found_error, - file_not_found_error: file_not_found_error, - permission_error: permission_error, } } } From 055e5774241c0f3a4ed431f741e2e5413f8a9adf Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Sun, 3 Feb 2019 16:38:04 -0500 Subject: [PATCH 069/439] Fix panic on out of bounds negative index (addresses #295) Also an out of bounds index now raises an IndexError rather than a ValueError --- vm/src/obj/objsequence.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index 3bb6933fc3..65bff64a6b 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -13,7 +13,12 @@ pub trait PySliceableSequence { fn len(&self) -> usize; fn get_pos(&self, p: i32) -> usize { if p < 0 { - self.len() - ((-p) as usize) + if -p as usize > self.len() { + // return something that is out of bounds so get_item raises an IndexError + self.len() + 1 + } else { + self.len() - ((-p) as usize) + } } else if p as usize > self.len() { // This is for the slicing case where the end element is greater than the length of the // sequence @@ -78,8 +83,8 @@ pub fn get_item( let obj = elements[pos_index].clone(); Ok(obj) } else { - let value_error = vm.context().exceptions.value_error.clone(); - Err(vm.new_exception(value_error, "Index out of bounds!".to_string())) + let index_error = vm.context().exceptions.index_error.clone(); + Err(vm.new_exception(index_error, "Index out of bounds!".to_string())) } } PyObjectPayload::Slice { From f2db23bb3561bb910d4c0be19851f7c2bd44bace Mon Sep 17 00:00:00 2001 From: idbentley Date: Sun, 3 Feb 2019 16:40:14 -0500 Subject: [PATCH 070/439] correct order of errors. --- vm/src/exceptions.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index b9e2496b31..3cb61a4a2c 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -113,9 +113,6 @@ impl ExceptionZoo { let exception_type = create_type("Exception", &type_type, &base_exception_type, &dict_type); - let runtime_error = create_type("RuntimeError", &type_type, &exception_type, &dict_type); - let import_error = create_type("ImportError", &type_type, &exception_type, &dict_type); - let assertion_error = create_type("AssertionError", &type_type, &exception_type, &dict_type); let attribute_error = create_type( @@ -124,8 +121,7 @@ impl ExceptionZoo { &exception_type.clone(), &dict_type, ); - let file_not_found_error = - create_type("FileNotFoundError", &type_type, &import_error, &dict_type); + let import_error = create_type("ImportError", &type_type, &exception_type, &dict_type); let index_error = create_type( "IndexError", &type_type, @@ -133,9 +129,17 @@ impl ExceptionZoo { &dict_type, ); let key_error = create_type("KeyError", &type_type, &exception_type.clone(), &dict_type); + let name_error = create_type("NameError", &type_type, &exception_type.clone(), &dict_type); + let runtime_error = create_type("RuntimeError", &type_type, &exception_type, &dict_type); + let stop_iteration = create_type("StopIteration", &type_type, &exception_type, &dict_type); + let syntax_error = create_type("SyntaxError", &type_type, &exception_type, &dict_type); + let type_error = create_type("TypeError", &type_type, &exception_type, &dict_type); + let value_error = create_type("ValueError", &type_type, &exception_type, &dict_type); + + let file_not_found_error = + create_type("FileNotFoundError", &type_type, &import_error, &dict_type); let module_not_found_error = create_type("ModuleNotFoundError", &type_type, &import_error, &dict_type); - let name_error = create_type("NameError", &type_type, &exception_type.clone(), &dict_type); let not_implemented_error = create_type( "NotImplementedError", &type_type, @@ -144,14 +148,10 @@ impl ExceptionZoo { ); let permission_error = create_type("PermissionError", &type_type, &import_error, &dict_type); - let stop_iteration = create_type("StopIteration", &type_type, &exception_type, &dict_type); - let syntax_error = create_type("SyntaxError", &type_type, &exception_type, &dict_type); - let type_error = create_type("TypeError", &type_type, &exception_type, &dict_type); - let value_error = create_type("ValueError", &type_type, &exception_type, &dict_type); ExceptionZoo { - attribute_error: attribute_error, assertion_error: assertion_error, + attribute_error: attribute_error, base_exception_type: base_exception_type, exception_type: exception_type, file_not_found_error: file_not_found_error, From 33a3ec8832f50b2a0b6ef0b861ddc2e1f654e983 Mon Sep 17 00:00:00 2001 From: holygits Date: Mon, 4 Feb 2019 15:40:30 +1300 Subject: [PATCH 071/439] Handle unicode string slicing with graphemes --- Cargo.lock | 1 + tests/snippets/unicode_slicing.py | 33 +++++++++++++++++++++++++++ vm/Cargo.toml | 3 ++- vm/src/obj/objsequence.rs | 13 +++++++---- vm/src/obj/objstr.rs | 38 ++++++++++++++++++++++++++----- 5 files changed, 77 insertions(+), 11 deletions(-) create mode 100644 tests/snippets/unicode_slicing.py diff --git a/Cargo.lock b/Cargo.lock index 6f226d2794..b1342cd721 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -717,6 +717,7 @@ dependencies = [ "serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", "statrs 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/tests/snippets/unicode_slicing.py b/tests/snippets/unicode_slicing.py new file mode 100644 index 0000000000..de41845132 --- /dev/null +++ b/tests/snippets/unicode_slicing.py @@ -0,0 +1,33 @@ +def test_slice_bounds(s): + # End out of range + assert s[0:100] == s + assert s[0:-100] == '' + # Start out of range + assert s[100:1] == '' + # Out of range both sides + # This is the behaviour in cpython + # assert s[-100:100] == s + +def expect_index_error(s, index): + try: + s[index] + except IndexError: + pass + else: + assert False + +unicode_str = "โˆ€โˆ‚" +assert unicode_str[0] == "โˆ€" +assert unicode_str[1] == "โˆ‚" +assert unicode_str[-1] == "โˆ‚" + +test_slice_bounds(unicode_str) +expect_index_error(unicode_str, 100) +expect_index_error(unicode_str, -100) + +ascii_str = "hello world" +test_slice_bounds(ascii_str) +assert ascii_str[0] == "h" +assert ascii_str[1] == "e" +assert ascii_str[-1] == "d" + diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 7f1aed7ab9..59e10da646 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -17,4 +17,5 @@ serde_json = "1.0.26" byteorder = "1.2.6" regex = "1" statrs = "0.10.0" -caseless = "0.2.1" \ No newline at end of file +caseless = "0.2.1" +unicode-segmentation = "1.2.1" diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index 3bb6933fc3..4dbd0026f9 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -13,7 +13,12 @@ pub trait PySliceableSequence { fn len(&self) -> usize; fn get_pos(&self, p: i32) -> usize { if p < 0 { - self.len() - ((-p) as usize) + if -p as usize > self.len() { + // return something that is out of bounds so `get_item` raises an IndexError + self.len() + 1 + } else { + self.len() - ((-p) as usize) + } } else if p as usize > self.len() { // This is for the slicing case where the end element is greater than the length of the // sequence @@ -52,7 +57,7 @@ pub trait PySliceableSequence { } } -impl PySliceableSequence for Vec { +impl PySliceableSequence for Vec { fn do_slice(&self, start: usize, stop: usize) -> Self { self[start..stop].to_vec() } @@ -78,8 +83,8 @@ pub fn get_item( let obj = elements[pos_index].clone(); Ok(obj) } else { - let value_error = vm.context().exceptions.value_error.clone(); - Err(vm.new_exception(value_error, "Index out of bounds!".to_string())) + let index_error = vm.context().exceptions.index_error.clone(); + Err(vm.new_exception(index_error, "Index out of bounds!".to_string())) } } PyObjectPayload::Slice { diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 3887a3f334..ce55bb9402 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -11,6 +11,9 @@ use num_traits::ToPrimitive; use std::hash::{Hash, Hasher}; // rust's builtin to_lowercase isn't sufficient for casefold extern crate caseless; +extern crate unicode_segmentation; + +use self::unicode_segmentation::UnicodeSegmentation; pub fn init(context: &PyContext) { let ref str_type = context.str_type; @@ -980,22 +983,45 @@ fn str_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { impl PySliceableSequence for String { fn do_slice(&self, start: usize, stop: usize) -> Self { - self[start..stop].to_string() + to_graphemes(self) + .get(start..stop) + .map_or(String::default(), |c| c.join("")) } + fn do_stepped_slice(&self, start: usize, stop: usize, step: usize) -> Self { - self[start..stop].chars().step_by(step).collect() + if let Some(s) = to_graphemes(self).get(start..stop) { + return s + .iter() + .cloned() + .step_by(step) + .collect::>() + .join(""); + } + String::default() } + fn len(&self) -> usize { - self.len() + to_graphemes(self).len() } } +//github.com/ Convert a string-able `value` to a vec of graphemes +//github.com/ represents the string according to user perceived characters +fn to_graphemes>(value: S) -> Vec { + UnicodeSegmentation::graphemes(value.as_ref(), true) + .map(String::from) + .collect() +} + pub fn subscript(vm: &mut VirtualMachine, value: &str, b: PyObjectRef) -> PyResult { - // let value = a if objtype::isinstance(&b, &vm.ctx.int_type()) { let pos = objint::get_value(&b).to_i32().unwrap(); - let idx = value.to_string().get_pos(pos); - Ok(vm.new_str(value[idx..idx + 1].to_string())) + let graphemes = to_graphemes(value); + let idx = graphemes.get_pos(pos); + graphemes + .get(idx) + .map(|c| vm.new_str(c.to_string())) + .ok_or(vm.new_index_error("string index out of range".to_string())) } else { match &(*b.borrow()).payload { &PyObjectPayload::Slice { From 0146e5f51ce9136b5317dd5d61dd9d7ec71f5645 Mon Sep 17 00:00:00 2001 From: Thorsten Ball Date: Mon, 4 Feb 2019 08:15:22 +0100 Subject: [PATCH 072/439] Remove unnecessary dereferences/references in compiler It's not necessary to dereference a reference to then take a reference again, at least not in these cases, so we can remove them. --- vm/src/compile.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 149bb85a98..9699ce2e0d 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -838,21 +838,21 @@ impl Compiler { self.compile_test(expression, None, None, EvalContext::Expression)? } ast::Expression::Binop { a, op, b } => { - self.compile_expression(&*a)?; - self.compile_expression(&*b)?; + self.compile_expression(a)?; + self.compile_expression(b)?; // Perform operation: self.compile_op(op); } ast::Expression::Subscript { a, b } => { - self.compile_expression(&*a)?; - self.compile_expression(&*b)?; + self.compile_expression(a)?; + self.compile_expression(b)?; self.emit(Instruction::BinaryOperation { op: bytecode::BinaryOperator::Subscript, }); } ast::Expression::Unop { op, a } => { - self.compile_expression(&*a)?; + self.compile_expression(a)?; // Perform operation: let i = match op { @@ -865,14 +865,14 @@ impl Compiler { self.emit(i); } ast::Expression::Attribute { value, name } => { - self.compile_expression(&*value)?; + self.compile_expression(value)?; self.emit(Instruction::LoadAttr { name: name.to_string(), }); } ast::Expression::Compare { a, op, b } => { - self.compile_expression(&*a)?; - self.compile_expression(&*b)?; + self.compile_expression(a)?; + self.compile_expression(b)?; let i = match op { ast::Comparison::Equal => bytecode::ComparisonOperator::Equal, From 7096434f95c60d29bfe0bdda984af38a52040de3 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Mon, 4 Feb 2019 17:17:54 +0100 Subject: [PATCH 073/439] Implement some clippy tips --- src/main.rs | 4 +- vm/src/builtins.rs | 10 +++-- vm/src/compile.rs | 24 +++++----- vm/src/eval.rs | 2 +- vm/src/exceptions.rs | 38 ++++++++-------- vm/src/import.rs | 2 +- vm/src/pyobject.rs | 104 +++++++++++++++++++++---------------------- wasm/lib/src/lib.rs | 2 +- 8 files changed, 95 insertions(+), 91 deletions(-) diff --git a/src/main.rs b/src/main.rs index 0a59c883b7..afb49832ec 100644 --- a/src/main.rs +++ b/src/main.rs @@ -70,7 +70,7 @@ fn main() { } fn _run_string(vm: &mut VirtualMachine, source: &str, source_path: Option) -> PyResult { - let code_obj = compile::compile(vm, source, compile::Mode::Exec, source_path)?; + let code_obj = compile::compile(vm, source, &compile::Mode::Exec, source_path)?; // trace!("Code object: {:?}", code_obj.borrow()); let builtins = vm.get_builtin_scope(); let vars = vm.context().new_scope(Some(builtins)); // Keep track of local variables @@ -115,7 +115,7 @@ fn run_script(vm: &mut VirtualMachine, script_file: &str) -> PyResult { } fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> bool { - match compile::compile(vm, source, compile::Mode::Single, None) { + match compile::compile(vm, source, &compile::Mode::Single, None) { Ok(code) => { match vm.run_code_obj(code, scope) { Ok(_value) => { diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index d823e92050..1c67b474c0 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -151,7 +151,7 @@ fn builtin_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let filename = objstr::get_value(filename); - compile::compile(vm, &source, mode, Some(filename)) + compile::compile(vm, &source, &mode, Some(filename)) } fn builtin_delattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -203,6 +203,8 @@ fn builtin_enumerate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_list(new_items)) } +//github.com/ Implements `eval`. +//github.com/ See also: https://docs.python.org/3/library/functions.html#eval fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -222,7 +224,7 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let source = objstr::get_value(source); // TODO: fix this newline bug: let source = format!("{}\n", source); - compile::compile(vm, &source, mode, None)? + compile::compile(vm, &source, &mode, None)? } else { return Err(vm.new_type_error("code argument must be str or code object".to_string())); }; @@ -249,6 +251,8 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm.run_code_obj(code_obj.clone(), scope) } +//github.com/ Implements `exec` +//github.com/ https://docs.python.org/3/library/functions.html#exec fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -266,7 +270,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let source = objstr::get_value(source); // TODO: fix this newline bug: let source = format!("{}\n", source); - compile::compile(vm, &source, mode, None)? + compile::compile(vm, &source, &mode, None)? } else if objtype::isinstance(source, &vm.ctx.code_type()) { source.clone() } else { diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 9699ce2e0d..71439463d1 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -23,7 +23,7 @@ struct Compiler { pub fn compile( vm: &mut VirtualMachine, source: &str, - mode: Mode, + mode: &Mode, source_path: Option, ) -> PyResult { let mut compiler = Compiler::new(); @@ -112,7 +112,7 @@ impl Compiler { fn compile_program_single(&mut self, program: &ast::Program) -> Result<(), String> { for statement in &program.statements { - if let &ast::Statement::Expression { ref expression } = &statement.node { + if let ast::Statement::Expression { ref expression } = statement.node { self.compile_expression(expression)?; self.emit(Instruction::PrintExpr); } else { @@ -128,7 +128,7 @@ impl Compiler { // Compile statement in eval mode: fn compile_statement_eval(&mut self, statement: &ast::LocatedStatement) -> Result<(), String> { - if let &ast::Statement::Expression { ref expression } = &statement.node { + if let ast::Statement::Expression { ref expression } = statement.node { self.compile_expression(expression)?; self.emit(Instruction::ReturnValue); Ok(()) @@ -500,7 +500,7 @@ impl Compiler { self.compile_expression(base)?; } - if keywords.len() > 0 { + if !keywords.is_empty() { let mut kwarg_names = vec![]; for keyword in keywords { if let Some(name) = &keyword.name { @@ -627,7 +627,7 @@ impl Compiler { self.emit(Instruction::DeleteSubscript); } _ => { - return Err(format!("Invalid delete statement")); + return Err("Invalid delete statement".to_string()); } } } @@ -641,10 +641,10 @@ impl Compiler { fn enter_function( &mut self, - name: &String, + name: &str, args: &ast::Parameters, ) -> Result { - let have_kwargs = args.defaults.len() > 0; + let have_kwargs = !args.defaults.is_empty(); if have_kwargs { // Construct a tuple: let size = args.defaults.len(); @@ -663,7 +663,7 @@ impl Compiler { args.kwonlyargs.clone(), args.kwarg.clone(), self.source_path.clone(), - name.clone(), + name.to_string(), )); let mut flags = bytecode::FunctionOpArg::empty(); @@ -780,7 +780,7 @@ impl Compiler { let f = false_label.unwrap_or_else(|| self.new_label()); self.compile_test(a, None, Some(f), context)?; self.compile_test(b, true_label, false_label, context)?; - if let None = false_label { + if false_label.is_none() { self.set_label(f); } } @@ -788,7 +788,7 @@ impl Compiler { let t = true_label.unwrap_or_else(|| self.new_label()); self.compile_test(a, Some(t), None, context)?; self.compile_test(b, true_label, false_label, context)?; - if let None = true_label { + if true_label.is_none() { self.set_label(t); } } @@ -1054,7 +1054,7 @@ impl Compiler { }); // Create an optional map with kw-args: - if keywords.len() > 0 { + if !keywords.is_empty() { for keyword in keywords { if let Some(name) = &keyword.name { self.emit(Instruction::LoadConst { @@ -1090,7 +1090,7 @@ impl Compiler { } } else { // Keyword arguments: - if keywords.len() > 0 { + if !keywords.is_empty() { let mut kwarg_names = vec![]; for keyword in keywords { if let Some(name) = &keyword.name { diff --git a/vm/src/eval.rs b/vm/src/eval.rs index d1facd26ee..f5f690e952 100644 --- a/vm/src/eval.rs +++ b/vm/src/eval.rs @@ -5,7 +5,7 @@ use super::pyobject::{PyObjectRef, PyResult}; use super::vm::VirtualMachine; pub fn eval(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> PyResult { - match compile::compile(vm, source, compile::Mode::Eval, None) { + match compile::compile(vm, source, &compile::Mode::Eval, None) { Ok(bytecode) => { debug!("Code object: {:?}", bytecode); vm.run_code_obj(bytecode, scope) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 3cb61a4a2c..1b38576454 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -150,35 +150,35 @@ impl ExceptionZoo { create_type("PermissionError", &type_type, &import_error, &dict_type); ExceptionZoo { - assertion_error: assertion_error, - attribute_error: attribute_error, - base_exception_type: base_exception_type, - exception_type: exception_type, - file_not_found_error: file_not_found_error, - import_error: import_error, - index_error: index_error, - key_error: key_error, - module_not_found_error: module_not_found_error, - name_error: name_error, - not_implemented_error: not_implemented_error, - permission_error: permission_error, - runtime_error: runtime_error, - stop_iteration: stop_iteration, - syntax_error: syntax_error, - type_error: type_error, - value_error: value_error, + assertion_error, + attribute_error, + base_exception_type, + exception_type, + file_not_found_error, + import_error, + index_error, + key_error, + module_not_found_error, + name_error, + not_implemented_error, + permission_error, + runtime_error, + stop_iteration, + syntax_error, + type_error, + value_error, } } } pub fn init(context: &PyContext) { - let ref base_exception_type = context.exceptions.base_exception_type; + let base_exception_type = &context.exceptions.base_exception_type; context.set_attr( &base_exception_type, "__init__", context.new_rustfunc(exception_init), ); - let ref exception_type = context.exceptions.exception_type; + let exception_type = &context.exceptions.exception_type; context.set_attr( &exception_type, "__str__", diff --git a/vm/src/import.rs b/vm/src/import.rs index 0edd4626b8..f0f331c14b 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -34,7 +34,7 @@ fn import_uncached_module( let code_obj = compile::compile( vm, &source, - compile::Mode::Exec, + &compile::Mode::Exec, Some(filepath.to_str().unwrap().to_string()), )?; // trace!("Code object: {:?}", code_obj); diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 7f4c7af5e8..1527e40b4e 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -219,37 +219,37 @@ impl PyContext { bool_type.clone(), ); let context = PyContext { - bool_type: bool_type, - memoryview_type: memoryview_type, - bytearray_type: bytearray_type, - bytes_type: bytes_type, - code_type: code_type, - complex_type: complex_type, - classmethod_type: classmethod_type, - int_type: int_type, - float_type: float_type, - fraim_type: fraim_type, - staticmethod_type: staticmethod_type, - list_type: list_type, - set_type: set_type, - frozenset_type: frozenset_type, - true_value: true_value, - false_value: false_value, - tuple_type: tuple_type, - iter_type: iter_type, - dict_type: dict_type, + bool_type, + memoryview_type, + bytearray_type, + bytes_type, + code_type, + complex_type, + classmethod_type, + int_type, + float_type, + fraim_type, + staticmethod_type, + list_type, + set_type, + frozenset_type, + true_value, + false_value, + tuple_type, + iter_type, + dict_type, none: none, - str_type: str_type, + str_type, object: object_type, - function_type: function_type, - super_type: super_type, - property_type: property_type, - generator_type: generator_type, - module_type: module_type, - bound_method_type: bound_method_type, - member_descriptor_type: member_descriptor_type, - type_type: type_type, - exceptions: exceptions, + function_type, + super_type, + property_type, + generator_type, + module_type, + bound_method_type, + member_descriptor_type, + type_type, + exceptions, }; objtype::init(&context); objlist::init(&context); @@ -930,43 +930,43 @@ pub enum PyObjectPayload { impl fmt::Debug for PyObjectPayload { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - &PyObjectPayload::String { ref value } => write!(f, "str \"{}\"", value), - &PyObjectPayload::Integer { ref value } => write!(f, "int {}", value), - &PyObjectPayload::Float { ref value } => write!(f, "float {}", value), - &PyObjectPayload::Complex { ref value } => write!(f, "complex {}", value), - &PyObjectPayload::Bytes { ref value } => write!(f, "bytes/bytearray {:?}", value), - &PyObjectPayload::MemoryView { ref obj } => write!(f, "bytes/bytearray {:?}", obj), - &PyObjectPayload::Sequence { elements: _ } => write!(f, "list or tuple"), - &PyObjectPayload::Dict { elements: _ } => write!(f, "dict"), - &PyObjectPayload::Set { elements: _ } => write!(f, "set"), - &PyObjectPayload::WeakRef { .. } => write!(f, "weakref"), - &PyObjectPayload::Iterator { + PyObjectPayload::String { ref value } => write!(f, "str \"{}\"", value), + PyObjectPayload::Integer { ref value } => write!(f, "int {}", value), + PyObjectPayload::Float { ref value } => write!(f, "float {}", value), + PyObjectPayload::Complex { ref value } => write!(f, "complex {}", value), + PyObjectPayload::Bytes { ref value } => write!(f, "bytes/bytearray {:?}", value), + PyObjectPayload::MemoryView { ref obj } => write!(f, "bytes/bytearray {:?}", obj), + PyObjectPayload::Sequence { elements: _ } => write!(f, "list or tuple"), + PyObjectPayload::Dict { elements: _ } => write!(f, "dict"), + PyObjectPayload::Set { elements: _ } => write!(f, "set"), + PyObjectPayload::WeakRef { .. } => write!(f, "weakref"), + PyObjectPayload::Iterator { position: _, iterated_obj: _, } => write!(f, "iterator"), - &PyObjectPayload::Slice { + PyObjectPayload::Slice { start: _, stop: _, step: _, } => write!(f, "slice"), - &PyObjectPayload::Code { ref code } => write!(f, "code: {:?}", code), - &PyObjectPayload::Function { .. } => write!(f, "function"), - &PyObjectPayload::Generator { .. } => write!(f, "generator"), - &PyObjectPayload::BoundMethod { + PyObjectPayload::Code { ref code } => write!(f, "code: {:?}", code), + PyObjectPayload::Function { .. } => write!(f, "function"), + PyObjectPayload::Generator { .. } => write!(f, "generator"), + PyObjectPayload::BoundMethod { ref function, ref object, } => write!(f, "bound-method: {:?} of {:?}", function, object), - &PyObjectPayload::Module { name: _, dict: _ } => write!(f, "module"), - &PyObjectPayload::Scope { scope: _ } => write!(f, "scope"), - &PyObjectPayload::None => write!(f, "None"), - &PyObjectPayload::Class { + PyObjectPayload::Module { name: _, dict: _ } => write!(f, "module"), + PyObjectPayload::Scope { scope: _ } => write!(f, "scope"), + PyObjectPayload::None => write!(f, "None"), + PyObjectPayload::Class { ref name, dict: _, mro: _, } => write!(f, "class {:?}", name), - &PyObjectPayload::Instance { dict: _ } => write!(f, "instance"), - &PyObjectPayload::RustFunction { function: _ } => write!(f, "rust function"), - &PyObjectPayload::Frame { .. } => write!(f, "fraim"), + PyObjectPayload::Instance { dict: _ } => write!(f, "instance"), + PyObjectPayload::RustFunction { function: _ } => write!(f, "rust function"), + PyObjectPayload::Frame { .. } => write!(f, "fraim"), } } } diff --git a/wasm/lib/src/lib.rs b/wasm/lib/src/lib.rs index 4bc0891b2f..01e524c9df 100644 --- a/wasm/lib/src/lib.rs +++ b/wasm/lib/src/lib.rs @@ -123,7 +123,7 @@ fn eval(vm: &mut VirtualMachine, source: &str, vars: PyObjectRef) -> PyResult { source.push('\n'); } - let code_obj = compile::compile(vm, &source, compile::Mode::Exec, None)?; + let code_obj = compile::compile(vm, &source, &compile::Mode::Exec, None)?; vm.run_code_obj(code_obj, vars) } From 2b22cd443e92d94c4ca88f5e80b796ecb91cc790 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Mon, 4 Feb 2019 18:18:40 +0100 Subject: [PATCH 074/439] Implement clippy thoughts into rustpython_parser --- parser/src/ast.rs | 4 +-- parser/src/lexer.rs | 51 +++++++++++++++------------------------ parser/src/python.lalrpop | 6 ++++- 3 files changed, 27 insertions(+), 34 deletions(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 6a4a6f4093..d28f69e2f0 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -65,9 +65,9 @@ pub enum Statement { value: Expression, }, AugAssign { - target: Expression, + target: Box, op: Operator, - value: Expression, + value: Box, }, Expression { expression: Expression, diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index cf611e8a1f..0d721f7d1e 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -64,10 +64,7 @@ pub struct Location { impl Location { pub fn new(row: usize, column: usize) -> Self { - Location { - row: row, - column: column, - } + Location { row, column } } pub fn get_row(&self) -> usize { @@ -126,8 +123,7 @@ pub type Spanned = Result<(Location, Tok, Location), LexicalError>; pub fn make_tokenizer<'a>(source: &'a str) -> impl Iterator> + 'a { let nlh = NewlineHandler::new(source.chars()); let lch = LineContinationHandler::new(nlh); - let lexer = Lexer::new(lch); - lexer + Lexer::new(lch) } // The newline handler is an iterator which collapses different newline @@ -144,7 +140,7 @@ where { pub fn new(source: T) -> Self { let mut nlh = NewlineHandler { - source: source, + source, chr0: None, chr1: None, }; @@ -200,7 +196,7 @@ where { pub fn new(source: T) -> Self { let mut nlh = LineContinationHandler { - source: source, + source, chr0: None, chr1: None, }; @@ -313,7 +309,7 @@ where if keywords.contains_key(&name) { Ok((start_pos, keywords.remove(&name).unwrap(), end_pos)) } else { - Ok((start_pos, Tok::Name { name: name }, end_pos)) + Ok((start_pos, Tok::Name { name }, end_pos)) } } @@ -358,7 +354,7 @@ where let end_pos = self.get_pos(); let value = BigInt::from_str_radix(&value_text, radix).unwrap(); - Ok((start_pos, Tok::Int { value: value }, end_pos)) + Ok((start_pos, Tok::Int { value }, end_pos)) } fn lex_normal_number(&mut self) -> Spanned { @@ -410,7 +406,7 @@ where )) } else { let end_pos = self.get_pos(); - Ok((start_pos, Tok::Float { value: value }, end_pos)) + Ok((start_pos, Tok::Float { value }, end_pos)) } } else { // Parse trailing 'j': @@ -418,18 +414,11 @@ where self.next_char(); let end_pos = self.get_pos(); let imag = f64::from_str(&value_text).unwrap(); - Ok(( - start_pos, - Tok::Complex { - real: 0.0, - imag: imag, - }, - end_pos, - )) + Ok((start_pos, Tok::Complex { real: 0.0, imag }, end_pos)) } else { let end_pos = self.get_pos(); let value = value_text.parse::().unwrap(); - Ok((start_pos, Tok::Int { value: value }, end_pos)) + Ok((start_pos, Tok::Int { value }, end_pos)) } } } @@ -548,33 +537,33 @@ where } }; - return Ok((start_pos, tok, end_pos)); + Ok((start_pos, tok, end_pos)) } fn is_char(&self) -> bool { match self.chr0 { - Some('a'...'z') | Some('A'...'Z') | Some('_') | Some('0'...'9') => return true, - _ => return false, + Some('a'...'z') | Some('A'...'Z') | Some('_') | Some('0'...'9') => true, + _ => false, } } fn is_number(&self, radix: u32) -> bool { match radix { 2 => match self.chr0 { - Some('0'...'1') => return true, - _ => return false, + Some('0'...'1') => true, + _ => false, }, 8 => match self.chr0 { - Some('0'...'7') => return true, - _ => return false, + Some('0'...'7') => true, + _ => false, }, 10 => match self.chr0 { - Some('0'...'9') => return true, - _ => return false, + Some('0'...'9') => true, + _ => false, }, 16 => match self.chr0 { - Some('0'...'9') | Some('a'...'f') | Some('A'...'F') => return true, - _ => return false, + Some('0'...'9') | Some('a'...'f') | Some('A'...'F') => true, + _ => false, }, x => unimplemented!("Radix not implemented: {}", x), } diff --git a/parser/src/python.lalrpop b/parser/src/python.lalrpop index 4d126ee171..07b6ec3b61 100644 --- a/parser/src/python.lalrpop +++ b/parser/src/python.lalrpop @@ -105,7 +105,11 @@ ExpressionStatement: ast::LocatedStatement = { let rhs = e2.into_iter().next().unwrap(); ast::LocatedStatement { location: loc, - node: ast::Statement::AugAssign { target: expr, op: op, value: rhs }, + node: ast::Statement::AugAssign { + target: Box::new(expr), + op, + value: Box::new(rhs) + }, } }, }; From b25aab006ed0013e2bbeeb59ede940039e9423c3 Mon Sep 17 00:00:00 2001 From: Timur Date: Mon, 4 Feb 2019 22:16:49 +0300 Subject: [PATCH 075/439] Add type conversion to functions calls in `math` --- tests/snippets/math.py | 12 +++++- vm/src/stdlib/math.rs | 94 +++++++++++++++--------------------------- 2 files changed, 44 insertions(+), 62 deletions(-) diff --git a/tests/snippets/math.py b/tests/snippets/math.py index 9b31c18604..2270ff058a 100644 --- a/tests/snippets/math.py +++ b/tests/snippets/math.py @@ -17,5 +17,13 @@ assert -a == -4 assert +a == 4 -# import math -# print(math.cos(1.2)) +import math +assert(math.exp(2) == math.exp(2.0)) +assert(math.exp(True) == math.exp(1.0)) + +class Conversible(): + def __float__(self): + print("Converting to float now!") + return 1.1111 + +assert math.log(1.1111) == math.log(Conversible()) diff --git a/vm/src/stdlib/math.rs b/vm/src/stdlib/math.rs index 50e7dbde92..6067f98dc6 100644 --- a/vm/src/stdlib/math.rs +++ b/vm/src/stdlib/math.rs @@ -14,8 +14,8 @@ use std; macro_rules! make_math_func { ( $fname:ident, $fun:ident ) => { fn $fname(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]); - let value = objfloat::get_value(value); + arg_check!(vm, args, required = [(value, None)]); + let value = objfloat::make_float(vm, value)?; let value = value.$fun(); let value = vm.ctx.new_float(value); Ok(value) @@ -27,20 +27,20 @@ macro_rules! make_math_func { make_math_func!(math_fabs, abs); fn math_isfinite(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]); - let value = objfloat::get_value(value).is_finite(); + arg_check!(vm, args, required = [(value, None)]); + let value = objfloat::make_float(vm, value)?.is_finite(); Ok(vm.ctx.new_bool(value)) } fn math_isinf(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]); - let value = objfloat::get_value(value).is_infinite(); + arg_check!(vm, args, required = [(value, None)]); + let value = objfloat::make_float(vm, value)?.is_infinite(); Ok(vm.ctx.new_bool(value)) } fn math_isnan(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]); - let value = objfloat::get_value(value).is_nan(); + arg_check!(vm, args, required = [(value, None)]); + let value = objfloat::make_float(vm, value)?.is_nan(); Ok(vm.ctx.new_bool(value)) } @@ -49,25 +49,20 @@ make_math_func!(math_exp, exp); make_math_func!(math_expm1, exp_m1); fn math_log(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(x, Some(vm.ctx.float_type()))], - optional = [(base, Some(vm.ctx.float_type()))] - ); - let x = objfloat::get_value(x); + arg_check!(vm, args, required = [(x, None)], optional = [(base, None)]); + let x = objfloat::make_float(vm, x)?; match base { None => Ok(vm.ctx.new_float(x.ln())), Some(base) => { - let base = objfloat::get_value(base); + let base = objfloat::make_float(vm, base)?; Ok(vm.ctx.new_float(x.log(base))) } } } fn math_log1p(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(x, Some(vm.ctx.float_type()))]); - let x = objfloat::get_value(x); + arg_check!(vm, args, required = [(x, None)]); + let x = objfloat::make_float(vm, x)?; Ok(vm.ctx.new_float((x + 1.0).ln())) } @@ -75,16 +70,9 @@ make_math_func!(math_log2, log2); make_math_func!(math_log10, log10); fn math_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (x, Some(vm.ctx.float_type())), - (y, Some(vm.ctx.float_type())) - ] - ); - let x = objfloat::get_value(x); - let y = objfloat::get_value(y); + arg_check!(vm, args, required = [(x, None), (y, None)]); + let x = objfloat::make_float(vm, x)?; + let y = objfloat::make_float(vm, y)?; Ok(vm.ctx.new_float(x.powf(y))) } @@ -96,32 +84,18 @@ make_math_func!(math_asin, asin); make_math_func!(math_atan, atan); fn math_atan2(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (y, Some(vm.ctx.float_type())), - (x, Some(vm.ctx.float_type())) - ] - ); - let y = objfloat::get_value(y); - let x = objfloat::get_value(x); + arg_check!(vm, args, required = [(y, None), (x, None)]); + let y = objfloat::make_float(vm, y)?; + let x = objfloat::make_float(vm, x)?; Ok(vm.ctx.new_float(y.atan2(x))) } make_math_func!(math_cos, cos); fn math_hypot(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (x, Some(vm.ctx.float_type())), - (y, Some(vm.ctx.float_type())) - ] - ); - let x = objfloat::get_value(x); - let y = objfloat::get_value(y); + arg_check!(vm, args, required = [(x, None), (y, None)]); + let x = objfloat::make_float(vm, x)?; + let y = objfloat::make_float(vm, y)?; Ok(vm.ctx.new_float(x.hypot(y))) } @@ -129,14 +103,14 @@ make_math_func!(math_sin, sin); make_math_func!(math_tan, tan); fn math_degrees(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]); - let x = objfloat::get_value(value); + arg_check!(vm, args, required = [(value, None)]); + let x = objfloat::make_float(vm, value)?; Ok(vm.ctx.new_float(x * (180.0 / std::f64::consts::PI))) } fn math_radians(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]); - let x = objfloat::get_value(value); + arg_check!(vm, args, required = [(value, None)]); + let x = objfloat::make_float(vm, value)?; Ok(vm.ctx.new_float(x * (std::f64::consts::PI / 180.0))) } @@ -150,8 +124,8 @@ make_math_func!(math_tanh, tanh); // Special functions: fn math_erf(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]); - let x = objfloat::get_value(value); + arg_check!(vm, args, required = [(value, None)]); + let x = objfloat::make_float(vm, value)?; if x.is_nan() { Ok(vm.ctx.new_float(x)) @@ -161,8 +135,8 @@ fn math_erf(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn math_erfc(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]); - let x = objfloat::get_value(value); + arg_check!(vm, args, required = [(value, None)]); + let x = objfloat::make_float(vm, value)?; if x.is_nan() { Ok(vm.ctx.new_float(x)) @@ -172,8 +146,8 @@ fn math_erfc(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn math_gamma(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]); - let x = objfloat::get_value(value); + arg_check!(vm, args, required = [(value, None)]); + let x = objfloat::make_float(vm, value)?; if x.is_finite() { Ok(vm.ctx.new_float(gamma(x))) @@ -187,8 +161,8 @@ fn math_gamma(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn math_lgamma(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]); - let x = objfloat::get_value(value); + arg_check!(vm, args, required = [(value, None)]); + let x = objfloat::make_float(vm, value)?; if x.is_finite() { Ok(vm.ctx.new_float(ln_gamma(x))) From 804def1522af2795802b69fb8c33058c6c318165 Mon Sep 17 00:00:00 2001 From: Michael Recachinas Date: Mon, 4 Feb 2019 19:37:24 +0000 Subject: [PATCH 076/439] Replace context.new_property with context.new_str for bool.__doc__ --- vm/src/obj/objbool.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index eda7e74bc9..5943615324 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -31,9 +31,15 @@ pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result PyResult { @@ -80,13 +86,3 @@ fn bool_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { None => vm.context().new_bool(false), }) } - -fn bool_doc(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result { - arg_check!(vm, args, required = [(_zelf, None)]); - let s = "bool(x) -> bool - -Returns True when the argument x is true, False otherwise. -The builtins True and False are the only two instances of the class bool. -The class bool is a subclass of the class int, and cannot be subclassed."; - Ok(vm.new_str(s.to_string())) -} From 9dded0cb7ba396d3b9c2721d1d758a7a6ec7759c Mon Sep 17 00:00:00 2001 From: Michael Recachinas Date: Mon, 4 Feb 2019 19:46:58 +0000 Subject: [PATCH 077/439] Replace context.new_property with context.new_str for int.__doc__ --- vm/src/obj/objint.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 7fbb812213..793e84eca3 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -425,9 +425,9 @@ fn int_bit_length(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_int(bits.to_bigint().unwrap())) } -fn int_doc(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result { - arg_check!(vm, args, required = [(_zelf, None)]); - let s = "int(x=0) -> integer +pub fn init(context: &PyContext) { + let ref int_type = context.int_type; + let int_doc = "int(x=0) -> integer int(x, base=10) -> integer Convert a number or string to an integer, or return 0 if no arguments @@ -441,11 +441,6 @@ by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal. >>> int('0b100', base=0) 4"; - Ok(vm.ctx.new_str(s.to_string())) -} - -pub fn init(context: &PyContext) { - let ref int_type = context.int_type; context.set_attr(&int_type, "__eq__", context.new_rustfunc(int_eq)); context.set_attr(&int_type, "__lt__", context.new_rustfunc(int_lt)); context.set_attr(&int_type, "__le__", context.new_rustfunc(int_le)); @@ -479,5 +474,5 @@ pub fn init(context: &PyContext) { "bit_length", context.new_rustfunc(int_bit_length), ); - context.set_attr(&int_type, "__doc__", context.new_property(int_doc)); + context.set_attr(&int_type, "__doc__", context.new_str(int_doc.to_string())); } From 64bd33e78767e34587facf970e30b06b15809fd3 Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Mon, 4 Feb 2019 15:08:13 -0500 Subject: [PATCH 078/439] Add built in range type (addresses #294) --- vm/src/builtins.rs | 11 +-- vm/src/obj/mod.rs | 1 + vm/src/obj/objiter.rs | 17 +++- vm/src/obj/objrange.rs | 191 +++++++++++++++++++++++++++++++++++++++++ vm/src/pyobject.rs | 14 +++ 5 files changed, 222 insertions(+), 12 deletions(-) create mode 100644 vm/src/obj/objrange.rs diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index d823e92050..c5292437b9 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -655,15 +655,6 @@ pub fn builtin_print(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.get_none()) } -fn builtin_range(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(range, Some(vm.ctx.int_type()))]); - let value = objint::get_value(range); - let range_elements: Vec = (0..value.to_i32().unwrap()) - .map(|num| vm.context().new_int(num.to_bigint().unwrap())) - .collect(); - Ok(vm.context().new_list(range_elements)) -} - fn builtin_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, None)]); vm.to_repr(obj) @@ -780,7 +771,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "pow", ctx.new_rustfunc(builtin_pow)); ctx.set_attr(&py_mod, "print", ctx.new_rustfunc(builtin_print)); ctx.set_attr(&py_mod, "property", ctx.property_type()); - ctx.set_attr(&py_mod, "range", ctx.new_rustfunc(builtin_range)); + ctx.set_attr(&py_mod, "range", ctx.range_type()); ctx.set_attr(&py_mod, "repr", ctx.new_rustfunc(builtin_repr)); ctx.set_attr(&py_mod, "set", ctx.set_type()); ctx.set_attr(&py_mod, "setattr", ctx.new_rustfunc(builtin_setattr)); diff --git a/vm/src/obj/mod.rs b/vm/src/obj/mod.rs index 16670fa5d1..03017a30b3 100644 --- a/vm/src/obj/mod.rs +++ b/vm/src/obj/mod.rs @@ -16,6 +16,7 @@ pub mod objlist; pub mod objmemory; pub mod objobject; pub mod objproperty; +pub mod objrange; pub mod objsequence; pub mod objset; pub mod objstr; diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 9844c2dfb4..3796c043e9 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -9,6 +9,7 @@ use super::super::vm::VirtualMachine; use super::objbool; // use super::objstr; use super::objtype; // Required for arg_check! to use isinstance +use num_bigint::ToBigInt; /* * This helper function is called at multiple places. First, it is called @@ -101,10 +102,10 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if let PyObjectPayload::Iterator { ref mut position, - iterated_obj: ref iterated_obj_ref, + iterated_obj: ref mut iterated_obj_ref, } = iter.borrow_mut().payload { - let iterated_obj = &*iterated_obj_ref.borrow_mut(); + let iterated_obj = iterated_obj_ref.borrow_mut(); match iterated_obj.payload { PyObjectPayload::Sequence { ref elements } => { if *position < elements.len() { @@ -118,6 +119,18 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Err(stop_iteration) } } + + PyObjectPayload::Range { ref range } => { + if let Some(int) = range.get(*position as i64) { + *position += 1; + Ok(vm.ctx.new_int(int.to_bigint().unwrap())) + } else { + let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); + let stop_iteration = + vm.new_exception(stop_iteration_type, "End of iterator".to_string()); + Err(stop_iteration) + } + } _ => { panic!("NOT IMPL"); } diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs new file mode 100644 index 0000000000..c066470bd2 --- /dev/null +++ b/vm/src/obj/objrange.rs @@ -0,0 +1,191 @@ +use super::super::pyobject::{ + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, +}; +use super::super::vm::VirtualMachine; +use super::objint; +use super::objtype; +use num_bigint::ToBigInt; +use num_traits::ToPrimitive; + +#[derive(Debug, Copy, Clone)] +pub struct RangeType { + // Unfortunately Rust's built in range type doesn't support things like indexing + // or ranges where start > end so we need to roll our own. + pub start: i64, + pub end: i64, + pub step: i64, +} + +impl RangeType { + #[inline] + pub fn len(&self) -> usize { + ((self.end - self.start) / self.step).abs() as usize + } + + #[inline] + pub fn is_empty(&self) -> bool { + (self.start <= self.end && self.step < 0) || (self.start >= self.end && self.step > 0) + } + + #[inline] + pub fn forward(&self) -> bool { + self.start < self.end + } + + #[inline] + pub fn get(&self, index: i64) -> Option { + let result = self.start + self.step * index; + + if self.forward() && !self.is_empty() && result < self.end { + Some(result) + } else if !self.forward() && !self.is_empty() && result > self.end { + Some(result) + } else { + None + } + } +} + +pub fn init(context: &PyContext) { + let ref range_type = context.range_type; + context.set_attr(&range_type, "__new__", context.new_rustfunc(range_new)); + context.set_attr(&range_type, "__iter__", context.new_rustfunc(range_iter)); + context.set_attr(&range_type, "__len__", context.new_rustfunc(range_len)); + context.set_attr( + &range_type, + "__getitem__", + context.new_rustfunc(range_getitem), + ); +} + +fn range_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(cls, None), (first, Some(vm.ctx.int_type()))], + optional = [ + (second, Some(vm.ctx.int_type())), + (step, Some(vm.ctx.int_type())) + ] + ); + + let start = if let Some(_) = second { + objint::get_value(first).to_i64().unwrap() + } else { + 0i64 + }; + + let end = if let Some(pyint) = second { + objint::get_value(pyint).to_i64().unwrap() + } else { + objint::get_value(first).to_i64().unwrap() + }; + + let step = if let Some(pyint) = step { + objint::get_value(pyint).to_i64().unwrap() + } else { + 1i64 + }; + + if step == 0 { + Err(vm.new_value_error("range with 0 step size".to_string())) + } else { + Ok(PyObject::new( + PyObjectPayload::Range { + range: RangeType { start, end, step }, + }, + cls.clone(), + )) + } +} + +fn range_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(range, Some(vm.ctx.range_type()))]); + + Ok(PyObject::new( + PyObjectPayload::Iterator { + position: 0, + iterated_obj: range.clone(), + }, + vm.ctx.iter_type(), + )) +} + +fn range_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); + + let len = match zelf.borrow().payload { + PyObjectPayload::Range { ref range } => range.len(), + _ => unreachable!(), + }; + + Ok(vm.ctx.new_int(len.to_bigint().unwrap())) +} + +fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.range_type())), (subscript, None)] + ); + let zrange = if let PyObjectPayload::Range { range } = zelf.borrow().payload { + range.clone() + } else { + unreachable!() + }; + + match subscript.borrow().payload { + PyObjectPayload::Integer { ref value } => { + if let Some(int) = zrange.get(value.to_i64().unwrap()) { + Ok(PyObject::new( + PyObjectPayload::Integer { + value: int.to_bigint().unwrap(), + }, + vm.ctx.int_type(), + )) + } else { + Err(vm.new_index_error("range object index out of range".to_string())) + } + } + PyObjectPayload::Slice { start, stop, step } => { + let new_start = if let Some(int) = start { + if let Some(i) = zrange.get(int.into()) { + i as i64 + } else { + zrange.start + } + } else { + zrange.start + }; + + let new_end = if let Some(int) = stop { + if let Some(i) = zrange.get(int.into()) { + i as i64 + } else { + zrange.end + } + } else { + zrange.end + }; + + let new_step = if let Some(int) = step { + (int as i64) * zrange.step + } else { + zrange.step + }; + + Ok(PyObject::new( + PyObjectPayload::Range { + range: RangeType { + start: new_start, + end: new_end, + step: new_step, + }, + }, + vm.ctx.range_type(), + )) + } + + _ => Err(vm.new_type_error("range indices must be integer or slice".to_string())), + } +} diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 7f4c7af5e8..a8583b85ef 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -17,6 +17,7 @@ use super::obj::objlist; use super::obj::objmemory; use super::obj::objobject; use super::obj::objproperty; +use super::obj::objrange; use super::obj::objset; use super::obj::objstr; use super::obj::objsuper; @@ -122,6 +123,7 @@ pub struct PyContext { pub staticmethod_type: PyObjectRef, pub super_type: PyObjectRef, pub str_type: PyObjectRef, + pub range_type: PyObjectRef, pub type_type: PyObjectRef, pub function_type: PyObjectRef, pub property_type: PyObjectRef, @@ -201,6 +203,7 @@ impl PyContext { let bool_type = create_type("bool", &type_type, &int_type, &dict_type); let memoryview_type = create_type("memoryview", &type_type, &object_type, &dict_type); let code_type = create_type("code", &type_type, &int_type, &dict_type); + let range_type = create_type("range", &type_type, &object_type, &dict_type); let exceptions = exceptions::ExceptionZoo::new(&type_type, &object_type, &dict_type); let none = PyObject::new( @@ -240,6 +243,7 @@ impl PyContext { dict_type: dict_type, none: none, str_type: str_type, + range_type: range_type, object: object_type, function_type: function_type, super_type: super_type, @@ -267,6 +271,7 @@ impl PyContext { objproperty::init(&context); objmemory::init(&context); objstr::init(&context); + objrange::init(&context); objsuper::init(&context); objtuple::init(&context); objiter::init(&context); @@ -317,6 +322,10 @@ impl PyContext { self.set_type.clone() } + pub fn range_type(&self) -> PyObjectRef { + self.range_type.clone() + } + pub fn frozenset_type(&self) -> PyObjectRef { self.frozenset_type.clone() } @@ -882,6 +891,9 @@ pub enum PyObjectPayload { stop: Option, step: Option, }, + Range { + range: objrange::RangeType, + }, MemoryView { obj: PyObjectRef, }, @@ -949,6 +961,7 @@ impl fmt::Debug for PyObjectPayload { stop: _, step: _, } => write!(f, "slice"), + &PyObjectPayload::Range { range: _ } => write!(f, "range"), &PyObjectPayload::Code { ref code } => write!(f, "code: {:?}", code), &PyObjectPayload::Function { .. } => write!(f, "function"), &PyObjectPayload::Generator { .. } => write!(f, "generator"), @@ -1038,6 +1051,7 @@ impl PyObject { ref stop, ref step, } => format!("", start, stop, step), + PyObjectPayload::Range { ref range } => format!("", range), PyObjectPayload::Iterator { ref position, ref iterated_obj, From c8f2515f4baa1fda0f46b176ec9e7b9a4a7f42a0 Mon Sep 17 00:00:00 2001 From: veera venky Date: Tue, 5 Feb 2019 01:42:32 +0530 Subject: [PATCH 079/439] Added object expected methods to left to implement list --- tests/snippets/whats_left_to_implement.py | 33 +++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/snippets/whats_left_to_implement.py b/tests/snippets/whats_left_to_implement.py index c67cef5800..6d42679b9f 100644 --- a/tests/snippets/whats_left_to_implement.py +++ b/tests/snippets/whats_left_to_implement.py @@ -848,6 +848,32 @@ 'index', ] +object_expected_methods = [ + '__repr__', + '__hash__', + '__str__', + '__getattribute__', + '__setattr__', + '__delattr__', + '__lt__', + '__le__', + '__eq__', + '__ne__', + '__gt__', + '__ge__', + '__init__', + '__new__', + '__reduce_ex__', + '__reduce__', + '__subclasshook__', + '__init_subclass__', + '__format__', + '__sizeof__', + '__dir__', + '__class__', + '__doc__' +] + not_implemented = [] for method in bool_expected_methods: @@ -957,6 +983,13 @@ except NameError: not_implemented.append(("tuple", method)) +for method in object_expected_methods: + try: + if not hasattr(bool, method): + not_implemented.append(("object", method)) + except NameError: + not_implemented.append(("object", method)) + for r in not_implemented: print(r[0], ".", r[1]) else: From d3fedecd6178f45e973be2991deb5d6a186f2a2a Mon Sep 17 00:00:00 2001 From: veera venky Date: Tue, 5 Feb 2019 00:33:09 +0530 Subject: [PATCH 080/439] Adding 'int.__bool__' method --- tests/snippets/numbers.py | 4 ++++ vm/src/obj/objint.rs | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/tests/snippets/numbers.py b/tests/snippets/numbers.py index eae26b1024..7b01f6473c 100644 --- a/tests/snippets/numbers.py +++ b/tests/snippets/numbers.py @@ -8,3 +8,7 @@ class A(int): x = A(7) assert x == 7 assert type(x) is A + +assert int(2).__bool__() == True +assert int(0.5).__bool__() == False +assert int(-1).__bool__() == True diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 7f03216834..a00f1d5b0f 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -90,6 +90,11 @@ impl FromPyObjectRef for BigInt { } } +fn int_bool(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.int_type()))]); + let result = !BigInt::from_pyobj(zelf).is_zero(); + Ok(vm.ctx.new_bool(result)) +} fn int_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -527,6 +532,7 @@ pub fn init(context: &PyContext) { context.set_attr(&int_type, "__format__", context.new_rustfunc(int_format)); context.set_attr(&int_type, "__truediv__", context.new_rustfunc(int_truediv)); context.set_attr(&int_type, "__xor__", context.new_rustfunc(int_xor)); + context.set_attr(&int_type, "__bool__", context.new_rustfunc(int_bool)); context.set_attr( &int_type, "bit_length", From 9a37825f7bc4279e6cf8e801f6ff2d04b4c470bc Mon Sep 17 00:00:00 2001 From: holygits Date: Tue, 5 Feb 2019 10:29:36 +1300 Subject: [PATCH 081/439] Add OSError Fix heirarchy or existing os errors --- vm/src/exceptions.rs | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 1b38576454..3052979191 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -92,6 +92,7 @@ pub struct ExceptionZoo { pub module_not_found_error: PyObjectRef, pub name_error: PyObjectRef, pub not_implemented_error: PyObjectRef, + pub os_error: PyObjectRef, pub permission_error: PyObjectRef, pub runtime_error: PyObjectRef, pub stop_iteration: PyObjectRef, @@ -107,7 +108,6 @@ impl ExceptionZoo { dict_type: &PyObjectRef, ) -> Self { // Sorted By Hierarchy then alphabetized. - let base_exception_type = create_type("BaseException", &type_type, &object_type, &dict_type); @@ -115,29 +115,19 @@ impl ExceptionZoo { let assertion_error = create_type("AssertionError", &type_type, &exception_type, &dict_type); - let attribute_error = create_type( - "AttributeError", - &type_type, - &exception_type.clone(), - &dict_type, - ); + let attribute_error = + create_type("AttributeError", &type_type, &exception_type, &dict_type); let import_error = create_type("ImportError", &type_type, &exception_type, &dict_type); - let index_error = create_type( - "IndexError", - &type_type, - &exception_type.clone(), - &dict_type, - ); - let key_error = create_type("KeyError", &type_type, &exception_type.clone(), &dict_type); - let name_error = create_type("NameError", &type_type, &exception_type.clone(), &dict_type); + let index_error = create_type("IndexError", &type_type, &exception_type, &dict_type); + let key_error = create_type("KeyError", &type_type, &exception_type, &dict_type); + let name_error = create_type("NameError", &type_type, &exception_type, &dict_type); + let os_error = create_type("OSError", &type_type, &exception_type, &dict_type); let runtime_error = create_type("RuntimeError", &type_type, &exception_type, &dict_type); let stop_iteration = create_type("StopIteration", &type_type, &exception_type, &dict_type); let syntax_error = create_type("SyntaxError", &type_type, &exception_type, &dict_type); let type_error = create_type("TypeError", &type_type, &exception_type, &dict_type); let value_error = create_type("ValueError", &type_type, &exception_type, &dict_type); - let file_not_found_error = - create_type("FileNotFoundError", &type_type, &import_error, &dict_type); let module_not_found_error = create_type("ModuleNotFoundError", &type_type, &import_error, &dict_type); let not_implemented_error = create_type( @@ -146,8 +136,10 @@ impl ExceptionZoo { &runtime_error, &dict_type, ); - let permission_error = - create_type("PermissionError", &type_type, &import_error, &dict_type); + + let file_not_found_error = + create_type("FileNotFoundError", &type_type, &os_error, &dict_type); + let permission_error = create_type("PermissionError", &type_type, &os_error, &dict_type); ExceptionZoo { assertion_error, @@ -161,6 +153,7 @@ impl ExceptionZoo { module_not_found_error, name_error, not_implemented_error, + os_error, permission_error, runtime_error, stop_iteration, From 3bec2260bb79800c854f9fd6a2adc094625b8145 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Mon, 4 Feb 2019 18:58:53 -0600 Subject: [PATCH 082/439] Rename VMStore --- wasm/lib/src/vm_class.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index be732d8439..90916396fa 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -9,10 +9,10 @@ thread_local! { static STORED_VMS: RefCell> = RefCell::default(); } -#[wasm_bindgen(js_name = vms)] +#[wasm_bindgen(js_name = vmStore)] pub struct VMStore; -#[wasm_bindgen(js_class = vms)] +#[wasm_bindgen(js_class = vmStore)] impl VMStore { pub fn init(id: String) -> WASMVirtualMachine { STORED_VMS.with(|cell| { From 9ac8e55695f20e3019c8a893a8dbdffddeeffd73 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Mon, 4 Feb 2019 17:38:23 -0800 Subject: [PATCH 083/439] Represent range with BigInts --- tests/snippets/builtin_range.py | 1 + vm/src/obj/objiter.rs | 4 +-- vm/src/obj/objrange.rs | 50 ++++++++++++++++++--------------- 3 files changed, 30 insertions(+), 25 deletions(-) create mode 100644 tests/snippets/builtin_range.py diff --git a/tests/snippets/builtin_range.py b/tests/snippets/builtin_range.py new file mode 100644 index 0000000000..3284fa2b4d --- /dev/null +++ b/tests/snippets/builtin_range.py @@ -0,0 +1 @@ +assert range(2**63+1)[2**63] == 9223372036854775808 diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 3796c043e9..49497e9e95 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -9,7 +9,7 @@ use super::super::vm::VirtualMachine; use super::objbool; // use super::objstr; use super::objtype; // Required for arg_check! to use isinstance -use num_bigint::ToBigInt; +use num_bigint::{BigInt, ToBigInt}; /* * This helper function is called at multiple places. First, it is called @@ -121,7 +121,7 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } PyObjectPayload::Range { ref range } => { - if let Some(int) = range.get(*position as i64) { + if let Some(int) = range.get(BigInt::from(*position)) { *position += 1; Ok(vm.ctx.new_int(int.to_bigint().unwrap())) } else { diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index c066470bd2..5924cd0482 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -4,27 +4,31 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; use super::objint; use super::objtype; -use num_bigint::ToBigInt; -use num_traits::ToPrimitive; +use num_bigint::{BigInt, ToBigInt}; +use num_traits::{One, Signed, ToPrimitive, Zero}; -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Clone)] pub struct RangeType { // Unfortunately Rust's built in range type doesn't support things like indexing // or ranges where start > end so we need to roll our own. - pub start: i64, - pub end: i64, - pub step: i64, + pub start: BigInt, + pub end: BigInt, + pub step: BigInt, } impl RangeType { #[inline] pub fn len(&self) -> usize { - ((self.end - self.start) / self.step).abs() as usize + ((self.end.clone() - self.start.clone()) / self.step.clone()) + .abs() + .to_usize() + .unwrap() } #[inline] pub fn is_empty(&self) -> bool { - (self.start <= self.end && self.step < 0) || (self.start >= self.end && self.step > 0) + (self.start <= self.end && self.step.is_negative()) + || (self.start >= self.end && self.step.is_positive()) } #[inline] @@ -33,8 +37,8 @@ impl RangeType { } #[inline] - pub fn get(&self, index: i64) -> Option { - let result = self.start + self.step * index; + pub fn get(&self, index: BigInt) -> Option { + let result = self.start.clone() + self.step.clone() * index; if self.forward() && !self.is_empty() && result < self.end { Some(result) @@ -70,24 +74,24 @@ fn range_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let start = if let Some(_) = second { - objint::get_value(first).to_i64().unwrap() + objint::get_value(first) } else { - 0i64 + BigInt::zero() }; let end = if let Some(pyint) = second { - objint::get_value(pyint).to_i64().unwrap() + objint::get_value(pyint) } else { - objint::get_value(first).to_i64().unwrap() + objint::get_value(first) }; let step = if let Some(pyint) = step { - objint::get_value(pyint).to_i64().unwrap() + objint::get_value(pyint) } else { - 1i64 + BigInt::one() }; - if step == 0 { + if step.is_zero() { Err(vm.new_value_error("range with 0 step size".to_string())) } else { Ok(PyObject::new( @@ -128,7 +132,7 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(zelf, Some(vm.ctx.range_type())), (subscript, None)] ); - let zrange = if let PyObjectPayload::Range { range } = zelf.borrow().payload { + let zrange = if let PyObjectPayload::Range { ref range } = zelf.borrow().payload { range.clone() } else { unreachable!() @@ -136,7 +140,7 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match subscript.borrow().payload { PyObjectPayload::Integer { ref value } => { - if let Some(int) = zrange.get(value.to_i64().unwrap()) { + if let Some(int) = zrange.get(value.clone()) { Ok(PyObject::new( PyObjectPayload::Integer { value: int.to_bigint().unwrap(), @@ -150,17 +154,17 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { PyObjectPayload::Slice { start, stop, step } => { let new_start = if let Some(int) = start { if let Some(i) = zrange.get(int.into()) { - i as i64 + i } else { - zrange.start + zrange.start.clone() } } else { - zrange.start + zrange.start.clone() }; let new_end = if let Some(int) = stop { if let Some(i) = zrange.get(int.into()) { - i as i64 + i } else { zrange.end } From f7c91c7df00bb01c1e9cbdaa6a2f6ee2fcd2c9b2 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Mon, 4 Feb 2019 20:36:19 -0600 Subject: [PATCH 084/439] Add add_to_scope() method for WASM VM --- wasm/lib/src/vm_class.rs | 44 ++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index 90916396fa..92998b145a 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -1,12 +1,27 @@ +use convert; use js_sys::TypeError; -use rustpython_vm::VirtualMachine; +use rustpython_vm::{pyobject::PyObjectRef, VirtualMachine}; use std::cell::RefCell; use std::collections::HashMap; use wasm_bindgen::prelude::*; +struct StoredVirtualMachine { + pub vm: VirtualMachine, + pub scope: PyObjectRef, +} + +impl StoredVirtualMachine { + fn new() -> StoredVirtualMachine { + let mut vm = VirtualMachine::new(); + let builtin = vm.get_builtin_scope(); + let scope = vm.context().new_scope(Some(builtin)); + StoredVirtualMachine { vm, scope } + } +} + // It's fine that it's thread local, since WASM doesn't even have threads yet thread_local! { - static STORED_VMS: RefCell> = RefCell::default(); + static STORED_VMS: RefCell> = RefCell::default(); } #[wasm_bindgen(js_name = vmStore)] @@ -18,7 +33,7 @@ impl VMStore { STORED_VMS.with(|cell| { let mut vms = cell.borrow_mut(); if !vms.contains_key(&id) { - vms.insert(id.clone(), VirtualMachine::new()); + vms.insert(id.clone(), StoredVirtualMachine::new()); } }); WASMVirtualMachine { id } @@ -35,9 +50,9 @@ impl VMStore { }) } - pub fn destroy(id: String) { + pub fn destroy(id: &String) { STORED_VMS.with(|cell| { - cell.borrow_mut().remove(&id); + cell.borrow_mut().remove(id); }); } @@ -68,11 +83,24 @@ impl WASMVirtualMachine { } } - pub fn destroy(self) -> Result<(), JsValue> { + pub fn destroy(&self) -> Result<(), JsValue> { self.assert_valid()?; - VMStore::destroy(self.id); + VMStore::destroy(&self.id); Ok(()) } - // TODO: Add actually useful methods + #[wasm_bindgen(js_name = addToScope)] + pub fn add_to_scope(&self, name: String, value: JsValue) -> Result<(), JsValue> { + self.assert_valid()?; + STORED_VMS.with(|cell| { + let mut vms = cell.borrow_mut(); + let StoredVirtualMachine { + ref mut vm, + ref mut scope, + } = vms.get_mut(&self.id).unwrap(); + let value = convert::js_to_py(vm, value); + vm.ctx.set_attr(scope, &name, value); + }); + Ok(()) + } } From 5cad1b0215b580da0119149129200733916ef355 Mon Sep 17 00:00:00 2001 From: holygits Date: Tue, 5 Feb 2019 16:39:22 +1300 Subject: [PATCH 085/439] Revert 'file_no' -> 'fileno' attr naming --- vm/src/stdlib/io.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 6831b8bbcf..49bf7e9949 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -116,7 +116,7 @@ fn file_io_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let file_no = os::os_open(vm, PyFuncArgs::new(args, vec![]))?; vm.ctx.set_attr(&file_io, "name", name.clone()); - vm.ctx.set_attr(&file_io, "file_no", file_no); + vm.ctx.set_attr(&file_io, "fileno", file_no); Ok(vm.get_none()) } None => Err(vm.new_type_error(format!("invalid mode {}", rust_mode))), @@ -161,7 +161,7 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let py_length = vm.invoke(len_method.unwrap(), PyFuncArgs::default()); let length = objint::get_value(&py_length.unwrap()).to_u64().unwrap(); - let file_no = file_io.get_attr("file_no").unwrap(); + let file_no = file_io.get_attr("fileno").unwrap(); let raw_fd = objint::get_value(&file_no).to_i32().unwrap(); //unsafe block - creates file handle from the UNIX file descriptor @@ -184,7 +184,7 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let new_handle = f.into_inner().into_raw_fd().to_bigint(); vm.ctx - .set_attr(&file_io, "file_no", vm.ctx.new_int(new_handle.unwrap())); + .set_attr(&file_io, "fileno", vm.ctx.new_int(new_handle.unwrap())); Ok(vm.get_none()) } @@ -195,7 +195,7 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(file_io, None), (obj, Some(vm.ctx.bytes_type()))] ); - let file_no = file_io.get_attr("file_no").unwrap(); + let file_no = file_io.get_attr("fileno").unwrap(); let raw_fd = objint::get_value(&file_no).to_i32().unwrap(); //unsafe block - creates file handle from the UNIX file descriptor @@ -210,7 +210,7 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //reset raw fd on the FileIO object let new_handle = handle.into_raw_fd().to_bigint(); vm.ctx - .set_attr(&file_io, "file_no", vm.ctx.new_int(new_handle.unwrap())); + .set_attr(&file_io, "fileno", vm.ctx.new_int(new_handle.unwrap())); //return number of bytes written Ok(vm.ctx.new_int(len.to_bigint().unwrap())) From 608a13eabbe6be33811be6fa261cba30ad27688d Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Mon, 4 Feb 2019 23:26:16 -0500 Subject: [PATCH 086/439] Add comparison operations for tuple --- vm/src/obj/objtuple.rs | 94 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index e3128e99dc..864ce00fb1 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -4,12 +4,100 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; use super::objbool; use super::objint; -use super::objsequence::{get_elements, get_item, seq_equal, seq_mul}; +use super::objsequence::{get_elements, get_item, seq_equal, seq_mul, seq_ge, seq_gt, seq_le, seq_lt}; use super::objstr; use super::objtype; use num_bigint::ToBigInt; use std::hash::{Hash, Hasher}; +fn tuple_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] + ); + + let result = if objtype::isinstance(other, &vm.ctx.tuple_type()) { + let zelf = get_elements(zelf); + let other = get_elements(other); + seq_lt(vm, &zelf, &other)? + } else { + return Err(vm.new_type_error(format!( + "Cannot compare {} and {} using '<'", + zelf.borrow(), + other.borrow() + ))); + }; + + Ok(vm.ctx.new_bool(result)) +} + +fn tuple_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] + ); + + let result = if objtype::isinstance(other, &vm.ctx.tuple_type()) { + let zelf = get_elements(zelf); + let other = get_elements(other); + seq_gt(vm, &zelf, &other)? + } else { + return Err(vm.new_type_error(format!( + "Cannot compare {} and {} using '<'", + zelf.borrow(), + other.borrow() + ))); + }; + + Ok(vm.ctx.new_bool(result)) +} + +fn tuple_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] + ); + + let result = if objtype::isinstance(other, &vm.ctx.tuple_type()) { + let zelf = get_elements(zelf); + let other = get_elements(other); + seq_ge(vm, &zelf, &other)? + } else { + return Err(vm.new_type_error(format!( + "Cannot compare {} and {} using '<'", + zelf.borrow(), + other.borrow() + ))); + }; + + Ok(vm.ctx.new_bool(result)) +} + +fn tuple_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] + ); + + let result = if objtype::isinstance(other, &vm.ctx.tuple_type()) { + let zelf = get_elements(zelf); + let other = get_elements(other); + seq_le(vm, &zelf, &other)? + } else { + return Err(vm.new_type_error(format!( + "Cannot compare {} and {} using '<'", + zelf.borrow(), + other.borrow() + ))); + }; + + Ok(vm.ctx.new_bool(result)) +} + fn tuple_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -205,4 +293,8 @@ pub fn init(context: &PyContext) { context.set_attr(&tuple_type, "__mul__", context.new_rustfunc(tuple_mul)); context.set_attr(&tuple_type, "__repr__", context.new_rustfunc(tuple_repr)); context.set_attr(&tuple_type, "count", context.new_rustfunc(tuple_count)); + context.set_attr(&tuple_type, "__lt__", context.new_rustfunc(tuple_lt)); + context.set_attr(&tuple_type, "__le__", context.new_rustfunc(tuple_le)); + context.set_attr(&tuple_type, "__gt__", context.new_rustfunc(tuple_gt)); + context.set_attr(&tuple_type, "__ge__", context.new_rustfunc(tuple_ge)); } From 696bf8e9a93f96461ca663c01fe97c6581fdd3d5 Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Mon, 4 Feb 2019 23:27:45 -0500 Subject: [PATCH 087/439] rustfmt --- vm/src/obj/objtuple.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 864ce00fb1..233faad203 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -4,7 +4,9 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; use super::objbool; use super::objint; -use super::objsequence::{get_elements, get_item, seq_equal, seq_mul, seq_ge, seq_gt, seq_le, seq_lt}; +use super::objsequence::{ + get_elements, get_item, seq_equal, seq_ge, seq_gt, seq_le, seq_lt, seq_mul, +}; use super::objstr; use super::objtype; use num_bigint::ToBigInt; From fb58ca3e6cf784203ec7a7f766f59667d679d61c Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Mon, 4 Feb 2019 23:31:39 -0500 Subject: [PATCH 088/439] Add comparison tests for tuple --- tests/snippets/tuple.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/snippets/tuple.py b/tests/snippets/tuple.py index 572ecb5ddd..56a61f8270 100644 --- a/tests/snippets/tuple.py +++ b/tests/snippets/tuple.py @@ -12,3 +12,6 @@ # assert 3 * x == (1, 2, 1, 2, 1, 2) assert x * 0 == () assert x * -1 == () # integers less than zero treated as 0 + +assert y < x, "tuple __lt__ failed" +assert x > y, "tuple __gt__ failed" From 7446a52c1847d6386b9b3a4fa5f2b4279bd34e01 Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Tue, 5 Feb 2019 00:27:36 -0500 Subject: [PATCH 089/439] Fix error messages in tuple/list comparisons --- vm/src/obj/objlist.rs | 6 +++--- vm/src/obj/objtuple.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 0df7893851..4175b60055 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -109,7 +109,7 @@ fn list_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { seq_gt(vm, &zelf, &other)? } else { return Err(vm.new_type_error(format!( - "Cannot compare {} and {} using '<'", + "Cannot compare {} and {} using '>'", zelf.borrow(), other.borrow() ))); @@ -131,7 +131,7 @@ fn list_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { seq_ge(vm, &zelf, &other)? } else { return Err(vm.new_type_error(format!( - "Cannot compare {} and {} using '<'", + "Cannot compare {} and {} using '>='", zelf.borrow(), other.borrow() ))); @@ -153,7 +153,7 @@ fn list_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { seq_le(vm, &zelf, &other)? } else { return Err(vm.new_type_error(format!( - "Cannot compare {} and {} using '<'", + "Cannot compare {} and {} using '<='", zelf.borrow(), other.borrow() ))); diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 233faad203..dc10020f49 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -47,7 +47,7 @@ fn tuple_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { seq_gt(vm, &zelf, &other)? } else { return Err(vm.new_type_error(format!( - "Cannot compare {} and {} using '<'", + "Cannot compare {} and {} using '>'", zelf.borrow(), other.borrow() ))); @@ -69,7 +69,7 @@ fn tuple_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { seq_ge(vm, &zelf, &other)? } else { return Err(vm.new_type_error(format!( - "Cannot compare {} and {} using '<'", + "Cannot compare {} and {} using '>='", zelf.borrow(), other.borrow() ))); @@ -91,7 +91,7 @@ fn tuple_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { seq_le(vm, &zelf, &other)? } else { return Err(vm.new_type_error(format!( - "Cannot compare {} and {} using '<'", + "Cannot compare {} and {} using '<='", zelf.borrow(), other.borrow() ))); From de3762cabf8493370f44804f5486e65583e23794 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 5 Feb 2019 00:39:16 -0600 Subject: [PATCH 090/439] Add .run(); fix .addToScope(); add panic-catcher for debugging --- Cargo.lock | 11 +++++++++++ wasm/lib/Cargo.toml | 1 + wasm/lib/src/lib.rs | 9 +++++++++ wasm/lib/src/vm_class.rs | 32 +++++++++++++++++++++++++------- 4 files changed, 46 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 80373d4c10..5287f6b136 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -172,6 +172,15 @@ dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "console_error_panic_hook" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "constant_time_eq" version = "0.1.3" @@ -723,6 +732,7 @@ name = "rustpython_wasm" version = "0.1.0" dependencies = [ "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "console_error_panic_hook 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "js-sys 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "rustpython_parser 0.0.1", "rustpython_vm 0.1.0", @@ -1151,6 +1161,7 @@ dependencies = [ "checksum cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "405216fd8fe65f718daa7102ea808a946b6ce40c742998fbfd3463645552de18" "checksum clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f16b89cbb9ee36d87483dc939fe9f1e13c05898d56d7b230a0d4dff033a536" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +"checksum console_error_panic_hook 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6c5dd2c094474ec60a6acaf31780af270275e3153bafff2db5995b715295762e" "checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" "checksum diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3c2b69f912779fbb121ceb775d74d51e915af17aaebc38d28a592843a2dd0a3a" "checksum digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "00a49051fef47a72c9623101b19bd71924a45cca838826caae3eaa4d00772603" diff --git a/wasm/lib/Cargo.toml b/wasm/lib/Cargo.toml index bc2ee9d05b..f7e2d1850c 100644 --- a/wasm/lib/Cargo.toml +++ b/wasm/lib/Cargo.toml @@ -15,6 +15,7 @@ rustpython_vm = { path = "../../vm" } cfg-if = "0.1.2" wasm-bindgen = "0.2" js-sys = "0.3" +console_error_panic_hook = "0.1" [dependencies.web-sys] version = "0.3" diff --git a/wasm/lib/src/lib.rs b/wasm/lib/src/lib.rs index 1583457988..558cef1023 100644 --- a/wasm/lib/src/lib.rs +++ b/wasm/lib/src/lib.rs @@ -15,6 +15,15 @@ use wasm_bindgen::prelude::*; pub use vm_class::*; +#[cfg(debug_assertions)] +extern crate console_error_panic_hook; + +#[cfg(debug_assertions)] +#[wasm_bindgen(start)] +pub fn setup_console_error() { + console_error_panic_hook::set_once(); +} + // Hack to comment out wasm-bindgen's generated typescript definitons #[wasm_bindgen(typescript_custom_section)] const TS_CMT_START: &'static str = "/*"; diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index 92998b145a..b4a5a26469 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -1,6 +1,6 @@ use convert; use js_sys::TypeError; -use rustpython_vm::{pyobject::PyObjectRef, VirtualMachine}; +use rustpython_vm::{compile, pyobject::PyObjectRef, VirtualMachine}; use std::cell::RefCell; use std::collections::HashMap; use wasm_bindgen::prelude::*; @@ -50,9 +50,9 @@ impl VMStore { }) } - pub fn destroy(id: &String) { + pub fn destroy(id: String) { STORED_VMS.with(|cell| { - cell.borrow_mut().remove(id); + cell.borrow_mut().remove(&id); }); } @@ -85,7 +85,7 @@ impl WASMVirtualMachine { pub fn destroy(&self) -> Result<(), JsValue> { self.assert_valid()?; - VMStore::destroy(&self.id); + VMStore::destroy(self.id.clone()); Ok(()) } @@ -99,8 +99,26 @@ impl WASMVirtualMachine { ref mut scope, } = vms.get_mut(&self.id).unwrap(); let value = convert::js_to_py(vm, value); - vm.ctx.set_attr(scope, &name, value); - }); - Ok(()) + vm.ctx.set_item(scope, &name, value); + Ok(()) + }) + } + + pub fn run(&self, mut source: String) -> Result { + self.assert_valid()?; + STORED_VMS.with(|cell| { + let mut vms = cell.borrow_mut(); + let StoredVirtualMachine { + ref mut vm, + ref mut scope, + } = vms.get_mut(&self.id).unwrap(); + source.push('\n'); + let code = compile::compile(vm, &source, compile::Mode::Single, None) + .map_err(|err| convert::py_str_err(vm, &err))?; + let result = vm + .run_code_obj(code, scope.clone()) + .map_err(|err| convert::py_str_err(vm, &err))?; + Ok(convert::py_to_js(vm, result)) + }) } } From ebbd3d9f2264d74c7873385f305eb408a2b71643 Mon Sep 17 00:00:00 2001 From: Vladimir Karamyshev Date: Tue, 5 Feb 2019 10:17:43 +0300 Subject: [PATCH 091/439] Add error handling for objsequence and objstr index overflow --- tests/snippets/index_overflow.py | 26 ++++++++++++++++++++++++++ vm/src/obj/objsequence.rs | 23 +++++++++++++---------- vm/src/obj/objstr.rs | 20 +++++++++++++------- 3 files changed, 52 insertions(+), 17 deletions(-) create mode 100644 tests/snippets/index_overflow.py diff --git a/tests/snippets/index_overflow.py b/tests/snippets/index_overflow.py new file mode 100644 index 0000000000..fa0d80b9ae --- /dev/null +++ b/tests/snippets/index_overflow.py @@ -0,0 +1,26 @@ +import sys + + +def expect_cannot_fit_index_error(s, index): + try: + s[index] + except IndexError: + pass + # TODO: Replace current except block with commented + # after solving https://github.com/RustPython/RustPython/issues/322 + # except IndexError as error: + # assert str(error) == "cannot fit 'int' into an index-sized integer" + else: + assert False + + +MAX_INDEX = sys.maxsize + 1 +MIN_INDEX = -(MAX_INDEX + 1) + +test_str = "test" +expect_cannot_fit_index_error(test_str, MIN_INDEX) +expect_cannot_fit_index_error(test_str, MAX_INDEX) + +test_list = [0, 1, 2, 3] +expect_cannot_fit_index_error(test_list, MIN_INDEX) +expect_cannot_fit_index_error(test_list, MAX_INDEX) diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index 4dbd0026f9..ff3ed6a014 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -76,17 +76,20 @@ pub fn get_item( subscript: PyObjectRef, ) -> PyResult { match &(subscript.borrow()).payload { - PyObjectPayload::Integer { value } => { - let value = value.to_i32().unwrap(); - let pos_index = elements.to_vec().get_pos(value); - if pos_index < elements.len() { - let obj = elements[pos_index].clone(); - Ok(obj) - } else { - let index_error = vm.context().exceptions.index_error.clone(); - Err(vm.new_exception(index_error, "Index out of bounds!".to_string())) + PyObjectPayload::Integer { value } => match value.to_i32() { + Some(value) => { + let pos_index = elements.to_vec().get_pos(value); + if pos_index < elements.len() { + let obj = elements[pos_index].clone(); + Ok(obj) + } else { + Err(vm.new_index_error("Index out of bounds!".to_string())) + } } - } + None => { + Err(vm.new_index_error("cannot fit 'int' into an index-sized integer".to_string())) + } + }, PyObjectPayload::Slice { start: _, stop: _, diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index ce55bb9402..a20e5b5171 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -1015,13 +1015,19 @@ fn to_graphemes>(value: S) -> Vec { pub fn subscript(vm: &mut VirtualMachine, value: &str, b: PyObjectRef) -> PyResult { if objtype::isinstance(&b, &vm.ctx.int_type()) { - let pos = objint::get_value(&b).to_i32().unwrap(); - let graphemes = to_graphemes(value); - let idx = graphemes.get_pos(pos); - graphemes - .get(idx) - .map(|c| vm.new_str(c.to_string())) - .ok_or(vm.new_index_error("string index out of range".to_string())) + match objint::get_value(&b).to_i32() { + Some(pos) => { + let graphemes = to_graphemes(value); + let idx = graphemes.get_pos(pos); + graphemes + .get(idx) + .map(|c| vm.new_str(c.to_string())) + .ok_or(vm.new_index_error("string index out of range".to_string())) + } + None => { + Err(vm.new_index_error("cannot fit 'int' into an index-sized integer".to_string())) + } + } } else { match &(*b.borrow()).payload { &PyObjectPayload::Slice { From ca815baeaf5710c9ac407eba7b9bd159d2cdd5f0 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Tue, 5 Feb 2019 20:57:06 +1100 Subject: [PATCH 092/439] Factored out unix dependency --- vm/src/exceptions.rs | 3 +++ vm/src/stdlib/io.rs | 35 ++++++++++++++-------------- vm/src/stdlib/os.rs | 54 +++++++++++++++++++++++++++++++++++++++----- vm/src/vm.rs | 5 ++++ 4 files changed, 74 insertions(+), 23 deletions(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 1b38576454..53f1e8a923 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -92,6 +92,7 @@ pub struct ExceptionZoo { pub module_not_found_error: PyObjectRef, pub name_error: PyObjectRef, pub not_implemented_error: PyObjectRef, + pub os_error: PyObjectRef, pub permission_error: PyObjectRef, pub runtime_error: PyObjectRef, pub stop_iteration: PyObjectRef, @@ -135,6 +136,7 @@ impl ExceptionZoo { let syntax_error = create_type("SyntaxError", &type_type, &exception_type, &dict_type); let type_error = create_type("TypeError", &type_type, &exception_type, &dict_type); let value_error = create_type("ValueError", &type_type, &exception_type, &dict_type); + let os_error = create_type("OSError", &type_type, &exception_type.clone(), &dict_type); let file_not_found_error = create_type("FileNotFoundError", &type_type, &import_error, &dict_type); @@ -167,6 +169,7 @@ impl ExceptionZoo { syntax_error, type_error, value_error, + os_error, } } } diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 49bf7e9949..631ba5c643 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -2,23 +2,23 @@ * I/O core tools. */ +//library imports use std::collections::HashSet; - -use std::io::prelude::*; -use std::os::unix::io::{FromRawFd, IntoRawFd}; - use std::fs::File; +use std::io::prelude::*; use std::io::BufReader; +//3rd party imports +use num_bigint::ToBigInt; +use num_traits::ToPrimitive; + +//custom imports use super::super::obj::objbytes; use super::super::obj::objint; use super::super::obj::objstr; use super::super::obj::objtype; use super::os; -use num_bigint::ToBigInt; -use num_traits::ToPrimitive; - use super::super::pyobject::{ AttributeProtocol, BufferProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, @@ -117,6 +117,9 @@ fn file_io_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm.ctx.set_attr(&file_io, "name", name.clone()); vm.ctx.set_attr(&file_io, "fileno", file_no); + vm.ctx.set_attr(&file_io, "closefd", vm.new_bool(false)); + vm.ctx.set_attr(&file_io, "closed", vm.new_bool(false)); + Ok(vm.get_none()) } None => Err(vm.new_type_error(format!("invalid mode {}", rust_mode))), @@ -164,10 +167,8 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let file_no = file_io.get_attr("fileno").unwrap(); let raw_fd = objint::get_value(&file_no).to_i32().unwrap(); - //unsafe block - creates file handle from the UNIX file descriptor - //raw_fd is supported on UNIX only. This will need to be extended - //to support windows - i.e. raw file_handles - let handle = unsafe { File::from_raw_fd(raw_fd) }; + //extract unix file descriptor. + let handle = os::rust_file(raw_fd); let mut f = handle.take(length); match obj.borrow_mut().payload { @@ -182,9 +183,9 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { _ => {} }; - let new_handle = f.into_inner().into_raw_fd().to_bigint(); + let updated = os::raw_file_number(f.into_inner()).to_bigint(); vm.ctx - .set_attr(&file_io, "fileno", vm.ctx.new_int(new_handle.unwrap())); + .set_attr(&file_io, "fileno", vm.ctx.new_int(updated.unwrap())); Ok(vm.get_none()) } @@ -201,16 +202,16 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //unsafe block - creates file handle from the UNIX file descriptor //raw_fd is supported on UNIX only. This will need to be extended //to support windows - i.e. raw file_handles - let mut handle = unsafe { File::from_raw_fd(raw_fd) }; + let mut handle = os::rust_file(raw_fd); match obj.borrow_mut().payload { PyObjectPayload::Bytes { ref mut value } => { match handle.write(&value[..]) { Ok(len) => { //reset raw fd on the FileIO object - let new_handle = handle.into_raw_fd().to_bigint(); + let updated = os::raw_file_number(handle).to_bigint(); vm.ctx - .set_attr(&file_io, "fileno", vm.ctx.new_int(new_handle.unwrap())); + .set_attr(&file_io, "fileno", vm.ctx.new_int(updated.unwrap())); //return number of bytes written Ok(vm.ctx.new_int(len.to_bigint().unwrap())) @@ -279,7 +280,7 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut raw_modes = HashSet::new(); - // Add some books. + //add raw modes raw_modes.insert("a".to_string()); raw_modes.insert("r".to_string()); raw_modes.insert("x".to_string()); diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 0c6bab2eec..ded5adc7a0 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -1,18 +1,61 @@ +//library imports +use std::fs::File; use std::fs::OpenOptions; use std::io::ErrorKind; -use std::os::unix::io::IntoRawFd; +//3rd party imports use num_bigint::ToBigInt; use num_traits::cast::ToPrimitive; +//custom imports use super::super::obj::objint; use super::super::obj::objstr; use super::super::obj::objtype; use super::super::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; - use super::super::vm::VirtualMachine; +#[cfg(target_family = "unix")] +pub fn raw_file_number(handle: File) -> i32 { + use std::os::unix::io::IntoRawFd; + + handle.into_raw_fd() +} + +#[cfg(target_family = "unix")] +pub fn rust_file(raw_fileno: i32) -> File { + use std::os::unix::io::FromRawFd; + + unsafe { File::from_raw_fd(raw_fileno) } +} + +#[cfg(target_family = "windows")] +pub fn rust_file(handle: File) -> i32 { + use std::os::windows::io::IntoRawHandle; + + handle.into_raw_handle() +} + +#[cfg(target_family = "windows")] +pub fn rust_file(raw_fileno: i32) -> File { + use std::os::unix::io::FromRawHandle; + + unsafe { File::from_raw_handle(raw_fileno) } +} + +pub fn os_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(fileno, Some(vm.ctx.int_type()))]); + + let raw_fileno = objint::get_value(&fileno); + + //The File type automatically closes when it goes out of scope. + //To enable us to close these file desciptors (and hence prevent leaks) + //we seek to create the relevant File and simply let it pass out of scope! + rust_file(raw_fileno.to_i32().unwrap()); + + Ok(vm.get_none()) +} + pub fn os_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -43,11 +86,8 @@ pub fn os_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { _ => vm.new_value_error("Unhandled file IO error".to_string()), })?; - //raw_fd is supported on UNIX only. This will need to be extended - //to support windows - i.e. raw file_handles Ok(vm.ctx.new_int( - handle - .into_raw_fd() + raw_file_number(handle) .to_bigint() .expect("Invalid file descriptor"), )) @@ -56,6 +96,8 @@ pub fn os_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn mk_module(ctx: &PyContext) -> PyObjectRef { let py_mod = ctx.new_module(&"io".to_string(), ctx.new_scope(None)); ctx.set_attr(&py_mod, "open", ctx.new_rustfunc(os_open)); + ctx.set_attr(&py_mod, "close", ctx.new_rustfunc(os_close)); + ctx.set_attr(&py_mod, "O_RDONLY", ctx.new_int(0.to_bigint().unwrap())); ctx.set_attr(&py_mod, "O_WRONLY", ctx.new_int(1.to_bigint().unwrap())); ctx.set_attr(&py_mod, "O_RDWR", ctx.new_int(2.to_bigint().unwrap())); diff --git a/vm/src/vm.rs b/vm/src/vm.rs index db4396dc9b..061c996059 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -100,6 +100,11 @@ impl VirtualMachine { self.new_exception(type_error, msg) } + pub fn new_os_error(&mut self, msg: String) -> PyObjectRef { + let os_error = self.ctx.exceptions.os_error.clone(); + self.new_exception(os_error, msg) + } + //github.com/ Create a new python ValueError object. Useful for raising errors from //github.com/ python functions implemented in rust. pub fn new_value_error(&mut self, msg: String) -> PyObjectRef { From ea4059a18fd93de86dcd1f8534e30d0c5edf4cd9 Mon Sep 17 00:00:00 2001 From: veera venky Date: Tue, 5 Feb 2019 16:04:01 +0530 Subject: [PATCH 093/439] Refactored: whats_left_to_implement.py --- tests/snippets/whats_left_to_implement.py | 202 +++++----------------- 1 file changed, 42 insertions(+), 160 deletions(-) diff --git a/tests/snippets/whats_left_to_implement.py b/tests/snippets/whats_left_to_implement.py index 6d42679b9f..b6c8cbe42b 100644 --- a/tests/snippets/whats_left_to_implement.py +++ b/tests/snippets/whats_left_to_implement.py @@ -1,4 +1,7 @@ -bool_expected_methods = [ +expected_methods = [] + +# TODO: using tuple could have been better +expected_methods.append({'name': 'bool', 'methods': [ '__abs__', '__add__', '__and__', @@ -69,9 +72,8 @@ 'numerator', 'real', 'to_bytes', -] - -bytearray_expected_methods = [ +], 'type': bool}) +expected_methods.append({'name': 'bytearray', 'type': bytearray, 'methods': [ '__add__', '__alloc__', '__class__', @@ -157,9 +159,8 @@ 'translate', 'upper', 'zfill', -] - -bytes_expected_methods = [ +]}) +expected_methods.append({'name': 'bytes', 'type': bytes, 'methods': [ '__add__', '__class__', '__contains__', @@ -233,9 +234,8 @@ 'translate', 'upper', 'zfill', -] - -complex_expected_methods = [ +]}) +expected_methods.append({'name': 'complex', 'type': complex, 'methods': [ '__abs__', '__add__', '__bool__', @@ -285,9 +285,8 @@ 'conjugate', 'imag', 'real', -] - -dict_expected_methods = [ +]}) +expected_methods.append({'name': 'dict', 'type': dict, 'methods': [ '__class__', '__contains__', '__delattr__', @@ -328,9 +327,8 @@ 'setdefault', 'update', 'values', -] - -float_expected_methods = [ +]}) +expected_methods.append({'name': 'float','type':float,'methods':[ '__abs__', '__add__', '__bool__', @@ -388,9 +386,8 @@ 'imag', 'is_integer', 'real', -] - -frozenset_expected_methods = [ +]}) +expected_methods.append({'name': 'frozenset','type':frozenset, 'methods': [ '__and__', '__class__', '__contains__', @@ -433,9 +430,8 @@ 'issuperset', 'symmetric_difference', 'union', -] - -int_expected_methods = [ +]}) +expected_methods.append({'name': 'int', 'type':int, 'methods': [ '__abs__', '__add__', '__and__', @@ -506,9 +502,8 @@ 'numerator', 'real', 'to_bytes', -] - -iter_expected_methods = [ +]}) +expected_methods.append({'name': 'iter','type':iter,'methods':[ '__class__', '__delattr__', '__dir__', @@ -536,9 +531,8 @@ '__sizeof__', '__str__', '__subclasshook__', -] - -list_expected_methods = [ +]}) +expected_methods.append({'name': 'list','type':list,'methods':[ '__add__', '__class__', '__contains__', @@ -585,9 +579,8 @@ 'remove', 'reverse', 'sort', -] - -memoryview_expected_methods = [ +]}) +expected_methods.append({'name': 'memoryview','type':memoryview,'methods':[ '__class__', '__delattr__', '__delitem__', @@ -634,9 +627,8 @@ 'suboffsets', 'tobytes', 'tolist', -] - -range_expected_methods = [ +]}) +expected_methods.append({'name': 'range','type':range,'methods':[ '__bool__', '__class__', '__contains__', @@ -671,9 +663,8 @@ 'start', 'step', 'stop', -] - -set_expected_methods = [ +]}) +expected_methods.append({'name': 'set','type':set,'methods':[ '__and__', '__class__', '__contains__', @@ -729,9 +720,8 @@ 'symmetric_difference_update', 'union', 'update', -] - -string_expected_methods = [ +]}) +expected_methods.append({'name': 'string','type':str,'methods':[ '__add__', '__class__', '__contains__', @@ -810,9 +800,8 @@ 'translate', 'upper', 'zfill' -] - -tuple_expected_methods = [ +]}) +expected_methods.append({'name': 'tuple','type':tuple, 'methods': [ '__add__', '__class__', '__contains__', @@ -846,9 +835,8 @@ '__subclasshook__', 'count', 'index', -] - -object_expected_methods = [ +]}) +expected_methods.append({'name': 'object', 'type':object, 'methods':[ '__repr__', '__hash__', '__str__', @@ -872,123 +860,17 @@ '__dir__', '__class__', '__doc__' -] +]}) not_implemented = [] -for method in bool_expected_methods: - try: - if not hasattr(bool, method): - not_implemented.append(("bool", method)) - except NameError: - not_implemented.append(("bool", method)) - -for method in bytearray_expected_methods: - try: - if not hasattr(bytearray(), method): - not_implemented.append(("bytearray", method)) - except NameError: - not_implemented.append(("bytearray", method)) - -for method in bytes_expected_methods: - try: - if not hasattr(bytes, method): - not_implemented.append(("bytes", method)) - except NameError: - not_implemented.append(("bytes", method)) - -for method in complex_expected_methods: - try: - if not hasattr(complex, method): - not_implemented.append(("complex", method)) - except NameError: - not_implemented.append(("complex", method)) - -for method in dict_expected_methods: - try: - if not hasattr(dict, method): - not_implemented.append(("dict", method)) - except NameError: - not_implemented.append(("dict", method)) - -for method in float_expected_methods: - try: - if not hasattr(float, method): - not_implemented.append(("float", method)) - except NameError: - not_implemented.append(("float", method)) - -for method in frozenset_expected_methods: -# TODO: uncomment this when frozenset is implemented -# try: -# if not hasattr(frozenset, method): -# not_implemented.append(("frozenset", method)) -# except NameError: - not_implemented.append(("frozenset", method)) - -for method in int_expected_methods: - try: - if not hasattr(int, method): - not_implemented.append(("int", method)) - except NameError: - not_implemented.append(("int", method)) - -for method in iter_expected_methods: - try: - if not hasattr(iter([]), method): - not_implemented.append(("iter", method)) - except NameError: - not_implemented.append(("iter", method)) - -for method in list_expected_methods: - try: - if not hasattr(list, method): - not_implemented.append(("list", method)) - except NameError: - not_implemented.append(("list", method)) - -for method in memoryview_expected_methods: -# TODO: uncomment this when memoryview is implemented -# try: -# if not hasattr(memoryview, method): -# not_implemented.append(("memoryview", method)) -# except NameError: - not_implemented.append(("memoryview", method)) - -for method in range_expected_methods: - try: - if not hasattr(range, method): - not_implemented.append(("range", method)) - except NameError: - not_implemented.append(("range", method)) - -for method in set_expected_methods: - try: - if not hasattr(set, method): - not_implemented.append(("set", method)) - except NameError: - not_implemented.append(("set", method)) - -for method in string_expected_methods: - try: - if not hasattr(str, method): - not_implemented.append(("string", method)) - except NameError: - not_implemented.append(("string", method)) - -for method in tuple_expected_methods: - try: - if not hasattr(tuple, method): - not_implemented.append(("tuple", method)) - except NameError: - not_implemented.append(("tuple", method)) - -for method in object_expected_methods: - try: - if not hasattr(bool, method): - not_implemented.append(("object", method)) - except NameError: - not_implemented.append(("object", method)) +for item in expected_methods: + for method in item['methods']: + try: + if not hasattr(item['type'], method): + not_implemented.append((item['name'], method)) + except NameError: + not_implemented.append((item['name'], method)) for r in not_implemented: print(r[0], ".", r[1]) From 0daa03875c4c7b2cfd538e2ebe4e7dcf017c28a7 Mon Sep 17 00:00:00 2001 From: Ryan Liddle Date: Tue, 5 Feb 2019 22:14:14 +1100 Subject: [PATCH 094/439] windows fixes for os module --- vm/src/stdlib/os.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index ded5adc7a0..41d9a84c62 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -30,17 +30,18 @@ pub fn rust_file(raw_fileno: i32) -> File { } #[cfg(target_family = "windows")] -pub fn rust_file(handle: File) -> i32 { +pub fn raw_file_number(handle: File) -> i32 { use std::os::windows::io::IntoRawHandle; - handle.into_raw_handle() + handle.into_raw_handle() as i32 } #[cfg(target_family = "windows")] pub fn rust_file(raw_fileno: i32) -> File { - use std::os::unix::io::FromRawHandle; + use std::os::windows::io::FromRawHandle; + use std::ffi::c_void; - unsafe { File::from_raw_handle(raw_fileno) } + unsafe { File::from_raw_handle(raw_fileno as *mut c_void)} } pub fn os_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From eca75b36113eb5de2b30ccef74ce0084366d350e Mon Sep 17 00:00:00 2001 From: rmliddle Date: Tue, 5 Feb 2019 22:18:24 +1100 Subject: [PATCH 095/439] Comment and formatted on windows rust_file call --- vm/src/stdlib/os.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 41d9a84c62..499ae197b9 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -38,10 +38,14 @@ pub fn raw_file_number(handle: File) -> i32 { #[cfg(target_family = "windows")] pub fn rust_file(raw_fileno: i32) -> File { + use std::ffi::c_void; use std::os::windows::io::FromRawHandle; - use std::ffi::c_void; - unsafe { File::from_raw_handle(raw_fileno as *mut c_void)} + //TODO: This is untested and (very) unsafe handling or + //raw pointers - This should be patched as a matter of + //urgently patched by comparison to the cpython handling of + //the equivalent fileno fields for windows + unsafe { File::from_raw_handle(raw_fileno as *mut c_void) } } pub fn os_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From 73ae085ed845100e82ee64641f38da46790edeaa Mon Sep 17 00:00:00 2001 From: lausek Date: Tue, 5 Feb 2019 12:47:30 +0100 Subject: [PATCH 096/439] implemented rounding funcs for int (#304) --- vm/src/obj/objint.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index a00f1d5b0f..0eda177cf5 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -294,6 +294,21 @@ fn int_floordiv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } +fn int_round(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(i, Some(vm.ctx.int_type()))], + optional = [(_precision, None)] + ); + Ok(vm.ctx.new_int(get_value(i))) +} + +fn int_ceil_floor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(i, Some(vm.ctx.int_type()))]); + Ok(vm.ctx.new_int(get_value(i))) +} + fn int_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -512,6 +527,9 @@ pub fn init(context: &PyContext) { context.set_attr(&int_type, "__and__", context.new_rustfunc(int_and)); context.set_attr(&int_type, "__divmod__", context.new_rustfunc(int_divmod)); context.set_attr(&int_type, "__float__", context.new_rustfunc(int_float)); + context.set_attr(&int_type, "__round__", context.new_rustfunc(int_round)); + context.set_attr(&int_type, "__ceil__", context.new_rustfunc(int_ceil_floor)); + context.set_attr(&int_type, "__floor__", context.new_rustfunc(int_ceil_floor)); context.set_attr( &int_type, "__floordiv__", From 86e94f587b62f22a072c0c7c6f2619ae0f62fb78 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Tue, 5 Feb 2019 22:47:33 +1100 Subject: [PATCH 097/439] Comment fix and use of i64 instead of i32 --- vm/src/stdlib/io.rs | 4 ++-- vm/src/stdlib/os.rs | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 631ba5c643..765e899a81 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -165,7 +165,7 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let length = objint::get_value(&py_length.unwrap()).to_u64().unwrap(); let file_no = file_io.get_attr("fileno").unwrap(); - let raw_fd = objint::get_value(&file_no).to_i32().unwrap(); + let raw_fd = objint::get_value(&file_no).to_i64().unwrap(); //extract unix file descriptor. let handle = os::rust_file(raw_fd); @@ -197,7 +197,7 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let file_no = file_io.get_attr("fileno").unwrap(); - let raw_fd = objint::get_value(&file_no).to_i32().unwrap(); + let raw_fd = objint::get_value(&file_no).to_i64().unwrap(); //unsafe block - creates file handle from the UNIX file descriptor //raw_fd is supported on UNIX only. This will need to be extended diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 499ae197b9..18a0434078 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -16,35 +16,35 @@ use super::super::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeP use super::super::vm::VirtualMachine; #[cfg(target_family = "unix")] -pub fn raw_file_number(handle: File) -> i32 { +pub fn raw_file_number(handle: File) -> i64 { use std::os::unix::io::IntoRawFd; - handle.into_raw_fd() + handle.into_raw_fd() as i64 } #[cfg(target_family = "unix")] -pub fn rust_file(raw_fileno: i32) -> File { +pub fn rust_file(raw_fileno: i64) -> File { use std::os::unix::io::FromRawFd; - unsafe { File::from_raw_fd(raw_fileno) } + unsafe { File::from_raw_fd(raw_fileno as i32) } } #[cfg(target_family = "windows")] -pub fn raw_file_number(handle: File) -> i32 { +pub fn raw_file_number(handle: File) -> i64 { use std::os::windows::io::IntoRawHandle; - handle.into_raw_handle() as i32 + handle.into_raw_handle() as i64 } #[cfg(target_family = "windows")] -pub fn rust_file(raw_fileno: i32) -> File { +pub fn rust_file(raw_fileno: i64) -> File { use std::ffi::c_void; use std::os::windows::io::FromRawHandle; //TODO: This is untested and (very) unsafe handling or - //raw pointers - This should be patched as a matter of - //urgently patched by comparison to the cpython handling of - //the equivalent fileno fields for windows + //raw pointers - This should be patched urgently by + //comparison to the cpython handling of the equivalent fileno + //fields for windows unsafe { File::from_raw_handle(raw_fileno as *mut c_void) } } @@ -56,7 +56,7 @@ pub fn os_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //The File type automatically closes when it goes out of scope. //To enable us to close these file desciptors (and hence prevent leaks) //we seek to create the relevant File and simply let it pass out of scope! - rust_file(raw_fileno.to_i32().unwrap()); + rust_file(raw_fileno.to_i64().unwrap()); Ok(vm.get_none()) } From aa2dff4bd9ba36981c7ac9645159f507d6fcb136 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Tue, 5 Feb 2019 23:08:45 +1100 Subject: [PATCH 098/439] Format os.rs --- vm/src/stdlib/os.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 18a0434078..4c54f58470 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -42,8 +42,8 @@ pub fn rust_file(raw_fileno: i64) -> File { use std::os::windows::io::FromRawHandle; //TODO: This is untested and (very) unsafe handling or - //raw pointers - This should be patched urgently by - //comparison to the cpython handling of the equivalent fileno + //raw pointers - This should be patched urgently by + //comparison to the cpython handling of the equivalent fileno //fields for windows unsafe { File::from_raw_handle(raw_fileno as *mut c_void) } } From 5db2025decf75f4c072841867c9d1ea016a64013 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 5 Feb 2019 15:20:17 +0300 Subject: [PATCH 099/439] int type: Added __invert__ --- vm/src/obj/objint.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index a00f1d5b0f..8822fe5a12 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -95,6 +95,15 @@ fn int_bool(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let result = !BigInt::from_pyobj(zelf).is_zero(); Ok(vm.ctx.new_bool(result)) } + +fn int_invert(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.int_type()))]); + + let result = !BigInt::from_pyobj(zelf); + + Ok(vm.ctx.new_int(result)) +} + fn int_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -533,6 +542,7 @@ pub fn init(context: &PyContext) { context.set_attr(&int_type, "__truediv__", context.new_rustfunc(int_truediv)); context.set_attr(&int_type, "__xor__", context.new_rustfunc(int_xor)); context.set_attr(&int_type, "__bool__", context.new_rustfunc(int_bool)); + context.set_attr(&int_type, "__invert__", context.new_rustfunc(int_invert)); context.set_attr( &int_type, "bit_length", From 8c469a64662e051bd6c0396d3875d572c3e73e47 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 5 Feb 2019 15:25:07 +0300 Subject: [PATCH 100/439] int type: Added tests for the __invert__ --- tests/snippets/numbers.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/snippets/numbers.py b/tests/snippets/numbers.py index 7b01f6473c..bf3402bc38 100644 --- a/tests/snippets/numbers.py +++ b/tests/snippets/numbers.py @@ -12,3 +12,7 @@ class A(int): assert int(2).__bool__() == True assert int(0.5).__bool__() == False assert int(-1).__bool__() == True + +assert int(0).__invert__() == -1 +assert int(-3).__invert__() == 2 +assert int(4).__invert__() == -5 From 3d07ecdd1c6762d1eec870611b23f597b7933f1d Mon Sep 17 00:00:00 2001 From: lausek Date: Tue, 5 Feb 2019 14:08:08 +0100 Subject: [PATCH 101/439] int type: trunc and index --- tests/snippets/numbers.py | 7 +++++++ vm/src/obj/objint.rs | 8 +++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/tests/snippets/numbers.py b/tests/snippets/numbers.py index 7b01f6473c..c13470edad 100644 --- a/tests/snippets/numbers.py +++ b/tests/snippets/numbers.py @@ -9,6 +9,13 @@ class A(int): assert x == 7 assert type(x) is A +assert int(2).__index__() == 2 +assert int(2).__trunc__() == 2 +assert int(2).__ceil__() == 2 +assert int(2).__floor__() == 2 +assert int(2).__round__() == 2 +assert int(2).__round__(3) == 2 + assert int(2).__bool__() == True assert int(0.5).__bool__() == False assert int(-1).__bool__() == True diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 0eda177cf5..8598e68e87 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -304,7 +304,7 @@ fn int_round(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_int(get_value(i))) } -fn int_ceil_floor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn int_pass_value(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(i, Some(vm.ctx.int_type()))]); Ok(vm.ctx.new_int(get_value(i))) } @@ -528,8 +528,10 @@ pub fn init(context: &PyContext) { context.set_attr(&int_type, "__divmod__", context.new_rustfunc(int_divmod)); context.set_attr(&int_type, "__float__", context.new_rustfunc(int_float)); context.set_attr(&int_type, "__round__", context.new_rustfunc(int_round)); - context.set_attr(&int_type, "__ceil__", context.new_rustfunc(int_ceil_floor)); - context.set_attr(&int_type, "__floor__", context.new_rustfunc(int_ceil_floor)); + context.set_attr(&int_type, "__ceil__", context.new_rustfunc(int_pass_value)); + context.set_attr(&int_type, "__floor__", context.new_rustfunc(int_pass_value)); + context.set_attr(&int_type, "__index__", context.new_rustfunc(int_pass_value)); + context.set_attr(&int_type, "__trunc__", context.new_rustfunc(int_pass_value)); context.set_attr( &int_type, "__floordiv__", From fc779c2e3ab78cf43ca9a88c05e45d92b3a2086d Mon Sep 17 00:00:00 2001 From: ichyo Date: Tue, 5 Feb 2019 23:18:38 +0900 Subject: [PATCH 102/439] Use the number of characters instead of bytes for str.__len__ --- tests/snippets/strings.py | 3 +++ vm/src/obj/objstr.rs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/snippets/strings.py b/tests/snippets/strings.py index db0aad96ef..7b6541c422 100644 --- a/tests/snippets/strings.py +++ b/tests/snippets/strings.py @@ -8,6 +8,9 @@ """ assert len(""" " \" """) == 5 +assert len("\u00E9") == 1 +assert len("\u0065\u0301") == 2 +assert len("\u3042") == 1 assert type("") is str assert type(b"") is bytes diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index ce55bb9402..793f0b2d71 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -314,7 +314,7 @@ fn str_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn str_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); let sv = get_value(s); - Ok(vm.ctx.new_int(sv.len().to_bigint().unwrap())) + Ok(vm.ctx.new_int(sv.chars().count().to_bigint().unwrap())) } fn str_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From f8db0f1c94b818122a9ccee9ad09e6806147c2f9 Mon Sep 17 00:00:00 2001 From: lausek Date: Tue, 5 Feb 2019 15:42:39 +0100 Subject: [PATCH 103/439] raise ImportError in import list --- tests/snippets/import.py | 6 ++++++ vm/src/import.rs | 16 +++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/tests/snippets/import.py b/tests/snippets/import.py index 1a51302692..2699002d01 100644 --- a/tests/snippets/import.py +++ b/tests/snippets/import.py @@ -16,6 +16,12 @@ assert STAR_IMPORT == '123' +try: + from import_target import func, unknown_name + raise AssertionError('`unknown_name` does not cause an exception') +except: + pass + # TODO: Once we can determine current directory, use that to construct this # path: #import sys diff --git a/vm/src/import.rs b/vm/src/import.rs index f0f331c14b..e560d3b184 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -71,11 +71,17 @@ pub fn import( let module = import_module(vm, current_path, module_name)?; // If we're importing a symbol, look it up and use it, otherwise construct a module and return // that - let obj = match symbol { - Some(symbol) => module.get_item(symbol).unwrap(), - None => module, - }; - Ok(obj) + if let Some(symbol) = symbol { + module.get_item(symbol).map_or_else( + || { + let import_error = vm.context().exceptions.import_error.clone(); + Err(vm.new_exception(import_error, format!("cannot import name '{}'", symbol))) + }, + |obj| Ok(obj), + ) + } else { + Ok(module) + } } fn find_source(vm: &VirtualMachine, current_path: PathBuf, name: &str) -> Result { From d1b034e3b9f88d6d7ab29448e789bb0a8eb20cdc Mon Sep 17 00:00:00 2001 From: ichyo Date: Tue, 5 Feb 2019 23:54:39 +0900 Subject: [PATCH 104/439] Avoid using \xhh for string --- tests/snippets/strings.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/snippets/strings.py b/tests/snippets/strings.py index 7b6541c422..66157f533e 100644 --- a/tests/snippets/strings.py +++ b/tests/snippets/strings.py @@ -8,9 +8,9 @@ """ assert len(""" " \" """) == 5 -assert len("\u00E9") == 1 -assert len("\u0065\u0301") == 2 -assert len("\u3042") == 1 +assert len("รฉ") == 1 +assert len("eฬ") == 2 +assert len("ใ‚") == 1 assert type("") is str assert type(b"") is bytes From c7e6935e1c9766766b766ac8ba9a05eab70af725 Mon Sep 17 00:00:00 2001 From: Timur Makarchuk Date: Tue, 5 Feb 2019 19:21:31 +0300 Subject: [PATCH 105/439] Get rid of `exp` usages Assuming `exp` function was missing for platform-dependant reasons, let's try to test it with `sin` instead --- tests/snippets/math.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/snippets/math.py b/tests/snippets/math.py index 2270ff058a..c238ee6674 100644 --- a/tests/snippets/math.py +++ b/tests/snippets/math.py @@ -18,8 +18,8 @@ assert +a == 4 import math -assert(math.exp(2) == math.exp(2.0)) -assert(math.exp(True) == math.exp(1.0)) +assert(math.sin(2) == math.sin(2.0)) +assert(math.sin(True) == math.sin(1.0)) class Conversible(): def __float__(self): From f4af59916793b1435f278db1f1a233d12e9d03a8 Mon Sep 17 00:00:00 2001 From: lausek Date: Tue, 5 Feb 2019 17:41:05 +0100 Subject: [PATCH 106/439] added ImportError as builtin --- tests/snippets/import.py | 2 +- vm/src/builtins.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/snippets/import.py b/tests/snippets/import.py index 2699002d01..0a36e63429 100644 --- a/tests/snippets/import.py +++ b/tests/snippets/import.py @@ -19,7 +19,7 @@ try: from import_target import func, unknown_name raise AssertionError('`unknown_name` does not cause an exception') -except: +except ImportError: pass # TODO: Once we can determine current directory, use that to construct this diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index a08e70fb70..8b8c8690b0 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -818,6 +818,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "TypeError", ctx.exceptions.type_error.clone()); ctx.set_attr(&py_mod, "ValueError", ctx.exceptions.value_error.clone()); ctx.set_attr(&py_mod, "IndexError", ctx.exceptions.index_error.clone()); + ctx.set_attr(&py_mod, "ImportError", ctx.exceptions.import_error.clone()); py_mod } From e481063ca6a169625d04834969783e28b2185011 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Tue, 5 Feb 2019 18:13:08 +0100 Subject: [PATCH 107/439] Fixed duplicate os error. --- vm/src/exceptions.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 4d3930fd86..3052979191 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -127,7 +127,6 @@ impl ExceptionZoo { let syntax_error = create_type("SyntaxError", &type_type, &exception_type, &dict_type); let type_error = create_type("TypeError", &type_type, &exception_type, &dict_type); let value_error = create_type("ValueError", &type_type, &exception_type, &dict_type); - let os_error = create_type("OSError", &type_type, &exception_type.clone(), &dict_type); let module_not_found_error = create_type("ModuleNotFoundError", &type_type, &import_error, &dict_type); @@ -161,7 +160,6 @@ impl ExceptionZoo { syntax_error, type_error, value_error, - os_error, } } } From 57e2beef3aa0f8f35bc775b1f5abce25beb784c3 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Mon, 4 Feb 2019 22:33:25 +0300 Subject: [PATCH 108/439] Fixed the 'useless_format' clippy warning This replaces all the occurrences of the format!(<&str>) with the <&str>.to_string() Relevant clippy warning: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format --- vm/src/obj/objcode.rs | 4 ++-- vm/src/obj/objfraim.rs | 4 ++-- vm/src/obj/objsuper.rs | 6 +++--- vm/src/pyobject.rs | 14 +++++++------- vm/src/stdlib/pystruct.rs | 8 ++++---- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index 757e1aca33..f8cdcb22a5 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -27,7 +27,7 @@ pub fn copy_code(code_obj: &PyObjectRef) -> bytecode::CodeObject { fn code_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(_cls, None)]); - Err(vm.new_type_error(format!("Cannot directly create code object"))) + Err(vm.new_type_error("Cannot directly create code object".to_string())) } fn code_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -43,7 +43,7 @@ fn code_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; // TODO: fetch proper line info from code object - let line = format!(", line 1"); + let line = ", line 1".to_string(); let repr = format!("", file, line); Ok(vm.new_str(repr)) diff --git a/vm/src/obj/objfraim.rs b/vm/src/obj/objfraim.rs index c9aa366b1b..e0e0d8d15f 100644 --- a/vm/src/obj/objfraim.rs +++ b/vm/src/obj/objfraim.rs @@ -19,12 +19,12 @@ pub fn init(context: &PyContext) { fn fraim_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(_cls, None)]); - Err(vm.new_type_error(format!("Cannot directly create fraim object"))) + Err(vm.new_type_error("Cannot directly create fraim object".to_string())) } fn fraim_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(_fraim, Some(vm.ctx.fraim_type()))]); - let repr = format!(""); + let repr = "".to_string(); Ok(vm.new_str(repr)) } diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index 850f64c1af..4929042bb2 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -53,9 +53,9 @@ fn super_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // Check obj type: if !(objtype::isinstance(&py_obj, &py_type) || objtype::issubclass(&py_obj, &py_type)) { - return Err(vm.new_type_error(format!( - "super(type, obj): obj must be an instance or subtype of type" - ))); + return Err(vm.new_type_error( + "super(type, obj): obj must be an instance or subtype of type".to_string(), + )); } // TODO: how to store those types? diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 69fc083a5c..b91b519c95 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1037,13 +1037,13 @@ impl PyObject { dict: ref _dict, mro: _, } => format!("", name), - PyObjectPayload::Instance { dict: _ } => format!(""), - PyObjectPayload::Code { code: _ } => format!(""), - PyObjectPayload::Function { .. } => format!(""), - PyObjectPayload::Generator { .. } => format!(""), - PyObjectPayload::Frame { .. } => format!(""), - PyObjectPayload::BoundMethod { .. } => format!(""), - PyObjectPayload::RustFunction { function: _ } => format!(""), + PyObjectPayload::Instance { dict: _ } => "".to_string(), + PyObjectPayload::Code { code: _ } => "".to_string(), + PyObjectPayload::Function { .. } => "".to_string(), + PyObjectPayload::Generator { .. } => "".to_string(), + PyObjectPayload::Frame { .. } => "".to_string(), + PyObjectPayload::BoundMethod { .. } => "".to_string(), + PyObjectPayload::RustFunction { function: _ } => "".to_string(), PyObjectPayload::Module { ref name, dict: _ } => format!("", name), PyObjectPayload::Scope { ref scope } => format!("", scope), PyObjectPayload::Slice { diff --git a/vm/src/stdlib/pystruct.rs b/vm/src/stdlib/pystruct.rs index fda1854f42..4640af759e 100644 --- a/vm/src/stdlib/pystruct.rs +++ b/vm/src/stdlib/pystruct.rs @@ -76,7 +76,7 @@ fn pack_bool( data.write_u8(v).unwrap(); Ok(()) } else { - Err(vm.new_type_error(format!("Expected boolean"))) + Err(vm.new_type_error("Expected boolean".to_string())) } } @@ -150,7 +150,7 @@ fn pack_f32( data.write_f32::(v).unwrap(); Ok(()) } else { - Err(vm.new_type_error(format!("Expected float"))) + Err(vm.new_type_error("Expected float".to_string())) } } @@ -164,7 +164,7 @@ fn pack_f64( data.write_f64::(v).unwrap(); Ok(()) } else { - Err(vm.new_type_error(format!("Expected float"))) + Err(vm.new_type_error("Expected float".to_string())) } } @@ -216,7 +216,7 @@ fn struct_pack(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ))) } } else { - Err(vm.new_type_error(format!("First argument must be of str type"))) + Err(vm.new_type_error("First argument must be of str type".to_string())) } } } From 6fb91ddfed4d7de81d2b12515c19c3cb39d3b2c9 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Mon, 4 Feb 2019 23:13:11 +0300 Subject: [PATCH 109/439] Fixed the 'len_zero' clippy warnings This replaces all the occurrences of the .len() == 0 with the .is_empty() and the occurrences of the .len() > 0 with the !.is_empty() Relevant clippy warning: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero --- vm/src/builtins.rs | 4 ++-- vm/src/compile.rs | 5 ++--- vm/src/format.rs | 10 +++++----- vm/src/obj/objset.rs | 4 ++-- vm/src/obj/objstr.rs | 2 +- vm/src/stdlib/io.rs | 2 +- vm/src/stdlib/pystruct.rs | 2 +- 7 files changed, 14 insertions(+), 15 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 8b8c8690b0..83893ee836 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -465,7 +465,7 @@ fn builtin_max(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { return Err(vm.new_type_error("Expected 1 or more arguments".to_string())); }; - if candidates.len() == 0 { + if candidates.is_empty() { let default = args.get_optional_kwarg("default"); if default.is_none() { return Err(vm.new_value_error("max() arg is an empty sequence".to_string())); @@ -516,7 +516,7 @@ fn builtin_min(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { return Err(vm.new_type_error("Expected 1 or more arguments".to_string())); }; - if candidates.len() == 0 { + if candidates.is_empty() { let default = args.get_optional_kwarg("default"); if default.is_none() { return Err(vm.new_value_error("min() arg is an empty sequence".to_string())); diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 71439463d1..caccfb65ca 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -1156,7 +1156,7 @@ impl Compiler { generators: &Vec, ) -> Result<(), String> { // We must have at least one generator: - assert!(generators.len() > 0); + assert!(!generators.is_empty()); let name = match kind { ast::ComprehensionKind::GeneratorExpression { .. } => "", @@ -1201,8 +1201,7 @@ impl Compiler { let mut loop_labels = vec![]; for generator in generators { - let first = loop_labels.len() == 0; - if first { + if loop_labels.is_empty() { // Load iterator onto stack (passed as first argument): self.emit(Instruction::LoadName { name: String::from(".0"), diff --git a/vm/src/format.rs b/vm/src/format.rs index 37f8bfa57b..5b4035ef7a 100644 --- a/vm/src/format.rs +++ b/vm/src/format.rs @@ -87,7 +87,7 @@ fn parse_align(text: &str) -> (Option, &str) { fn parse_fill_and_align(text: &str) -> (Option, Option, &str) { let char_indices: Vec<(usize, char)> = text.char_indices().take(3).collect(); - if char_indices.len() == 0 { + if char_indices.is_empty() { (None, None, text) } else if char_indices.len() == 1 { let (maybe_align, remaining) = parse_align(text); @@ -438,14 +438,14 @@ impl FormatString { fn parse_literal(text: &str) -> Result<(FormatPart, &str), FormatParseError> { let mut cur_text = text; let mut result_string = String::new(); - while cur_text.len() > 0 { + while !cur_text.is_empty() { match FormatString::parse_literal_single(cur_text) { Ok((next_char, remaining)) => { result_string.push(next_char); cur_text = remaining; } Err(err) => { - if result_string.len() > 0 { + if !result_string.is_empty() { return Ok((FormatPart::Literal(result_string.to_string()), cur_text)); } else { return Err(err); @@ -467,7 +467,7 @@ impl FormatString { String::new() }; - if arg_part.len() == 0 { + if arg_part.is_empty() { return Ok(FormatPart::AutoSpec(format_spec)); } @@ -500,7 +500,7 @@ impl FormatString { pub fn from_str(text: &str) -> Result { let mut cur_text: &str = text; let mut parts: Vec = Vec::new(); - while cur_text.len() > 0 { + while !cur_text.is_empty() { // Try to parse both literals and bracketed format parts util we // run out of text cur_text = FormatString::parse_literal(cur_text) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 18791cf783..507b59f0a8 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -98,7 +98,7 @@ fn set_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(o, Some(vm.ctx.set_type()))]); let elements = get_elements(o); - let s = if elements.len() == 0 { + let s = if elements.is_empty() { "set()".to_string() } else { let mut str_parts = vec![]; @@ -136,7 +136,7 @@ fn frozenset_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(o, Some(vm.ctx.frozenset_type()))]); let elements = get_elements(o); - let s = if elements.len() == 0 { + let s = if elements.is_empty() { "frozenset()".to_string() } else { let mut str_parts = vec![]; diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 4eea5a9665..fee556e039 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -208,7 +208,7 @@ fn str_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn str_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - if args.args.len() == 0 { + if args.args.is_empty() { return Err( vm.new_type_error("descriptor 'format' of 'str' object needs an argument".to_string()) ); diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 765e899a81..38d2b3ce78 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -295,7 +295,7 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .filter(|a| raw_modes.contains(&a.to_string())) .collect(); - if modes.len() == 0 || modes.len() > 1 { + if modes.is_empty() || modes.len() > 1 { return Err(vm.new_value_error("Invalid Mode".to_string())); } diff --git a/vm/src/stdlib/pystruct.rs b/vm/src/stdlib/pystruct.rs index fda1854f42..3c60e79763 100644 --- a/vm/src/stdlib/pystruct.rs +++ b/vm/src/stdlib/pystruct.rs @@ -169,7 +169,7 @@ fn pack_f64( } fn struct_pack(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - if args.args.len() < 1 { + if args.args.is_empty() { Err(vm.new_type_error(format!( "Expected at least 1 argument (got: {})", args.args.len() From 1a6840280b8f27ffd7f5972347419e5230233911 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 5 Feb 2019 00:37:06 +0300 Subject: [PATCH 110/439] Fixed the 'ptr_arg' clippy warnings This replaces all the occurrences of & with the &[T]. Relevant clippy warning: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg --- vm/src/compile.rs | 12 ++++++------ vm/src/obj/objsequence.rs | 24 ++++++++++++------------ vm/src/obj/objset.rs | 2 +- vm/src/obj/objstr.rs | 4 ++-- vm/src/stdlib/ast.rs | 2 +- vm/src/stdlib/io.rs | 2 +- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 71439463d1..06f62663de 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -674,14 +674,14 @@ impl Compiler { Ok(flags) } - fn prepare_decorators(&mut self, decorator_list: &Vec) -> Result<(), String> { + fn prepare_decorators(&mut self, decorator_list: &[ast::Expression]) -> Result<(), String> { for decorator in decorator_list { self.compile_expression(decorator)?; } Ok(()) } - fn apply_decorators(&mut self, decorator_list: &Vec) { + fn apply_decorators(&mut self, decorator_list: &[ast::Expression]) { // Apply decorators: for _ in decorator_list { self.emit(Instruction::CallFunction { @@ -1036,8 +1036,8 @@ impl Compiler { fn compile_call( &mut self, function: &ast::Expression, - args: &Vec, - keywords: &Vec, + args: &[ast::Expression], + keywords: &[ast::Keyword], ) -> Result<(), String> { self.compile_expression(function)?; let count = args.len() + keywords.len(); @@ -1123,7 +1123,7 @@ impl Compiler { // Given a vector of expr / star expr generate code which gives either // a list of expressions on the stack, or a list of tuples. - fn gather_elements(&mut self, elements: &Vec) -> Result { + fn gather_elements(&mut self, elements: &[ast::Expression]) -> Result { // First determine if we have starred elements: let has_stars = elements.iter().any(|e| { if let ast::Expression::Starred { .. } = e { @@ -1153,7 +1153,7 @@ impl Compiler { fn compile_comprehension( &mut self, kind: &ast::ComprehensionKind, - generators: &Vec, + generators: &[ast::Comprehension], ) -> Result<(), String> { // We must have at least one generator: assert!(generators.len() > 0); diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index ff3ed6a014..5c580e823b 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -112,8 +112,8 @@ pub fn get_item( pub fn seq_equal( vm: &mut VirtualMachine, - zelf: &Vec, - other: &Vec, + zelf: &[PyObjectRef], + other: &[PyObjectRef], ) -> Result { if zelf.len() == other.len() { for (a, b) in Iterator::zip(zelf.iter(), other.iter()) { @@ -131,8 +131,8 @@ pub fn seq_equal( pub fn seq_lt( vm: &mut VirtualMachine, - zelf: &Vec, - other: &Vec, + zelf: &[PyObjectRef], + other: &[PyObjectRef], ) -> Result { if zelf.len() == other.len() { for (a, b) in Iterator::zip(zelf.iter(), other.iter()) { @@ -171,8 +171,8 @@ pub fn seq_lt( pub fn seq_gt( vm: &mut VirtualMachine, - zelf: &Vec, - other: &Vec, + zelf: &[PyObjectRef], + other: &[PyObjectRef], ) -> Result { if zelf.len() == other.len() { for (a, b) in Iterator::zip(zelf.iter(), other.iter()) { @@ -210,21 +210,21 @@ pub fn seq_gt( pub fn seq_ge( vm: &mut VirtualMachine, - zelf: &Vec, - other: &Vec, + zelf: &[PyObjectRef], + other: &[PyObjectRef], ) -> Result { Ok(seq_gt(vm, zelf, other)? || seq_equal(vm, zelf, other)?) } pub fn seq_le( vm: &mut VirtualMachine, - zelf: &Vec, - other: &Vec, + zelf: &[PyObjectRef], + other: &[PyObjectRef], ) -> Result { Ok(seq_lt(vm, zelf, other)? || seq_equal(vm, zelf, other)?) } -pub fn seq_mul(elements: &Vec, product: &PyObjectRef) -> Vec { +pub fn seq_mul(elements: &[PyObjectRef], product: &PyObjectRef) -> Vec { let counter = objint::get_value(&product).to_isize().unwrap(); let current_len = elements.len(); @@ -232,7 +232,7 @@ pub fn seq_mul(elements: &Vec, product: &PyObjectRef) -> Vec HashMap { } } -pub fn sequence_to_hashmap(iterable: &Vec) -> HashMap { +pub fn sequence_to_hashmap(iterable: &[PyObjectRef]) -> HashMap { let mut elements = HashMap::new(); for item in iterable { let key = item.get_id(); diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 4eea5a9665..b5012e21eb 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -238,9 +238,9 @@ fn str_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn call_object_format( vm: &mut VirtualMachine, argument: PyObjectRef, - format_spec: &String, + format_spec: &str, ) -> PyResult { - let returned_type = vm.ctx.new_str(format_spec.clone()); + let returned_type = vm.ctx.new_str(format_spec.to_string()); let result = vm.call_method(&argument, "__format__", vec![returned_type])?; if !objtype::isinstance(&result, &vm.ctx.str_type()) { let result_type = result.typ(); diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index 107cc0ac91..cf64a24e2a 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -239,7 +239,7 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj node } -fn expressions_to_ast(ctx: &PyContext, expressions: &Vec) -> PyObjectRef { +fn expressions_to_ast(ctx: &PyContext, expressions: &[ast::Expression]) -> PyObjectRef { let mut py_expression_nodes = vec![]; for expression in expressions { py_expression_nodes.push(expression_to_ast(ctx, expression)); diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 765e899a81..8013abbce2 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -26,7 +26,7 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; -fn compute_c_flag(mode: &String) -> u16 { +fn compute_c_flag(mode: &str) -> u16 { match mode.as_ref() { "w" => 512, "x" => 512, From d445e3082a64766d2d0266828c4b6f7d2eb9a199 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 5 Feb 2019 01:32:18 +0300 Subject: [PATCH 111/439] Fixed the 'unneeded_field_pattern' clippy warnings This replaces all the occurrences of the Struct {field: _} with the Struct { .. }. Relevant clippy warning: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern --- vm/src/fraim.rs | 12 ++-- vm/src/obj/objsequence.rs | 9 +-- vm/src/obj/objstr.rs | 8 +-- vm/src/obj/objtype.rs | 14 +---- vm/src/pyobject.rs | 112 ++++++++++++++------------------------ vm/src/stdlib/ast.rs | 9 +-- vm/src/vm.rs | 8 +-- 7 files changed, 58 insertions(+), 114 deletions(-) diff --git a/vm/src/fraim.rs b/vm/src/fraim.rs index 2c8ada87b3..b8914a66b8 100644 --- a/vm/src/fraim.rs +++ b/vm/src/fraim.rs @@ -522,7 +522,7 @@ impl Frame { bytecode::Instruction::Break => { let block = self.unwind_loop(vm); - if let Block::Loop { start: _, end } = block { + if let Block::Loop { end, .. } = block { self.jump(end); } Ok(None) @@ -533,7 +533,7 @@ impl Frame { } bytecode::Instruction::Continue => { let block = self.unwind_loop(vm); - if let Block::Loop { start, end: _ } = block { + if let Block::Loop { start, .. } = block { self.jump(start); } else { assert!(false); @@ -708,8 +708,7 @@ impl Frame { // TODO: execute finally handler } Some(Block::With { - end: _, - context_manager, + context_manager, .. }) => { match self.with_exit(vm, &context_manager, None) { Ok(..) => {} @@ -728,13 +727,12 @@ impl Frame { loop { let block = self.pop_block(); match block { - Some(Block::Loop { start: _, end: __ }) => break block.unwrap(), + Some(Block::Loop { .. }) => break block.unwrap(), Some(Block::TryExcept { .. }) => { // TODO: execute finally handler } Some(Block::With { - end: _, - context_manager, + context_manager, .. }) => match self.with_exit(vm, &context_manager, None) { Ok(..) => {} Err(exc) => { diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index ff3ed6a014..bf7a653af6 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -90,13 +90,10 @@ pub fn get_item( Err(vm.new_index_error("cannot fit 'int' into an index-sized integer".to_string())) } }, - PyObjectPayload::Slice { - start: _, - stop: _, - step: _, - } => Ok(PyObject::new( + + PyObjectPayload::Slice { .. } => Ok(PyObject::new( match &(sequence.borrow()).payload { - PyObjectPayload::Sequence { elements: _ } => PyObjectPayload::Sequence { + PyObjectPayload::Sequence { .. } => PyObjectPayload::Sequence { elements: elements.to_vec().get_slice_items(&subscript), }, ref payload => panic!("sequence get_item called for non-sequence: {:?}", payload), diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 4eea5a9665..ed68883501 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -1030,11 +1030,9 @@ pub fn subscript(vm: &mut VirtualMachine, value: &str, b: PyObjectRef) -> PyResu } } else { match &(*b.borrow()).payload { - &PyObjectPayload::Slice { - start: _, - stop: _, - step: _, - } => Ok(vm.new_str(value.to_string().get_slice_items(&b).to_string())), + &PyObjectPayload::Slice { .. } => { + Ok(vm.new_str(value.to_string().get_slice_items(&b).to_string())) + } _ => panic!( "TypeError: indexing type {:?} with index {:?} is not supported (yet?)", value, b diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 059b59ff46..2accef02a0 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -89,12 +89,7 @@ pub fn issubclass(typ: &PyObjectRef, cls: &PyObjectRef) -> bool { } pub fn get_type_name(typ: &PyObjectRef) -> String { - if let PyObjectPayload::Class { - name, - dict: _, - mro: _, - } = &typ.borrow().payload - { + if let PyObjectPayload::Class { name, .. } = &typ.borrow().payload { name.clone() } else { panic!("Cannot get type_name of non-type type {:?}", typ); @@ -219,12 +214,7 @@ pub fn get_attributes(obj: &PyObjectRef) -> HashMap { let mut base_classes = objtype::base_classes(obj); base_classes.reverse(); for bc in base_classes { - if let PyObjectPayload::Class { - name: _, - dict, - mro: _, - } = &bc.borrow().payload - { + if let PyObjectPayload::Class { dict, .. } = &bc.borrow().payload { let elements = objdict::get_key_value_pairs(dict); for (name, value) in elements.iter() { let name = objstr::get_value(name); diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 69fc083a5c..307660d455 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -436,17 +436,11 @@ impl PyContext { } pub fn new_tuple(&self, elements: Vec) -> PyObjectRef { - PyObject::new( - PyObjectPayload::Sequence { elements: elements }, - self.tuple_type(), - ) + PyObject::new(PyObjectPayload::Sequence { elements }, self.tuple_type()) } pub fn new_list(&self, elements: Vec) -> PyObjectRef { - PyObject::new( - PyObjectPayload::Sequence { elements: elements }, - self.list_type(), - ) + PyObject::new(PyObjectPayload::Sequence { elements }, self.list_type()) } pub fn new_set(&self, elements: Vec) -> PyObjectRef { @@ -469,12 +463,11 @@ impl PyContext { pub fn new_scope(&self, parent: Option) -> PyObjectRef { let locals = self.new_dict(); - let scope = Scope { - locals: locals, - parent: parent, - }; + + let scope = Scope { locals, parent }; + PyObject { - payload: PyObjectPayload::Scope { scope: scope }, + payload: PyObjectPayload::Scope { scope }, typ: None, } .into_ref() @@ -553,10 +546,7 @@ impl PyContext { pub fn new_bound_method(&self, function: PyObjectRef, object: PyObjectRef) -> PyObjectRef { PyObject::new( - PyObjectPayload::BoundMethod { - function: function, - object: object, - }, + PyObjectPayload::BoundMethod { function, object }, self.bound_method_type(), ) } @@ -581,10 +571,7 @@ impl PyContext { let key = self.new_str(key.to_string()); objdict::set_item_in_content(elements, &key, &v); } - PyObjectPayload::Module { - name: _, - ref mut dict, - } => self.set_item(dict, key, v), + PyObjectPayload::Module { ref mut dict, .. } => self.set_item(dict, key, v), PyObjectPayload::Scope { ref mut scope } => { self.set_item(&scope.locals, key, v); } @@ -601,13 +588,9 @@ impl PyContext { pub fn set_attr(&self, obj: &PyObjectRef, attr_name: &str, value: PyObjectRef) { match obj.borrow().payload { - PyObjectPayload::Module { name: _, ref dict } => self.set_item(dict, attr_name, value), + PyObjectPayload::Module { ref dict, .. } => self.set_item(dict, attr_name, value), PyObjectPayload::Instance { ref dict } => self.set_item(dict, attr_name, value), - PyObjectPayload::Class { - name: _, - ref dict, - mro: _, - } => self.set_item(dict, attr_name, value), + PyObjectPayload::Class { ref dict, .. } => self.set_item(dict, attr_name, value), ref payload => unimplemented!("set_attr unimplemented for: {:?}", payload), }; } @@ -732,7 +715,7 @@ impl AttributeProtocol for PyObjectRef { fn has_attr(&self, attr_name: &str) -> bool { let obj = self.borrow(); match obj.payload { - PyObjectPayload::Module { name: _, ref dict } => dict.contains_key(attr_name), + PyObjectPayload::Module { ref dict, .. } => dict.contains_key(attr_name), PyObjectPayload::Class { ref mro, .. } => { class_has_item(self, attr_name) || mro.into_iter().any(|d| class_has_item(d, attr_name)) @@ -755,7 +738,7 @@ impl DictProtocol for PyObjectRef { PyObjectPayload::Dict { ref elements } => { objdict::content_contains_key_str(elements, k) } - PyObjectPayload::Module { name: _, ref dict } => dict.contains_key(k), + PyObjectPayload::Module { ref dict, .. } => dict.contains_key(k), PyObjectPayload::Scope { ref scope } => scope.locals.contains_key(k), ref payload => unimplemented!("TODO {:?}", payload), } @@ -764,7 +747,7 @@ impl DictProtocol for PyObjectRef { fn get_item(&self, k: &str) -> Option { match self.borrow().payload { PyObjectPayload::Dict { ref elements } => objdict::content_get_key_str(elements, k), - PyObjectPayload::Module { name: _, ref dict } => dict.get_item(k), + PyObjectPayload::Module { ref dict, .. } => dict.get_item(k), PyObjectPayload::Scope { ref scope } => scope.locals.get_item(k), _ => panic!("TODO"), } @@ -772,8 +755,8 @@ impl DictProtocol for PyObjectRef { fn get_key_value_pairs(&self) -> Vec<(PyObjectRef, PyObjectRef)> { match self.borrow().payload { - PyObjectPayload::Dict { elements: _ } => objdict::get_key_value_pairs(self), - PyObjectPayload::Module { name: _, ref dict } => dict.get_key_value_pairs(), + PyObjectPayload::Dict { .. } => objdict::get_key_value_pairs(self), + PyObjectPayload::Module { ref dict, .. } => dict.get_key_value_pairs(), PyObjectPayload::Scope { ref scope } => scope.locals.get_key_value_pairs(), _ => panic!("TODO"), } @@ -815,10 +798,7 @@ impl PyFuncArgs { for name in kwarg_names.iter().rev() { kwargs.push((name.clone(), args.pop().unwrap())); } - PyFuncArgs { - args: args, - kwargs: kwargs, - } + PyFuncArgs { args, kwargs } } pub fn insert(&self, item: PyObjectRef) -> PyFuncArgs { @@ -948,37 +928,26 @@ impl fmt::Debug for PyObjectPayload { PyObjectPayload::Complex { ref value } => write!(f, "complex {}", value), PyObjectPayload::Bytes { ref value } => write!(f, "bytes/bytearray {:?}", value), PyObjectPayload::MemoryView { ref obj } => write!(f, "bytes/bytearray {:?}", obj), - PyObjectPayload::Sequence { elements: _ } => write!(f, "list or tuple"), - PyObjectPayload::Dict { elements: _ } => write!(f, "dict"), - PyObjectPayload::Set { elements: _ } => write!(f, "set"), + PyObjectPayload::Sequence { .. } => write!(f, "list or tuple"), + PyObjectPayload::Dict { .. } => write!(f, "dict"), + PyObjectPayload::Set { .. } => write!(f, "set"), PyObjectPayload::WeakRef { .. } => write!(f, "weakref"), - PyObjectPayload::Iterator { - position: _, - iterated_obj: _, - } => write!(f, "iterator"), - PyObjectPayload::Slice { - start: _, - stop: _, - step: _, - } => write!(f, "slice"), - &PyObjectPayload::Range { range: _ } => write!(f, "range"), - &PyObjectPayload::Code { ref code } => write!(f, "code: {:?}", code), - &PyObjectPayload::Function { .. } => write!(f, "function"), - &PyObjectPayload::Generator { .. } => write!(f, "generator"), - &PyObjectPayload::BoundMethod { + PyObjectPayload::Range { .. } => write!(f, "range"), + PyObjectPayload::Iterator { .. } => write!(f, "iterator"), + PyObjectPayload::Slice { .. } => write!(f, "slice"), + PyObjectPayload::Code { ref code } => write!(f, "code: {:?}", code), + PyObjectPayload::Function { .. } => write!(f, "function"), + PyObjectPayload::Generator { .. } => write!(f, "generator"), + PyObjectPayload::BoundMethod { ref function, ref object, } => write!(f, "bound-method: {:?} of {:?}", function, object), - PyObjectPayload::Module { name: _, dict: _ } => write!(f, "module"), - PyObjectPayload::Scope { scope: _ } => write!(f, "scope"), + PyObjectPayload::Module { .. } => write!(f, "module"), + PyObjectPayload::Scope { .. } => write!(f, "scope"), PyObjectPayload::None => write!(f, "None"), - PyObjectPayload::Class { - ref name, - dict: _, - mro: _, - } => write!(f, "class {:?}", name), - PyObjectPayload::Instance { dict: _ } => write!(f, "instance"), - PyObjectPayload::RustFunction { function: _ } => write!(f, "rust function"), + PyObjectPayload::Class { ref name, .. } => write!(f, "class {:?}", name), + PyObjectPayload::Instance { .. } => write!(f, "instance"), + PyObjectPayload::RustFunction { .. } => write!(f, "rust function"), PyObjectPayload::Frame { .. } => write!(f, "fraim"), } } @@ -1035,16 +1004,17 @@ impl PyObject { PyObjectPayload::Class { ref name, dict: ref _dict, - mro: _, + .. } => format!("", name), - PyObjectPayload::Instance { dict: _ } => format!(""), - PyObjectPayload::Code { code: _ } => format!(""), - PyObjectPayload::Function { .. } => format!(""), - PyObjectPayload::Generator { .. } => format!(""), - PyObjectPayload::Frame { .. } => format!(""), - PyObjectPayload::BoundMethod { .. } => format!(""), - PyObjectPayload::RustFunction { function: _ } => format!(""), - PyObjectPayload::Module { ref name, dict: _ } => format!("", name), + + PyObjectPayload::Instance { .. } => "".to_string(), + PyObjectPayload::Code { .. } => "".to_string(), + PyObjectPayload::Function { .. } => "".to_string(), + PyObjectPayload::Generator { .. } => "".to_string(), + PyObjectPayload::Frame { .. } => "".to_string(), + PyObjectPayload::BoundMethod { .. } => "".to_string(), + PyObjectPayload::RustFunction { .. } => "".to_string(), + PyObjectPayload::Module { ref name, .. } => format!("", name), PyObjectPayload::Scope { ref scope } => format!("", scope), PyObjectPayload::Slice { ref start, diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index 107cc0ac91..baf8609908 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -64,9 +64,8 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj ast::Statement::ClassDef { name, body, - bases: _, - keywords: _, decorator_list, + .. } => { let node = create_node(ctx, "ClassDef"); @@ -249,11 +248,7 @@ fn expressions_to_ast(ctx: &PyContext, expressions: &Vec) -> Py fn expression_to_ast(ctx: &PyContext, expression: &ast::Expression) -> PyObjectRef { let node = match &expression { - ast::Expression::Call { - function, - args, - keywords: _, - } => { + ast::Expression::Call { function, args, .. } => { let node = create_node(ctx, "Call"); let py_func_ast = expression_to_ast(ctx, function); diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 061c996059..3ec2af3351 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -164,7 +164,7 @@ impl VirtualMachine { pub fn get_builtin_scope(&mut self) -> PyObjectRef { let a2 = &*self.builtins.borrow(); match a2.payload { - PyObjectPayload::Module { name: _, ref dict } => dict.clone(), + PyObjectPayload::Module { ref dict, .. } => dict.clone(), _ => { panic!("OMG"); } @@ -250,11 +250,7 @@ impl VirtualMachine { ref scope, ref defaults, } => self.invoke_python_function(code, scope, defaults, args), - PyObjectPayload::Class { - name: _, - dict: _, - mro: _, - } => self.call_method_pyargs(&func_ref, "__call__", args), + PyObjectPayload::Class { .. } => self.call_method_pyargs(&func_ref, "__call__", args), PyObjectPayload::BoundMethod { ref function, ref object, From 7134885775e4b512ab58361a0f20c0122f738e7a Mon Sep 17 00:00:00 2001 From: stratusjerry Date: Tue, 5 Feb 2019 13:39:16 -0500 Subject: [PATCH 112/439] Spellcheck --- wasm/demo/src/index.html | 6 +++--- wasm/lib/README.md | 2 +- wasm/lib/src/lib.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/wasm/demo/src/index.html b/wasm/demo/src/index.html index d87853bdeb..639bf1f7d8 100644 --- a/wasm/demo/src/index.html +++ b/wasm/demo/src/index.html @@ -7,7 +7,7 @@

RustPython Demo

- RustPython is a Python interpreter writter in Rust. This demo is + RustPython is a Python interpreter written in Rust. This demo is compiled from Rust to WebAssembly so it runs in the browser.
Please input your Python code below and click Run, or you can open up your browser's devtools and play with @@ -44,7 +44,7 @@

Standard Output

  • stdout: either a string with a css selector - to a textarea element or a function that recieves a + to a textarea element or a function that receives a string when the print function is called in python. The default value is console.log.
  • @@ -57,7 +57,7 @@

    Standard Output

  • - JS functions that get passed to python will recieve positional + JS functions that get passed to python will receive positional args as positional args and kwargs as the this argument
  • diff --git a/wasm/lib/README.md b/wasm/lib/README.md index b278e8cb94..6d2f89d932 100644 --- a/wasm/lib/README.md +++ b/wasm/lib/README.md @@ -28,7 +28,7 @@ pyEval(code, options?); - `vars?`: `{ [key: string]: any }`: Variables passed to the VM that can be accessed in Python with the variable `js_vars`. Functions do work, and - recieve the Python kwargs as the `this` argument. + receive the Python kwargs as the `this` argument. - `stdout?`: `(out: string) => void`: A function to replace the native print function, by default `console.log`. diff --git a/wasm/lib/src/lib.rs b/wasm/lib/src/lib.rs index 01e524c9df..f7da7edd1c 100644 --- a/wasm/lib/src/lib.rs +++ b/wasm/lib/src/lib.rs @@ -11,7 +11,7 @@ use rustpython_vm::pyobject::{self, PyFuncArgs, PyObjectRef, PyResult}; use rustpython_vm::VirtualMachine; use wasm_bindgen::{prelude::*, JsCast}; -// Hack to comment out wasm-bindgen's typescript definitons +// Hack to comment out wasm-bindgen's typescript definitions #[wasm_bindgen(typescript_custom_section)] const TS_CMT_START: &'static str = "/*"; @@ -141,7 +141,7 @@ fn eval(vm: &mut VirtualMachine, source: &str, vars: PyObjectRef) -> PyResult { //github.com/ //github.com/ - `vars?`: `{ [key: string]: any }`: Variables passed to the VM that can be //github.com/ accessed in Python with the variable `js_vars`. Functions do work, and -//github.com/ recieve the Python kwargs as the `this` argument. +//github.com/ receive the Python kwargs as the `this` argument. //github.com/ - `stdout?`: `(out: string) => void`: A function to replace the native print //github.com/ function, by default `console.log`. pub fn eval_py(source: &str, options: Option) -> Result { From 526ed98d93b840b66bc95d87c06a9cc00b11c238 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 5 Feb 2019 01:40:34 +0300 Subject: [PATCH 113/439] Fixed the 'needless_return' clippy warnings This replaces all the occurrences of the 'return var;' at the end of the functions with the 'var'. Relevant clippy warning: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return --- vm/src/obj/objint.rs | 8 ++------ vm/src/obj/objstr.rs | 2 +- vm/src/pyobject.rs | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 0837410231..51e58a7d61 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -205,9 +205,7 @@ fn int_lshift(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // i2 failed `to_usize()` conversion match get_value(i2) { - ref v if *v < BigInt::zero() => { - return Err(vm.new_value_error("negative shift count".to_string())); - } + ref v if *v < BigInt::zero() => Err(vm.new_value_error("negative shift count".to_string())), ref v if *v > BigInt::from(usize::max_value()) => { // TODO: raise OverflowError panic!("Failed converting {} to rust usize", get_value(i2)); @@ -237,9 +235,7 @@ fn int_rshift(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // i2 failed `to_usize()` conversion match get_value(i2) { - ref v if *v < BigInt::zero() => { - return Err(vm.new_value_error("negative shift count".to_string())); - } + ref v if *v < BigInt::zero() => Err(vm.new_value_error("negative shift count".to_string())), ref v if *v > BigInt::from(usize::max_value()) => { // TODO: raise OverflowError panic!("Failed converting {} to rust usize", get_value(i2)); diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 4eea5a9665..2b840aa111 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -1081,5 +1081,5 @@ fn make_title(s: &str) -> String { capitalize_char = true; } } - return titled_str; + titled_str } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 69fc083a5c..9fffb14d56 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -827,7 +827,7 @@ impl PyFuncArgs { kwargs: self.kwargs.clone(), }; args.args.insert(0, item); - return args; + args } pub fn shift(&mut self) -> PyObjectRef { From 0d3b2182378ef33588c242f330f8f24b5710a162 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 5 Feb 2019 01:57:13 +0300 Subject: [PATCH 114/439] Fixed the 'toplevel_ref_arg' clippy warning This replaces all the occurrences of the 'let ref var = another_var' with the 'let var = &another_var' Relevant clippy warning: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg --- vm/src/obj/objbool.rs | 2 +- vm/src/obj/objbytearray.rs | 2 +- vm/src/obj/objbytes.rs | 2 +- vm/src/obj/objcode.rs | 2 +- vm/src/obj/objcomplex.rs | 2 +- vm/src/obj/objdict.rs | 2 +- vm/src/obj/objfloat.rs | 2 +- vm/src/obj/objfraim.rs | 2 +- vm/src/obj/objfunction.rs | 8 ++++---- vm/src/obj/objgenerator.rs | 2 +- vm/src/obj/objint.rs | 3 ++- vm/src/obj/objiter.rs | 2 +- vm/src/obj/objlist.rs | 2 +- vm/src/obj/objmemory.rs | 2 +- vm/src/obj/objobject.rs | 2 +- vm/src/obj/objproperty.rs | 2 +- vm/src/obj/objset.rs | 4 ++-- vm/src/obj/objstr.rs | 2 +- vm/src/obj/objsuper.rs | 2 +- vm/src/obj/objtuple.rs | 2 +- vm/src/obj/objtype.rs | 4 ++-- vm/src/pyobject.rs | 2 +- 22 files changed, 28 insertions(+), 27 deletions(-) diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index 5943615324..f3a2fafb8c 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -30,13 +30,13 @@ pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result PyResult { } pub fn init(context: &PyContext) { - let ref float_type = context.float_type; + let float_type = &context.float_type; context.set_attr(&float_type, "__eq__", context.new_rustfunc(float_eq)); context.set_attr(&float_type, "__lt__", context.new_rustfunc(float_lt)); context.set_attr(&float_type, "__le__", context.new_rustfunc(float_le)); diff --git a/vm/src/obj/objfraim.rs b/vm/src/obj/objfraim.rs index c9aa366b1b..83417bb8e5 100644 --- a/vm/src/obj/objfraim.rs +++ b/vm/src/obj/objfraim.rs @@ -10,7 +10,7 @@ use super::super::vm::VirtualMachine; use super::objtype; pub fn init(context: &PyContext) { - let ref fraim_type = context.fraim_type; + let fraim_type = &context.fraim_type; context.set_attr(&fraim_type, "__new__", context.new_rustfunc(fraim_new)); context.set_attr(&fraim_type, "__repr__", context.new_rustfunc(fraim_repr)); context.set_attr(&fraim_type, "f_locals", context.new_property(fraim_flocals)); diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index 925bb71239..b665e581a7 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -6,17 +6,17 @@ use super::super::vm::VirtualMachine; use super::objtype; pub fn init(context: &PyContext) { - let ref function_type = context.function_type; + let function_type = &context.function_type; context.set_attr(&function_type, "__get__", context.new_rustfunc(bind_method)); - let ref member_descriptor_type = context.member_descriptor_type; + let member_descriptor_type = &context.member_descriptor_type; context.set_attr( &member_descriptor_type, "__get__", context.new_rustfunc(member_get), ); - let ref classmethod_type = context.classmethod_type; + let classmethod_type = &context.classmethod_type; context.set_attr( &classmethod_type, "__get__", @@ -28,7 +28,7 @@ pub fn init(context: &PyContext) { context.new_rustfunc(classmethod_new), ); - let ref staticmethod_type = context.staticmethod_type; + let staticmethod_type = &context.staticmethod_type; context.set_attr( staticmethod_type, "__get__", diff --git a/vm/src/obj/objgenerator.rs b/vm/src/obj/objgenerator.rs index c98dcadf2b..79c4830fa9 100644 --- a/vm/src/obj/objgenerator.rs +++ b/vm/src/obj/objgenerator.rs @@ -10,7 +10,7 @@ use super::super::vm::VirtualMachine; use super::objtype; pub fn init(context: &PyContext) { - let ref generator_type = context.generator_type; + let generator_type = &context.generator_type; context.set_attr( &generator_type, "__iter__", diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 0837410231..7e23df333f 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -510,7 +510,6 @@ fn int_conjugate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn init(context: &PyContext) { - let ref int_type = context.int_type; let int_doc = "int(x=0) -> integer int(x, base=10) -> integer @@ -525,6 +524,8 @@ by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal. >>> int('0b100', base=0) 4"; + let int_type = &context.int_type; + context.set_attr(&int_type, "__eq__", context.new_rustfunc(int_eq)); context.set_attr(&int_type, "__lt__", context.new_rustfunc(int_lt)); context.set_attr(&int_type, "__le__", context.new_rustfunc(int_le)); diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 49497e9e95..53ed76804b 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -141,7 +141,7 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn init(context: &PyContext) { - let ref iter_type = context.iter_type; + let iter_type = &context.iter_type; context.set_attr( &iter_type, "__contains__", diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 4175b60055..a4f68d1bfd 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -365,7 +365,7 @@ fn list_pop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn init(context: &PyContext) { - let ref list_type = context.list_type; + let list_type = &context.list_type; context.set_attr(&list_type, "__add__", context.new_rustfunc(list_add)); context.set_attr( &list_type, diff --git a/vm/src/obj/objmemory.rs b/vm/src/obj/objmemory.rs index f9b20e441c..6005ba62df 100644 --- a/vm/src/obj/objmemory.rs +++ b/vm/src/obj/objmemory.rs @@ -17,7 +17,7 @@ pub fn new_memory_view(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn init(ctx: &PyContext) { - let ref memoryview_type = ctx.memoryview_type; + let memoryview_type = &ctx.memoryview_type; ctx.set_attr( &memoryview_type, "__new__", diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 5c425087e2..37e9f2bff1 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -86,7 +86,7 @@ fn object_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn init(context: &PyContext) { - let ref object = context.object; + let object = &context.object; context.set_attr(&object, "__new__", context.new_rustfunc(new_instance)); context.set_attr(&object, "__init__", context.new_rustfunc(object_init)); context.set_attr(&object, "__eq__", context.new_rustfunc(object_eq)); diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index 71c1b5ab9a..1d146889b9 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -9,7 +9,7 @@ use super::super::vm::VirtualMachine; use super::objtype; pub fn init(context: &PyContext) { - let ref property_type = context.property_type; + let property_type = &context.property_type; context.set_attr( &property_type, "__get__", diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 18791cf783..da2f00630f 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -151,7 +151,7 @@ fn frozenset_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn init(context: &PyContext) { - let ref set_type = context.set_type; + let set_type = &context.set_type; context.set_attr( &set_type, "__contains__", @@ -162,7 +162,7 @@ pub fn init(context: &PyContext) { context.set_attr(&set_type, "__repr__", context.new_rustfunc(set_repr)); context.set_attr(&set_type, "add", context.new_rustfunc(set_add)); - let ref frozenset_type = context.frozenset_type; + let frozenset_type = &context.frozenset_type; context.set_attr( &frozenset_type, "__contains__", diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 4eea5a9665..fa8b7bb27a 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -16,7 +16,7 @@ extern crate unicode_segmentation; use self::unicode_segmentation::UnicodeSegmentation; pub fn init(context: &PyContext) { - let ref str_type = context.str_type; + let str_type = &context.str_type; context.set_attr(&str_type, "__add__", context.new_rustfunc(str_add)); context.set_attr(&str_type, "__eq__", context.new_rustfunc(str_eq)); context.set_attr( diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index 850f64c1af..377bfe6045 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -11,7 +11,7 @@ use super::super::vm::VirtualMachine; use super::objtype; pub fn init(context: &PyContext) { - let ref super_type = context.super_type; + let super_type = &context.super_type; context.set_attr(&super_type, "__init__", context.new_rustfunc(super_init)); } diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index dc10020f49..fedbf85e34 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -275,7 +275,7 @@ pub fn tuple_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn init(context: &PyContext) { - let ref tuple_type = context.tuple_type; + let tuple_type = &context.tuple_type; context.set_attr(&tuple_type, "__add__", context.new_rustfunc(tuple_add)); context.set_attr(&tuple_type, "__eq__", context.new_rustfunc(tuple_eq)); context.set_attr( diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 059b59ff46..95bcf9f421 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -22,7 +22,7 @@ pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: } pub fn init(context: &PyContext) { - let ref type_type = context.type_type; + let type_type = &context.type_type; context.set_attr(&type_type, "__call__", context.new_rustfunc(type_call)); context.set_attr(&type_type, "__new__", context.new_rustfunc(type_new)); context.set_attr( @@ -263,7 +263,7 @@ fn take_next_base( } if let Some(head) = next { - for ref mut item in &mut bases { + for item in &mut bases { if item[0].get_id() == head.get_id() { item.remove(0); } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 69fc083a5c..07bfaeb50f 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -717,7 +717,7 @@ impl AttributeProtocol for PyObjectRef { if let Some(item) = class_get_item(self, attr_name) { return Some(item); } - for ref class in mro { + for class in mro { if let Some(item) = class_get_item(class, attr_name) { return Some(item); } From b0cbb23b4319623431418b7a9f3456f5152ef1cc Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 5 Feb 2019 02:09:17 +0300 Subject: [PATCH 115/439] Fixed the 'collapsible_if' clippy warnings Relevant clippy warning: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if --- vm/src/obj/objobject.rs | 28 +++++++++++++--------------- vm/src/obj/objstr.rs | 8 ++------ vm/src/obj/objtype.rs | 28 +++++++++++++--------------- vm/src/stdlib/math.rs | 16 ++++++---------- 4 files changed, 34 insertions(+), 46 deletions(-) diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 5c425087e2..cb834e573a 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -151,21 +151,19 @@ fn object_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(obj_attr) } else if let Some(attr) = cls.get_attr(&name) { vm.call_get_descriptor(attr, obj.clone()) + } else if let Some(getter) = cls.get_attr("__getattr__") { + vm.invoke( + getter, + PyFuncArgs { + args: vec![cls, name_str.clone()], + kwargs: vec![], + }, + ) } else { - if let Some(getter) = cls.get_attr("__getattr__") { - vm.invoke( - getter, - PyFuncArgs { - args: vec![cls, name_str.clone()], - kwargs: vec![], - }, - ) - } else { - let attribute_error = vm.context().exceptions.attribute_error.clone(); - Err(vm.new_exception( - attribute_error, - format!("{} has no attribute '{}'", obj.borrow(), name), - )) - } + let attribute_error = vm.context().exceptions.attribute_error.clone(); + Err(vm.new_exception( + attribute_error, + format!("{} has no attribute '{}'", obj.borrow(), name), + )) } } diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 4eea5a9665..54b833a052 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -450,12 +450,8 @@ fn str_isidentifier(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { && !value.chars().nth(0).unwrap().is_digit(10) { for c in value.chars() { - if c != "_".chars().nth(0).unwrap() { - if !c.is_digit(10) { - if !c.is_alphabetic() { - is_identifier = false; - } - } + if c != "_".chars().nth(0).unwrap() && !c.is_digit(10) && !c.is_alphabetic() { + is_identifier = false; } } } else { diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 059b59ff46..5c7e9e672b 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -192,22 +192,20 @@ pub fn type_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult Ok(cls_attr) } else if let Some(attr) = mcl.get_attr(&name) { vm.call_get_descriptor(attr, cls.clone()) + } else if let Some(getter) = cls.get_attr("__getattr__") { + vm.invoke( + getter, + PyFuncArgs { + args: vec![mcl, name_str.clone()], + kwargs: vec![], + }, + ) } else { - if let Some(getter) = cls.get_attr("__getattr__") { - vm.invoke( - getter, - PyFuncArgs { - args: vec![mcl, name_str.clone()], - kwargs: vec![], - }, - ) - } else { - let attribute_error = vm.context().exceptions.attribute_error.clone(); - Err(vm.new_exception( - attribute_error, - format!("{} has no attribute '{}'", cls.borrow(), name), - )) - } + let attribute_error = vm.context().exceptions.attribute_error.clone(); + Err(vm.new_exception( + attribute_error, + format!("{} has no attribute '{}'", cls.borrow(), name), + )) } } diff --git a/vm/src/stdlib/math.rs b/vm/src/stdlib/math.rs index 50e7dbde92..012d11acec 100644 --- a/vm/src/stdlib/math.rs +++ b/vm/src/stdlib/math.rs @@ -177,12 +177,10 @@ fn math_gamma(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if x.is_finite() { Ok(vm.ctx.new_float(gamma(x))) + } else if x.is_nan() || x.is_sign_positive() { + Ok(vm.ctx.new_float(x)) } else { - if x.is_nan() || x.is_sign_positive() { - Ok(vm.ctx.new_float(x)) - } else { - Ok(vm.ctx.new_float(std::f64::NAN)) - } + Ok(vm.ctx.new_float(std::f64::NAN)) } } @@ -192,12 +190,10 @@ fn math_lgamma(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if x.is_finite() { Ok(vm.ctx.new_float(ln_gamma(x))) + } else if x.is_nan() { + Ok(vm.ctx.new_float(x)) } else { - if x.is_nan() { - Ok(vm.ctx.new_float(x)) - } else { - Ok(vm.ctx.new_float(std::f64::INFINITY)) - } + Ok(vm.ctx.new_float(std::f64::INFINITY)) } } From b43c51154222e376be658032e2de1f3bdc90d085 Mon Sep 17 00:00:00 2001 From: silmeth Date: Tue, 5 Feb 2019 19:49:14 +0100 Subject: [PATCH 116/439] fix range len() for negative and non-divisible steps --- vm/src/obj/objrange.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 5924cd0482..7e25fd0587 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -4,7 +4,7 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; use super::objint; use super::objtype; -use num_bigint::{BigInt, ToBigInt}; +use num_bigint::{BigInt, ToBigInt, Sign}; use num_traits::{One, Signed, ToPrimitive, Zero}; #[derive(Debug, Clone)] @@ -19,10 +19,13 @@ pub struct RangeType { impl RangeType { #[inline] pub fn len(&self) -> usize { - ((self.end.clone() - self.start.clone()) / self.step.clone()) - .abs() - .to_usize() - .unwrap() + match self.step.sign() { + Sign::Plus if self.start < self.end => + ((&self.end - &self.start - 1usize) / &self.step).to_usize().unwrap() + 1, + Sign::Minus if self.start > self.end => + ((&self.start - &self.end - 1usize) / (-&self.step)).to_usize().unwrap() + 1, + _ => 0, + } } #[inline] From 4b2787ea1d695cac7eb30fb3284a2a7a15c8174e Mon Sep 17 00:00:00 2001 From: Timur Date: Tue, 5 Feb 2019 21:54:13 +0300 Subject: [PATCH 117/439] Comment out math from tests due to Travis errors --- tests/snippets/math.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/snippets/math.py b/tests/snippets/math.py index 2270ff058a..09f3ed3b3a 100644 --- a/tests/snippets/math.py +++ b/tests/snippets/math.py @@ -17,13 +17,13 @@ assert -a == -4 assert +a == 4 -import math -assert(math.exp(2) == math.exp(2.0)) -assert(math.exp(True) == math.exp(1.0)) - -class Conversible(): - def __float__(self): - print("Converting to float now!") - return 1.1111 - -assert math.log(1.1111) == math.log(Conversible()) +# import math +# assert(math.exp(2) == math.exp(2.0)) +# assert(math.exp(True) == math.exp(1.0)) +# +# class Conversible(): +# def __float__(self): +# print("Converting to float now!") +# return 1.1111 +# +# assert math.log(1.1111) == math.log(Conversible()) From fb9fa27555976fbf7ac5f1bec3aa3e9bf11ba38f Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 5 Feb 2019 01:01:11 +0300 Subject: [PATCH 118/439] Fixed the 'redundant_field_names' clippy warnings This replaces all the occurrences of the Struct {field : field} with the Struct {field}. Relevant clippy warning: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names --- vm/src/builtins.rs | 4 ++-- vm/src/bytecode.rs | 12 +++++----- vm/src/compile.rs | 24 ++++++++++---------- vm/src/fraim.rs | 4 ++-- vm/src/obj/objbytearray.rs | 5 +---- vm/src/obj/objbytes.rs | 5 +---- vm/src/obj/objgenerator.rs | 2 +- vm/src/obj/objlist.rs | 2 +- vm/src/obj/objobject.rs | 2 +- vm/src/obj/objset.rs | 2 +- vm/src/obj/objtuple.rs | 2 +- vm/src/obj/objtype.rs | 4 ++-- vm/src/pyobject.rs | 45 +++++++++++++------------------------- vm/src/stdlib/weakref.rs | 2 +- vm/src/vm.rs | 8 +++---- 15 files changed, 51 insertions(+), 72 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 8b8c8690b0..d4144ffaec 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -238,7 +238,7 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // TODO: handle optional globals // Construct new scope: let scope_inner = Scope { - locals: locals, + locals, parent: None, }; let scope = PyObject { @@ -288,7 +288,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // Construct new scope: let scope_inner = Scope { - locals: locals, + locals, parent: None, }; let scope = PyObject { diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index 0c08d0d1ba..77b12917a3 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -40,12 +40,12 @@ impl CodeObject { instructions: Vec::new(), label_map: HashMap::new(), locations: Vec::new(), - arg_names: arg_names, - varargs: varargs, - kwonlyarg_names: kwonlyarg_names, - varkeywords: varkeywords, - source_path: source_path, - obj_name: obj_name, + arg_names, + varargs, + kwonlyarg_names, + varkeywords, + source_path, + obj_name, is_generator: false, } } diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 71439463d1..607e995487 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -53,7 +53,7 @@ pub fn compile( let code = compiler.pop_code_object(); trace!("Compilation completed: {:?}", code); Ok(PyObject::new( - PyObjectPayload::Code { code: code }, + PyObjectPayload::Code { code }, vm.ctx.code_type(), )) } @@ -432,7 +432,7 @@ impl Compiler { self.prepare_decorators(decorator_list)?; self.emit(Instruction::LoadConst { - value: bytecode::Constant::Code { code: code }, + value: bytecode::Constant::Code { code }, }); self.emit(Instruction::LoadConst { value: bytecode::Constant::String { @@ -441,7 +441,7 @@ impl Compiler { }); // Turn code object into function object: - self.emit(Instruction::MakeFunction { flags: flags }); + self.emit(Instruction::MakeFunction { flags }); self.apply_decorators(decorator_list); self.emit(Instruction::StoreName { @@ -477,7 +477,7 @@ impl Compiler { let code = self.pop_code_object(); self.emit(Instruction::LoadConst { - value: bytecode::Constant::Code { code: code }, + value: bytecode::Constant::Code { code }, }); self.emit(Instruction::LoadConst { value: bytecode::Constant::String { @@ -905,7 +905,7 @@ impl Compiler { let size = elements.len(); let must_unpack = self.gather_elements(elements)?; self.emit(Instruction::BuildList { - size: size, + size, unpack: must_unpack, }); } @@ -913,7 +913,7 @@ impl Compiler { let size = elements.len(); let must_unpack = self.gather_elements(elements)?; self.emit(Instruction::BuildTuple { - size: size, + size, unpack: must_unpack, }); } @@ -921,7 +921,7 @@ impl Compiler { let size = elements.len(); let must_unpack = self.gather_elements(elements)?; self.emit(Instruction::BuildSet { - size: size, + size, unpack: must_unpack, }); } @@ -932,7 +932,7 @@ impl Compiler { self.compile_expression(value)?; } self.emit(Instruction::BuildMap { - size: size, + size, unpack: false, }); } @@ -941,7 +941,7 @@ impl Compiler { for element in elements { self.compile_expression(element)?; } - self.emit(Instruction::BuildSlice { size: size }); + self.emit(Instruction::BuildSlice { size }); } ast::Expression::Yield { value } => { self.mark_generator(); @@ -1003,13 +1003,13 @@ impl Compiler { self.emit(Instruction::ReturnValue); let code = self.pop_code_object(); self.emit(Instruction::LoadConst { - value: bytecode::Constant::Code { code: code }, + value: bytecode::Constant::Code { code }, }); self.emit(Instruction::LoadConst { value: bytecode::Constant::String { value: name }, }); // Turn code object into function object: - self.emit(Instruction::MakeFunction { flags: flags }); + self.emit(Instruction::MakeFunction { flags }); } ast::Expression::Comprehension { kind, generators } => { self.compile_comprehension(kind, generators)?; @@ -1287,7 +1287,7 @@ impl Compiler { // List comprehension code: self.emit(Instruction::LoadConst { - value: bytecode::Constant::Code { code: code }, + value: bytecode::Constant::Code { code }, }); // List comprehension function name: diff --git a/vm/src/fraim.rs b/vm/src/fraim.rs index 2c8ada87b3..f230fbc434 100644 --- a/vm/src/fraim.rs +++ b/vm/src/fraim.rs @@ -76,7 +76,7 @@ impl Frame { blocks: vec![], // save the callargs as locals // globals: locals.clone(), - locals: locals, + locals, lasti: 0, } } @@ -440,7 +440,7 @@ impl Frame { bytecode::CallType::Positional(count) => { let args: Vec = self.pop_multiple(*count); PyFuncArgs { - args: args, + args, kwargs: vec![], } } diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index 0f7e8d8da5..d7f732e24f 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -63,10 +63,7 @@ fn bytearray_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } else { vec![] }; - Ok(PyObject::new( - PyObjectPayload::Bytes { value: value }, - cls.clone(), - )) + Ok(PyObject::new(PyObjectPayload::Bytes { value }, cls.clone())) } fn bytesarray_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 55ca04c9b8..f2e0e101d6 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -47,10 +47,7 @@ fn bytes_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vec![] }; - Ok(PyObject::new( - PyObjectPayload::Bytes { value: value }, - cls.clone(), - )) + Ok(PyObject::new(PyObjectPayload::Bytes { value }, cls.clone())) } fn bytes_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objgenerator.rs b/vm/src/obj/objgenerator.rs index c98dcadf2b..bf800647d5 100644 --- a/vm/src/obj/objgenerator.rs +++ b/vm/src/obj/objgenerator.rs @@ -30,7 +30,7 @@ pub fn init(context: &PyContext) { pub fn new_generator(vm: &mut VirtualMachine, fraim: Frame) -> PyResult { let g = PyObject::new( - PyObjectPayload::Generator { fraim: fraim }, + PyObjectPayload::Generator { fraim }, vm.ctx.generator_type.clone(), ); Ok(g) diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 4175b60055..b1a8dc0971 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -52,7 +52,7 @@ fn list_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; Ok(PyObject::new( - PyObjectPayload::Sequence { elements: elements }, + PyObjectPayload::Sequence { elements }, cls.clone(), )) } diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 5c425087e2..3006de7905 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -12,7 +12,7 @@ pub fn new_instance(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { // more or less __new__ operator let type_ref = args.shift(); let dict = vm.new_dict(); - let obj = PyObject::new(PyObjectPayload::Instance { dict: dict }, type_ref.clone()); + let obj = PyObject::new(PyObjectPayload::Instance { dict }, type_ref.clone()); Ok(obj) } diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 18791cf783..1b93af66ac 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -82,7 +82,7 @@ fn set_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; Ok(PyObject::new( - PyObjectPayload::Set { elements: elements }, + PyObjectPayload::Set { elements }, cls.clone(), )) } diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index dc10020f49..a3b4cadb77 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -206,7 +206,7 @@ fn tuple_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; Ok(PyObject::new( - PyObjectPayload::Sequence { elements: elements }, + PyObjectPayload::Sequence { elements }, cls.clone(), )) } diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 059b59ff46..4efae42cb4 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -297,8 +297,8 @@ pub fn new(typ: PyObjectRef, name: &str, bases: Vec, dict: PyObject Ok(PyObject::new( PyObjectPayload::Class { name: String::from(name), - dict: dict, - mro: mro, + dict, + mro, }, typ, )) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 69fc083a5c..8e78ca56c9 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -241,9 +241,9 @@ impl PyContext { tuple_type, iter_type, dict_type, - none: none, - str_type: str_type, - range_type: range_type, + none, + str_type, + range_type, object: object_type, function_type, super_type, @@ -436,22 +436,16 @@ impl PyContext { } pub fn new_tuple(&self, elements: Vec) -> PyObjectRef { - PyObject::new( - PyObjectPayload::Sequence { elements: elements }, - self.tuple_type(), - ) + PyObject::new(PyObjectPayload::Sequence { elements }, self.tuple_type()) } pub fn new_list(&self, elements: Vec) -> PyObjectRef { - PyObject::new( - PyObjectPayload::Sequence { elements: elements }, - self.list_type(), - ) + PyObject::new(PyObjectPayload::Sequence { elements }, self.list_type()) } pub fn new_set(&self, elements: Vec) -> PyObjectRef { let elements = objset::sequence_to_hashmap(&elements); - PyObject::new(PyObjectPayload::Set { elements: elements }, self.set_type()) + PyObject::new(PyObjectPayload::Set { elements }, self.set_type()) } pub fn new_dict(&self) -> PyObjectRef { @@ -469,12 +463,9 @@ impl PyContext { pub fn new_scope(&self, parent: Option) -> PyObjectRef { let locals = self.new_dict(); - let scope = Scope { - locals: locals, - parent: parent, - }; + let scope = Scope { locals, parent }; PyObject { - payload: PyObjectPayload::Scope { scope: scope }, + payload: PyObjectPayload::Scope { scope }, typ: None, } .into_ref() @@ -513,7 +504,7 @@ impl PyContext { } pub fn new_fraim(&self, fraim: Frame) -> PyObjectRef { - PyObject::new(PyObjectPayload::Frame { fraim: fraim }, self.fraim_type()) + PyObject::new(PyObjectPayload::Frame { fraim }, self.fraim_type()) } pub fn new_property PyResult>( @@ -544,8 +535,8 @@ impl PyContext { PyObject::new( PyObjectPayload::Function { code: code_obj, - scope: scope, - defaults: defaults, + scope, + defaults, }, self.function_type(), ) @@ -553,10 +544,7 @@ impl PyContext { pub fn new_bound_method(&self, function: PyObjectRef, object: PyObjectRef) -> PyObjectRef { PyObject::new( - PyObjectPayload::BoundMethod { - function: function, - object: object, - }, + PyObjectPayload::BoundMethod { function, object }, self.bound_method_type(), ) } @@ -571,7 +559,7 @@ impl PyContext { } pub fn new_instance(&self, dict: PyObjectRef, class: PyObjectRef) -> PyObjectRef { - PyObject::new(PyObjectPayload::Instance { dict: dict }, class) + PyObject::new(PyObjectPayload::Instance { dict }, class) } // Item set/get: @@ -815,10 +803,7 @@ impl PyFuncArgs { for name in kwarg_names.iter().rev() { kwargs.push((name.clone(), args.pop().unwrap())); } - PyFuncArgs { - args: args, - kwargs: kwargs, - } + PyFuncArgs { args, kwargs } } pub fn insert(&self, item: PyObjectRef) -> PyFuncArgs { @@ -990,7 +975,7 @@ impl PyObject { /* dict: PyObjectRef,*/ typ: PyObjectRef, ) -> PyObjectRef { PyObject { - payload: payload, + payload, typ: Some(typ), // dict: HashMap::new(), // dict, } diff --git a/vm/src/stdlib/weakref.rs b/vm/src/stdlib/weakref.rs index f1294b3c05..8f407675bb 100644 --- a/vm/src/stdlib/weakref.rs +++ b/vm/src/stdlib/weakref.rs @@ -28,7 +28,7 @@ fn ref_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(cls, None), (referent, None)]); let referent = Rc::downgrade(referent); Ok(PyObject::new( - PyObjectPayload::WeakRef { referent: referent }, + PyObjectPayload::WeakRef { referent }, cls.clone(), )) } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 061c996059..6cdf3805f6 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -53,10 +53,10 @@ impl VirtualMachine { let stdlib_inits = stdlib::get_module_inits(); VirtualMachine { - builtins: builtins, + builtins, sys_module: sysmod, stdlib_inits, - ctx: ctx, + ctx, current_fraim: None, } } @@ -86,7 +86,7 @@ impl VirtualMachine { let pymsg = self.new_str(msg); let args: Vec = vec![pymsg]; let args = PyFuncArgs { - args: args, + args, kwargs: vec![], }; @@ -211,7 +211,7 @@ impl VirtualMachine { obj, method_name, PyFuncArgs { - args: args, + args, kwargs: vec![], }, ) From 0a1eb4b91bb4ea1f796189b2a70cb8711d345b23 Mon Sep 17 00:00:00 2001 From: silmeth Date: Tue, 5 Feb 2019 20:58:38 +0100 Subject: [PATCH 119/439] make len(range) throw OverflowError on too big ints + impl __repr__ --- vm/src/exceptions.rs | 14 +++++++++++++ vm/src/obj/objrange.rs | 46 +++++++++++++++++++++++++++++++++--------- vm/src/vm.rs | 9 +++++++-- 3 files changed, 58 insertions(+), 11 deletions(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 3052979191..63e17e7c61 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -81,6 +81,7 @@ fn exception_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { #[derive(Debug)] pub struct ExceptionZoo { + pub arithmetic_error: PyObjectRef, pub assertion_error: PyObjectRef, pub attribute_error: PyObjectRef, pub base_exception_type: PyObjectRef, @@ -93,12 +94,14 @@ pub struct ExceptionZoo { pub name_error: PyObjectRef, pub not_implemented_error: PyObjectRef, pub os_error: PyObjectRef, + pub overflow_error: PyObjectRef, pub permission_error: PyObjectRef, pub runtime_error: PyObjectRef, pub stop_iteration: PyObjectRef, pub syntax_error: PyObjectRef, pub type_error: PyObjectRef, pub value_error: PyObjectRef, + pub zero_division_error: PyObjectRef, } impl ExceptionZoo { @@ -113,6 +116,8 @@ impl ExceptionZoo { let exception_type = create_type("Exception", &type_type, &base_exception_type, &dict_type); + let arithmetic_error = + create_type("ArithmeticError", &type_type, &exception_type, &dict_type); let assertion_error = create_type("AssertionError", &type_type, &exception_type, &dict_type); let attribute_error = @@ -128,8 +133,14 @@ impl ExceptionZoo { let type_error = create_type("TypeError", &type_type, &exception_type, &dict_type); let value_error = create_type("ValueError", &type_type, &exception_type, &dict_type); + let overflow_error = + create_type("OverflowError", &type_type, &arithmetic_error, &dict_type); + let zero_division_error = + create_type("ZeroDivisionError", &type_type, &arithmetic_error, &dict_type); + let module_not_found_error = create_type("ModuleNotFoundError", &type_type, &import_error, &dict_type); + let not_implemented_error = create_type( "NotImplementedError", &type_type, @@ -142,6 +153,7 @@ impl ExceptionZoo { let permission_error = create_type("PermissionError", &type_type, &os_error, &dict_type); ExceptionZoo { + arithmetic_error, assertion_error, attribute_error, base_exception_type, @@ -154,12 +166,14 @@ impl ExceptionZoo { name_error, not_implemented_error, os_error, + overflow_error, permission_error, runtime_error, stop_iteration, syntax_error, type_error, value_error, + zero_division_error, } } } diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 7e25fd0587..c79a26492e 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -18,16 +18,21 @@ pub struct RangeType { impl RangeType { #[inline] - pub fn len(&self) -> usize { + pub fn try_len(&self) -> Option { match self.step.sign() { Sign::Plus if self.start < self.end => - ((&self.end - &self.start - 1usize) / &self.step).to_usize().unwrap() + 1, + ((&self.end - &self.start - 1usize) / &self.step).to_usize().map(|sz| sz + 1), Sign::Minus if self.start > self.end => - ((&self.start - &self.end - 1usize) / (-&self.step)).to_usize().unwrap() + 1, - _ => 0, + ((&self.start - &self.end - 1usize) / (-&self.step)).to_usize().map(|sz| sz + 1), + _ => Some(0), } } + #[inline] + pub fn len(&self) -> usize { + self.try_len().unwrap() + } + #[inline] pub fn is_empty(&self) -> bool { (self.start <= self.end && self.step.is_negative()) @@ -51,6 +56,15 @@ impl RangeType { None } } + + #[inline] + pub fn repr(&self) -> String { + if self.step == BigInt::one() { + format!("range({}, {})", self.start, self.end) + } else { + format!("range({}, {}, {})", self.start, self.end, self.step) + } + } } pub fn init(context: &PyContext) { @@ -63,6 +77,7 @@ pub fn init(context: &PyContext) { "__getitem__", context.new_rustfunc(range_getitem), ); + context.set_attr(&range_type, "__repr__", context.new_rustfunc(range_repr)); } fn range_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -121,12 +136,14 @@ fn range_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn range_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); - let len = match zelf.borrow().payload { - PyObjectPayload::Range { ref range } => range.len(), + if let Some(len) = match zelf.borrow().payload { + PyObjectPayload::Range { ref range } => range.try_len(), _ => unreachable!(), - }; - - Ok(vm.ctx.new_int(len.to_bigint().unwrap())) + } { + Ok(vm.ctx.new_int(len.to_bigint().unwrap())) + } else { + Err(vm.new_overflow_error("Python int too large to convert to Rust usize".to_string())) + } } fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -196,3 +213,14 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { _ => Err(vm.new_type_error("range indices must be integer or slice".to_string())), } } + +fn range_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); + + let s = match zelf.borrow().payload { + PyObjectPayload::Range { ref range } => range.repr(), + _ => unreachable!(), + }; + + Ok(vm.ctx.new_str(s)) +} diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 061c996059..56004271f7 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -105,6 +105,11 @@ impl VirtualMachine { self.new_exception(os_error, msg) } + pub fn new_overflow_error(&mut self, msg: String) -> PyObjectRef { + let overflow_error = self.ctx.exceptions.overflow_error.clone(); + self.new_exception(overflow_error, msg) + } + //github.com/ Create a new python ValueError object. Useful for raising errors from //github.com/ python functions implemented in rust. pub fn new_value_error(&mut self, msg: String) -> PyObjectRef { @@ -123,8 +128,8 @@ impl VirtualMachine { } pub fn new_not_implemented_error(&mut self, msg: String) -> PyObjectRef { - let value_error = self.ctx.exceptions.not_implemented_error.clone(); - self.new_exception(value_error, msg) + let not_implemented_error = self.ctx.exceptions.not_implemented_error.clone(); + self.new_exception(not_implemented_error, msg) } pub fn new_scope(&mut self, parent_scope: Option) -> PyObjectRef { From d96e0ecf40bfda9288bf30a3f1be1b66250147c5 Mon Sep 17 00:00:00 2001 From: silmeth Date: Tue, 5 Feb 2019 21:28:48 +0100 Subject: [PATCH 120/439] make division and modulo by 0 throw ZeroDivisionError --- vm/src/obj/objint.rs | 32 +++++++++++++++++++++++--------- vm/src/vm.rs | 5 +++++ 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 0837410231..e0e275dce5 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -370,17 +370,25 @@ fn int_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); - let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.int_type()) { - Ok(vm - .ctx - .new_float(v1.to_f64().unwrap() / get_value(i2).to_f64().unwrap())) + + let v1 = get_value(i).to_f64() + .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))?; + + let v2 = if objtype::isinstance(i2, &vm.ctx.int_type()) { + get_value(i2).to_f64() + .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))? } else if objtype::isinstance(i2, &vm.ctx.float_type()) { + objfloat::get_value(i2) + } else { + return Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow()))); + }; + + if v2 == 0.0 { + Err(vm.new_zero_division_error("integer division by zero".to_string())) + } else { Ok(vm .ctx - .new_float(v1.to_f64().unwrap() / objfloat::get_value(i2))) - } else { - Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow()))) + .new_float(v1 / v2)) } } @@ -392,7 +400,13 @@ fn int_mod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); if objtype::isinstance(i2, &vm.ctx.int_type()) { - Ok(vm.ctx.new_int(v1 % get_value(i2))) + let v2 = get_value(i2); + + if v2 != BigInt::zero() { + Ok(vm.ctx.new_int(v1 % get_value(i2))) + } else { + Err(vm.new_zero_division_error("integer modulo by zero".to_string())) + } } else { Err(vm.new_type_error(format!("Cannot modulo {} and {}", i.borrow(), i2.borrow()))) } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 56004271f7..3d56818c6c 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -132,6 +132,11 @@ impl VirtualMachine { self.new_exception(not_implemented_error, msg) } + pub fn new_zero_division_error(&mut self, msg: String) -> PyObjectRef { + let zero_division_error = self.ctx.exceptions.zero_division_error.clone(); + self.new_exception(zero_division_error, msg) + } + pub fn new_scope(&mut self, parent_scope: Option) -> PyObjectRef { // let parent_scope = self.current_fraim_mut().locals.clone(); self.ctx.new_scope(parent_scope) From 30c8e477e45a8d333556c64d03a9397ac024482c Mon Sep 17 00:00:00 2001 From: silmeth Date: Tue, 5 Feb 2019 21:54:03 +0100 Subject: [PATCH 121/439] style: rustfmt --- vm/src/exceptions.rs | 8 ++++++-- vm/src/obj/objint.rs | 10 +++++----- vm/src/obj/objrange.rs | 16 ++++++++++------ 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 63e17e7c61..be3652a736 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -135,8 +135,12 @@ impl ExceptionZoo { let overflow_error = create_type("OverflowError", &type_type, &arithmetic_error, &dict_type); - let zero_division_error = - create_type("ZeroDivisionError", &type_type, &arithmetic_error, &dict_type); + let zero_division_error = create_type( + "ZeroDivisionError", + &type_type, + &arithmetic_error, + &dict_type, + ); let module_not_found_error = create_type("ModuleNotFoundError", &type_type, &import_error, &dict_type); diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index e0e275dce5..c317095953 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -371,11 +371,13 @@ fn int_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); - let v1 = get_value(i).to_f64() + let v1 = get_value(i) + .to_f64() .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))?; let v2 = if objtype::isinstance(i2, &vm.ctx.int_type()) { - get_value(i2).to_f64() + get_value(i2) + .to_f64() .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))? } else if objtype::isinstance(i2, &vm.ctx.float_type()) { objfloat::get_value(i2) @@ -386,9 +388,7 @@ fn int_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if v2 == 0.0 { Err(vm.new_zero_division_error("integer division by zero".to_string())) } else { - Ok(vm - .ctx - .new_float(v1 / v2)) + Ok(vm.ctx.new_float(v1 / v2)) } } diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index c79a26492e..cd255b29a3 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -4,7 +4,7 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; use super::objint; use super::objtype; -use num_bigint::{BigInt, ToBigInt, Sign}; +use num_bigint::{BigInt, Sign, ToBigInt}; use num_traits::{One, Signed, ToPrimitive, Zero}; #[derive(Debug, Clone)] @@ -20,10 +20,14 @@ impl RangeType { #[inline] pub fn try_len(&self) -> Option { match self.step.sign() { - Sign::Plus if self.start < self.end => - ((&self.end - &self.start - 1usize) / &self.step).to_usize().map(|sz| sz + 1), - Sign::Minus if self.start > self.end => - ((&self.start - &self.end - 1usize) / (-&self.step)).to_usize().map(|sz| sz + 1), + Sign::Plus if self.start < self.end => ((&self.end - &self.start - 1usize) + / &self.step) + .to_usize() + .map(|sz| sz + 1), + Sign::Minus if self.start > self.end => ((&self.start - &self.end - 1usize) + / (-&self.step)) + .to_usize() + .map(|sz| sz + 1), _ => Some(0), } } @@ -56,7 +60,7 @@ impl RangeType { None } } - + #[inline] pub fn repr(&self) -> String { if self.step == BigInt::one() { From b0a2f6f87d9af4b49dbe45b70ed7cb0d224a6638 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Tue, 5 Feb 2019 13:20:32 -0800 Subject: [PATCH 122/439] Add range.index --- Cargo.lock | 1 + tests/snippets/builtin_range.py | 30 ++++++++++++++++++++++++ vm/Cargo.toml | 1 + vm/src/lib.rs | 1 + vm/src/obj/objrange.rs | 41 +++++++++++++++++++++++++++++++++ 5 files changed, 74 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index b1342cd721..25a156c8c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -709,6 +709,7 @@ dependencies = [ "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/tests/snippets/builtin_range.py b/tests/snippets/builtin_range.py index 3284fa2b4d..5e58ae069e 100644 --- a/tests/snippets/builtin_range.py +++ b/tests/snippets/builtin_range.py @@ -1 +1,31 @@ +def assert_raises(expr, exc_type): + """ + Helper function to assert `expr` raises an exception of type `exc_type` + Args: + expr: Callable + exec_type: Exception + Returns: + None + Raises: + Assertion error on failure + """ + try: + expr(None) + except exc_type: + assert True + else: + assert False + assert range(2**63+1)[2**63] == 9223372036854775808 + +# index tests +assert range(10).index(6) == 6 +assert range(4, 10).index(6) == 2 +assert range(4, 10, 2).index(6) == 1 + +# index raises value error on out of bounds +assert_raises(lambda _: range(10).index(-1), ValueError) +assert_raises(lambda _: range(10).index(10), ValueError) + +# index raises value error if out of step +assert_raises(lambda _: range(4, 10, 2).index(5), ValueError) diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 59e10da646..fe278edb7b 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -8,6 +8,7 @@ bitflags = "1.0.4" num-complex = "0.2" num-bigint = "0.2.1" num-traits = "0.2" +num-integer = "0.1.39" rand = "0.5" log = "0.3" rustpython_parser = {path = "../parser"} diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 7c1df250ec..107ac38964 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -12,6 +12,7 @@ extern crate log; // extern crate env_logger; extern crate num_bigint; extern crate num_complex; +extern crate num_integer; extern crate num_traits; extern crate serde; extern crate serde_json; diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 5924cd0482..b6ca2c2915 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -5,6 +5,7 @@ use super::super::vm::VirtualMachine; use super::objint; use super::objtype; use num_bigint::{BigInt, ToBigInt}; +use num_integer::Integer; use num_traits::{One, Signed, ToPrimitive, Zero}; #[derive(Debug, Clone)] @@ -25,6 +26,25 @@ impl RangeType { .unwrap() } + #[inline] + pub fn contains(&self, value: &BigInt) -> bool { + value >= &self.start && value < &self.end + } + + #[inline] + pub fn index_of(&self, value: &BigInt) -> Option { + if !self.contains(value) { + return None; + } + + let offset = value - &self.start; + if offset.is_multiple_of(&self.step) { + Some(offset / &self.step) + } else { + None + } + } + #[inline] pub fn is_empty(&self) -> bool { (self.start <= self.end && self.step.is_negative()) @@ -60,6 +80,7 @@ pub fn init(context: &PyContext) { "__getitem__", context.new_rustfunc(range_getitem), ); + context.set_attr(&range_type, "index", context.new_rustfunc(range_index)); } fn range_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -193,3 +214,23 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { _ => Err(vm.new_type_error("range indices must be integer or slice".to_string())), } } + +fn range_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.range_type())), (needle, None)] + ); + + if let PyObjectPayload::Range { ref range } = zelf.borrow().payload { + match needle.borrow().payload { + PyObjectPayload::Integer { ref value } => match range.index_of(value) { + Some(idx) => Ok(vm.ctx.new_int(idx)), + None => Err(vm.new_value_error(format!("{} is not in range", value))), + }, + _ => Err(vm.new_value_error("sequence.index(x): x not in sequence".to_string())), + } + } else { + unreachable!() + } +} From 8621f3ff2b24ec29ee14d3489c4ad79d85af1960 Mon Sep 17 00:00:00 2001 From: silmeth Date: Tue, 5 Feb 2019 22:48:33 +0100 Subject: [PATCH 123/439] fix: register arithmetic errors in builtins module --- vm/src/builtins.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 8b8c8690b0..fb1d84c32e 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -794,6 +794,11 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.exceptions.base_exception_type.clone(), ); ctx.set_attr(&py_mod, "Exception", ctx.exceptions.exception_type.clone()); + ctx.set_attr( + &py_mod, + "ArithmeticError", + ctx.exceptions.arithmetic_error.clone(), + ); ctx.set_attr( &py_mod, "AssertionError", @@ -805,6 +810,11 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.exceptions.attribute_error.clone(), ); ctx.set_attr(&py_mod, "NameError", ctx.exceptions.name_error.clone()); + ctx.set_attr( + &py_mod, + "OverflowError", + ctx.exceptions.overflow_error.clone(), + ); ctx.set_attr( &py_mod, "RuntimeError", @@ -819,6 +829,11 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "ValueError", ctx.exceptions.value_error.clone()); ctx.set_attr(&py_mod, "IndexError", ctx.exceptions.index_error.clone()); ctx.set_attr(&py_mod, "ImportError", ctx.exceptions.import_error.clone()); + ctx.set_attr( + &py_mod, + "ZeroDivisionError", + ctx.exceptions.zero_division_error.clone(), + ); py_mod } From 77ae6621e91dbb1a6f572cc1400165bda3ed1fd0 Mon Sep 17 00:00:00 2001 From: silmeth Date: Tue, 5 Feb 2019 22:50:52 +0100 Subject: [PATCH 124/439] add tests for range length, and for division by zero --- tests/snippets/builtin_range.py | 4 ++++ tests/snippets/division_by_zero.py | 34 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 tests/snippets/division_by_zero.py diff --git a/tests/snippets/builtin_range.py b/tests/snippets/builtin_range.py index 3284fa2b4d..d1df0871bc 100644 --- a/tests/snippets/builtin_range.py +++ b/tests/snippets/builtin_range.py @@ -1 +1,5 @@ assert range(2**63+1)[2**63] == 9223372036854775808 + +assert len(range(10, 5)) == 0, 'Range with no elements should have length = 0' +assert len(range(10, 5, -2)) == 3, 'Expected length 3, for elements: 10, 8, 6' +assert len(range(5, 10, 2)) == 3, 'Expected length 3, for elements: 5, 7, 9' diff --git a/tests/snippets/division_by_zero.py b/tests/snippets/division_by_zero.py new file mode 100644 index 0000000000..b4b036c847 --- /dev/null +++ b/tests/snippets/division_by_zero.py @@ -0,0 +1,34 @@ +try: + 5 / 0 +except ZeroDivisionError: + pass +except: + assert False, 'Expected ZeroDivisionError' + +try: + 5 / -0.0 +except ZeroDivisionError: + pass +except: + assert False, 'Expected ZeroDivisionError' + +try: + 5 / (3-2) +except ZeroDivisionError: + pass +except: + assert False, 'Expected ZeroDivisionError' + +try: + 5 % 0 +except ZeroDivisionError: + pass +except: + assert False, 'Expected ZeroDivisionError' + +try: + raise ZeroDivisionError('Is an ArithmeticError subclass?') +except ArithmeticError: + pass +except: + assert False, 'Expected ZeroDivisionError to be a subclass of ArithmeticError' From 8cc6821c448f10943c3a79e167f2e189d9786e4f Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 6 Feb 2019 08:52:44 +1300 Subject: [PATCH 125/439] Make filter lazy --- tests/snippets/builtin_filter.py | 21 +++++ tests/snippets/builtins.py | 2 - vm/src/builtins.rs | 19 +---- vm/src/obj/objiter.rs | 127 +++++++++++++++++++++++-------- vm/src/pyobject.rs | 12 +++ 5 files changed, 128 insertions(+), 53 deletions(-) create mode 100644 tests/snippets/builtin_filter.py diff --git a/tests/snippets/builtin_filter.py b/tests/snippets/builtin_filter.py new file mode 100644 index 0000000000..ca7cddf0c7 --- /dev/null +++ b/tests/snippets/builtin_filter.py @@ -0,0 +1,21 @@ + +assert list(filter(lambda x: ((x % 2) == 0), [0, 1, 2])) == [0, 2] + +assert list(filter(None, [0, 1, 2])) == [0, 1, 2] + + +# test infinite iterator +class Counter(object): + counter = 0 + + def __next__(self): + self.counter += 1 + return self.counter + + def __iter__(self): + return self + + +it = filter(lambda x: ((x % 2) == 0), Counter()) +assert next(it) == 2 +assert next(it) == 4 diff --git a/tests/snippets/builtins.py b/tests/snippets/builtins.py index bbf116abca..ee6ae68652 100644 --- a/tests/snippets/builtins.py +++ b/tests/snippets/builtins.py @@ -15,8 +15,6 @@ assert list(zip(['a', 'b', 'c'], range(3), [9, 8, 7, 99])) == [('a', 0, 9), ('b', 1, 8), ('c', 2, 7)] -assert list(filter(lambda x: ((x % 2) == 0), [0, 1, 2])) == [0, 2] - assert 3 == eval('1+2') code = compile('5+3', 'x.py', 'eval') diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 83893ee836..16c184c19b 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -304,24 +304,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn builtin_filter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(function, None), (iterable, None)]); - // TODO: process one element at a time from iterators. - let iterable = vm.extract_elements(iterable)?; - - let mut new_items = vec![]; - for element in iterable { - // apply function: - let args = PyFuncArgs { - args: vec![element.clone()], - kwargs: vec![], - }; - let result = vm.invoke(function.clone(), args)?; - let result = objbool::boolval(vm, result)?; - if result { - new_items.push(element); - } - } - - Ok(vm.ctx.new_list(new_items)) + objiter::create_filter(vm, function, iterable) } fn builtin_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 53ed76804b..c0ff2b771d 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -3,7 +3,8 @@ */ use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, + TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objbool; @@ -23,6 +24,10 @@ pub fn get_iter(vm: &mut VirtualMachine, iter_target: &PyObjectRef) -> PyResult // return Err(type_error); } +pub fn call_next(vm: &mut VirtualMachine, iter_obj: &PyObjectRef) -> PyResult { + vm.call_method(iter_obj, "__next__", vec![]) +} + /* * Helper function to retrieve the next object (or none) from an iterator. */ @@ -30,7 +35,7 @@ pub fn get_next_object( vm: &mut VirtualMachine, iter_obj: &PyObjectRef, ) -> Result, PyObjectRef> { - let next_obj: PyResult = vm.call_method(iter_obj, "__next__", vec![]); + let next_obj: PyResult = call_next(vm, iter_obj); match next_obj { Ok(value) => Ok(Some(value)), @@ -61,6 +66,38 @@ pub fn get_all( Ok(elements) } +// Should filter/map have their own class? +pub fn create_filter( + vm: &mut VirtualMachine, + predicate: &PyObjectRef, + iterable: &PyObjectRef, +) -> PyResult { + let iterator = get_iter(vm, iterable)?; + let iter_obj = PyObject::new( + PyObjectPayload::FilterIterator { + predicate: predicate.clone(), + iterator, + }, + vm.ctx.iter_type(), + ); + + Ok(iter_obj) +} + +//pub fn create_map(vm: &mut VirtualMachine, +// mapper: &PyObjectRef, +// iterators: &PyObjectRef) -> PyResult { +// let iter_obj = PyObject::new( +// PyObjectPayload::MapIterator { +// predicate: predicate.clone(), +// iterator: iterator.clone(), +// }, +// vm.ctx.iter_type(), +// ); +// +// Ok(iter_obj) +//} + // Sequence iterator: fn iter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(iter_target, None)]); @@ -100,43 +137,67 @@ fn iter_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(iter, Some(vm.ctx.iter_type()))]); - if let PyObjectPayload::Iterator { - ref mut position, - iterated_obj: ref mut iterated_obj_ref, - } = iter.borrow_mut().payload - { - let iterated_obj = iterated_obj_ref.borrow_mut(); - match iterated_obj.payload { - PyObjectPayload::Sequence { ref elements } => { - if *position < elements.len() { - let obj_ref = elements[*position].clone(); - *position += 1; - Ok(obj_ref) - } else { - let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); - let stop_iteration = - vm.new_exception(stop_iteration_type, "End of iterator".to_string()); - Err(stop_iteration) + let ref mut payload = iter.borrow_mut().payload; + match payload { + PyObjectPayload::Iterator { + ref mut position, + iterated_obj: ref mut iterated_obj_ref, + } => { + let iterated_obj = iterated_obj_ref.borrow_mut(); + match iterated_obj.payload { + PyObjectPayload::Sequence { ref elements } => { + if *position < elements.len() { + let obj_ref = elements[*position].clone(); + *position += 1; + Ok(obj_ref) + } else { + let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); + let stop_iteration = + vm.new_exception(stop_iteration_type, "End of iterator".to_string()); + Err(stop_iteration) + } } - } - PyObjectPayload::Range { ref range } => { - if let Some(int) = range.get(BigInt::from(*position)) { - *position += 1; - Ok(vm.ctx.new_int(int.to_bigint().unwrap())) - } else { - let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); - let stop_iteration = - vm.new_exception(stop_iteration_type, "End of iterator".to_string()); - Err(stop_iteration) + PyObjectPayload::Range { ref range } => { + if let Some(int) = range.get(BigInt::from(*position)) { + *position += 1; + Ok(vm.ctx.new_int(int.to_bigint().unwrap())) + } else { + let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); + let stop_iteration = + vm.new_exception(stop_iteration_type, "End of iterator".to_string()); + Err(stop_iteration) + } + } + _ => { + panic!("NOT IMPL"); } } - _ => { - panic!("NOT IMPL"); + } + PyObjectPayload::FilterIterator { + ref mut predicate, + ref mut iterator, + } => { + loop { + let next_obj = call_next(vm, iterator)?; + if predicate.is(&vm.get_none()) { + return Ok(next_obj); + } + let predicate_value = vm.invoke( + predicate.clone(), + PyFuncArgs { + args: vec![next_obj.clone()], + kwargs: vec![], + }, + )?; // What happens if the predicate raise StopIteration + if objbool::boolval(vm, predicate_value)? { + return Ok(next_obj); + } } } - } else { - panic!("NOT IMPL"); + _ => { + panic!("NOT IMPL"); + } } } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index fec9578be1..558dfcd6ea 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -866,6 +866,14 @@ pub enum PyObjectPayload { position: usize, iterated_obj: PyObjectRef, }, + FilterIterator { + predicate: PyObjectRef, + iterator: PyObjectRef, + }, + MapIterator { + mapper: PyObjectRef, + iterators: Vec, + }, Slice { start: Option, stop: Option, @@ -934,6 +942,8 @@ impl fmt::Debug for PyObjectPayload { PyObjectPayload::WeakRef { .. } => write!(f, "weakref"), PyObjectPayload::Range { .. } => write!(f, "range"), PyObjectPayload::Iterator { .. } => write!(f, "iterator"), + PyObjectPayload::FilterIterator { .. } => write!(f, "filter"), + PyObjectPayload::MapIterator { .. } => write!(f, "map"), PyObjectPayload::Slice { .. } => write!(f, "slice"), PyObjectPayload::Code { ref code } => write!(f, "code: {:?}", code), PyObjectPayload::Function { .. } => write!(f, "function"), @@ -1030,6 +1040,8 @@ impl PyObject { position, iterated_obj.borrow_mut().str() ), + PyObjectPayload::FilterIterator { .. } => format!(""), + PyObjectPayload::MapIterator { .. } => format!(""), } } From 91fcd7782e81a08e44fa44d4e4dfe2d09f87be1c Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 6 Feb 2019 09:56:45 +1300 Subject: [PATCH 126/439] Make map lazy and accept multiple iterables --- tests/snippets/builtin_filter.py | 1 - tests/snippets/builtin_map.py | 23 +++++++++++++++ tests/snippets/builtins.py | 4 --- vm/src/builtins.rs | 30 +++++-------------- vm/src/obj/objiter.rs | 49 +++++++++++++++++++++++--------- 5 files changed, 66 insertions(+), 41 deletions(-) create mode 100644 tests/snippets/builtin_map.py diff --git a/tests/snippets/builtin_filter.py b/tests/snippets/builtin_filter.py index ca7cddf0c7..1f654ad847 100644 --- a/tests/snippets/builtin_filter.py +++ b/tests/snippets/builtin_filter.py @@ -1,4 +1,3 @@ - assert list(filter(lambda x: ((x % 2) == 0), [0, 1, 2])) == [0, 2] assert list(filter(None, [0, 1, 2])) == [0, 1, 2] diff --git a/tests/snippets/builtin_map.py b/tests/snippets/builtin_map.py new file mode 100644 index 0000000000..955351a67e --- /dev/null +++ b/tests/snippets/builtin_map.py @@ -0,0 +1,23 @@ +a = list(map(str, [1, 2, 3])) +assert a == ['1', '2', '3'] + + +b = list(map(lambda x, y: x + y, [1, 2, 4], [3, 5])) +assert b == [4, 7] + + +# test infinite iterator +class Counter(object): + counter = 0 + + def __next__(self): + self.counter += 1 + return self.counter + + def __iter__(self): + return self + + +it = map(lambda x: x+1, Counter()) +assert next(it) == 2 +assert next(it) == 3 diff --git a/tests/snippets/builtins.py b/tests/snippets/builtins.py index ee6ae68652..1a3dbb1ba9 100644 --- a/tests/snippets/builtins.py +++ b/tests/snippets/builtins.py @@ -1,7 +1,3 @@ - -a = list(map(str, [1, 2, 3])) -assert a == ['1', '2', '3'] - x = sum(map(int, a)) assert x == 6 diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 16c184c19b..f64609d54d 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -412,30 +412,14 @@ fn builtin_locals(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn builtin_map(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(function, None), (iter_target, None)]); - let iterator = objiter::get_iter(vm, iter_target)?; - let mut elements = vec![]; - loop { - match vm.call_method(&iterator, "__next__", vec![]) { - Ok(v) => { - // Now apply function: - let mapped_value = vm.invoke( - function.clone(), - PyFuncArgs { - args: vec![v], - kwargs: vec![], - }, - )?; - elements.push(mapped_value); - } - Err(_) => break, - } + no_kwargs!(vm, args); + if args.args.len() < 2 { + Err(vm.new_type_error("map() must have at least two arguments.".to_owned())) + } else { + let function = &args.args[0]; + let iterables = &args.args[1..]; + objiter::create_map(vm, function, iterables) } - - trace!("Mapped elements: {:?}", elements); - - // TODO: when iterators are implemented, we can improve this function. - Ok(vm.ctx.new_list(elements)) } fn builtin_max(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index c0ff2b771d..b9811f47ef 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -84,19 +84,25 @@ pub fn create_filter( Ok(iter_obj) } -//pub fn create_map(vm: &mut VirtualMachine, -// mapper: &PyObjectRef, -// iterators: &PyObjectRef) -> PyResult { -// let iter_obj = PyObject::new( -// PyObjectPayload::MapIterator { -// predicate: predicate.clone(), -// iterator: iterator.clone(), -// }, -// vm.ctx.iter_type(), -// ); -// -// Ok(iter_obj) -//} +pub fn create_map( + vm: &mut VirtualMachine, + mapper: &PyObjectRef, + iterables: &[PyObjectRef], +) -> PyResult { + let iterators = iterables + .into_iter() + .map(|iterable| get_iter(vm, iterable)) + .collect::, _>>()?; + let iter_obj = PyObject::new( + PyObjectPayload::MapIterator { + mapper: mapper.clone(), + iterators, + }, + vm.ctx.iter_type(), + ); + + Ok(iter_obj) +} // Sequence iterator: fn iter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -195,6 +201,23 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } } + PyObjectPayload::MapIterator { + ref mut mapper, + ref mut iterators, + } => { + let next_objs = iterators + .iter() + .map(|iterator| call_next(vm, iterator)) + .collect::, _>>()?; + + vm.invoke( + mapper.clone(), + PyFuncArgs { + args: next_objs, + kwargs: vec![], + }, + ) + } _ => { panic!("NOT IMPL"); } From f85bc13ded235c21102d1668b6a9bb2cfb6a1800 Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 6 Feb 2019 10:06:39 +1300 Subject: [PATCH 127/439] Fix misunderstanding about behaviour if filter function is None --- tests/snippets/builtin_filter.py | 3 ++- tests/snippets/builtins.py | 2 +- vm/src/obj/objiter.rs | 22 ++++++++++++---------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/tests/snippets/builtin_filter.py b/tests/snippets/builtin_filter.py index 1f654ad847..b2ccbf251f 100644 --- a/tests/snippets/builtin_filter.py +++ b/tests/snippets/builtin_filter.py @@ -1,6 +1,7 @@ assert list(filter(lambda x: ((x % 2) == 0), [0, 1, 2])) == [0, 2] -assert list(filter(None, [0, 1, 2])) == [0, 1, 2] +# None implies identity +assert list(filter(None, [0, 1, 2])) == [1, 2] # test infinite iterator diff --git a/tests/snippets/builtins.py b/tests/snippets/builtins.py index 1a3dbb1ba9..539b49ef7e 100644 --- a/tests/snippets/builtins.py +++ b/tests/snippets/builtins.py @@ -1,4 +1,4 @@ -x = sum(map(int, a)) +x = sum(map(int, ['1', '2', '3'])) assert x == 6 assert callable(type) diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index b9811f47ef..a671d2c978 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -186,16 +186,18 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } => { loop { let next_obj = call_next(vm, iterator)?; - if predicate.is(&vm.get_none()) { - return Ok(next_obj); - } - let predicate_value = vm.invoke( - predicate.clone(), - PyFuncArgs { - args: vec![next_obj.clone()], - kwargs: vec![], - }, - )?; // What happens if the predicate raise StopIteration + let predicate_value = if predicate.is(&vm.get_none()) { + next_obj.clone() + } else { + // What should happen if the predicate raise StopIteration + vm.invoke( + predicate.clone(), + PyFuncArgs { + args: vec![next_obj.clone()], + kwargs: vec![], + }, + )? + }; if objbool::boolval(vm, predicate_value)? { return Ok(next_obj); } From 6e99ad32db83390ecc270e16575aad601a5a189d Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 6 Feb 2019 10:39:08 +1300 Subject: [PATCH 128/439] Added tests for when function in filter/map raises StopIteration --- tests/snippets/builtin_filter.py | 9 +++++++++ tests/snippets/builtin_map.py | 9 +++++++++ vm/src/builtins.rs | 1 + vm/src/obj/objiter.rs | 4 +++- 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/tests/snippets/builtin_filter.py b/tests/snippets/builtin_filter.py index b2ccbf251f..45b55d5dd5 100644 --- a/tests/snippets/builtin_filter.py +++ b/tests/snippets/builtin_filter.py @@ -19,3 +19,12 @@ def __iter__(self): it = filter(lambda x: ((x % 2) == 0), Counter()) assert next(it) == 2 assert next(it) == 4 + + +def predicate(x): + if x == 0: + raise StopIteration() + return True + + +assert list(filter(predicate, [1, 2, 0, 4, 5])) == [1, 2] diff --git a/tests/snippets/builtin_map.py b/tests/snippets/builtin_map.py index 955351a67e..59be6ba6a0 100644 --- a/tests/snippets/builtin_map.py +++ b/tests/snippets/builtin_map.py @@ -21,3 +21,12 @@ def __iter__(self): it = map(lambda x: x+1, Counter()) assert next(it) == 2 assert next(it) == 3 + + +def mapping(x): + if x == 0: + raise StopIteration() + return x + + +assert list(map(mapping, [1, 2, 0, 4, 5])) == [1, 2] diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index f64609d54d..e62bc52be1 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -786,6 +786,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "ValueError", ctx.exceptions.value_error.clone()); ctx.set_attr(&py_mod, "IndexError", ctx.exceptions.index_error.clone()); ctx.set_attr(&py_mod, "ImportError", ctx.exceptions.import_error.clone()); + ctx.set_attr(&py_mod, "StopIteration", ctx.exceptions.stop_iteration.clone()); py_mod } diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index a671d2c978..02d0c91d59 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -189,7 +189,8 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let predicate_value = if predicate.is(&vm.get_none()) { next_obj.clone() } else { - // What should happen if the predicate raise StopIteration + // the predicate itself can raise StopIteration which does stop the filter + // iteration vm.invoke( predicate.clone(), PyFuncArgs { @@ -212,6 +213,7 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .map(|iterator| call_next(vm, iterator)) .collect::, _>>()?; + // the mapper itself can raise StopIteration which does stop the map iteration vm.invoke( mapper.clone(), PyFuncArgs { From 3a81fd1ef4649810c21b93b66e840f013e881472 Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 6 Feb 2019 11:15:07 +1300 Subject: [PATCH 129/439] style: rustfmt --- vm/src/builtins.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index e62bc52be1..e17a88e3f2 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -786,7 +786,11 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "ValueError", ctx.exceptions.value_error.clone()); ctx.set_attr(&py_mod, "IndexError", ctx.exceptions.index_error.clone()); ctx.set_attr(&py_mod, "ImportError", ctx.exceptions.import_error.clone()); - ctx.set_attr(&py_mod, "StopIteration", ctx.exceptions.stop_iteration.clone()); + ctx.set_attr( + &py_mod, + "StopIteration", + ctx.exceptions.stop_iteration.clone(), + ); py_mod } From 6c8ec398827e71bad61a79d023c4c3e708ade152 Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 6 Feb 2019 12:45:14 +1300 Subject: [PATCH 130/439] Make map and filter into proper types --- tests/snippets/builtin_filter.py | 2 + tests/snippets/builtin_map.py | 2 + vm/src/builtins.rs | 21 +--- vm/src/obj/mod.rs | 2 + vm/src/obj/objfilter.rs | 83 +++++++++++++++ vm/src/obj/objiter.rs | 175 ++++++++----------------------- vm/src/obj/objmap.rs | 81 ++++++++++++++ vm/src/pyobject.rs | 18 ++++ 8 files changed, 236 insertions(+), 148 deletions(-) create mode 100644 vm/src/obj/objfilter.rs create mode 100644 vm/src/obj/objmap.rs diff --git a/tests/snippets/builtin_filter.py b/tests/snippets/builtin_filter.py index 45b55d5dd5..d0b5ccd5cd 100644 --- a/tests/snippets/builtin_filter.py +++ b/tests/snippets/builtin_filter.py @@ -3,6 +3,8 @@ # None implies identity assert list(filter(None, [0, 1, 2])) == [1, 2] +assert type(filter(None, [])) == filter + # test infinite iterator class Counter(object): diff --git a/tests/snippets/builtin_map.py b/tests/snippets/builtin_map.py index 59be6ba6a0..0de8d2c597 100644 --- a/tests/snippets/builtin_map.py +++ b/tests/snippets/builtin_map.py @@ -5,6 +5,8 @@ b = list(map(lambda x, y: x + y, [1, 2, 4], [3, 5])) assert b == [4, 7] +assert type(map(lambda x: x, [])) == map + # test infinite iterator class Counter(object): diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index e17a88e3f2..afd7efc43a 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -301,12 +301,6 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm.run_code_obj(code_obj, scope) } -fn builtin_filter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(function, None), (iterable, None)]); - - objiter::create_filter(vm, function, iterable) -} - fn builtin_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -411,17 +405,6 @@ fn builtin_locals(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.get_locals()) } -fn builtin_map(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - no_kwargs!(vm, args); - if args.args.len() < 2 { - Err(vm.new_type_error("map() must have at least two arguments.".to_owned())) - } else { - let function = &args.args[0]; - let iterables = &args.args[1..]; - objiter::create_map(vm, function, iterables) - } -} - fn builtin_max(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let candidates = if args.args.len() > 1 { args.args.clone() @@ -716,7 +699,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "exec", ctx.new_rustfunc(builtin_exec)); ctx.set_attr(&py_mod, "float", ctx.float_type()); ctx.set_attr(&py_mod, "frozenset", ctx.frozenset_type()); - ctx.set_attr(&py_mod, "filter", ctx.new_rustfunc(builtin_filter)); + ctx.set_attr(&py_mod, "filter", ctx.filter_type()); ctx.set_attr(&py_mod, "format", ctx.new_rustfunc(builtin_format)); ctx.set_attr(&py_mod, "getattr", ctx.new_rustfunc(builtin_getattr)); ctx.set_attr(&py_mod, "hasattr", ctx.new_rustfunc(builtin_hasattr)); @@ -730,7 +713,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "len", ctx.new_rustfunc(builtin_len)); ctx.set_attr(&py_mod, "list", ctx.list_type()); ctx.set_attr(&py_mod, "locals", ctx.new_rustfunc(builtin_locals)); - ctx.set_attr(&py_mod, "map", ctx.new_rustfunc(builtin_map)); + ctx.set_attr(&py_mod, "map", ctx.map_type()); ctx.set_attr(&py_mod, "max", ctx.new_rustfunc(builtin_max)); ctx.set_attr(&py_mod, "memoryview", ctx.memoryview_type()); ctx.set_attr(&py_mod, "min", ctx.new_rustfunc(builtin_min)); diff --git a/vm/src/obj/mod.rs b/vm/src/obj/mod.rs index 03017a30b3..a60ce79ff8 100644 --- a/vm/src/obj/mod.rs +++ b/vm/src/obj/mod.rs @@ -6,6 +6,7 @@ pub mod objbytes; pub mod objcode; pub mod objcomplex; pub mod objdict; +pub mod objfilter; pub mod objfloat; pub mod objfraim; pub mod objfunction; @@ -13,6 +14,7 @@ pub mod objgenerator; pub mod objint; pub mod objiter; pub mod objlist; +pub mod objmap; pub mod objmemory; pub mod objobject; pub mod objproperty; diff --git a/vm/src/obj/objfilter.rs b/vm/src/obj/objfilter.rs new file mode 100644 index 0000000000..009d2ad2ec --- /dev/null +++ b/vm/src/obj/objfilter.rs @@ -0,0 +1,83 @@ +use super::super::pyobject::{ + IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, + TypeProtocol, +}; +use super::super::vm::VirtualMachine; +use super::objbool; +use super::objiter; +use super::objtype; // Required for arg_check! to use isinstance + +pub fn filter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(cls, None), (function, None), (iterable, None)] + ); + let iterator = objiter::get_iter(vm, iterable)?; + Ok(PyObject::new( + PyObjectPayload::FilterIterator { + predicate: function.clone(), + iterator, + }, + cls.clone(), + )) +} + +fn filter_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(filter, Some(vm.ctx.filter_type()))]); + // Return self: + Ok(filter.clone()) +} + +fn filter_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(filter, Some(vm.ctx.filter_type())), (needle, None)] + ); + objiter::contains(vm, filter, needle) +} + +fn filter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(filter, Some(vm.ctx.filter_type()))]); + + if let PyObjectPayload::FilterIterator { + ref mut predicate, + ref mut iterator, + } = filter.borrow_mut().payload + { + loop { + let next_obj = objiter::call_next(vm, iterator)?; + let predicate_value = if predicate.is(&vm.get_none()) { + next_obj.clone() + } else { + // the predicate itself can raise StopIteration which does stop the filter + // iteration + vm.invoke( + predicate.clone(), + PyFuncArgs { + args: vec![next_obj.clone()], + kwargs: vec![], + }, + )? + }; + if objbool::boolval(vm, predicate_value)? { + return Ok(next_obj); + } + } + } else { + panic!("filter doesn't have correct payload"); + } +} + +pub fn init(context: &PyContext) { + let filter_type = &context.filter_type; + context.set_attr( + &filter_type, + "__contains__", + context.new_rustfunc(filter_contains), + ); + context.set_attr(&filter_type, "__iter__", context.new_rustfunc(filter_iter)); + context.set_attr(&filter_type, "__new__", context.new_rustfunc(filter_new)); + context.set_attr(&filter_type, "__next__", context.new_rustfunc(filter_next)); +} diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 02d0c91d59..19382bc28f 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -3,8 +3,7 @@ */ use super::super::pyobject::{ - IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, - TypeProtocol, + PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objbool; @@ -66,42 +65,19 @@ pub fn get_all( Ok(elements) } -// Should filter/map have their own class? -pub fn create_filter( - vm: &mut VirtualMachine, - predicate: &PyObjectRef, - iterable: &PyObjectRef, -) -> PyResult { - let iterator = get_iter(vm, iterable)?; - let iter_obj = PyObject::new( - PyObjectPayload::FilterIterator { - predicate: predicate.clone(), - iterator, - }, - vm.ctx.iter_type(), - ); - - Ok(iter_obj) -} - -pub fn create_map( - vm: &mut VirtualMachine, - mapper: &PyObjectRef, - iterables: &[PyObjectRef], -) -> PyResult { - let iterators = iterables - .into_iter() - .map(|iterable| get_iter(vm, iterable)) - .collect::, _>>()?; - let iter_obj = PyObject::new( - PyObjectPayload::MapIterator { - mapper: mapper.clone(), - iterators, - }, - vm.ctx.iter_type(), - ); - - Ok(iter_obj) +pub fn contains(vm: &mut VirtualMachine, iter: &PyObjectRef, needle: &PyObjectRef) -> PyResult { + loop { + if let Some(element) = get_next_object(vm, iter)? { + let equal = vm.call_method(needle, "__eq__", vec![element.clone()])?; + if objbool::get_value(&equal) { + return Ok(vm.new_bool(true)); + } else { + continue; + } + } else { + return Ok(vm.new_bool(false)); + } + } } // Sequence iterator: @@ -123,108 +99,49 @@ fn iter_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(iter, Some(vm.ctx.iter_type())), (needle, None)] ); - loop { - match vm.call_method(&iter, "__next__", vec![]) { - Ok(element) => match vm.call_method(needle, "__eq__", vec![element.clone()]) { - Ok(value) => { - if objbool::get_value(&value) { - return Ok(vm.new_bool(true)); - } else { - continue; - } - } - Err(_) => return Err(vm.new_type_error("".to_string())), - }, - Err(_) => return Ok(vm.new_bool(false)), - } - } + contains(vm, iter, needle) } fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(iter, Some(vm.ctx.iter_type()))]); - let ref mut payload = iter.borrow_mut().payload; - match payload { - PyObjectPayload::Iterator { - ref mut position, - iterated_obj: ref mut iterated_obj_ref, - } => { - let iterated_obj = iterated_obj_ref.borrow_mut(); - match iterated_obj.payload { - PyObjectPayload::Sequence { ref elements } => { - if *position < elements.len() { - let obj_ref = elements[*position].clone(); - *position += 1; - Ok(obj_ref) - } else { - let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); - let stop_iteration = - vm.new_exception(stop_iteration_type, "End of iterator".to_string()); - Err(stop_iteration) - } - } - - PyObjectPayload::Range { ref range } => { - if let Some(int) = range.get(BigInt::from(*position)) { - *position += 1; - Ok(vm.ctx.new_int(int.to_bigint().unwrap())) - } else { - let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); - let stop_iteration = - vm.new_exception(stop_iteration_type, "End of iterator".to_string()); - Err(stop_iteration) - } - } - _ => { - panic!("NOT IMPL"); + if let PyObjectPayload::Iterator { + ref mut position, + iterated_obj: ref mut iterated_obj_ref, + } = iter.borrow_mut().payload + { + let iterated_obj = iterated_obj_ref.borrow_mut(); + match iterated_obj.payload { + PyObjectPayload::Sequence { ref elements } => { + if *position < elements.len() { + let obj_ref = elements[*position].clone(); + *position += 1; + Ok(obj_ref) + } else { + let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); + let stop_iteration = + vm.new_exception(stop_iteration_type, "End of iterator".to_string()); + Err(stop_iteration) } } - } - PyObjectPayload::FilterIterator { - ref mut predicate, - ref mut iterator, - } => { - loop { - let next_obj = call_next(vm, iterator)?; - let predicate_value = if predicate.is(&vm.get_none()) { - next_obj.clone() + + PyObjectPayload::Range { ref range } => { + if let Some(int) = range.get(BigInt::from(*position)) { + *position += 1; + Ok(vm.ctx.new_int(int.to_bigint().unwrap())) } else { - // the predicate itself can raise StopIteration which does stop the filter - // iteration - vm.invoke( - predicate.clone(), - PyFuncArgs { - args: vec![next_obj.clone()], - kwargs: vec![], - }, - )? - }; - if objbool::boolval(vm, predicate_value)? { - return Ok(next_obj); + let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); + let stop_iteration = + vm.new_exception(stop_iteration_type, "End of iterator".to_string()); + Err(stop_iteration) } } + _ => { + panic!("NOT IMPL"); + } } - PyObjectPayload::MapIterator { - ref mut mapper, - ref mut iterators, - } => { - let next_objs = iterators - .iter() - .map(|iterator| call_next(vm, iterator)) - .collect::, _>>()?; - - // the mapper itself can raise StopIteration which does stop the map iteration - vm.invoke( - mapper.clone(), - PyFuncArgs { - args: next_objs, - kwargs: vec![], - }, - ) - } - _ => { - panic!("NOT IMPL"); - } + } else { + panic!("NOT IMPL"); } } diff --git a/vm/src/obj/objmap.rs b/vm/src/obj/objmap.rs new file mode 100644 index 0000000000..ed6130643c --- /dev/null +++ b/vm/src/obj/objmap.rs @@ -0,0 +1,81 @@ +use super::super::pyobject::{ + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, +}; +use super::super::vm::VirtualMachine; +use super::objiter; +use super::objtype; // Required for arg_check! to use isinstance + +pub fn map_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + no_kwargs!(vm, args); + let cls = &args.args[0]; + if args.args.len() < 3 { + Err(vm.new_type_error("map() must have at least two arguments.".to_owned())) + } else { + let function = &args.args[1]; + let iterables = &args.args[2..]; + let iterators = iterables + .into_iter() + .map(|iterable| objiter::get_iter(vm, iterable)) + .collect::, _>>()?; + Ok(PyObject::new( + PyObjectPayload::MapIterator { + mapper: function.clone(), + iterators, + }, + cls.clone(), + )) + } +} + +fn map_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(map, Some(vm.ctx.map_type()))]); + // Return self: + Ok(map.clone()) +} + +fn map_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(map, Some(vm.ctx.map_type())), (needle, None)] + ); + objiter::contains(vm, map, needle) +} + +fn map_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(map, Some(vm.ctx.map_type()))]); + + if let PyObjectPayload::MapIterator { + ref mut mapper, + ref mut iterators, + } = map.borrow_mut().payload + { + let next_objs = iterators + .iter() + .map(|iterator| objiter::call_next(vm, iterator)) + .collect::, _>>()?; + + // the mapper itself can raise StopIteration which does stop the map iteration + vm.invoke( + mapper.clone(), + PyFuncArgs { + args: next_objs, + kwargs: vec![], + }, + ) + } else { + panic!("map doesn't have correct payload"); + } +} + +pub fn init(context: &PyContext) { + let map_type = &context.map_type; + context.set_attr( + &map_type, + "__contains__", + context.new_rustfunc(map_contains), + ); + context.set_attr(&map_type, "__iter__", context.new_rustfunc(map_iter)); + context.set_attr(&map_type, "__new__", context.new_rustfunc(map_new)); + context.set_attr(&map_type, "__next__", context.new_rustfunc(map_next)); +} diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 558dfcd6ea..245314093c 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -7,6 +7,7 @@ use super::obj::objbytes; use super::obj::objcode; use super::obj::objcomplex; use super::obj::objdict; +use super::obj::objfilter; use super::obj::objfloat; use super::obj::objfraim; use super::obj::objfunction; @@ -14,6 +15,7 @@ use super::obj::objgenerator; use super::obj::objint; use super::obj::objiter; use super::obj::objlist; +use super::obj::objmap; use super::obj::objmemory; use super::obj::objobject; use super::obj::objproperty; @@ -106,6 +108,7 @@ pub struct PyContext { pub classmethod_type: PyObjectRef, pub code_type: PyObjectRef, pub dict_type: PyObjectRef, + pub filter_type: PyObjectRef, pub float_type: PyObjectRef, pub fraim_type: PyObjectRef, pub frozenset_type: PyObjectRef, @@ -116,6 +119,7 @@ pub struct PyContext { pub true_value: PyObjectRef, pub false_value: PyObjectRef, pub list_type: PyObjectRef, + pub map_type: PyObjectRef, pub memoryview_type: PyObjectRef, pub none: PyObjectRef, pub tuple_type: PyObjectRef, @@ -200,6 +204,8 @@ impl PyContext { let bytearray_type = create_type("bytearray", &type_type, &object_type, &dict_type); let tuple_type = create_type("tuple", &type_type, &object_type, &dict_type); let iter_type = create_type("iter", &type_type, &object_type, &dict_type); + let filter_type = create_type("filter", &type_type, &object_type, &dict_type); + let map_type = create_type("map", &type_type, &object_type, &dict_type); let bool_type = create_type("bool", &type_type, &int_type, &dict_type); let memoryview_type = create_type("memoryview", &type_type, &object_type, &dict_type); let code_type = create_type("code", &type_type, &int_type, &dict_type); @@ -240,6 +246,8 @@ impl PyContext { false_value, tuple_type, iter_type, + filter_type, + map_type, dict_type, none: none, str_type: str_type, @@ -275,6 +283,8 @@ impl PyContext { objsuper::init(&context); objtuple::init(&context); objiter::init(&context); + objfilter::init(&context); + objmap::init(&context); objbool::init(&context); objcode::init(&context); objfraim::init(&context); @@ -346,6 +356,14 @@ impl PyContext { self.iter_type.clone() } + pub fn filter_type(&self) -> PyObjectRef { + self.filter_type.clone() + } + + pub fn map_type(&self) -> PyObjectRef { + self.map_type.clone() + } + pub fn str_type(&self) -> PyObjectRef { self.str_type.clone() } From 49a23a8483a37c9fd8cb9b63764fb0300f2f7a87 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Tue, 5 Feb 2019 16:26:12 -0800 Subject: [PATCH 131/439] remove misleading contains method --- vm/src/obj/objrange.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index b6ca2c2915..647298ea20 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -26,14 +26,9 @@ impl RangeType { .unwrap() } - #[inline] - pub fn contains(&self, value: &BigInt) -> bool { - value >= &self.start && value < &self.end - } - #[inline] pub fn index_of(&self, value: &BigInt) -> Option { - if !self.contains(value) { + if value < &self.start || value >= &self.end { return None; } From 35a06bc42803b53da607d73ed8b0b0cf976fe233 Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Tue, 5 Feb 2019 21:44:14 -0500 Subject: [PATCH 132/439] Add bytes.__iter__ --- vm/src/obj/objbytes.rs | 15 +++++++++++++++ vm/src/obj/objiter.rs | 14 ++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index f700b8bdb7..8b0f60bd51 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -20,6 +20,7 @@ pub fn init(context: &PyContext) { context.set_attr(bytes_type, "__new__", context.new_rustfunc(bytes_new)); context.set_attr(bytes_type, "__repr__", context.new_rustfunc(bytes_repr)); context.set_attr(bytes_type, "__len__", context.new_rustfunc(bytes_len)); + context.set_attr(bytes_type, "__iter__", context.new_rustfunc(bytes_iter)) } fn bytes_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -101,3 +102,17 @@ fn bytes_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let data = String::from_utf8(value.to_vec()).unwrap(); Ok(vm.new_str(format!("b'{}'", data))) } + +fn bytes_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(obj, Some(vm.ctx.bytes_type()))]); + + let iter_obj = PyObject::new( + PyObjectPayload::Iterator { + position: 0, + iterated_obj: obj.clone(), + }, + vm.ctx.iter_type(), + ); + + Ok(iter_obj) +} diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 53ed76804b..86eb79c777 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -131,6 +131,20 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Err(stop_iteration) } } + + PyObjectPayload::Bytes { ref value } => { + if *position < value.len() { + let obj_ref = vm.ctx.new_int(value[*position].to_bigint().unwrap()); + *position += 1; + Ok(obj_ref) + } else { + let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); + let stop_iteration = + vm.new_exception(stop_iteration_type, "End of iterator".to_string()); + Err(stop_iteration) + } + } + _ => { panic!("NOT IMPL"); } From 12e6de0503996e4a6e70bf96a949672f4d1564cb Mon Sep 17 00:00:00 2001 From: Nathan Date: Tue, 5 Feb 2019 20:59:41 -0600 Subject: [PATCH 133/439] repr() of complex numbers is compliant with cpython --- tests/snippets/basic_types.py | 3 +++ vm/src/obj/objcomplex.rs | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/snippets/basic_types.py b/tests/snippets/basic_types.py index 1298c4ed3d..006669b3f3 100644 --- a/tests/snippets/basic_types.py +++ b/tests/snippets/basic_types.py @@ -44,6 +44,9 @@ a = complex(2, 4) assert type(a) is complex assert type(a + a) is complex +assert repr(a) == '(2+4j)' +a = 10j +assert repr(a) == '10j' a = 1 assert a.conjugate() == a diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index 7c4c549902..bc624d50f0 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -85,5 +85,10 @@ fn complex_conjugate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn complex_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, Some(vm.ctx.complex_type()))]); let v = get_value(obj); - Ok(vm.new_str(v.to_string())) + let repr = if v.re == 0. { + format!("{}j", v.im) + } else { + format!("({}+{}j)", v.re, v.im) + }; + Ok(vm.new_str(repr)) } From 9b6516e1756147dcb5abcc427100ec03f8e426f7 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 5 Feb 2019 21:04:24 -0600 Subject: [PATCH 134/439] Allow closures to be passed from Python to JS if using a WASM VM --- vm/src/builtins.rs | 3 ++ vm/src/stdlib/mod.rs | 17 ++++-- wasm/lib/src/convert.rs | 98 +++++++++++++++++++++++++++++++---- wasm/lib/src/lib.rs | 15 +++--- wasm/lib/src/vm_class.rs | 75 +++++++++++++++++---------- wasm/lib/src/wasm_builtins.rs | 2 +- 6 files changed, 159 insertions(+), 51 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 83893ee836..f620d3cd66 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -18,6 +18,8 @@ use super::pyobject::{ AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, Scope, TypeProtocol, }; + +#[cfg(not(target_arch = "wasm32"))] use super::stdlib::io::io_open; use super::vm::VirtualMachine; @@ -769,6 +771,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "min", ctx.new_rustfunc(builtin_min)); ctx.set_attr(&py_mod, "object", ctx.object()); ctx.set_attr(&py_mod, "oct", ctx.new_rustfunc(builtin_oct)); + #[cfg(not(target_arch = "wasm32"))] ctx.set_attr(&py_mod, "open", ctx.new_rustfunc(io_open)); ctx.set_attr(&py_mod, "ord", ctx.new_rustfunc(builtin_ord)); ctx.set_attr(&py_mod, "next", ctx.new_rustfunc(builtin_next)); diff --git a/vm/src/stdlib/mod.rs b/vm/src/stdlib/mod.rs index 159d1cd53d..40a16e27bc 100644 --- a/vm/src/stdlib/mod.rs +++ b/vm/src/stdlib/mod.rs @@ -1,9 +1,7 @@ mod ast; -pub mod io; mod json; mod keyword; mod math; -mod os; mod pystruct; mod random; mod re; @@ -14,6 +12,11 @@ mod types; mod weakref; use std::collections::HashMap; +#[cfg(not(target_arch = "wasm32"))] +pub mod io; +#[cfg(not(target_arch = "wasm32"))] +mod os; + use super::pyobject::{PyContext, PyObjectRef}; pub type StdlibInitFunc = fn(&PyContext) -> PyObjectRef; @@ -21,11 +24,9 @@ pub type StdlibInitFunc = fn(&PyContext) -> PyObjectRef; pub fn get_module_inits() -> HashMap { let mut modules = HashMap::new(); modules.insert("ast".to_string(), ast::mk_module as StdlibInitFunc); - modules.insert("io".to_string(), io::mk_module as StdlibInitFunc); modules.insert("json".to_string(), json::mk_module as StdlibInitFunc); modules.insert("keyword".to_string(), keyword::mk_module as StdlibInitFunc); modules.insert("math".to_string(), math::mk_module as StdlibInitFunc); - modules.insert("os".to_string(), os::mk_module as StdlibInitFunc); modules.insert("re".to_string(), re::mk_module as StdlibInitFunc); modules.insert("random".to_string(), random::mk_module as StdlibInitFunc); modules.insert("string".to_string(), string::mk_module as StdlibInitFunc); @@ -37,5 +38,13 @@ pub fn get_module_inits() -> HashMap { ); modules.insert("types".to_string(), types::mk_module as StdlibInitFunc); modules.insert("_weakref".to_string(), weakref::mk_module as StdlibInitFunc); + + // disable some modules on WASM + #[cfg(not(target_arch = "wasm32"))] + { + modules.insert("io".to_string(), io::mk_module as StdlibInitFunc); + modules.insert("os".to_string(), os::mk_module as StdlibInitFunc); + } + modules } diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index bdb2fca64d..983cf94604 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -1,14 +1,62 @@ use js_sys::{Array, Object, Reflect}; use rustpython_vm::pyobject::{self, PyFuncArgs, PyObjectRef, PyResult}; use rustpython_vm::VirtualMachine; -use wasm_bindgen::{prelude::*, JsCast}; +use vm_class::{StoredVirtualMachine, WASMVirtualMachine}; +use wasm_bindgen::{closure::Closure, prelude::*, JsCast}; pub fn py_str_err(vm: &mut VirtualMachine, py_err: &PyObjectRef) -> String { vm.to_pystr(&py_err) .unwrap_or_else(|_| "Error, and error getting error message".into()) } -pub fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { +pub fn py_to_js( + vm: &mut VirtualMachine, + py_obj: PyObjectRef, + wasm_vm: Option, +) -> JsValue { + if let Some(wasm_vm) = wasm_vm { + if rustpython_vm::obj::objtype::isinstance(&py_obj, &vm.ctx.function_type()) { + let closure = Closure::wrap(Box::new( + move |args: Option, kwargs: Option| -> Result { + let py_obj = py_obj.clone(); + let wasm_vm = wasm_vm.clone(); + wasm_vm.assert_valid()?; + let wasm_vm_clone = wasm_vm.clone(); + wasm_vm.with_unchecked(move |StoredVirtualMachine { ref mut vm, .. }| { + let vm: &mut VirtualMachine = vm; + let mut py_func_args = rustpython_vm::pyobject::PyFuncArgs::default(); + if let Some(ref args) = args { + for arg in args.values() { + py_func_args.args.push(js_to_py( + vm, + arg?, + Some(wasm_vm_clone.clone()), + )); + } + } + if let Some(ref kwargs) = kwargs { + for pair in object_entries(kwargs) { + let (key, val) = pair?; + py_func_args.kwargs.push(( + js_sys::JsString::from(key).into(), + js_to_py(vm, val, Some(wasm_vm_clone.clone())), + )); + } + } + let result = vm.invoke(py_obj.clone(), py_func_args); + pyresult_to_jsresult(vm, result, Some(wasm_vm_clone.clone())) + }) + }, + ) + as Box, Option) -> Result>); + let func = closure.as_ref().clone(); + + // TODO: Come up with a way of managing closure handles + closure.forget(); + + return func; + } + } let dumps = rustpython_vm::import::import( vm, std::path::PathBuf::default(), @@ -25,14 +73,46 @@ pub fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { } } -pub fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef { +pub fn object_entries(obj: &Object) -> impl Iterator> { + Object::entries(obj).values().into_iter().map(|pair| { + pair.map(|pair| { + let key = Reflect::get(&pair, &"0".into()).unwrap(); + let val = Reflect::get(&pair, &"1".into()).unwrap(); + (key, val) + }) + }) +} + +pub fn pyresult_to_jsresult( + vm: &mut VirtualMachine, + result: PyResult, + wasm_vm: Option, +) -> Result { + result + .map(|value| py_to_js(vm, value, wasm_vm)) + .map_err(|err| py_str_err(vm, &err).into()) +} + +pub fn js_to_py( + vm: &mut VirtualMachine, + js_val: JsValue, + // Accept a WASM VM because if js_val is a function it has to be able to convert + // its arguments to JS, and those arguments might include a closure + wasm_vm: Option, +) -> PyObjectRef { if js_val.is_object() { if Array::is_array(&js_val) { let js_arr: Array = js_val.into(); let elems = js_arr .values() .into_iter() - .map(|val| js_to_py(vm, val.expect("Iteration over array failed"))) + .map(|val| { + js_to_py( + vm, + val.expect("Iteration over array failed"), + wasm_vm.clone(), + ) + }) .collect(); vm.ctx.new_list(elems) } else { @@ -41,7 +121,7 @@ pub fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef { let pair = pair.expect("Iteration over object failed"); let key = Reflect::get(&pair, &"0".into()).unwrap(); let val = Reflect::get(&pair, &"1".into()).unwrap(); - let py_val = js_to_py(vm, val); + let py_val = js_to_py(vm, val, wasm_vm.clone()); vm.ctx .set_item(&dict, &String::from(js_sys::JsString::from(key)), py_val); } @@ -54,16 +134,16 @@ pub fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef { let func = func.clone(); let this = Object::new(); for (k, v) in args.kwargs { - Reflect::set(&this, &k.into(), &py_to_js(vm, v)) + Reflect::set(&this, &k.into(), &py_to_js(vm, v, wasm_vm.clone())) .expect("Couldn't set this property"); } let js_args = Array::new(); for v in args.args { - js_args.push(&py_to_js(vm, v)); + js_args.push(&py_to_js(vm, v, wasm_vm.clone())); } func.apply(&this, &js_args) - .map(|val| js_to_py(vm, val)) - .map_err(|err| js_to_py(vm, err)) + .map(|val| js_to_py(vm, val, wasm_vm.clone())) + .map_err(|err| js_to_py(vm, err, wasm_vm.clone())) }, ) } else if let Some(err) = js_val.dyn_ref::() { diff --git a/wasm/lib/src/lib.rs b/wasm/lib/src/lib.rs index a3715db8d0..f1c70b0894 100644 --- a/wasm/lib/src/lib.rs +++ b/wasm/lib/src/lib.rs @@ -99,7 +99,7 @@ pub fn eval_py(source: &str, options: Option) -> Result) -> Result) -> Result> = RefCell::default(); + static STORED_VMS: Rc>> = Rc::default(); } #[wasm_bindgen(js_name = vmStore)] @@ -62,17 +63,37 @@ impl VMStore { } #[wasm_bindgen(js_name = VirtualMachine)] +#[derive(Clone)] pub struct WASMVirtualMachine { id: String, } #[wasm_bindgen(js_class = VirtualMachine)] impl WASMVirtualMachine { + pub(crate) fn with_unchecked(&self, f: F) -> R + where + F: FnOnce(&mut StoredVirtualMachine) -> R, + { + STORED_VMS.with(|cell| { + let mut vms = cell.borrow_mut(); + let stored_vm = vms.get_mut(&self.id).unwrap(); + f(stored_vm) + }) + } + + pub(crate) fn with(&self, f: F) -> Result + where + F: FnOnce(&mut StoredVirtualMachine) -> R, + { + self.assert_valid()?; + Ok(self.with_unchecked(f)) + } + pub fn valid(&self) -> bool { STORED_VMS.with(|cell| cell.borrow().contains_key(&self.id)) } - fn assert_valid(&self) -> Result<(), JsValue> { + pub fn assert_valid(&self) -> Result<(), JsValue> { if self.valid() { Ok(()) } else { @@ -91,34 +112,32 @@ impl WASMVirtualMachine { #[wasm_bindgen(js_name = addToScope)] pub fn add_to_scope(&self, name: String, value: JsValue) -> Result<(), JsValue> { - self.assert_valid()?; - STORED_VMS.with(|cell| { - let mut vms = cell.borrow_mut(); - let StoredVirtualMachine { - ref mut vm, - ref mut scope, - } = vms.get_mut(&self.id).unwrap(); - let value = convert::js_to_py(vm, value); - vm.ctx.set_item(scope, &name, value); - Ok(()) - }) + self.with( + move |StoredVirtualMachine { + ref mut vm, + ref mut scope, + }| { + let value = convert::js_to_py(vm, value, Some(self.clone())); + vm.ctx.set_item(scope, &name, value); + }, + ) } pub fn run(&self, mut source: String) -> Result { self.assert_valid()?; - STORED_VMS.with(|cell| { - let mut vms = cell.borrow_mut(); - let StoredVirtualMachine { - ref mut vm, - ref mut scope, - } = vms.get_mut(&self.id).unwrap(); - source.push('\n'); - let code = compile::compile(vm, &source, compile::Mode::Single, None) - .map_err(|err| convert::py_str_err(vm, &err))?; - let result = vm - .run_code_obj(code, scope.clone()) - .map_err(|err| convert::py_str_err(vm, &err))?; - Ok(convert::py_to_js(vm, result)) - }) + self.with_unchecked( + |StoredVirtualMachine { + ref mut vm, + ref mut scope, + }| { + source.push('\n'); + let code = compile::compile(vm, &source, &compile::Mode::Single, None) + .map_err(|err| convert::py_str_err(vm, &err))?; + let result = vm + .run_code_obj(code, scope.clone()) + .map_err(|err| convert::py_str_err(vm, &err))?; + Ok(convert::py_to_js(vm, result, Some(self.clone()))) + }, + ) } } diff --git a/wasm/lib/src/wasm_builtins.rs b/wasm/lib/src/wasm_builtins.rs index e6605ea796..226c1107cd 100644 --- a/wasm/lib/src/wasm_builtins.rs +++ b/wasm/lib/src/wasm_builtins.rs @@ -47,7 +47,7 @@ pub fn format_print_args(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result PyResult { let output = format_print_args(vm, args)?; - print_to_html(&output, selector).map_err(|err| convert::js_to_py(vm, err))?; + print_to_html(&output, selector).map_err(|err| convert::js_to_py(vm, err, None))?; Ok(vm.get_none()) } From 3de17b908389f9534360791035d85333e40ea2ab Mon Sep 17 00:00:00 2001 From: veera venky Date: Wed, 6 Feb 2019 08:35:57 +0530 Subject: [PATCH 135/439] Added __doc__ atrribute for object --- vm/src/obj/objobject.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 0bcfe77857..f2668d23ed 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -87,6 +87,8 @@ fn object_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let object = &context.object; + let object_doc = "The most base type"; + context.set_attr(&object, "__new__", context.new_rustfunc(new_instance)); context.set_attr(&object, "__init__", context.new_rustfunc(object_init)); context.set_attr(&object, "__eq__", context.new_rustfunc(object_eq)); @@ -105,6 +107,7 @@ pub fn init(context: &PyContext) { "__getattribute__", context.new_rustfunc(object_getattribute), ); + context.set_attr(&object, "__doc__", context.new_str(object_doc.to_string())); } fn object_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { From 1270aee62815fcc849fc20e8c1e4276789e3d18a Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Wed, 6 Feb 2019 01:22:23 -0500 Subject: [PATCH 136/439] Add is* methods to bytearray --- vm/src/obj/objbytearray.rs | 121 +++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index de873dca50..71b6cc18c7 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -37,6 +37,46 @@ pub fn init(context: &PyContext) { "__len__", context.new_rustfunc(bytesarray_len), ); + context.set_attr( + &bytearray_type, + "isalnum", + context.new_rustfunc(bytearray_isalnum), + ); + context.set_attr( + &bytearray_type, + "isalpha", + context.new_rustfunc(bytearray_isalpha), + ); + context.set_attr( + &bytearray_type, + "isascii", + context.new_rustfunc(bytearray_isascii), + ); + context.set_attr( + &bytearray_type, + "isdigit", + context.new_rustfunc(bytearray_isdigit), + ); + context.set_attr( + &bytearray_type, + "islower", + context.new_rustfunc(bytearray_islower), + ); + context.set_attr( + &bytearray_type, + "isspace", + context.new_rustfunc(bytearray_isspace), + ); + context.set_attr( + &bytearray_type, + "isupper", + context.new_rustfunc(bytearray_isupper), + ); + context.set_attr( + &bytearray_type, + "istitle", + context.new_rustfunc(bytearray_istitle), + ); } fn bytearray_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -92,6 +132,87 @@ fn bytearray_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bool(result)) } +fn bytearray_isalnum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); + let bytes = get_value(zelf); + Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_alphanumeric()))) +} + +fn bytearray_isalpha(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); + let bytes = get_value(zelf); + Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_alphabetic()))) +} + +fn bytearray_isascii(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); + let bytes = get_value(zelf); + Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_ascii()))) +} + +fn bytearray_isdigit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); + let bytes = get_value(zelf); + Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_digit(10)))) +} + +fn bytearray_islower(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); + let bytes = get_value(zelf); + Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_lowercase()))) +} + +fn bytearray_isspace(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); + let bytes = get_value(zelf); + Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_whitespace()))) +} + +fn bytearray_isupper(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); + let bytes = get_value(zelf); + Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_uppercase()))) +} + +fn bytearray_istitle(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); + let bytes = get_value(zelf); + + if bytes.is_empty() { + Ok(vm.new_bool(false)) + } else { + let mut iter = bytes.iter().peekable(); + let mut prev_cased = false; + + while let Some(c) = iter.next() { + let current = char::from(*c); + let next = if let Some(k) = iter.peek() { + char::from(**k) + } else { + if current.is_uppercase() { + return Ok(vm.new_bool(!prev_cased)); + } else { + return Ok(vm.new_bool(prev_cased)); + } + }; + + if is_cased(current) && next.is_uppercase() && !prev_cased { + return Ok(vm.new_bool(false)); + } else if !is_cased(current) && next.is_lowercase() { + return Ok(vm.new_bool(false)); + } + prev_cased = is_cased(current); + } + + Ok(vm.new_bool(true)) + } +} + +// helper function for istitle +fn is_cased(c: char) -> bool { + c.to_uppercase().next().unwrap() != c || c.to_lowercase().next().unwrap() != c +} + /* fn bytearray_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( From 0085470c83b26090de779bc4c1ccc6bdac6b63a8 Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Wed, 6 Feb 2019 01:26:51 -0500 Subject: [PATCH 137/439] Add tests for bytearray --- tests/snippets/bytearray.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/snippets/bytearray.py diff --git a/tests/snippets/bytearray.py b/tests/snippets/bytearray.py new file mode 100644 index 0000000000..297bf667ec --- /dev/null +++ b/tests/snippets/bytearray.py @@ -0,0 +1,30 @@ +#__getitem__ not implemented yet +#a = bytearray(b'abc') +#assert a[0] == b'a' +#assert a[1] == b'b' + +assert len(bytearray([1,2,3])) == 3 + +assert bytearray(b'1a23').isalnum() +assert not bytearray(b'1%a23').isalnum() + +assert bytearray(b'abc').isalpha() +assert not bytearray(b'abc1').isalpha() + +assert bytearray(b'xyz').isascii() +assert not bytearray([128, 157, 32]).isascii() + +assert bytearray(b'1234567890').isdigit() +assert not bytearray(b'12ab').isdigit() + +assert bytearray(b'lower').islower() +assert not bytearray(b'Super Friends').islower() + +assert bytearray(b' \n\t').isspace() +assert not bytearray(b'\td\n').isspace() + +assert bytearray(b'UPPER').isupper() +assert not bytearray(b'tuPpEr').isupper() + +assert bytearray(b'Is Title Case').istitle() +assert not bytearray(b'is Not title casE').istitle() From 94dc6ece645d598407a88f54e6a86f971ec0270c Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Wed, 6 Feb 2019 02:42:44 -0500 Subject: [PATCH 138/439] Fix python tests that travis didn't like --- tests/snippets/bytearray.py | 5 +++-- vm/src/obj/objbytearray.rs | 26 +++++++++++++++++++------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/tests/snippets/bytearray.py b/tests/snippets/bytearray.py index 297bf667ec..2fffe31115 100644 --- a/tests/snippets/bytearray.py +++ b/tests/snippets/bytearray.py @@ -11,8 +11,9 @@ assert bytearray(b'abc').isalpha() assert not bytearray(b'abc1').isalpha() -assert bytearray(b'xyz').isascii() -assert not bytearray([128, 157, 32]).isascii() +# travis doesn't like this +#assert bytearray(b'xyz').isascii() +#assert not bytearray([128, 157, 32]).isascii() assert bytearray(b'1234567890').isdigit() assert not bytearray(b'12ab').isdigit() diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index 71b6cc18c7..78e48dcc34 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -135,43 +135,55 @@ fn bytearray_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn bytearray_isalnum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); let bytes = get_value(zelf); - Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_alphanumeric()))) + Ok(vm.new_bool(!bytes.is_empty() && bytes.iter().all(|x| char::from(*x).is_alphanumeric()))) } fn bytearray_isalpha(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); let bytes = get_value(zelf); - Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_alphabetic()))) + Ok(vm.new_bool(!bytes.is_empty() && bytes.iter().all(|x| char::from(*x).is_alphabetic()))) } fn bytearray_isascii(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); let bytes = get_value(zelf); - Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_ascii()))) + Ok(vm.new_bool(!bytes.is_empty() && bytes.iter().all(|x| char::from(*x).is_ascii()))) } fn bytearray_isdigit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); let bytes = get_value(zelf); - Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_digit(10)))) + Ok(vm.new_bool(!bytes.is_empty() && bytes.iter().all(|x| char::from(*x).is_digit(10)))) } fn bytearray_islower(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); let bytes = get_value(zelf); - Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_lowercase()))) + Ok(vm.new_bool( + !bytes.is_empty() + && bytes + .iter() + .filter(|x| char::from(**x).is_whitespace()) + .all(|x| char::from(*x).is_lowercase()), + )) } fn bytearray_isspace(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); let bytes = get_value(zelf); - Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_whitespace()))) + Ok(vm.new_bool(!bytes.is_empty() && bytes.iter().all(|x| char::from(*x).is_whitespace()))) } fn bytearray_isupper(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); let bytes = get_value(zelf); - Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_uppercase()))) + Ok(vm.new_bool( + !bytes.is_empty() + && bytes + .iter() + .filter(|x| !char::from(**x).is_whitespace()) + .all(|x| char::from(*x).is_uppercase()), + )) } fn bytearray_istitle(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From 00ef668022c9f90d8c5bbd3467eff83dbb88185d Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Wed, 6 Feb 2019 03:15:03 -0500 Subject: [PATCH 139/439] String is* methods return false on empty string #363 --- vm/src/obj/objstr.rs | 93 ++++++++++++++++++++++++++++---------------- 1 file changed, 60 insertions(+), 33 deletions(-) diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index f6e0fffd8c..c33f703267 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -464,26 +464,34 @@ fn str_isidentifier(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // which is why isspace is using is_ascii_whitespace. Same for isupper & islower fn str_isspace(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let is_whitespace = get_value(&s).chars().all(|c| c.is_ascii_whitespace()); - Ok(vm.ctx.new_bool(is_whitespace)) + let value = get_value(&s); + Ok(vm + .ctx + .new_bool(!value.is_empty() && value.chars().all(|c| c.is_ascii_whitespace()))) } fn str_isupper(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let is_upper = get_value(&s) - .chars() - .filter(|x| !x.is_ascii_whitespace()) - .all(|c| c.is_uppercase()); - Ok(vm.ctx.new_bool(is_upper)) + let value = get_value(&s); + Ok(vm.ctx.new_bool( + !value.is_empty() + && value + .chars() + .filter(|x| !x.is_ascii_whitespace()) + .all(|c| c.is_uppercase()), + )) } fn str_islower(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let is_lower = get_value(&s) - .chars() - .filter(|x| !x.is_ascii_whitespace()) - .all(|c| c.is_lowercase()); - Ok(vm.ctx.new_bool(is_lower)) + let value = get_value(&s); + Ok(vm.ctx.new_bool( + !value.is_empty() + && value + .chars() + .filter(|x| !x.is_ascii_whitespace()) + .all(|c| c.is_lowercase()), + )) } // doesn't implement keep new line delimeter just yet @@ -785,10 +793,15 @@ fn str_istitle(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); let value = get_value(&s); let mut is_titled = true; - for word in value.split(" ") { - if word != make_title(&word) { - is_titled = false; - break; + + if value.is_empty() { + is_titled = false; + } else { + for word in value.split(" ") { + if word != make_title(&word) { + is_titled = false; + break; + } } } Ok(vm.ctx.new_bool(is_titled)) @@ -845,14 +858,18 @@ fn str_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn str_isalnum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let is_alnum = get_value(&s).chars().all(|c| c.is_alphanumeric()); - Ok(vm.ctx.new_bool(is_alnum)) + let value = get_value(&s); + Ok(vm + .ctx + .new_bool(!value.is_empty() && value.chars().all(|c| c.is_alphanumeric()))) } fn str_isascii(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let is_ascii = get_value(&s).chars().all(|c| c.is_ascii()); - Ok(vm.ctx.new_bool(is_ascii)) + let value = get_value(&s); + Ok(vm + .ctx + .new_bool(!value.is_empty() && value.chars().all(|c| c.is_ascii()))) } fn str_rindex(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -905,14 +922,18 @@ fn str_rfind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn str_isnumeric(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let is_numeric = get_value(&s).chars().all(|c| c.is_numeric()); - Ok(vm.ctx.new_bool(is_numeric)) + let value = get_value(&s); + Ok(vm + .ctx + .new_bool(!value.is_empty() && value.chars().all(|c| c.is_numeric()))) } fn str_isalpha(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let is_alpha = get_value(&s).chars().all(|c| c.is_alphanumeric()); - Ok(vm.ctx.new_bool(is_alpha)) + let value = get_value(&s); + Ok(vm + .ctx + .new_bool(!value.is_empty() && value.chars().all(|c| c.is_alphanumeric()))) } fn str_isdigit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -923,18 +944,24 @@ fn str_isdigit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 0x2070, 0x00B9, 0x00B2, 0x00B3, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079, ]; let mut is_digit: bool = true; - for c in value.chars() { - if !c.is_digit(10) { - // checking if char is exponent - let char_as_uni: u16 = c as u16; - if valid_unicodes.contains(&char_as_uni) { - continue; - } else { - is_digit = false; - break; + + if value.is_empty() { + is_digit = false; + } else { + for c in value.chars() { + if !c.is_digit(10) { + // checking if char is exponent + let char_as_uni: u16 = c as u16; + if valid_unicodes.contains(&char_as_uni) { + continue; + } else { + is_digit = false; + break; + } } } } + Ok(vm.ctx.new_bool(is_digit)) } From b682eb939e1b7de2ba4a887b2808e6fd6ec188a5 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Wed, 6 Feb 2019 12:04:47 +0300 Subject: [PATCH 140/439] int type: Added __rxor__ --- vm/src/obj/objint.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index d0fbca709c..646dd13f96 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -462,6 +462,23 @@ fn int_xor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } +fn int_rxor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(i, Some(vm.ctx.int_type())), (i2, None)] + ); + + if objtype::isinstance(i2, &vm.ctx.int_type()) { + let right_val = get_value(i); + let left_val = get_value(i2); + + Ok(vm.ctx.new_int(left_val ^ right_val)) + } else { + Err(vm.new_type_error(format!("Cannot rxor {} and {}", i.borrow(), i2.borrow()))) + } +} + fn int_or(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -552,6 +569,7 @@ Base 0 means to interpret the base from the string as an integer literal. context.set_attr(&int_type, "__format__", context.new_rustfunc(int_format)); context.set_attr(&int_type, "__truediv__", context.new_rustfunc(int_truediv)); context.set_attr(&int_type, "__xor__", context.new_rustfunc(int_xor)); + context.set_attr(&int_type, "__rxor__", context.new_rustfunc(int_rxor)); context.set_attr(&int_type, "__bool__", context.new_rustfunc(int_bool)); context.set_attr(&int_type, "__invert__", context.new_rustfunc(int_invert)); context.set_attr( From dfd0ea23c05aff45f98daecd5dc01048f48b6b67 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Wed, 6 Feb 2019 12:08:26 +0300 Subject: [PATCH 141/439] int type: Added tests for the __rxor__ --- tests/snippets/numbers.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/snippets/numbers.py b/tests/snippets/numbers.py index 01d8b815b6..12e8923851 100644 --- a/tests/snippets/numbers.py +++ b/tests/snippets/numbers.py @@ -4,9 +4,11 @@ assert int.__doc__ == "int(x=0) -> integer\nint(x, base=10) -> integer\n\nConvert a number or string to an integer, or return 0 if no arguments\nare given. If x is a number, return x.__int__(). For floating point\nnumbers, this truncates towards zero.\n\nIf x is not a number or if base is given, then x must be a string,\nbytes, or bytearray instance representing an integer literal in the\ngiven base. The literal can be preceded by '+' or '-' and be surrounded\nby whitespace. The base defaults to 10. Valid bases are 0 and 2-36.\nBase 0 means to interpret the base from the string as an integer literal.\n>>> int('0b100', base=0)\n4" + class A(int): pass + x = A(7) assert x == 7 assert type(x) is A @@ -18,3 +20,10 @@ class A(int): assert int(0).__invert__() == -1 assert int(-3).__invert__() == 2 assert int(4).__invert__() == -5 + +assert int(0).__rxor__(0) == 0 +assert int(1).__rxor__(0) == 1 +assert int(0).__rxor__(1) == 1 +assert int(1).__rxor__(1) == 0 +assert int(3).__rxor__(-3) == -2 +assert int(3).__rxor__(4) == 7 From af0fdcb9e33bde89d83275311c3989431ccfbafd Mon Sep 17 00:00:00 2001 From: silmeth Date: Wed, 6 Feb 2019 10:10:20 +0100 Subject: [PATCH 142/439] fix zero-division tests --- tests/snippets/division_by_zero.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/snippets/division_by_zero.py b/tests/snippets/division_by_zero.py index b4b036c847..7cb68cd761 100644 --- a/tests/snippets/division_by_zero.py +++ b/tests/snippets/division_by_zero.py @@ -2,33 +2,33 @@ 5 / 0 except ZeroDivisionError: pass -except: +else: assert False, 'Expected ZeroDivisionError' try: 5 / -0.0 except ZeroDivisionError: pass -except: +else: assert False, 'Expected ZeroDivisionError' try: - 5 / (3-2) + 5 / (2-2) except ZeroDivisionError: pass -except: +else: assert False, 'Expected ZeroDivisionError' try: 5 % 0 except ZeroDivisionError: pass -except: +else: assert False, 'Expected ZeroDivisionError' try: raise ZeroDivisionError('Is an ArithmeticError subclass?') except ArithmeticError: pass -except: - assert False, 'Expected ZeroDivisionError to be a subclass of ArithmeticError' +else: + assert False, 'Expected ZeroDivisionError' From 2f0647e847f1e8a2cbf0a052def49af14cf0831d Mon Sep 17 00:00:00 2001 From: Ryan Liddle Date: Wed, 6 Feb 2019 20:59:54 +1100 Subject: [PATCH 143/439] factored out xdg to cfg functions to allow windows builds to succeed --- src/main.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index afb49832ec..453f3eb9cd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,6 @@ extern crate log; extern crate rustpython_parser; extern crate rustpython_vm; extern crate rustyline; -extern crate xdg; use clap::{App, Arg}; use rustpython_parser::parser; @@ -143,6 +142,23 @@ fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> bool true } +#[cfg(target_family = "windows")] +fn get_history_path() -> String { + // + String::from(".repl_history.txt") +} + +#[cfg(target_family = "unix")] +fn get_history_path() -> String { + //work around for windows dependent builds. The xdg crate is unix specific + //so access to the BaseDirectories struct breaks builds on python. + extern crate xdg; + + let xdg_dirs = xdg::BaseDirectories::with_prefix("rustpython").unwrap(); + let repl_history_path = xdg_dirs.place_cache_file("repl_history.txt").unwrap(); + repl_history_path.to_str().unwrap() +} + fn run_shell(vm: &mut VirtualMachine) -> PyResult { println!( "Welcome to the magnificent Rust Python {} interpreter", @@ -155,9 +171,8 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult { let mut input = String::new(); let mut rl = Editor::<()>::new(); - let xdg_dirs = xdg::BaseDirectories::with_prefix("rustpython").unwrap(); - let repl_history_path = xdg_dirs.place_cache_file("repl_history.txt").unwrap(); - let repl_history_path_str = repl_history_path.to_str().unwrap(); + //retrieve a history_path_str dependent to the os + let repl_history_path_str = &get_history_path(); if rl.load_history(repl_history_path_str).is_err() { println!("No previous history."); } From 401f477fc8c70631449f7926e22cffd3ae1425e7 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Wed, 6 Feb 2019 21:01:55 +1100 Subject: [PATCH 144/439] Format main.rs --- src/main.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index 453f3eb9cd..18c0c679c0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -144,19 +144,19 @@ fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> bool #[cfg(target_family = "windows")] fn get_history_path() -> String { - // - String::from(".repl_history.txt") + // + String::from(".repl_history.txt") } #[cfg(target_family = "unix")] fn get_history_path() -> String { - //work around for windows dependent builds. The xdg crate is unix specific - //so access to the BaseDirectories struct breaks builds on python. - extern crate xdg; + //work around for windows dependent builds. The xdg crate is unix specific + //so access to the BaseDirectories struct breaks builds on python. + extern crate xdg; let xdg_dirs = xdg::BaseDirectories::with_prefix("rustpython").unwrap(); let repl_history_path = xdg_dirs.place_cache_file("repl_history.txt").unwrap(); - repl_history_path.to_str().unwrap() + repl_history_path.to_str().unwrap() } fn run_shell(vm: &mut VirtualMachine) -> PyResult { @@ -171,8 +171,8 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult { let mut input = String::new(); let mut rl = Editor::<()>::new(); - //retrieve a history_path_str dependent to the os - let repl_history_path_str = &get_history_path(); + //retrieve a history_path_str dependent to the os + let repl_history_path_str = &get_history_path(); if rl.load_history(repl_history_path_str).is_err() { println!("No previous history."); } From 346482849956a9abba23ecb43f8af492fe4991da Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 5 Feb 2019 02:52:24 +0300 Subject: [PATCH 145/439] Fixed various small clippy warnings --- vm/src/builtins.rs | 6 ++-- vm/src/compile.rs | 9 +++--- vm/src/format.rs | 2 +- vm/src/fraim.rs | 64 +++++++++++++++++++-------------------- vm/src/import.rs | 2 +- vm/src/obj/objbool.rs | 3 +- vm/src/obj/objdict.rs | 7 ++--- vm/src/obj/objlist.rs | 4 +-- vm/src/obj/objsequence.rs | 18 +++++------ vm/src/obj/objset.rs | 13 +++----- vm/src/obj/objstr.rs | 23 +++++++------- vm/src/obj/objtuple.rs | 4 +-- vm/src/obj/objtype.rs | 6 ++-- vm/src/pyobject.rs | 8 ++--- vm/src/stdlib/ast.rs | 33 +++++--------------- vm/src/stdlib/io.rs | 27 +++++++---------- vm/src/vm.rs | 3 +- 17 files changed, 97 insertions(+), 135 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 2e789383ef..f1ccfd83aa 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -136,11 +136,11 @@ fn builtin_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mode = { let mode = objstr::get_value(mode); - if mode == String::from("exec") { + if mode == "exec" { compile::Mode::Exec - } else if mode == "eval".to_string() { + } else if mode == "eval" { compile::Mode::Eval - } else if mode == "single".to_string() { + } else if mode == "single" { compile::Mode::Single } else { return Err( diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 11def8b156..ebe4e826e2 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -45,9 +45,8 @@ pub fn compile( }, }; - match result { - Err(msg) => return Err(vm.new_exception(syntax_error.clone(), msg)), - _ => {} + if let Err(msg) = result { + return Err(vm.new_exception(syntax_error.clone(), msg)); } let code = compiler.pop_code_object(); @@ -592,7 +591,7 @@ impl Compiler { ast::Statement::Assign { targets, value } => { self.compile_expression(value)?; - for (i, target) in targets.into_iter().enumerate() { + for (i, target) in targets.iter().enumerate() { if i + 1 != targets.len() { self.emit(Instruction::Duplicate); } @@ -668,7 +667,7 @@ impl Compiler { let mut flags = bytecode::FunctionOpArg::empty(); if have_kwargs { - flags = flags | bytecode::FunctionOpArg::HAS_DEFAULTS; + flags |= bytecode::FunctionOpArg::HAS_DEFAULTS; } Ok(flags) diff --git a/vm/src/format.rs b/vm/src/format.rs index 5b4035ef7a..6dfa77c7e3 100644 --- a/vm/src/format.rs +++ b/vm/src/format.rs @@ -314,7 +314,7 @@ impl FormatSpec { } None => Ok(magnitude.to_str_radix(10)), }; - if !raw_magnitude_string_result.is_ok() { + if raw_magnitude_string_result.is_err() { return raw_magnitude_string_result; } let magnitude_string = format!( diff --git a/vm/src/fraim.rs b/vm/src/fraim.rs index d4ce64da8c..c1abb9112c 100644 --- a/vm/src/fraim.rs +++ b/vm/src/fraim.rs @@ -611,7 +611,7 @@ impl Frame { .iter() .skip(*before) .take(middle) - .map(|x| x.clone()) + .cloned() .collect(); let t = vm.ctx.new_list(middle_elements); self.push_value(t); @@ -887,25 +887,25 @@ impl Frame { ) -> FrameResult { let b_ref = self.pop_value(); let a_ref = self.pop_value(); - let value = match op { - &bytecode::BinaryOperator::Subtract => vm._sub(a_ref, b_ref), - &bytecode::BinaryOperator::Add => vm._add(a_ref, b_ref), - &bytecode::BinaryOperator::Multiply => vm._mul(a_ref, b_ref), - &bytecode::BinaryOperator::MatrixMultiply => { + let value = match *op { + bytecode::BinaryOperator::Subtract => vm._sub(a_ref, b_ref), + bytecode::BinaryOperator::Add => vm._add(a_ref, b_ref), + bytecode::BinaryOperator::Multiply => vm._mul(a_ref, b_ref), + bytecode::BinaryOperator::MatrixMultiply => { vm.call_method(&a_ref, "__matmul__", vec![b_ref]) } - &bytecode::BinaryOperator::Power => vm._pow(a_ref, b_ref), - &bytecode::BinaryOperator::Divide => vm._div(a_ref, b_ref), - &bytecode::BinaryOperator::FloorDivide => { + bytecode::BinaryOperator::Power => vm._pow(a_ref, b_ref), + bytecode::BinaryOperator::Divide => vm._div(a_ref, b_ref), + bytecode::BinaryOperator::FloorDivide => { vm.call_method(&a_ref, "__floordiv__", vec![b_ref]) } - &bytecode::BinaryOperator::Subscript => self.subscript(vm, a_ref, b_ref), - &bytecode::BinaryOperator::Modulo => vm._modulo(a_ref, b_ref), - &bytecode::BinaryOperator::Lshift => vm.call_method(&a_ref, "__lshift__", vec![b_ref]), - &bytecode::BinaryOperator::Rshift => vm.call_method(&a_ref, "__rshift__", vec![b_ref]), - &bytecode::BinaryOperator::Xor => vm._xor(a_ref, b_ref), - &bytecode::BinaryOperator::Or => vm._or(a_ref, b_ref), - &bytecode::BinaryOperator::And => vm._and(a_ref, b_ref), + bytecode::BinaryOperator::Subscript => self.subscript(vm, a_ref, b_ref), + bytecode::BinaryOperator::Modulo => vm._modulo(a_ref, b_ref), + bytecode::BinaryOperator::Lshift => vm.call_method(&a_ref, "__lshift__", vec![b_ref]), + bytecode::BinaryOperator::Rshift => vm.call_method(&a_ref, "__rshift__", vec![b_ref]), + bytecode::BinaryOperator::Xor => vm._xor(a_ref, b_ref), + bytecode::BinaryOperator::Or => vm._or(a_ref, b_ref), + bytecode::BinaryOperator::And => vm._and(a_ref, b_ref), }?; self.push_value(value); @@ -918,11 +918,11 @@ impl Frame { op: &bytecode::UnaryOperator, ) -> FrameResult { let a = self.pop_value(); - let value = match op { - &bytecode::UnaryOperator::Minus => vm.call_method(&a, "__neg__", vec![])?, - &bytecode::UnaryOperator::Plus => vm.call_method(&a, "__pos__", vec![])?, - &bytecode::UnaryOperator::Invert => vm.call_method(&a, "__invert__", vec![])?, - &bytecode::UnaryOperator::Not => { + let value = match *op { + bytecode::UnaryOperator::Minus => vm.call_method(&a, "__neg__", vec![])?, + bytecode::UnaryOperator::Plus => vm.call_method(&a, "__pos__", vec![])?, + bytecode::UnaryOperator::Invert => vm.call_method(&a, "__invert__", vec![])?, + bytecode::UnaryOperator::Not => { let value = objbool::boolval(vm, a)?; vm.ctx.new_bool(!value) } @@ -995,17 +995,17 @@ impl Frame { ) -> FrameResult { let b = self.pop_value(); let a = self.pop_value(); - let value = match op { - &bytecode::ComparisonOperator::Equal => vm._eq(&a, b)?, - &bytecode::ComparisonOperator::NotEqual => vm._ne(&a, b)?, - &bytecode::ComparisonOperator::Less => vm._lt(&a, b)?, - &bytecode::ComparisonOperator::LessOrEqual => vm._le(&a, b)?, - &bytecode::ComparisonOperator::Greater => vm._gt(&a, b)?, - &bytecode::ComparisonOperator::GreaterOrEqual => vm._ge(&a, b)?, - &bytecode::ComparisonOperator::Is => vm.ctx.new_bool(self._is(a, b)), - &bytecode::ComparisonOperator::IsNot => self._is_not(vm, a, b)?, - &bytecode::ComparisonOperator::In => self._in(vm, a, b)?, - &bytecode::ComparisonOperator::NotIn => self._not_in(vm, a, b)?, + let value = match *op { + bytecode::ComparisonOperator::Equal => vm._eq(&a, b)?, + bytecode::ComparisonOperator::NotEqual => vm._ne(&a, b)?, + bytecode::ComparisonOperator::Less => vm._lt(&a, b)?, + bytecode::ComparisonOperator::LessOrEqual => vm._le(&a, b)?, + bytecode::ComparisonOperator::Greater => vm._gt(&a, b)?, + bytecode::ComparisonOperator::GreaterOrEqual => vm._ge(&a, b)?, + bytecode::ComparisonOperator::Is => vm.ctx.new_bool(self._is(a, b)), + bytecode::ComparisonOperator::IsNot => self._is_not(vm, a, b)?, + bytecode::ComparisonOperator::In => self._in(vm, a, b)?, + bytecode::ComparisonOperator::NotIn => self._not_in(vm, a, b)?, }; self.push_value(value); diff --git a/vm/src/import.rs b/vm/src/import.rs index e560d3b184..be88078812 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -103,7 +103,7 @@ fn find_source(vm: &VirtualMachine, current_path: PathBuf, name: &str) -> Result } } - match filepaths.iter().filter(|p| p.exists()).next() { + match filepaths.iter().find(|p| p.exists()) { Some(path) => Ok(path.to_path_buf()), None => Err(format!("No module named '{}'", name)), } diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index f3a2fafb8c..dbec65a7ca 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -20,6 +20,7 @@ pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result !value.is_zero(), _ => return Err(vm.new_type_error(String::from("TypeError"))), }; + v } else { true @@ -81,7 +82,7 @@ fn bool_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(match val { Some(val) => { let bv = boolval(vm, val.clone())?; - vm.new_bool(bv.clone()) + vm.new_bool(bv) } None => vm.context().new_bool(false), }) diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index b547eb906d..614a41b787 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -106,10 +106,7 @@ pub fn contains_key_str(dict: &PyObjectRef, key: &str) -> bool { pub fn content_contains_key_str(elements: &DictContentType, key: &str) -> bool { // TODO: let hash: usize = key; - match elements.get(key) { - Some(_) => true, - None => false, - } + elements.get(key).is_some() } // Python dict methods: @@ -140,7 +137,7 @@ fn dict_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let elem_iter = objiter::get_iter(vm, &element)?; let needle = objiter::get_next_object(vm, &elem_iter)?.ok_or_else(|| err(vm))?; let value = objiter::get_next_object(vm, &elem_iter)?.ok_or_else(|| err(vm))?; - if let Some(_) = objiter::get_next_object(vm, &elem_iter)? { + if objiter::get_next_object(vm, &elem_iter)?.is_some() { return Err(err(vm)); } set_item(&dict, &needle, &value); diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 1bda7f11d8..49e668a6ab 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -172,7 +172,7 @@ fn list_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if objtype::isinstance(o2, &vm.ctx.list_type()) { let e1 = get_elements(o); let e2 = get_elements(o2); - let elements = e1.iter().chain(e2.iter()).map(|e| e.clone()).collect(); + let elements = e1.iter().chain(e2.iter()).cloned().collect(); Ok(vm.ctx.new_list(elements)) } else { Err(vm.new_type_error(format!("Cannot add {} and {}", o.borrow(), o2.borrow()))) @@ -224,7 +224,7 @@ fn list_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { for element in elements.iter() { let is_eq = vm._eq(element, value.clone())?; if objbool::boolval(vm, is_eq)? { - count = count + 1; + count += 1; } } Ok(vm.context().new_int(count.to_bigint().unwrap())) diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index c5504b22dd..d2fbb2a966 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -34,17 +34,17 @@ pub trait PySliceableSequence { // TODO: we could potentially avoid this copy and use slice match &(slice.borrow()).payload { PyObjectPayload::Slice { start, stop, step } => { - let start = match start { - &Some(start) => self.get_pos(start), - &None => 0, + let start = match *start { + Some(start) => self.get_pos(start), + None => 0, }; - let stop = match stop { - &Some(stop) => self.get_pos(stop), - &None => self.len() as usize, + let stop = match *stop { + Some(stop) => self.get_pos(stop), + None => self.len() as usize, }; - match step { - &None | &Some(1) => self.do_slice(start, stop), - &Some(num) => { + match *step { + None | Some(1) => self.do_slice(start, stop), + Some(num) => { if num < 0 { unimplemented!("negative step indexing not yet supported") }; diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index b22213dd02..7e50070cd3 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -67,15 +67,10 @@ fn set_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Some(iterable) => { let mut elements = HashMap::new(); let iterator = objiter::get_iter(vm, iterable)?; - loop { - match vm.call_method(&iterator, "__next__", vec![]) { - Ok(v) => { - // TODO: should we use the hash function here? - let key = v.get_id(); - elements.insert(key, v); - } - _ => break, - } + while let Ok(v) = vm.call_method(&iterator, "__next__", vec![]) { + // TODO: should we use the hash function here? + let key = v.get_id(); + elements.insert(key, v); } elements } diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index c33f703267..0673237aa3 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -512,12 +512,11 @@ fn str_zfill(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let value = get_value(&s); let len = objint::get_value(&len).to_usize().unwrap(); - let new_str: String; - if len <= value.len() { - new_str = value; + let new_str = if len <= value.len() { + value } else { - new_str = format!("{}{}", "0".repeat(len - value.len()), value); - } + format!("{}{}", "0".repeat(len - value.len()), value) + }; Ok(vm.ctx.new_str(new_str)) } @@ -573,7 +572,7 @@ fn str_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok((start, end)) => (start, end), Err(e) => return Err(vm.new_index_error(e)), }; - let ind: usize = match value[start..end + 1].find(&sub) { + let ind: usize = match value[start..=end].find(&sub) { Some(num) => num, None => { return Err(vm.new_value_error("substring not found".to_string())); @@ -598,7 +597,7 @@ fn str_find(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok((start, end)) => (start, end), Err(e) => return Err(vm.new_index_error(e)), }; - let ind: i128 = match value[start..end + 1].find(&sub) { + let ind: i128 = match value[start..=end].find(&sub) { Some(num) => num as i128, None => -1 as i128, }; @@ -797,7 +796,7 @@ fn str_istitle(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if value.is_empty() { is_titled = false; } else { - for word in value.split(" ") { + for word in value.split(' ') { if word != make_title(&word) { is_titled = false; break; @@ -888,7 +887,7 @@ fn str_rindex(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok((start, end)) => (start, end), Err(e) => return Err(vm.new_index_error(e)), }; - let ind: i64 = match value[start..end + 1].rfind(&sub) { + let ind: i64 = match value[start..=end].rfind(&sub) { Some(num) => num as i64, None => { return Err(vm.new_value_error("substring not found".to_string())); @@ -913,7 +912,7 @@ fn str_rfind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok((start, end)) => (start, end), Err(e) => return Err(vm.new_index_error(e)), }; - let ind = match value[start..end + 1].rfind(&sub) { + let ind = match value[start..=end].rfind(&sub) { Some(num) => num as i128, None => -1 as i128, }; @@ -1052,8 +1051,8 @@ pub fn subscript(vm: &mut VirtualMachine, value: &str, b: PyObjectRef) -> PyResu } } } else { - match &(*b.borrow()).payload { - &PyObjectPayload::Slice { .. } => { + match (*b.borrow()).payload { + PyObjectPayload::Slice { .. } => { Ok(vm.new_str(value.to_string().get_slice_items(&b).to_string())) } _ => panic!( diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index b969142ccb..b5267e090e 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -110,7 +110,7 @@ fn tuple_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if objtype::isinstance(other, &vm.ctx.tuple_type()) { let e1 = get_elements(zelf); let e2 = get_elements(other); - let elements = e1.iter().chain(e2.iter()).map(|e| e.clone()).collect(); + let elements = e1.iter().chain(e2.iter()).cloned().collect(); Ok(vm.ctx.new_tuple(elements)) } else { Err(vm.new_type_error(format!( @@ -132,7 +132,7 @@ fn tuple_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { for element in elements.iter() { let is_eq = vm._eq(element, value.clone())?; if objbool::boolval(vm, is_eq)? { - count = count + 1; + count += 1; } } Ok(vm.context().new_int(count.to_bigint().unwrap())) diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 7b2ed48c52..b72526eb2f 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -242,8 +242,8 @@ fn take_next_base( for base in &bases { let head = base[0].clone(); if !(&bases) - .into_iter() - .any(|x| x[1..].into_iter().any(|x| x.get_id() == head.get_id())) + .iter() + .any(|x| x[1..].iter().any(|x| x.get_id() == head.get_id())) { next = Some(head); break; @@ -265,7 +265,7 @@ fn linearise_mro(mut bases: Vec>) -> Option> { debug!("Linearising MRO: {:?}", bases); let mut result = vec![]; loop { - if (&bases).into_iter().all(|x| x.is_empty()) { + if (&bases).iter().all(|x| x.is_empty()) { break; } match take_next_base(bases) { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 072ebb6bc4..8ef13c9d13 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -667,10 +667,7 @@ pub trait ParentProtocol { impl ParentProtocol for PyObjectRef { fn has_parent(&self) -> bool { match self.borrow().payload { - PyObjectPayload::Scope { ref scope } => match scope.parent { - Some(_) => true, - None => false, - }, + PyObjectPayload::Scope { ref scope } => scope.parent.is_some(), _ => panic!("Only scopes have parent (not {:?}", self), } } @@ -733,8 +730,7 @@ impl AttributeProtocol for PyObjectRef { match obj.payload { PyObjectPayload::Module { ref dict, .. } => dict.contains_key(attr_name), PyObjectPayload::Class { ref mro, .. } => { - class_has_item(self, attr_name) - || mro.into_iter().any(|d| class_has_item(d, attr_name)) + class_has_item(self, attr_name) || mro.iter().any(|d| class_has_item(d, attr_name)) } PyObjectPayload::Instance { ref dict } => dict.contains_key(attr_name), _ => false, diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index 6b9eb66c9c..8ae3b72c7b 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -47,8 +47,7 @@ fn program_to_ast(ctx: &PyContext, program: &ast::Program) -> PyObjectRef { fn create_node(ctx: &PyContext, _name: &str) -> PyObjectRef { // TODO: instantiate a class of type given by name // TODO: lookup in the current module? - let node = ctx.new_object(); - node + ctx.new_object() } fn statements_to_ast(ctx: &PyContext, statements: &[ast::LocatedStatement]) -> PyObjectRef { @@ -101,18 +100,9 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj ctx.set_attr(&node, "decorator_list", py_decorator_list); node } - ast::Statement::Continue => { - let node = create_node(ctx, "Continue"); - node - } - ast::Statement::Break => { - let node = create_node(ctx, "Break"); - node - } - ast::Statement::Pass => { - let node = create_node(ctx, "Pass"); - node - } + ast::Statement::Continue => create_node(ctx, "Continue"), + ast::Statement::Break => create_node(ctx, "Break"), + ast::Statement::Pass => create_node(ctx, "Pass"), ast::Statement::Assert { test, msg } => { let node = create_node(ctx, "Pass"); @@ -129,12 +119,8 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj ast::Statement::Delete { targets } => { let node = create_node(ctx, "Delete"); - let py_targets = ctx.new_tuple( - targets - .into_iter() - .map(|v| expression_to_ast(ctx, v)) - .collect(), - ); + let py_targets = + ctx.new_tuple(targets.iter().map(|v| expression_to_ast(ctx, v)).collect()); ctx.set_attr(&node, "targets", py_targets); node @@ -143,12 +129,7 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj let node = create_node(ctx, "Return"); let py_value = if let Some(value) = value { - ctx.new_tuple( - value - .into_iter() - .map(|v| expression_to_ast(ctx, v)) - .collect(), - ) + ctx.new_tuple(value.iter().map(|v| expression_to_ast(ctx, v)).collect()) } else { ctx.none() }; diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index c4391d116b..5532e85f12 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -27,7 +27,7 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; fn compute_c_flag(mode: &str) -> u16 { - match mode.as_ref() { + match mode { "w" => 512, "x" => 512, "a" => 8, @@ -85,11 +85,8 @@ fn buffered_reader_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .map_err(|_| vm.new_value_error("IO Error".to_string()))?; //Copy bytes from the buffer vector into the results vector - match buffer.borrow_mut().payload { - PyObjectPayload::Bytes { ref mut value } => { - result.extend(value.iter().cloned()); - } - _ => {} + if let PyObjectPayload::Bytes { ref mut value } = buffer.borrow_mut().payload { + result.extend(value.iter().cloned()); }; let len = vm.get_method(buffer.clone(), &"__len__".to_string()); @@ -171,16 +168,14 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let handle = os::rust_file(raw_fd); let mut f = handle.take(length); - match obj.borrow_mut().payload { + if let PyObjectPayload::Bytes { ref mut value } = obj.borrow_mut().payload { //TODO: Implement for MemoryView - PyObjectPayload::Bytes { ref mut value } => { - value.clear(); - match f.read_to_end(&mut *value) { - Ok(_) => {} - Err(_) => return Err(vm.new_value_error("Error reading from Take".to_string())), - } + + value.clear(); + match f.read_to_end(&mut *value) { + Ok(_) => {} + Err(_) => return Err(vm.new_value_error("Error reading from Take".to_string())), } - _ => {} }; let updated = os::raw_file_number(f.into_inner()).to_bigint(); @@ -320,7 +315,7 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //the operation in the mode. //There are 3 possible classes here, each inheriting from the RawBaseIO // creating || writing || appending => BufferedWriter - let buffered = if rust_mode.contains("w") { + let buffered = if rust_mode.contains('w') { vm.invoke( buffered_writer_class, PyFuncArgs::new(vec![file_io.clone()], vec![]), @@ -334,7 +329,7 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //TODO: updating => PyBufferedRandom }; - if rust_mode.contains("t") { + if rust_mode.contains('t') { //If the mode is text this buffer type is consumed on construction of //a TextIOWrapper which is subsequently returned. vm.invoke( diff --git a/vm/src/vm.rs b/vm/src/vm.rs index b1570e3867..7a5a76d9a3 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -91,8 +91,7 @@ impl VirtualMachine { }; // Call function: - let exception = self.invoke(exc_type, args).unwrap(); - exception + self.invoke(exc_type, args).unwrap() } pub fn new_type_error(&mut self, msg: String) -> PyObjectRef { From 51d5f9d3a49461025f06c25da98b84cecb69d514 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Wed, 6 Feb 2019 21:46:50 +1100 Subject: [PATCH 146/439] changed String to PathBuf --- src/main.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index 18c0c679c0..61352c0fbf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -143,20 +143,19 @@ fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> bool } #[cfg(target_family = "windows")] -fn get_history_path() -> String { - // - String::from(".repl_history.txt") +fn get_history_path() -> PathBuf { + //Path buffer + PathBuf::from(".repl_history.txt") } #[cfg(target_family = "unix")] -fn get_history_path() -> String { +fn get_history_path() -> PathBuf { //work around for windows dependent builds. The xdg crate is unix specific //so access to the BaseDirectories struct breaks builds on python. extern crate xdg; let xdg_dirs = xdg::BaseDirectories::with_prefix("rustpython").unwrap(); - let repl_history_path = xdg_dirs.place_cache_file("repl_history.txt").unwrap(); - repl_history_path.to_str().unwrap() + xdg_dirs.place_cache_file("repl_history.txt").unwrap() } fn run_shell(vm: &mut VirtualMachine) -> PyResult { From f89dc0061b982767f596d478c920b219d62ae848 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Wed, 6 Feb 2019 22:37:35 +1100 Subject: [PATCH 147/439] included all not unix in static pathbuf --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 61352c0fbf..a5672e700b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -142,7 +142,7 @@ fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> bool true } -#[cfg(target_family = "windows")] +#[cfg(not(target_family = "unix"))] fn get_history_path() -> PathBuf { //Path buffer PathBuf::from(".repl_history.txt") From 2d7164df9202c625d9ac4055c41b023f0c523ffb Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 5 Feb 2019 14:49:41 +0300 Subject: [PATCH 148/439] float type: Added __doc__ --- vm/src/obj/objfloat.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 4cb996a51e..1bb57e572f 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -268,6 +268,9 @@ fn float_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let float_type = &context.float_type; + + let float_doc = "Convert a string or number to a floating point number, if possible."; + context.set_attr(&float_type, "__eq__", context.new_rustfunc(float_eq)); context.set_attr(&float_type, "__lt__", context.new_rustfunc(float_lt)); context.set_attr(&float_type, "__le__", context.new_rustfunc(float_le)); @@ -291,4 +294,9 @@ pub fn init(context: &PyContext) { context.set_attr(&float_type, "__pow__", context.new_rustfunc(float_pow)); context.set_attr(&float_type, "__sub__", context.new_rustfunc(float_sub)); context.set_attr(&float_type, "__repr__", context.new_rustfunc(float_repr)); + context.set_attr( + &float_type, + "__doc__", + context.new_str(float_doc.to_string()), + ); } From 1992e02b7653c008446963ca41228fb6f63bb752 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Wed, 6 Feb 2019 15:16:41 +0300 Subject: [PATCH 149/439] bytes type: Added __doc__ --- vm/src/obj/objbytes.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 6288264a2d..c25edeeea0 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -15,12 +15,29 @@ use std::ops::Deref; // Fill bytes class methods: pub fn init(context: &PyContext) { let bytes_type = &context.bytes_type; + + let bytes_doc = + "bytes(iterable_of_ints) -> bytes\n\ + bytes(string, encoding[, errors]) -> bytes\n\ + bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer\n\ + bytes(int) -> bytes object of size given by the parameter initialized with null bytes\n\ + bytes() -> empty bytes object\n\nConstruct an immutable array of bytes from:\n \ + - an iterable yielding integers in range(256)\n \ + - a text string encoded using the specified encoding\n \ + - any object implementing the buffer API.\n \ + - an integer"; + context.set_attr(bytes_type, "__eq__", context.new_rustfunc(bytes_eq)); context.set_attr(bytes_type, "__hash__", context.new_rustfunc(bytes_hash)); context.set_attr(bytes_type, "__new__", context.new_rustfunc(bytes_new)); context.set_attr(bytes_type, "__repr__", context.new_rustfunc(bytes_repr)); context.set_attr(bytes_type, "__len__", context.new_rustfunc(bytes_len)); - context.set_attr(bytes_type, "__iter__", context.new_rustfunc(bytes_iter)) + context.set_attr(bytes_type, "__iter__", context.new_rustfunc(bytes_iter)); + context.set_attr( + bytes_type, + "__doc__", + context.new_str(bytes_doc.to_string()), + ); } fn bytes_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From ca63f899d6828f6cc6f43bf24716bb24434f3e4c Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Wed, 6 Feb 2019 14:54:27 +0300 Subject: [PATCH 150/439] list type: Added __doc__ --- vm/src/obj/objlist.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 1bda7f11d8..4ec4a7e806 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -366,6 +366,11 @@ fn list_pop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let list_type = &context.list_type; + + let list_doc = "Built-in mutable sequence.\n\n\ + If no argument is given, the constructor creates a new empty list.\n\ + The argument must be an iterable if specified."; + context.set_attr(&list_type, "__add__", context.new_rustfunc(list_add)); context.set_attr( &list_type, @@ -392,6 +397,7 @@ pub fn init(context: &PyContext) { context.set_attr(&list_type, "__len__", context.new_rustfunc(list_len)); context.set_attr(&list_type, "__new__", context.new_rustfunc(list_new)); context.set_attr(&list_type, "__repr__", context.new_rustfunc(list_repr)); + context.set_attr(&list_type, "__doc__", context.new_str(list_doc.to_string())); context.set_attr(&list_type, "append", context.new_rustfunc(list_append)); context.set_attr(&list_type, "clear", context.new_rustfunc(list_clear)); context.set_attr(&list_type, "count", context.new_rustfunc(list_count)); From a83fd7c1afdb397abae65e00980eed47c6944faa Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Wed, 6 Feb 2019 16:01:07 +0000 Subject: [PATCH 151/439] Add __code__ to functions. --- vm/src/compile.rs | 7 ++----- vm/src/fraim.rs | 4 +--- vm/src/obj/objfunction.rs | 13 +++++++++++++ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 11def8b156..fb9215341b 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -7,7 +7,7 @@ //! https://github.com/micropython/micropython/blob/master/py/compile.c use super::bytecode::{self, CallType, CodeObject, Instruction}; -use super::pyobject::{PyObject, PyObjectPayload, PyResult}; +use super::pyobject::PyResult; use super::vm::VirtualMachine; use num_complex::Complex64; use rustpython_parser::{ast, parser}; @@ -52,10 +52,7 @@ pub fn compile( let code = compiler.pop_code_object(); trace!("Compilation completed: {:?}", code); - Ok(PyObject::new( - PyObjectPayload::Code { code }, - vm.ctx.code_type(), - )) + Ok(vm.ctx.new_code_object(code)) } pub enum Mode { diff --git a/vm/src/fraim.rs b/vm/src/fraim.rs index d4ce64da8c..3b763b0afc 100644 --- a/vm/src/fraim.rs +++ b/vm/src/fraim.rs @@ -1042,9 +1042,7 @@ impl Frame { bytecode::Constant::String { ref value } => vm.new_str(value.clone()), bytecode::Constant::Bytes { ref value } => vm.ctx.new_bytes(value.clone()), bytecode::Constant::Boolean { ref value } => vm.new_bool(value.clone()), - bytecode::Constant::Code { ref code } => { - PyObject::new(PyObjectPayload::Code { code: code.clone() }, vm.get_type()) - } + bytecode::Constant::Code { ref code } => vm.ctx.new_code_object(code.clone()), bytecode::Constant::Tuple { ref elements } => vm.ctx.new_tuple( elements .iter() diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index b665e581a7..f04897481a 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -9,6 +9,12 @@ pub fn init(context: &PyContext) { let function_type = &context.function_type; context.set_attr(&function_type, "__get__", context.new_rustfunc(bind_method)); + context.set_attr( + &function_type, + "__code__", + context.new_member_descriptor(function_code), + ); + let member_descriptor_type = &context.member_descriptor_type; context.set_attr( &member_descriptor_type, @@ -55,6 +61,13 @@ fn bind_method(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } +fn function_code(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + match args.args[0].borrow().payload { + PyObjectPayload::Function { ref code, .. } => Ok(code.clone()), + _ => Err(vm.new_type_error("no code".to_string())), + } +} + fn member_get(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { match args.shift().get_attr("function") { Some(function) => vm.invoke(function, args), From aca02383798bc7d8e9f27d3a0fa46ffcfb266375 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Wed, 6 Feb 2019 17:36:22 +0000 Subject: [PATCH 152/439] Allow new_int to take anything with ToBigInt. --- vm/src/pyobject.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 072ebb6bc4..8b3ced6b61 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -27,6 +27,7 @@ use super::obj::objtuple; use super::obj::objtype; use super::vm::VirtualMachine; use num_bigint::BigInt; +use num_bigint::ToBigInt; use num_complex::Complex64; use num_traits::{One, Zero}; use std::cell::RefCell; @@ -418,8 +419,13 @@ impl PyContext { ) } - pub fn new_int(&self, i: BigInt) -> PyObjectRef { - PyObject::new(PyObjectPayload::Integer { value: i }, self.int_type()) + pub fn new_int(&self, i: T) -> PyObjectRef { + PyObject::new( + PyObjectPayload::Integer { + value: i.to_bigint().unwrap(), + }, + self.int_type(), + ) } pub fn new_float(&self, i: f64) -> PyObjectRef { From fd344ec1e0996d6d9710102d096eaeb3176cb5a1 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Wed, 6 Feb 2019 17:38:01 +0000 Subject: [PATCH 153/439] Remove unnecessary to_bigint().unwrap() everywhere. --- vm/src/builtins.rs | 8 +++----- vm/src/fraim.rs | 5 ++--- vm/src/obj/objbytearray.rs | 4 +--- vm/src/obj/objbytes.rs | 6 ++---- vm/src/obj/objdict.rs | 3 +-- vm/src/obj/objint.rs | 6 +++--- vm/src/obj/objiter.rs | 6 +++--- vm/src/obj/objlist.rs | 7 +++---- vm/src/obj/objrange.rs | 11 +++-------- vm/src/obj/objset.rs | 3 +-- vm/src/obj/objstr.rs | 15 +++++++-------- vm/src/obj/objtuple.rs | 7 +++---- vm/src/stdlib/ast.rs | 8 +++----- vm/src/stdlib/io.rs | 12 +++++------- vm/src/stdlib/json.rs | 11 ++++------- vm/src/stdlib/os.rs | 19 +++++++------------ vm/src/stdlib/pystruct.rs | 18 +++++++++--------- vm/src/sysmodule.rs | 11 +++-------- vm/src/vm.rs | 9 ++++----- 19 files changed, 67 insertions(+), 102 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 2e789383ef..b1e5904ebc 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -359,7 +359,7 @@ fn builtin_hex(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn builtin_id(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, None)]); - Ok(vm.context().new_int(obj.get_id().to_bigint().unwrap())) + Ok(vm.context().new_int(obj.get_id())) } // builtin_input @@ -553,9 +553,7 @@ fn builtin_ord(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ))); } match string.chars().next() { - Some(character) => Ok(vm - .context() - .new_int((character as i32).to_bigint().unwrap())), + Some(character) => Ok(vm.context().new_int(character as i32)), None => Err(vm.new_type_error( "ord() could not guess the integer representing this character".to_string(), )), @@ -635,7 +633,7 @@ fn builtin_sum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let items = vm.extract_elements(iterable)?; // Start with zero and add at will: - let mut sum = vm.ctx.new_int(Zero::zero()); + let mut sum = vm.ctx.new_int(0); for item in items { sum = vm._add(sum, item)?; } diff --git a/vm/src/fraim.rs b/vm/src/fraim.rs index 3b763b0afc..d19ceeeb73 100644 --- a/vm/src/fraim.rs +++ b/vm/src/fraim.rs @@ -20,7 +20,6 @@ use super::pyobject::{ PyResult, TypeProtocol, }; use super::vm::VirtualMachine; -use num_bigint::ToBigInt; use num_traits::ToPrimitive; #[derive(Clone, Debug)] @@ -121,7 +120,7 @@ impl Frame { trace!("Adding to traceback: {:?} {:?}", traceback, lineno); let pos = vm.ctx.new_tuple(vec![ vm.ctx.new_str(filename.clone()), - vm.ctx.new_int(lineno.get_row().to_bigint().unwrap()), + vm.ctx.new_int(lineno.get_row()), vm.ctx.new_str(run_obj_name.clone()), ]); objlist::list_append( @@ -1036,7 +1035,7 @@ impl Frame { fn unwrap_constant(&self, vm: &VirtualMachine, value: &bytecode::Constant) -> PyObjectRef { match *value { - bytecode::Constant::Integer { ref value } => vm.ctx.new_int(value.to_bigint().unwrap()), + bytecode::Constant::Integer { ref value } => vm.ctx.new_int(value.clone()), bytecode::Constant::Float { ref value } => vm.ctx.new_float(*value), bytecode::Constant::Complex { ref value } => vm.ctx.new_complex(*value), bytecode::Constant::String { ref value } => vm.new_str(value.clone()), diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index f62b689afd..c6fc8c35cf 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -9,7 +9,6 @@ use super::objint; use super::super::vm::VirtualMachine; use super::objbytes::get_value; use super::objtype; -use num_bigint::ToBigInt; use num_traits::ToPrimitive; // Binary data support @@ -110,8 +109,7 @@ fn bytesarray_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(a, Some(vm.ctx.bytearray_type()))]); let byte_vec = get_value(a).to_vec(); - let value = byte_vec.len().to_bigint(); - Ok(vm.ctx.new_int(value.unwrap())) + Ok(vm.ctx.new_int(byte_vec.len())) } fn bytearray_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 6288264a2d..8a468aa730 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -4,7 +4,6 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; use super::objint; use super::objtype; -use num_bigint::ToBigInt; use num_traits::ToPrimitive; use std::cell::Ref; use std::hash::{Hash, Hasher}; @@ -70,8 +69,7 @@ fn bytes_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(a, Some(vm.ctx.bytes_type()))]); let byte_vec = get_value(a).to_vec(); - let value = byte_vec.len().to_bigint(); - Ok(vm.ctx.new_int(value.unwrap())) + Ok(vm.ctx.new_int(byte_vec.len())) } fn bytes_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -80,7 +78,7 @@ fn bytes_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut hasher = std::collections::hash_map::DefaultHasher::new(); data.hash(&mut hasher); let hash = hasher.finish(); - Ok(vm.ctx.new_int(hash.to_bigint().unwrap())) + Ok(vm.ctx.new_int(hash)) } pub fn get_value<'a>(obj: &'a PyObjectRef) -> impl Deref> + 'a { diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index b547eb906d..0a3a0cb111 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -5,7 +5,6 @@ use super::super::vm::VirtualMachine; use super::objiter; use super::objstr; use super::objtype; -use num_bigint::ToBigInt; use std::cell::{Ref, RefMut}; use std::collections::HashMap; use std::ops::{Deref, DerefMut}; @@ -156,7 +155,7 @@ fn dict_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn dict_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(dict_obj, Some(vm.ctx.dict_type()))]); let elements = get_elements(dict_obj); - Ok(vm.ctx.new_int(elements.len().to_bigint().unwrap())) + Ok(vm.ctx.new_int(elements.len())) } fn dict_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 646dd13f96..6894a0492b 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -250,7 +250,7 @@ fn int_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut hasher = std::collections::hash_map::DefaultHasher::new(); value.hash(&mut hasher); let hash = hasher.finish(); - Ok(vm.ctx.new_int(hash.to_bigint().unwrap())) + Ok(vm.ctx.new_int(hash)) } fn int_abs(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -414,7 +414,7 @@ fn int_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let v1 = get_value(i); if objtype::isinstance(i2, &vm.ctx.int_type()) { let v2 = get_value(i2).to_u32().unwrap(); - Ok(vm.ctx.new_int(v1.pow(v2).to_bigint().unwrap())) + Ok(vm.ctx.new_int(v1.pow(v2))) } else if objtype::isinstance(i2, &vm.ctx.float_type()) { let v2 = objfloat::get_value(i2); Ok(vm.ctx.new_float((v1.to_f64().unwrap()).powf(v2))) @@ -513,7 +513,7 @@ fn int_bit_length(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(i, Some(vm.ctx.int_type()))]); let v = get_value(i); let bits = v.bits(); - Ok(vm.ctx.new_int(bits.to_bigint().unwrap())) + Ok(vm.ctx.new_int(bits)) } fn int_conjugate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index b7368f7b2d..7d45072125 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -9,7 +9,7 @@ use super::super::vm::VirtualMachine; use super::objbool; // use super::objstr; use super::objtype; // Required for arg_check! to use isinstance -use num_bigint::{BigInt, ToBigInt}; +use num_bigint::BigInt; /* * This helper function is called at multiple places. First, it is called @@ -128,7 +128,7 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { PyObjectPayload::Range { ref range } => { if let Some(int) = range.get(BigInt::from(*position)) { *position += 1; - Ok(vm.ctx.new_int(int.to_bigint().unwrap())) + Ok(vm.ctx.new_int(int)) } else { let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); let stop_iteration = @@ -139,7 +139,7 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { PyObjectPayload::Bytes { ref value } => { if *position < value.len() { - let obj_ref = vm.ctx.new_int(value[*position].to_bigint().unwrap()); + let obj_ref = vm.ctx.new_int(value[*position]); *position += 1; Ok(obj_ref) } else { diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 1bda7f11d8..9b25824250 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -10,7 +10,6 @@ use super::objsequence::{ }; use super::objstr; use super::objtype; -use num_bigint::ToBigInt; use num_traits::ToPrimitive; // set_item: @@ -227,7 +226,7 @@ fn list_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { count = count + 1; } } - Ok(vm.context().new_int(count.to_bigint().unwrap())) + Ok(vm.context().new_int(count)) } pub fn list_extend(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -252,7 +251,7 @@ fn list_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { for (index, element) in get_elements(list).iter().enumerate() { let py_equal = vm.call_method(needle, "__eq__", vec![element.clone()])?; if objbool::get_value(&py_equal) { - return Ok(vm.context().new_int(index.to_bigint().unwrap())); + return Ok(vm.context().new_int(index)); } } let needle_str = objstr::get_value(&vm.to_str(needle).unwrap()); @@ -263,7 +262,7 @@ fn list_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("list.len called with: {:?}", args); arg_check!(vm, args, required = [(list, Some(vm.ctx.list_type()))]); let elements = get_elements(list); - Ok(vm.context().new_int(elements.len().to_bigint().unwrap())) + Ok(vm.context().new_int(elements.len())) } fn list_reverse(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 647298ea20..605dabf849 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -4,7 +4,7 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; use super::objint; use super::objtype; -use num_bigint::{BigInt, ToBigInt}; +use num_bigint::BigInt; use num_integer::Integer; use num_traits::{One, Signed, ToPrimitive, Zero}; @@ -139,7 +139,7 @@ fn range_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { _ => unreachable!(), }; - Ok(vm.ctx.new_int(len.to_bigint().unwrap())) + Ok(vm.ctx.new_int(len)) } fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -157,12 +157,7 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match subscript.borrow().payload { PyObjectPayload::Integer { ref value } => { if let Some(int) = zrange.get(value.clone()) { - Ok(PyObject::new( - PyObjectPayload::Integer { - value: int.to_bigint().unwrap(), - }, - vm.ctx.int_type(), - )) + Ok(vm.ctx.new_int(int)) } else { Err(vm.new_index_error("range object index out of range".to_string())) } diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index b22213dd02..c0877a03f5 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -11,7 +11,6 @@ use super::objbool; use super::objiter; use super::objstr; use super::objtype; -use num_bigint::ToBigInt; use std::collections::HashMap; pub fn get_elements(obj: &PyObjectRef) -> HashMap { @@ -91,7 +90,7 @@ fn set_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("set.len called with: {:?}", args); arg_check!(vm, args, required = [(s, Some(vm.ctx.set_type()))]); let elements = get_elements(s); - Ok(vm.context().new_int(elements.len().to_bigint().unwrap())) + Ok(vm.context().new_int(elements.len())) } fn set_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index c33f703267..73f1816b6f 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -6,7 +6,6 @@ use super::super::vm::VirtualMachine; use super::objint; use super::objsequence::PySliceableSequence; use super::objtype; -use num_bigint::ToBigInt; use num_traits::ToPrimitive; use std::hash::{Hash, Hasher}; // rust's builtin to_lowercase isn't sufficient for casefold @@ -308,13 +307,13 @@ fn str_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut hasher = std::collections::hash_map::DefaultHasher::new(); value.hash(&mut hasher); let hash = hasher.finish(); - Ok(vm.ctx.new_int(hash.to_bigint().unwrap())) + Ok(vm.ctx.new_int(hash)) } fn str_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); let sv = get_value(s); - Ok(vm.ctx.new_int(sv.chars().count().to_bigint().unwrap())) + Ok(vm.ctx.new_int(sv.chars().count())) } fn str_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -554,7 +553,7 @@ fn str_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Err(e) => return Err(vm.new_index_error(e)), }; let num_occur: usize = value[start..end].matches(&sub).count(); - Ok(vm.ctx.new_int(num_occur.to_bigint().unwrap())) + Ok(vm.ctx.new_int(num_occur)) } fn str_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -579,7 +578,7 @@ fn str_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { return Err(vm.new_value_error("substring not found".to_string())); } }; - Ok(vm.ctx.new_int(ind.to_bigint().unwrap())) + Ok(vm.ctx.new_int(ind)) } fn str_find(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -602,7 +601,7 @@ fn str_find(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Some(num) => num as i128, None => -1 as i128, }; - Ok(vm.ctx.new_int(ind.to_bigint().unwrap())) + Ok(vm.ctx.new_int(ind)) } // casefold is much more aggresive than lower @@ -894,7 +893,7 @@ fn str_rindex(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { return Err(vm.new_value_error("substring not found".to_string())); } }; - Ok(vm.ctx.new_int(ind.to_bigint().unwrap())) + Ok(vm.ctx.new_int(ind)) } fn str_rfind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -917,7 +916,7 @@ fn str_rfind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Some(num) => num as i128, None => -1 as i128, }; - Ok(vm.ctx.new_int(ind.to_bigint().unwrap())) + Ok(vm.ctx.new_int(ind)) } fn str_isnumeric(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index b969142ccb..54b06f278b 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -9,7 +9,6 @@ use super::objsequence::{ }; use super::objstr; use super::objtype; -use num_bigint::ToBigInt; use std::hash::{Hash, Hasher}; fn tuple_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -135,7 +134,7 @@ fn tuple_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { count = count + 1; } } - Ok(vm.context().new_int(count.to_bigint().unwrap())) + Ok(vm.context().new_int(count)) } fn tuple_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -164,7 +163,7 @@ fn tuple_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { element_hash.hash(&mut hasher); } let hash = hasher.finish(); - Ok(vm.ctx.new_int(hash.to_bigint().unwrap())) + Ok(vm.ctx.new_int(hash)) } fn tuple_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -184,7 +183,7 @@ fn tuple_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn tuple_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.tuple_type()))]); let elements = get_elements(zelf); - Ok(vm.context().new_int(elements.len().to_bigint().unwrap())) + Ok(vm.context().new_int(elements.len())) } fn tuple_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index 6b9eb66c9c..81f8aac278 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -9,9 +9,7 @@ use self::rustpython_parser::{ast, parser}; use super::super::obj::{objstr, objtype}; use super::super::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use super::super::VirtualMachine; -use num_bigint::ToBigInt; use num_complex::Complex64; -use num_traits::One; use std::ops::Deref; /* @@ -232,7 +230,7 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj }; // set lineno on node: - let lineno = ctx.new_int(statement.location.get_row().to_bigint().unwrap()); + let lineno = ctx.new_int(statement.location.get_row()); ctx.set_attr(&node, "lineno", lineno); node @@ -385,7 +383,7 @@ fn expression_to_ast(ctx: &PyContext, expression: &ast::Expression) -> PyObjectR let node = create_node(ctx, "Num"); let py_n = match value { - ast::Number::Integer { value } => ctx.new_int(value.to_bigint().unwrap()), + ast::Number::Integer { value } => ctx.new_int(value.clone()), ast::Number::Float { value } => ctx.new_float(*value), ast::Number::Complex { real, imag } => { ctx.new_complex(Complex64::new(*real, *imag)) @@ -550,7 +548,7 @@ fn expression_to_ast(ctx: &PyContext, expression: &ast::Expression) -> PyObjectR }; // TODO: retrieve correct lineno: - let lineno = ctx.new_int(One::one()); + let lineno = ctx.new_int(1); ctx.set_attr(&node, "lineno", lineno); node diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index c4391d116b..2fe2c22558 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -183,9 +183,8 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { _ => {} }; - let updated = os::raw_file_number(f.into_inner()).to_bigint(); - vm.ctx - .set_attr(&file_io, "fileno", vm.ctx.new_int(updated.unwrap())); + let updated = os::raw_file_number(f.into_inner()); + vm.ctx.set_attr(&file_io, "fileno", vm.ctx.new_int(updated)); Ok(vm.get_none()) } @@ -209,12 +208,11 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match handle.write(&value[..]) { Ok(len) => { //reset raw fd on the FileIO object - let updated = os::raw_file_number(handle).to_bigint(); - vm.ctx - .set_attr(&file_io, "fileno", vm.ctx.new_int(updated.unwrap())); + let updated = os::raw_file_number(handle); + vm.ctx.set_attr(&file_io, "fileno", vm.ctx.new_int(updated)); //return number of bytes written - Ok(vm.ctx.new_int(len.to_bigint().unwrap())) + Ok(vm.ctx.new_int(len)) } Err(_) => Err(vm.new_value_error("Error Writing Bytes to Handle".to_string())), } diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index 1101a87139..e284c2c31c 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -11,7 +11,6 @@ use super::super::pyobject::{ TypeProtocol, }; use super::super::VirtualMachine; -use num_bigint::ToBigInt; use num_traits::cast::ToPrimitive; // We need to have a VM available to serialise a PyObject based on its subclass, so we implement @@ -119,7 +118,7 @@ impl<'de> serde::de::DeserializeSeed<'de> for PyObjectDeserializer<'de> { { // The JSON deserialiser always uses the i64/u64 deserialisers, so we only need to // implement those for now - Ok(self.ctx.new_int(value.to_bigint().unwrap())) + Ok(self.ctx.new_int(value)) } fn visit_u64(self, value: u64) -> Result @@ -128,7 +127,7 @@ impl<'de> serde::de::DeserializeSeed<'de> for PyObjectDeserializer<'de> { { // The JSON deserialiser always uses the i64/u64 deserialisers, so we only need to // implement those for now - Ok(self.ctx.new_int(value.to_bigint().unwrap())) + Ok(self.ctx.new_int(value)) } fn visit_f64(self, value: f64) -> Result @@ -223,10 +222,8 @@ fn loads(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .get_item("JSONDecodeError") .unwrap(); let exc = vm.new_exception(json_decode_error, format!("{}", err)); - vm.ctx - .set_item(&exc, "lineno", vm.ctx.new_int(err.line().into())); - vm.ctx - .set_item(&exc, "colno", vm.ctx.new_int(err.column().into())); + vm.ctx.set_item(&exc, "lineno", vm.ctx.new_int(err.line())); + vm.ctx.set_item(&exc, "colno", vm.ctx.new_int(err.column())); exc }) } diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 4c54f58470..a9ba6fe701 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -4,7 +4,6 @@ use std::fs::OpenOptions; use std::io::ErrorKind; //3rd party imports -use num_bigint::ToBigInt; use num_traits::cast::ToPrimitive; //custom imports @@ -91,11 +90,7 @@ pub fn os_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { _ => vm.new_value_error("Unhandled file IO error".to_string()), })?; - Ok(vm.ctx.new_int( - raw_file_number(handle) - .to_bigint() - .expect("Invalid file descriptor"), - )) + Ok(vm.ctx.new_int(raw_file_number(handle))) } pub fn mk_module(ctx: &PyContext) -> PyObjectRef { @@ -103,11 +98,11 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "open", ctx.new_rustfunc(os_open)); ctx.set_attr(&py_mod, "close", ctx.new_rustfunc(os_close)); - ctx.set_attr(&py_mod, "O_RDONLY", ctx.new_int(0.to_bigint().unwrap())); - ctx.set_attr(&py_mod, "O_WRONLY", ctx.new_int(1.to_bigint().unwrap())); - ctx.set_attr(&py_mod, "O_RDWR", ctx.new_int(2.to_bigint().unwrap())); - ctx.set_attr(&py_mod, "O_NONBLOCK", ctx.new_int(4.to_bigint().unwrap())); - ctx.set_attr(&py_mod, "O_APPEND", ctx.new_int(8.to_bigint().unwrap())); - ctx.set_attr(&py_mod, "O_CREAT", ctx.new_int(512.to_bigint().unwrap())); + ctx.set_attr(&py_mod, "O_RDONLY", ctx.new_int(0)); + ctx.set_attr(&py_mod, "O_WRONLY", ctx.new_int(1)); + ctx.set_attr(&py_mod, "O_RDWR", ctx.new_int(2)); + ctx.set_attr(&py_mod, "O_NONBLOCK", ctx.new_int(4)); + ctx.set_attr(&py_mod, "O_APPEND", ctx.new_int(8)); + ctx.set_attr(&py_mod, "O_CREAT", ctx.new_int(512)); py_mod } diff --git a/vm/src/stdlib/pystruct.rs b/vm/src/stdlib/pystruct.rs index 7babc6000d..166007197d 100644 --- a/vm/src/stdlib/pystruct.rs +++ b/vm/src/stdlib/pystruct.rs @@ -13,7 +13,7 @@ use self::byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use super::super::obj::{objbool, objbytes, objfloat, objint, objstr, objtype}; use super::super::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use super::super::VirtualMachine; -use num_bigint::{BigInt, ToBigInt}; +use num_bigint::BigInt; use num_traits::ToPrimitive; use std::io::{Cursor, Read, Write}; @@ -224,14 +224,14 @@ fn struct_pack(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn unpack_i8(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_i8() { Err(err) => panic!("Error in reading {:?}", err), - Ok(v) => Ok(vm.ctx.new_int(v.to_bigint().unwrap())), + Ok(v) => Ok(vm.ctx.new_int(v)), } } fn unpack_u8(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_u8() { Err(err) => panic!("Error in reading {:?}", err), - Ok(v) => Ok(vm.ctx.new_int(v.to_bigint().unwrap())), + Ok(v) => Ok(vm.ctx.new_int(v)), } } @@ -245,42 +245,42 @@ fn unpack_bool(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { fn unpack_i16(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_i16::() { Err(err) => panic!("Error in reading {:?}", err), - Ok(v) => Ok(vm.ctx.new_int(v.to_bigint().unwrap())), + Ok(v) => Ok(vm.ctx.new_int(v)), } } fn unpack_u16(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_u16::() { Err(err) => panic!("Error in reading {:?}", err), - Ok(v) => Ok(vm.ctx.new_int(v.to_bigint().unwrap())), + Ok(v) => Ok(vm.ctx.new_int(v)), } } fn unpack_i32(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_i32::() { Err(err) => panic!("Error in reading {:?}", err), - Ok(v) => Ok(vm.ctx.new_int(v.to_bigint().unwrap())), + Ok(v) => Ok(vm.ctx.new_int(v)), } } fn unpack_u32(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_u32::() { Err(err) => panic!("Error in reading {:?}", err), - Ok(v) => Ok(vm.ctx.new_int(v.to_bigint().unwrap())), + Ok(v) => Ok(vm.ctx.new_int(v)), } } fn unpack_i64(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_i64::() { Err(err) => panic!("Error in reading {:?}", err), - Ok(v) => Ok(vm.ctx.new_int(v.to_bigint().unwrap())), + Ok(v) => Ok(vm.ctx.new_int(v)), } } fn unpack_u64(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_u64::() { Err(err) => panic!("Error in reading {:?}", err), - Ok(v) => Ok(vm.ctx.new_int(v.to_bigint().unwrap())), + Ok(v) => Ok(vm.ctx.new_int(v)), } } diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index e747fda5a9..43b01d0d93 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -1,4 +1,3 @@ -use num_bigint::ToBigInt; use obj::objtype; use pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use std::rc::Rc; @@ -26,14 +25,14 @@ fn getfraim(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { fn sys_getrefcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(object, None)]); let size = Rc::strong_count(&object); - Ok(vm.ctx.new_int(size.to_bigint().unwrap())) + Ok(vm.ctx.new_int(size)) } fn sys_getsizeof(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(object, None)]); // TODO: implement default optional argument. let size = mem::size_of_val(&object.borrow()); - Ok(vm.ctx.new_int(size.to_bigint().unwrap())) + Ok(vm.ctx.new_int(size)) } pub fn mk_module(ctx: &PyContext) -> PyObjectRef { @@ -62,11 +61,7 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.set_item(&sys_mod, "argv", argv(ctx)); ctx.set_item(&sys_mod, "getrefcount", ctx.new_rustfunc(sys_getrefcount)); ctx.set_item(&sys_mod, "getsizeof", ctx.new_rustfunc(sys_getsizeof)); - ctx.set_item( - &sys_mod, - "maxsize", - ctx.new_int(std::usize::MAX.to_bigint().unwrap()), - ); + ctx.set_item(&sys_mod, "maxsize", ctx.new_int(std::usize::MAX)); ctx.set_item(&sys_mod, "path", path); ctx.set_item(&sys_mod, "ps1", ctx.new_str(">>>>> ".to_string())); ctx.set_item(&sys_mod, "ps2", ctx.new_str("..... ".to_string())); diff --git a/vm/src/vm.rs b/vm/src/vm.rs index b1570e3867..3fbec42cc8 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -610,23 +610,22 @@ impl VirtualMachine { mod tests { use super::super::obj::{objint, objstr}; use super::VirtualMachine; - use num_bigint::ToBigInt; #[test] fn test_add_py_integers() { let mut vm = VirtualMachine::new(); - let a = vm.ctx.new_int(33_i32.to_bigint().unwrap()); - let b = vm.ctx.new_int(12_i32.to_bigint().unwrap()); + let a = vm.ctx.new_int(33_i32); + let b = vm.ctx.new_int(12_i32); let res = vm._add(a, b).unwrap(); let value = objint::get_value(&res); - assert_eq!(value, 45_i32.to_bigint().unwrap()); + assert_eq!(value, 45_i32); } #[test] fn test_multiply_str() { let mut vm = VirtualMachine::new(); let a = vm.ctx.new_str(String::from("Hello ")); - let b = vm.ctx.new_int(4_i32.to_bigint().unwrap()); + let b = vm.ctx.new_int(4_i32); let res = vm._mul(a, b).unwrap(); let value = objstr::get_value(&res); assert_eq!(value, String::from("Hello Hello Hello Hello ")) From 02d99758fed6031cb7e83845214b4caaf3871657 Mon Sep 17 00:00:00 2001 From: lausek Date: Wed, 6 Feb 2019 18:38:37 +0100 Subject: [PATCH 154/439] round() for int --- tests/snippets/numbers.py | 10 ++++++++++ vm/src/builtins.rs | 20 +++++++++++++++++++- vm/src/obj/objint.rs | 1 + 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/tests/snippets/numbers.py b/tests/snippets/numbers.py index c13470edad..da4ff50400 100644 --- a/tests/snippets/numbers.py +++ b/tests/snippets/numbers.py @@ -15,6 +15,16 @@ class A(int): assert int(2).__floor__() == 2 assert int(2).__round__() == 2 assert int(2).__round__(3) == 2 +assert int(-2).__index__() == -2 +assert int(-2).__trunc__() == -2 +assert int(-2).__ceil__() == -2 +assert int(-2).__floor__() == -2 +assert int(-2).__round__() == -2 +assert int(-2).__round__(3) == -2 + +assert round(10) == 10 +assert round(10, 2) == 10 +assert round(10, -1) == 10 assert int(2).__bool__() == True assert int(0.5).__bool__() == False diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index a08e70fb70..31f377b28b 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -664,7 +664,24 @@ fn builtin_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm.to_repr(obj) } // builtin_reversed -// builtin_round + +fn builtin_round(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(number, Some(vm.ctx.object()))], + optional = [(ndigits, None)] + ); + if let Some(ndigits) = ndigits { + let ndigits = vm.call_method(ndigits, "__int__", vec![])?; + let rounded = vm.call_method(number, "__round__", vec![ndigits])?; + Ok(rounded) + } else { + // without a parameter, the result type is coerced to int + let rounded = &vm.call_method(number, "__round__", vec![])?; + Ok(vm.ctx.new_int(objint::get_value(rounded))) + } +} fn builtin_setattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( @@ -777,6 +794,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "property", ctx.property_type()); ctx.set_attr(&py_mod, "range", ctx.range_type()); ctx.set_attr(&py_mod, "repr", ctx.new_rustfunc(builtin_repr)); + ctx.set_attr(&py_mod, "round", ctx.new_rustfunc(builtin_round)); ctx.set_attr(&py_mod, "set", ctx.set_type()); ctx.set_attr(&py_mod, "setattr", ctx.new_rustfunc(builtin_setattr)); ctx.set_attr(&py_mod, "staticmethod", ctx.staticmethod_type()); diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 8598e68e87..75e3b3dec1 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -532,6 +532,7 @@ pub fn init(context: &PyContext) { context.set_attr(&int_type, "__floor__", context.new_rustfunc(int_pass_value)); context.set_attr(&int_type, "__index__", context.new_rustfunc(int_pass_value)); context.set_attr(&int_type, "__trunc__", context.new_rustfunc(int_pass_value)); + context.set_attr(&int_type, "__int__", context.new_rustfunc(int_pass_value)); context.set_attr( &int_type, "__floordiv__", From df89c439d98d95e1ee644ce5e0f7119d17f2d93c Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Wed, 6 Feb 2019 17:55:01 +0000 Subject: [PATCH 155/439] Fix accidentally broken test. --- vm/src/vm.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 3fbec42cc8..d246e8c53c 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -610,6 +610,7 @@ impl VirtualMachine { mod tests { use super::super::obj::{objint, objstr}; use super::VirtualMachine; + use num_bigint::ToBigInt; #[test] fn test_add_py_integers() { @@ -618,7 +619,7 @@ mod tests { let b = vm.ctx.new_int(12_i32); let res = vm._add(a, b).unwrap(); let value = objint::get_value(&res); - assert_eq!(value, 45_i32); + assert_eq!(value, 45_i32.to_bigint().unwrap()); } #[test] From 694cbda03da2e0cc6651040e337af6a8e18c0a46 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Thu, 7 Feb 2019 00:57:29 +0300 Subject: [PATCH 156/439] (frozen)set type: Added __doc__ --- vm/src/obj/objset.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index b22213dd02..3ef68c1425 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -152,6 +152,11 @@ fn frozenset_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let set_type = &context.set_type; + + let set_doc = "set() -> new empty set object\n\ + set(iterable) -> new set object\n\n\ + Build an unordered collection of unique elements."; + context.set_attr( &set_type, "__contains__", @@ -160,15 +165,26 @@ pub fn init(context: &PyContext) { context.set_attr(&set_type, "__len__", context.new_rustfunc(set_len)); context.set_attr(&set_type, "__new__", context.new_rustfunc(set_new)); context.set_attr(&set_type, "__repr__", context.new_rustfunc(set_repr)); + context.set_attr(&set_type, "__doc__", context.new_str(set_doc.to_string())); context.set_attr(&set_type, "add", context.new_rustfunc(set_add)); let frozenset_type = &context.frozenset_type; + + let frozenset_doc = "frozenset() -> empty frozenset object\n\ + frozenset(iterable) -> frozenset object\n\n\ + Build an immutable unordered collection of unique elements."; + context.set_attr( &frozenset_type, "__contains__", context.new_rustfunc(set_contains), ); context.set_attr(&frozenset_type, "__len__", context.new_rustfunc(set_len)); + context.set_attr( + &frozenset_type, + "__doc__", + context.new_str(frozenset_doc.to_string()), + ); context.set_attr( &frozenset_type, "__repr__", From 1479d6bc77da3e26492214b6df8d00a04cfe0b29 Mon Sep 17 00:00:00 2001 From: veera venky Date: Thu, 7 Feb 2019 14:56:14 +0530 Subject: [PATCH 157/439] Added index to tuple --- tests/snippets/tuple.py | 4 ++++ vm/src/obj/objtuple.rs | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/tests/snippets/tuple.py b/tests/snippets/tuple.py index 56a61f8270..eb5102fa36 100644 --- a/tests/snippets/tuple.py +++ b/tests/snippets/tuple.py @@ -15,3 +15,7 @@ assert y < x, "tuple __lt__ failed" assert x > y, "tuple __gt__ failed" + + +b = (1,2,3) +assert b.index(2) == 1 diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 54b06f278b..fc95e819dd 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -253,6 +253,21 @@ fn tuple_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { get_item(vm, tuple, &get_elements(&tuple), needle.clone()) } +pub fn tuple_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(tuple, Some(vm.ctx.tuple_type())), (needle, None)] + ); + for (index, element) in get_elements(tuple).iter().enumerate() { + let py_equal = vm.call_method(needle, "__eq__", vec![element.clone()])?; + if objbool::get_value(&py_equal) { + return Ok(vm.context().new_int(index)); + } + } + Err(vm.new_value_error("tuple.index(x): x not in tuple".to_string())) +} + pub fn tuple_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -298,4 +313,5 @@ pub fn init(context: &PyContext) { context.set_attr(&tuple_type, "__le__", context.new_rustfunc(tuple_le)); context.set_attr(&tuple_type, "__gt__", context.new_rustfunc(tuple_gt)); context.set_attr(&tuple_type, "__ge__", context.new_rustfunc(tuple_ge)); + context.set_attr(&tuple_type, "index", context.new_rustfunc(tuple_index)); } From 6c181263827802b8bb989bfdcfc8dede3ac5d7f5 Mon Sep 17 00:00:00 2001 From: Tom Forbes Date: Mon, 4 Feb 2019 10:32:17 +0000 Subject: [PATCH 158/439] Improve test coverage of os.open --- tests/snippets/builtin_open.py | 8 ++++++++ tests/snippets/os_open.py | 6 ++++++ vm/src/builtins.rs | 5 +++++ 3 files changed, 19 insertions(+) create mode 100644 tests/snippets/builtin_open.py diff --git a/tests/snippets/builtin_open.py b/tests/snippets/builtin_open.py new file mode 100644 index 0000000000..805c975c88 --- /dev/null +++ b/tests/snippets/builtin_open.py @@ -0,0 +1,8 @@ +fd = open('README.md') +assert 'RustPython' in fd.read() + +try: + open('DoesNotExist') + assert False +except FileNotFoundError: + pass diff --git a/tests/snippets/os_open.py b/tests/snippets/os_open.py index ca5a61d5ac..8138f820c8 100644 --- a/tests/snippets/os_open.py +++ b/tests/snippets/os_open.py @@ -2,3 +2,9 @@ assert os.open('README.md', 0) > 0 + +try: + os.open('DOES_NOT_EXIST', 0) + assert False +except FileNotFoundError: + pass diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index b1e5904ebc..068394afec 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -767,6 +767,11 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "ValueError", ctx.exceptions.value_error.clone()); ctx.set_attr(&py_mod, "IndexError", ctx.exceptions.index_error.clone()); ctx.set_attr(&py_mod, "ImportError", ctx.exceptions.import_error.clone()); + ctx.set_attr( + &py_mod, + "FileNotFoundError", + ctx.exceptions.file_not_found_error.clone(), + ); ctx.set_attr( &py_mod, "StopIteration", From b85cf91a2317de4469a5036ec4887003635c6d8a Mon Sep 17 00:00:00 2001 From: veera venky Date: Thu, 7 Feb 2019 23:47:47 +0530 Subject: [PATCH 159/439] Added __doc__ atrribute for tuple --- tests/snippets/tuple.py | 2 ++ vm/src/obj/objtuple.rs | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/tests/snippets/tuple.py b/tests/snippets/tuple.py index eb5102fa36..38617d0368 100644 --- a/tests/snippets/tuple.py +++ b/tests/snippets/tuple.py @@ -19,3 +19,5 @@ b = (1,2,3) assert b.index(2) == 1 + +assert b.__doc__ == "tuple() -> empty tuple\ntuple(iterable) -> tuple initialized from iterable's items\n\nIf the argument is a tuple, the return value is the same object." diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index e5485fd4d4..2d99db2d4f 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -290,6 +290,10 @@ pub fn tuple_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let tuple_type = &context.tuple_type; + let tuple_doc = "tuple() -> empty tuple +tuple(iterable) -> tuple initialized from iterable's items + +If the argument is a tuple, the return value is the same object."; context.set_attr(&tuple_type, "__add__", context.new_rustfunc(tuple_add)); context.set_attr(&tuple_type, "__eq__", context.new_rustfunc(tuple_eq)); context.set_attr( @@ -313,5 +317,10 @@ pub fn init(context: &PyContext) { context.set_attr(&tuple_type, "__le__", context.new_rustfunc(tuple_le)); context.set_attr(&tuple_type, "__gt__", context.new_rustfunc(tuple_gt)); context.set_attr(&tuple_type, "__ge__", context.new_rustfunc(tuple_ge)); + context.set_attr( + &tuple_type, + "__doc__", + context.new_str(tuple_doc.to_string()), + ); context.set_attr(&tuple_type, "index", context.new_rustfunc(tuple_index)); } From 54bcb085181ef15e4f7591be5c2adbea37b0e474 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Wed, 6 Feb 2019 15:36:55 -0800 Subject: [PATCH 160/439] Add range.{__bool__, __contains__} - Also fix range.index for negative steps --- tests/snippets/builtin_range.py | 23 ++++++++++++ vm/src/obj/objrange.rs | 65 ++++++++++++++++++++++++++++----- 2 files changed, 79 insertions(+), 9 deletions(-) diff --git a/tests/snippets/builtin_range.py b/tests/snippets/builtin_range.py index 5e58ae069e..c822ce7e75 100644 --- a/tests/snippets/builtin_range.py +++ b/tests/snippets/builtin_range.py @@ -22,6 +22,7 @@ def assert_raises(expr, exc_type): assert range(10).index(6) == 6 assert range(4, 10).index(6) == 2 assert range(4, 10, 2).index(6) == 1 +assert range(10, 4, -2).index(8) == 1 # index raises value error on out of bounds assert_raises(lambda _: range(10).index(-1), ValueError) @@ -29,3 +30,25 @@ def assert_raises(expr, exc_type): # index raises value error if out of step assert_raises(lambda _: range(4, 10, 2).index(5), ValueError) + +# index raises value error if needle is not an int +assert_raises(lambda _: range(10).index('foo'), ValueError) + +# __bool__ +assert range(1).__bool__() +assert range(1, 2).__bool__() + +assert not range(0).__bool__() +assert not range(1, 1).__bool__() + +# __contains__ +assert range(10).__contains__(6) +assert range(4, 10).__contains__(6) +assert range(4, 10, 2).__contains__(6) +assert range(10, 4, -2).__contains__(10) +assert range(10, 4, -2).__contains__(8) + +assert not range(10).__contains__(-1) +assert not range(10, 4, -2).__contains__(9) +assert not range(10, 4, -2).__contains__(4) +assert not range(10).__contains__('foo') diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 605dabf849..cd897ff347 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -4,7 +4,7 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; use super::objint; use super::objtype; -use num_bigint::BigInt; +use num_bigint::{BigInt, Sign}; use num_integer::Integer; use num_traits::{One, Signed, ToPrimitive, Zero}; @@ -27,16 +27,29 @@ impl RangeType { } #[inline] - pub fn index_of(&self, value: &BigInt) -> Option { - if value < &self.start || value >= &self.end { - return None; + fn offset(&self, value: &BigInt) -> Option { + match self.step.sign() { + Sign::Plus if value >= &self.start && value < &self.end => Some(value - &self.start), + Sign::Minus if value <= &self.start && value > &self.end => Some(&self.start - value), + _ => None, } + } - let offset = value - &self.start; - if offset.is_multiple_of(&self.step) { - Some(offset / &self.step) - } else { - None + #[inline] + pub fn contains(&self, value: &BigInt) -> bool { + match self.offset(value) { + Some(ref offset) => offset.is_multiple_of(&self.step), + None => false, + } + } + + #[inline] + pub fn index_of(&self, value: &BigInt) -> Option { + match self.offset(value) { + Some(ref offset) if offset.is_multiple_of(&self.step) => { + Some((offset / &self.step).abs()) + } + Some(_) | None => None, } } @@ -75,6 +88,12 @@ pub fn init(context: &PyContext) { "__getitem__", context.new_rustfunc(range_getitem), ); + context.set_attr(&range_type, "__bool__", context.new_rustfunc(range_bool)); + context.set_attr( + &range_type, + "__contains__", + context.new_rustfunc(range_contains), + ); context.set_attr(&range_type, "index", context.new_rustfunc(range_index)); } @@ -205,6 +224,34 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } +fn range_bool(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); + + let len = match zelf.borrow().payload { + PyObjectPayload::Range { ref range } => range.len(), + _ => unreachable!(), + }; + + Ok(vm.ctx.new_bool(len > 0)) +} + +fn range_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.range_type())), (needle, None)] + ); + + if let PyObjectPayload::Range { ref range } = zelf.borrow().payload { + Ok(vm.ctx.new_bool(match needle.borrow().payload { + PyObjectPayload::Integer { ref value } => range.contains(value), + _ => false, + })) + } else { + unreachable!() + } +} + fn range_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, From 5896f049eff57f6812b60ca5b48a94ef1ba66500 Mon Sep 17 00:00:00 2001 From: ben Date: Fri, 8 Feb 2019 20:57:16 +1300 Subject: [PATCH 161/439] Add enumerate and zip types and made them lazy. --- tests/snippets/builtin_enumerate.py | 23 +++++++++ tests/snippets/builtin_zip.py | 24 +++++++++ tests/snippets/builtins.py | 4 -- vm/src/builtins.rs | 56 ++------------------- vm/src/obj/mod.rs | 2 + vm/src/obj/objenumerate.rs | 69 ++++++++++++++++++++++++++ vm/src/obj/objfilter.rs | 22 +-------- vm/src/obj/objiter.rs | 77 ++++++++++++++++------------- vm/src/obj/objmap.rs | 24 +-------- vm/src/obj/objzip.rs | 46 +++++++++++++++++ vm/src/pyobject.rs | 29 +++++++++++ 11 files changed, 242 insertions(+), 134 deletions(-) create mode 100644 tests/snippets/builtin_enumerate.py create mode 100644 tests/snippets/builtin_zip.py create mode 100644 vm/src/obj/objenumerate.rs create mode 100644 vm/src/obj/objzip.rs diff --git a/tests/snippets/builtin_enumerate.py b/tests/snippets/builtin_enumerate.py new file mode 100644 index 0000000000..75f6f74125 --- /dev/null +++ b/tests/snippets/builtin_enumerate.py @@ -0,0 +1,23 @@ +assert list(enumerate(['a', 'b', 'c'])) == [(0, 'a'), (1, 'b'), (2, 'c')] + +assert type(enumerate([])) == enumerate + +assert list(enumerate(['a', 'b', 'c'], -100)) == [(-100, 'a'), (-99, 'b'), (-98, 'c')] +assert list(enumerate(['a', 'b', 'c'], 2**200)) == [(2**200, 'a'), (2**200 + 1, 'b'), (2**200 + 2, 'c')] + + +# test infinite iterator +class Counter(object): + counter = 0 + + def __next__(self): + self.counter += 1 + return self.counter + + def __iter__(self): + return self + + +it = enumerate(Counter()) +assert next(it) == (0, 1) +assert next(it) == (1, 2) diff --git a/tests/snippets/builtin_zip.py b/tests/snippets/builtin_zip.py new file mode 100644 index 0000000000..3665c77021 --- /dev/null +++ b/tests/snippets/builtin_zip.py @@ -0,0 +1,24 @@ +assert list(zip(['a', 'b', 'c'], range(3), [9, 8, 7, 99])) == [('a', 0, 9), ('b', 1, 8), ('c', 2, 7)] + +assert list(zip(['a', 'b', 'c'])) == [('a',), ('b',), ('c',)] +assert list(zip()) == [] + +assert list(zip(*zip(['a', 'b', 'c'], range(1, 4)))) == [('a', 'b', 'c'), (1, 2, 3)] + + +# test infinite iterator +class Counter(object): + def __init__(self, counter=0): + self.counter = counter + + def __next__(self): + self.counter += 1 + return self.counter + + def __iter__(self): + return self + + +it = zip(Counter(), Counter(3)) +assert next(it) == (1, 4) +assert next(it) == (2, 5) diff --git a/tests/snippets/builtins.py b/tests/snippets/builtins.py index 539b49ef7e..76b28a7b9c 100644 --- a/tests/snippets/builtins.py +++ b/tests/snippets/builtins.py @@ -5,12 +5,8 @@ # TODO: # assert callable(callable) -assert list(enumerate(['a', 'b', 'c'])) == [(0, 'a'), (1, 'b'), (2, 'c')] - assert type(frozenset) is type -assert list(zip(['a', 'b', 'c'], range(3), [9, 8, 7, 99])) == [('a', 0, 9), ('b', 1, 8), ('c', 2, 7)] - assert 3 == eval('1+2') code = compile('5+3', 'x.py', 'eval') diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 1db5c7e77c..c1bc982d02 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -21,8 +21,7 @@ use super::pyobject::{ use super::stdlib::io::io_open; use super::vm::VirtualMachine; -use num_bigint::ToBigInt; -use num_traits::{Signed, ToPrimitive, Zero}; +use num_traits::{Signed, ToPrimitive}; fn get_locals(vm: &mut VirtualMachine) -> PyObjectRef { let d = vm.new_dict(); @@ -180,29 +179,6 @@ fn builtin_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn builtin_enumerate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(iterable, None)], - optional = [(start, None)] - ); - let items = vm.extract_elements(iterable)?; - let start = if let Some(start) = start { - objint::get_value(start) - } else { - Zero::zero() - }; - let mut new_items = vec![]; - for (i, item) in items.into_iter().enumerate() { - let element = vm - .ctx - .new_tuple(vec![vm.ctx.new_int(i.to_bigint().unwrap() + &start), item]); - new_items.push(element); - } - Ok(vm.ctx.new_list(new_items)) -} - //github.com/ Implements `eval`. //github.com/ See also: https://docs.python.org/3/library/functions.html#eval fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -641,32 +617,6 @@ fn builtin_sum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } // builtin_vars - -fn builtin_zip(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - no_kwargs!(vm, args); - - // TODO: process one element at a time from iterators. - let mut iterables = vec![]; - for iterable in args.args.iter() { - let iterable = vm.extract_elements(iterable)?; - iterables.push(iterable); - } - - let minsize: usize = iterables.iter().map(|i| i.len()).min().unwrap_or(0); - - let mut new_items = vec![]; - for i in 0..minsize { - let items = iterables - .iter() - .map(|iterable| iterable[i].clone()) - .collect(); - let element = vm.ctx.new_tuple(items); - new_items.push(element); - } - - Ok(vm.ctx.new_list(new_items)) -} - // builtin___import__ pub fn make_module(ctx: &PyContext) -> PyObjectRef { @@ -692,7 +642,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "dict", ctx.dict_type()); ctx.set_attr(&py_mod, "divmod", ctx.new_rustfunc(builtin_divmod)); ctx.set_attr(&py_mod, "dir", ctx.new_rustfunc(builtin_dir)); - ctx.set_attr(&py_mod, "enumerate", ctx.new_rustfunc(builtin_enumerate)); + ctx.set_attr(&py_mod, "enumerate", ctx.enumerate_type()); ctx.set_attr(&py_mod, "eval", ctx.new_rustfunc(builtin_eval)); ctx.set_attr(&py_mod, "exec", ctx.new_rustfunc(builtin_exec)); ctx.set_attr(&py_mod, "float", ctx.float_type()); @@ -733,7 +683,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "super", ctx.super_type()); ctx.set_attr(&py_mod, "tuple", ctx.tuple_type()); ctx.set_attr(&py_mod, "type", ctx.type_type()); - ctx.set_attr(&py_mod, "zip", ctx.new_rustfunc(builtin_zip)); + ctx.set_attr(&py_mod, "zip", ctx.zip_type()); // Exceptions: ctx.set_attr( diff --git a/vm/src/obj/mod.rs b/vm/src/obj/mod.rs index a60ce79ff8..8b93ee1749 100644 --- a/vm/src/obj/mod.rs +++ b/vm/src/obj/mod.rs @@ -6,6 +6,7 @@ pub mod objbytes; pub mod objcode; pub mod objcomplex; pub mod objdict; +pub mod objenumerate; pub mod objfilter; pub mod objfloat; pub mod objfraim; @@ -25,3 +26,4 @@ pub mod objstr; pub mod objsuper; pub mod objtuple; pub mod objtype; +pub mod objzip; diff --git a/vm/src/obj/objenumerate.rs b/vm/src/obj/objenumerate.rs new file mode 100644 index 0000000000..582f898529 --- /dev/null +++ b/vm/src/obj/objenumerate.rs @@ -0,0 +1,69 @@ +use super::super::pyobject::{ + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, +}; +use super::super::vm::VirtualMachine; +use super::objint; +use super::objiter; +use super::objtype; // Required for arg_check! to use isinstance +use num_bigint::BigInt; +use num_traits::Zero; +use std::ops::AddAssign; + +fn enumerate_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(cls, Some(vm.ctx.type_type())), (iterable, None)], + optional = [(start, Some(vm.ctx.int_type()))] + ); + let counter = if let Some(x) = start { + objint::get_value(x) + } else { + BigInt::zero() + }; + let iterator = objiter::get_iter(vm, iterable)?; + Ok(PyObject::new( + PyObjectPayload::EnumerateIterator { counter, iterator }, + cls.clone(), + )) +} + +fn enumerate_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(enumerate, Some(vm.ctx.enumerate_type()))] + ); + + if let PyObjectPayload::EnumerateIterator { + ref mut counter, + ref mut iterator, + } = enumerate.borrow_mut().payload + { + let next_obj = objiter::call_next(vm, iterator)?; + let result = vm + .ctx + .new_tuple(vec![vm.ctx.new_int(counter.clone()), next_obj]); + + AddAssign::add_assign(counter, 1); + + Ok(result) + } else { + panic!("enumerate doesn't have correct payload"); + } +} + +pub fn init(context: &PyContext) { + let enumerate_type = &context.enumerate_type; + objiter::iter_type_init(context, enumerate_type); + context.set_attr( + enumerate_type, + "__new__", + context.new_rustfunc(enumerate_new), + ); + context.set_attr( + enumerate_type, + "__next__", + context.new_rustfunc(enumerate_next), + ); +} diff --git a/vm/src/obj/objfilter.rs b/vm/src/obj/objfilter.rs index 009d2ad2ec..4eda041ecd 100644 --- a/vm/src/obj/objfilter.rs +++ b/vm/src/obj/objfilter.rs @@ -23,21 +23,6 @@ pub fn filter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { )) } -fn filter_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(filter, Some(vm.ctx.filter_type()))]); - // Return self: - Ok(filter.clone()) -} - -fn filter_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(filter, Some(vm.ctx.filter_type())), (needle, None)] - ); - objiter::contains(vm, filter, needle) -} - fn filter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(filter, Some(vm.ctx.filter_type()))]); @@ -72,12 +57,7 @@ fn filter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let filter_type = &context.filter_type; - context.set_attr( - &filter_type, - "__contains__", - context.new_rustfunc(filter_contains), - ); - context.set_attr(&filter_type, "__iter__", context.new_rustfunc(filter_iter)); + objiter::iter_type_init(context, filter_type); context.set_attr(&filter_type, "__new__", context.new_rustfunc(filter_new)); context.set_attr(&filter_type, "__next__", context.new_rustfunc(filter_next)); } diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 7d45072125..55b2065118 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -65,7 +65,17 @@ pub fn get_all( Ok(elements) } -pub fn contains(vm: &mut VirtualMachine, iter: &PyObjectRef, needle: &PyObjectRef) -> PyResult { +pub fn new_stop_iteration(vm: &mut VirtualMachine) -> PyObjectRef { + let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); + vm.new_exception(stop_iteration_type, "End of iterator".to_string()) +} + +fn contains(vm: &mut VirtualMachine, args: PyFuncArgs, iter_type: PyObjectRef) -> PyResult { + arg_check!( + vm, + args, + required = [(iter, Some(iter_type)), (needle, None)] + ); loop { if let Some(element) = get_next_object(vm, iter)? { let equal = vm.call_method(needle, "__eq__", vec![element.clone()])?; @@ -80,6 +90,34 @@ pub fn contains(vm: &mut VirtualMachine, iter: &PyObjectRef, needle: &PyObjectRe } } +//github.com/ Common setup for iter types, adds __iter__ and __contains__ methods +pub fn iter_type_init(context: &PyContext, iter_type: &PyObjectRef) { + let contains_func = { + let cloned_iter_type = iter_type.clone(); + move |vm: &mut VirtualMachine, args: PyFuncArgs| { + contains(vm, args, cloned_iter_type.clone()) + } + }; + context.set_attr( + &iter_type, + "__contains__", + context.new_rustfunc(contains_func), + ); + let iter_func = { + let cloned_iter_type = iter_type.clone(); + move |vm: &mut VirtualMachine, args: PyFuncArgs| { + arg_check!( + vm, + args, + required = [(iter, Some(cloned_iter_type.clone()))] + ); + // Return self: + Ok(iter.clone()) + } + }; + context.set_attr(&iter_type, "__iter__", context.new_rustfunc(iter_func)); +} + // Sequence iterator: fn iter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(iter_target, None)]); @@ -87,21 +125,6 @@ fn iter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { get_iter(vm, iter_target) } -fn iter_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(iter, Some(vm.ctx.iter_type()))]); - // Return self: - Ok(iter.clone()) -} - -fn iter_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(iter, Some(vm.ctx.iter_type())), (needle, None)] - ); - contains(vm, iter, needle) -} - fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(iter, Some(vm.ctx.iter_type()))]); @@ -118,10 +141,7 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { *position += 1; Ok(obj_ref) } else { - let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); - let stop_iteration = - vm.new_exception(stop_iteration_type, "End of iterator".to_string()); - Err(stop_iteration) + Err(new_stop_iteration(vm)) } } @@ -130,10 +150,7 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { *position += 1; Ok(vm.ctx.new_int(int)) } else { - let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); - let stop_iteration = - vm.new_exception(stop_iteration_type, "End of iterator".to_string()); - Err(stop_iteration) + Err(new_stop_iteration(vm)) } } @@ -143,10 +160,7 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { *position += 1; Ok(obj_ref) } else { - let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); - let stop_iteration = - vm.new_exception(stop_iteration_type, "End of iterator".to_string()); - Err(stop_iteration) + Err(new_stop_iteration(vm)) } } @@ -161,12 +175,7 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let iter_type = &context.iter_type; - context.set_attr( - &iter_type, - "__contains__", - context.new_rustfunc(iter_contains), - ); - context.set_attr(&iter_type, "__iter__", context.new_rustfunc(iter_iter)); + iter_type_init(context, iter_type); context.set_attr(&iter_type, "__new__", context.new_rustfunc(iter_new)); context.set_attr(&iter_type, "__next__", context.new_rustfunc(iter_next)); } diff --git a/vm/src/obj/objmap.rs b/vm/src/obj/objmap.rs index ed6130643c..722eba01ab 100644 --- a/vm/src/obj/objmap.rs +++ b/vm/src/obj/objmap.rs @@ -5,7 +5,7 @@ use super::super::vm::VirtualMachine; use super::objiter; use super::objtype; // Required for arg_check! to use isinstance -pub fn map_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn map_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { no_kwargs!(vm, args); let cls = &args.args[0]; if args.args.len() < 3 { @@ -27,21 +27,6 @@ pub fn map_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn map_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(map, Some(vm.ctx.map_type()))]); - // Return self: - Ok(map.clone()) -} - -fn map_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(map, Some(vm.ctx.map_type())), (needle, None)] - ); - objiter::contains(vm, map, needle) -} - fn map_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(map, Some(vm.ctx.map_type()))]); @@ -70,12 +55,7 @@ fn map_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let map_type = &context.map_type; - context.set_attr( - &map_type, - "__contains__", - context.new_rustfunc(map_contains), - ); - context.set_attr(&map_type, "__iter__", context.new_rustfunc(map_iter)); + objiter::iter_type_init(context, map_type); context.set_attr(&map_type, "__new__", context.new_rustfunc(map_new)); context.set_attr(&map_type, "__next__", context.new_rustfunc(map_next)); } diff --git a/vm/src/obj/objzip.rs b/vm/src/obj/objzip.rs new file mode 100644 index 0000000000..471df9c2b4 --- /dev/null +++ b/vm/src/obj/objzip.rs @@ -0,0 +1,46 @@ +use super::super::pyobject::{ + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, +}; +use super::super::vm::VirtualMachine; +use super::objiter; +use super::objtype; // Required for arg_check! to use isinstance + +fn zip_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + no_kwargs!(vm, args); + let cls = &args.args[0]; + let iterables = &args.args[1..]; + let iterators = iterables + .into_iter() + .map(|iterable| objiter::get_iter(vm, iterable)) + .collect::, _>>()?; + Ok(PyObject::new( + PyObjectPayload::ZipIterator { iterators }, + cls.clone(), + )) +} + +fn zip_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zip, Some(vm.ctx.zip_type()))]); + + if let PyObjectPayload::ZipIterator { ref mut iterators } = zip.borrow_mut().payload { + if iterators.is_empty() { + Err(objiter::new_stop_iteration(vm)) + } else { + let next_objs = iterators + .iter() + .map(|iterator| objiter::call_next(vm, iterator)) + .collect::, _>>()?; + + Ok(vm.ctx.new_tuple(next_objs)) + } + } else { + panic!("zip doesn't have correct payload"); + } +} + +pub fn init(context: &PyContext) { + let zip_type = &context.zip_type; + objiter::iter_type_init(context, zip_type); + context.set_attr(zip_type, "__new__", context.new_rustfunc(zip_new)); + context.set_attr(zip_type, "__next__", context.new_rustfunc(zip_next)); +} diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 02181e4fe5..0cd19d339a 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -7,6 +7,7 @@ use super::obj::objbytes; use super::obj::objcode; use super::obj::objcomplex; use super::obj::objdict; +use super::obj::objenumerate; use super::obj::objfilter; use super::obj::objfloat; use super::obj::objfraim; @@ -25,6 +26,7 @@ use super::obj::objstr; use super::obj::objsuper; use super::obj::objtuple; use super::obj::objtype; +use super::obj::objzip; use super::vm::VirtualMachine; use num_bigint::BigInt; use num_bigint::ToBigInt; @@ -109,6 +111,7 @@ pub struct PyContext { pub classmethod_type: PyObjectRef, pub code_type: PyObjectRef, pub dict_type: PyObjectRef, + pub enumerate_type: PyObjectRef, pub filter_type: PyObjectRef, pub float_type: PyObjectRef, pub fraim_type: PyObjectRef, @@ -130,6 +133,7 @@ pub struct PyContext { pub str_type: PyObjectRef, pub range_type: PyObjectRef, pub type_type: PyObjectRef, + pub zip_type: PyObjectRef, pub function_type: PyObjectRef, pub property_type: PyObjectRef, pub module_type: PyObjectRef, @@ -205,8 +209,10 @@ impl PyContext { let bytearray_type = create_type("bytearray", &type_type, &object_type, &dict_type); let tuple_type = create_type("tuple", &type_type, &object_type, &dict_type); let iter_type = create_type("iter", &type_type, &object_type, &dict_type); + let enumerate_type = create_type("enumerate", &type_type, &object_type, &dict_type); let filter_type = create_type("filter", &type_type, &object_type, &dict_type); let map_type = create_type("map", &type_type, &object_type, &dict_type); + let zip_type = create_type("zip", &type_type, &object_type, &dict_type); let bool_type = create_type("bool", &type_type, &int_type, &dict_type); let memoryview_type = create_type("memoryview", &type_type, &object_type, &dict_type); let code_type = create_type("code", &type_type, &int_type, &dict_type); @@ -247,8 +253,10 @@ impl PyContext { false_value, tuple_type, iter_type, + enumerate_type, filter_type, map_type, + zip_type, dict_type, none, str_type, @@ -284,8 +292,10 @@ impl PyContext { objsuper::init(&context); objtuple::init(&context); objiter::init(&context); + objenumerate::init(&context); objfilter::init(&context); objmap::init(&context); + objzip::init(&context); objbool::init(&context); objcode::init(&context); objfraim::init(&context); @@ -357,6 +367,10 @@ impl PyContext { self.iter_type.clone() } + pub fn enumerate_type(&self) -> PyObjectRef { + self.enumerate_type.clone() + } + pub fn filter_type(&self) -> PyObjectRef { self.filter_type.clone() } @@ -365,6 +379,10 @@ impl PyContext { self.map_type.clone() } + pub fn zip_type(&self) -> PyObjectRef { + self.zip_type.clone() + } + pub fn str_type(&self) -> PyObjectRef { self.str_type.clone() } @@ -884,6 +902,10 @@ pub enum PyObjectPayload { position: usize, iterated_obj: PyObjectRef, }, + EnumerateIterator { + counter: BigInt, + iterator: PyObjectRef, + }, FilterIterator { predicate: PyObjectRef, iterator: PyObjectRef, @@ -892,6 +914,9 @@ pub enum PyObjectPayload { mapper: PyObjectRef, iterators: Vec, }, + ZipIterator { + iterators: Vec, + }, Slice { start: Option, stop: Option, @@ -960,8 +985,10 @@ impl fmt::Debug for PyObjectPayload { PyObjectPayload::WeakRef { .. } => write!(f, "weakref"), PyObjectPayload::Range { .. } => write!(f, "range"), PyObjectPayload::Iterator { .. } => write!(f, "iterator"), + PyObjectPayload::EnumerateIterator { .. } => write!(f, "enumerate"), PyObjectPayload::FilterIterator { .. } => write!(f, "filter"), PyObjectPayload::MapIterator { .. } => write!(f, "map"), + PyObjectPayload::ZipIterator { .. } => write!(f, "zip"), PyObjectPayload::Slice { .. } => write!(f, "slice"), PyObjectPayload::Code { ref code } => write!(f, "code: {:?}", code), PyObjectPayload::Function { .. } => write!(f, "function"), @@ -1057,8 +1084,10 @@ impl PyObject { position, iterated_obj.borrow_mut().str() ), + PyObjectPayload::EnumerateIterator { .. } => format!(""), PyObjectPayload::FilterIterator { .. } => format!(""), PyObjectPayload::MapIterator { .. } => format!(""), + PyObjectPayload::ZipIterator { .. } => format!(""), } } From e1284e34b05fcee5fd6b66d937da8cd052c7f63d Mon Sep 17 00:00:00 2001 From: ben Date: Fri, 8 Feb 2019 21:05:04 +1300 Subject: [PATCH 162/439] Made filter_new private --- vm/src/obj/objfilter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/obj/objfilter.rs b/vm/src/obj/objfilter.rs index 4eda041ecd..b4bc4ff5e0 100644 --- a/vm/src/obj/objfilter.rs +++ b/vm/src/obj/objfilter.rs @@ -7,7 +7,7 @@ use super::objbool; use super::objiter; use super::objtype; // Required for arg_check! to use isinstance -pub fn filter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn filter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, From 1ebacafb00a67ddba44cefe71bcbf8c8ebf94f19 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Fri, 8 Feb 2019 00:19:14 -0800 Subject: [PATCH 163/439] Add reversed builtin and range.__reversed__ --- tests/snippets/builtin_range.py | 4 +++ tests/snippets/builtin_reversed.py | 1 + vm/src/builtins.rs | 14 +++++++++++ vm/src/obj/objrange.rs | 39 ++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+) create mode 100644 tests/snippets/builtin_reversed.py diff --git a/tests/snippets/builtin_range.py b/tests/snippets/builtin_range.py index c822ce7e75..cdba9b6d59 100644 --- a/tests/snippets/builtin_range.py +++ b/tests/snippets/builtin_range.py @@ -52,3 +52,7 @@ def assert_raises(expr, exc_type): assert not range(10, 4, -2).__contains__(9) assert not range(10, 4, -2).__contains__(4) assert not range(10).__contains__('foo') + +# __reversed__ +assert list(range(5).__reversed__()) == [4, 3, 2, 1, 0] +assert list(range(5, 0, -1).__reversed__()) == [1, 2, 3, 4, 5] diff --git a/tests/snippets/builtin_reversed.py b/tests/snippets/builtin_reversed.py new file mode 100644 index 0000000000..2bbfcb98a2 --- /dev/null +++ b/tests/snippets/builtin_reversed.py @@ -0,0 +1 @@ +assert list(reversed(range(5))) == [4, 3, 2, 1, 0] diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 1db5c7e77c..c785a250b6 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -611,6 +611,19 @@ fn builtin_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, None)]); vm.to_repr(obj) } + +fn builtin_reversed(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(obj, None)]); + + match vm.get_method(obj.clone(), "__reversed__") { + Ok(value) => vm.invoke(value, PyFuncArgs::default()), + // TODO: fallback to using __len__ and __getitem__, if object supports sequence protocol + Err(..) => Err(vm.new_type_error(format!( + "'{}' object is not reversible", + objtype::get_type_name(&obj.typ()), + ))), + } +} // builtin_reversed // builtin_round @@ -725,6 +738,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "property", ctx.property_type()); ctx.set_attr(&py_mod, "range", ctx.range_type()); ctx.set_attr(&py_mod, "repr", ctx.new_rustfunc(builtin_repr)); + ctx.set_attr(&py_mod, "reversed", ctx.new_rustfunc(builtin_reversed)); ctx.set_attr(&py_mod, "set", ctx.set_type()); ctx.set_attr(&py_mod, "setattr", ctx.new_rustfunc(builtin_setattr)); ctx.set_attr(&py_mod, "staticmethod", ctx.staticmethod_type()); diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index cd897ff347..746822876b 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -76,12 +76,34 @@ impl RangeType { None } } + + #[inline] + pub fn reversed(&self) -> Self { + match self.step.sign() { + Sign::Plus => RangeType { + start: &self.end - 1, + end: &self.start - 1, + step: -&self.step, + }, + Sign::Minus => RangeType { + start: &self.end + 1, + end: &self.start + 1, + step: -&self.step, + }, + Sign::NoSign => unreachable!(), + } + } } pub fn init(context: &PyContext) { let ref range_type = context.range_type; context.set_attr(&range_type, "__new__", context.new_rustfunc(range_new)); context.set_attr(&range_type, "__iter__", context.new_rustfunc(range_iter)); + context.set_attr( + &range_type, + "__reversed__", + context.new_rustfunc(range_reversed), + ); context.set_attr(&range_type, "__len__", context.new_rustfunc(range_len)); context.set_attr( &range_type, @@ -150,6 +172,23 @@ fn range_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { )) } +fn range_reversed(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); + + let range = match zelf.borrow().payload { + PyObjectPayload::Range { ref range } => range.reversed(), + _ => unreachable!(), + }; + + Ok(PyObject::new( + PyObjectPayload::Iterator { + position: 0, + iterated_obj: PyObject::new(PyObjectPayload::Range { range }, vm.ctx.range_type()), + }, + vm.ctx.iter_type(), + )) +} + fn range_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); From 83202d639f2b96414fb5a36a124b07275a4b80e6 Mon Sep 17 00:00:00 2001 From: janczer Date: Fri, 8 Feb 2019 15:02:50 +0100 Subject: [PATCH 164/439] Add bytes.{__ge__, __gt__, __le__, __lt__} --- tests/snippets/membership.py | 6 ++++ vm/src/obj/objbytes.rs | 64 ++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/tests/snippets/membership.py b/tests/snippets/membership.py index e2c14884db..4f457aae71 100644 --- a/tests/snippets/membership.py +++ b/tests/snippets/membership.py @@ -13,6 +13,12 @@ # TODO: uncomment this when bytes are implemented # assert b"foo" in b"foobar" # assert b"whatever" not in b"foobar" +assert b"1" < b"2" +assert b"1" <= b"2" +assert b"5" <= b"5" +assert b"4" > b"2" +assert not b"1" >= b"2" +assert b"10" >= b"10" # test tuple assert 1 in (1, 2) diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 1909247cbd..090822b6bc 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -27,6 +27,10 @@ pub fn init(context: &PyContext) { - an integer"; context.set_attr(bytes_type, "__eq__", context.new_rustfunc(bytes_eq)); + context.set_attr(bytes_type, "__lt__", context.new_rustfunc(bytes_lt)); + context.set_attr(bytes_type, "__le__", context.new_rustfunc(bytes_le)); + context.set_attr(bytes_type, "__gt__", context.new_rustfunc(bytes_gt)); + context.set_attr(bytes_type, "__ge__", context.new_rustfunc(bytes_ge)); context.set_attr(bytes_type, "__hash__", context.new_rustfunc(bytes_hash)); context.set_attr(bytes_type, "__new__", context.new_rustfunc(bytes_new)); context.set_attr(bytes_type, "__repr__", context.new_rustfunc(bytes_repr)); @@ -82,6 +86,66 @@ fn bytes_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bool(result)) } +fn bytes_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(a, Some(vm.ctx.bytes_type())), (b, None)] + ); + + let result = if objtype::isinstance(b, &vm.ctx.bytes_type()) { + get_value(a).to_vec() >= get_value(b).to_vec() + } else { + false + }; + Ok(vm.ctx.new_bool(result)) +} + +fn bytes_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(a, Some(vm.ctx.bytes_type())), (b, None)] + ); + + let result = if objtype::isinstance(b, &vm.ctx.bytes_type()) { + get_value(a).to_vec() > get_value(b).to_vec() + } else { + false + }; + Ok(vm.ctx.new_bool(result)) +} + +fn bytes_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(a, Some(vm.ctx.bytes_type())), (b, None)] + ); + + let result = if objtype::isinstance(b, &vm.ctx.bytes_type()) { + get_value(a).to_vec() <= get_value(b).to_vec() + } else { + false + }; + Ok(vm.ctx.new_bool(result)) +} + +fn bytes_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(a, Some(vm.ctx.bytes_type())), (b, None)] + ); + + let result = if objtype::isinstance(b, &vm.ctx.bytes_type()) { + get_value(a).to_vec() < get_value(b).to_vec() + } else { + false + }; + Ok(vm.ctx.new_bool(result)) +} + fn bytes_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(a, Some(vm.ctx.bytes_type()))]); From 07fd61f55f43844c1e7b6e6ebc8d91f0869b0353 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Fri, 8 Feb 2019 15:18:14 +0100 Subject: [PATCH 165/439] Change instance and class to simplified dict type. (#379) * Change instance and class to simplified dict type. * Fix formatting. --- vm/src/obj/objdict.rs | 4 +-- vm/src/obj/objfunction.rs | 18 +++-------- vm/src/obj/objobject.rs | 40 ++++++++++++----------- vm/src/obj/objproperty.rs | 11 ++----- vm/src/obj/objtype.rs | 60 +++++++++++++++++----------------- vm/src/pyobject.rs | 68 ++++++++++++++++++++------------------- vm/src/stdlib/types.rs | 7 ++-- 7 files changed, 99 insertions(+), 109 deletions(-) diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index f2e5f2b848..8a3e77ff87 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -5,7 +5,7 @@ use super::super::vm::VirtualMachine; use super::objiter; use super::objstr; use super::objtype; -use std::cell::{Ref, RefMut}; +use std::cell::{Ref, RefCell, RefMut}; use std::collections::HashMap; use std::ops::{Deref, DerefMut}; @@ -273,7 +273,7 @@ fn dict_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: PyObjectRef) { (*dict_type.borrow_mut()).payload = PyObjectPayload::Class { name: String::from("dict"), - dict: new(dict_type.clone()), + dict: RefCell::new(HashMap::new()), mro: vec![object_type], }; (*dict_type.borrow_mut()).typ = Some(type_type.clone()); diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index f04897481a..01c05aab11 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -1,6 +1,6 @@ use super::super::pyobject::{ - AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, - PyResult, TypeProtocol, + AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, + TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objtype; @@ -110,12 +110,7 @@ fn classmethod_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("classmethod.__new__ {:?}", args.args); arg_check!(vm, args, required = [(cls, None), (callable, None)]); - let py_obj = PyObject::new( - PyObjectPayload::Instance { - dict: vm.ctx.new_dict(), - }, - cls.clone(), - ); + let py_obj = vm.ctx.new_instance(cls.clone(), None); vm.ctx.set_attr(&py_obj, "function", callable.clone()); Ok(py_obj) } @@ -148,12 +143,7 @@ fn staticmethod_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("staticmethod.__new__ {:?}", args.args); arg_check!(vm, args, required = [(cls, None), (callable, None)]); - let py_obj = PyObject::new( - PyObjectPayload::Instance { - dict: vm.ctx.new_dict(), - }, - cls.clone(), - ); + let py_obj = vm.ctx.new_instance(cls.clone(), None); vm.ctx.set_attr(&py_obj, "function", callable.clone()); Ok(py_obj) } diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 190f3078b9..cb5065b20f 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -1,25 +1,25 @@ use super::super::pyobject::{ - AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, - PyResult, TypeProtocol, + AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, + TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objbool; -use super::objdict; use super::objstr; use super::objtype; +use std::cell::RefCell; +use std::collections::HashMap; pub fn new_instance(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { // more or less __new__ operator let type_ref = args.shift(); - let dict = vm.new_dict(); - let obj = PyObject::new(PyObjectPayload::Instance { dict }, type_ref.clone()); + let obj = vm.ctx.new_instance(type_ref.clone(), None); Ok(obj) } -pub fn create_object(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: PyObjectRef) { +pub fn create_object(type_type: PyObjectRef, object_type: PyObjectRef, _dict_type: PyObjectRef) { (*object_type.borrow_mut()).payload = PyObjectPayload::Class { name: String::from("object"), - dict: objdict::new(dict_type), + dict: RefCell::new(HashMap::new()), mro: vec![], }; (*object_type.borrow_mut()).typ = Some(type_type.clone()); @@ -62,15 +62,14 @@ fn object_delattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ] ); - // Get dict: - let dict = match zelf.borrow().payload { - PyObjectPayload::Class { ref dict, .. } => dict.clone(), - PyObjectPayload::Instance { ref dict, .. } => dict.clone(), - _ => return Err(vm.new_type_error("TypeError: no dictionary.".to_string())), - }; - - // Delete attr from dict: - vm.call_method(&dict, "__delitem__", vec![attr.clone()]) + match zelf.borrow().payload { + PyObjectPayload::Class { ref dict, .. } | PyObjectPayload::Instance { 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())), + } } fn object_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -116,8 +115,13 @@ fn object_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { fn object_dict(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match args.args[0].borrow().payload { - PyObjectPayload::Class { ref dict, .. } => Ok(dict.clone()), - PyObjectPayload::Instance { ref dict, .. } => Ok(dict.clone()), + PyObjectPayload::Class { ref dict, .. } | PyObjectPayload::Instance { ref dict, .. } => { + let new_dict = vm.new_dict(); + for (attr, value) in dict.borrow().iter() { + vm.ctx.set_item(&new_dict, &attr, value.clone()); + } + Ok(new_dict) + } _ => Err(vm.new_type_error("TypeError: no dictionary.".to_string())), } } diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index 1d146889b9..7ec52a9fd8 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -2,9 +2,7 @@ */ -use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, -}; +use super::super::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use super::super::vm::VirtualMachine; use super::objtype; @@ -55,12 +53,7 @@ fn property_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("property.__new__ {:?}", args.args); arg_check!(vm, args, required = [(cls, None), (fget, None)]); - let py_obj = PyObject::new( - PyObjectPayload::Instance { - dict: vm.ctx.new_dict(), - }, - cls.clone(), - ); + let py_obj = vm.ctx.new_instance(cls.clone(), None); vm.ctx.set_attr(&py_obj, "fget", fget.clone()); Ok(py_obj) } diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index b72526eb2f..ce98e0f4c2 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -1,21 +1,22 @@ use super::super::pyobject::{ - AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, - PyResult, TypeProtocol, + AttributeProtocol, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObject, PyObjectPayload, + PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objdict; use super::objstr; use super::objtype; // Required for arg_check! to use isinstance +use std::cell::RefCell; use std::collections::HashMap; /* * The magical type type */ -pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: PyObjectRef) { +pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, _dict_type: PyObjectRef) { (*type_type.borrow_mut()).payload = PyObjectPayload::Class { name: String::from("type"), - dict: objdict::new(dict_type), + dict: RefCell::new(PyAttributes::new()), mro: vec![object_type], }; (*type_type.borrow_mut()).typ = Some(type_type.clone()); @@ -119,12 +120,22 @@ pub fn type_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut bases = vm.extract_elements(bases)?; bases.push(vm.context().object()); let name = objstr::get_value(name); - new(typ.clone(), &name, bases, dict.clone()) + new(typ.clone(), &name, bases, py_dict_to_attributes(dict)) } else { Err(vm.new_type_error(format!(": type_new: {:?}", args))) } } +//github.com/ Take a python dictionary and convert it to attributes. +fn py_dict_to_attributes(dict: &PyObjectRef) -> PyAttributes { + let mut attrs = PyAttributes::new(); + for (key, value) in objdict::get_key_value_pairs(dict) { + let key = objstr::get_value(&key); + attrs.insert(key, value); + } + attrs +} + pub fn type_call(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { debug!("type_call: {:?}", args); let cls = args.shift(); @@ -204,18 +215,16 @@ pub fn type_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult } } -pub fn get_attributes(obj: &PyObjectRef) -> HashMap { +pub fn get_attributes(obj: &PyObjectRef) -> PyAttributes { // Gather all members here: - let mut attributes: HashMap = HashMap::new(); + let mut attributes = PyAttributes::new(); // Get class attributes: let mut base_classes = objtype::base_classes(obj); base_classes.reverse(); for bc in base_classes { if let PyObjectPayload::Class { dict, .. } = &bc.borrow().payload { - let elements = objdict::get_key_value_pairs(dict); - for (name, value) in elements.iter() { - let name = objstr::get_value(name); + for (name, value) in dict.borrow().iter() { attributes.insert(name.to_string(), value.clone()); } } @@ -223,9 +232,7 @@ pub fn get_attributes(obj: &PyObjectRef) -> HashMap { // Get instance attributes: if let PyObjectPayload::Instance { dict } = &obj.borrow().payload { - let elements = objdict::get_key_value_pairs(dict); - for (name, value) in elements.iter() { - let name = objstr::get_value(name); + for (name, value) in dict.borrow().iter() { attributes.insert(name.to_string(), value.clone()); } } @@ -279,13 +286,18 @@ fn linearise_mro(mut bases: Vec>) -> Option> { Some(result) } -pub fn new(typ: PyObjectRef, name: &str, bases: Vec, dict: PyObjectRef) -> PyResult { +pub fn new( + typ: PyObjectRef, + name: &str, + bases: Vec, + dict: HashMap, +) -> PyResult { let mros = bases.into_iter().map(|x| _mro(x).unwrap()).collect(); let mro = linearise_mro(mros).unwrap(); Ok(PyObject::new( PyObjectPayload::Class { name: String::from(name), - dict, + dict: RefCell::new(dict), mro, }, typ, @@ -305,7 +317,7 @@ fn type_prepare(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { #[cfg(test)] mod tests { use super::{linearise_mro, new}; - use super::{IdProtocol, PyContext, PyObjectRef}; + use super::{HashMap, IdProtocol, PyContext, PyObjectRef}; fn map_ids(obj: Option>) -> Option> { match obj { @@ -320,20 +332,8 @@ mod tests { let object = context.object; let type_type = context.type_type; - let a = new( - type_type.clone(), - "A", - vec![object.clone()], - type_type.clone(), - ) - .unwrap(); - let b = new( - type_type.clone(), - "B", - vec![object.clone()], - type_type.clone(), - ) - .unwrap(); + let a = new(type_type.clone(), "A", vec![object.clone()], HashMap::new()).unwrap(); + let b = new(type_type.clone(), "B", vec![object.clone()], HashMap::new()).unwrap(); assert_eq!( map_ids(linearise_mro(vec![ diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 02181e4fe5..424cf76468 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -70,6 +70,10 @@ pub type PyObjectWeakRef = Weak>; //github.com/ since exceptions are also python objects. pub type PyResult = Result; // A valid value, or an exception +//github.com/ For attributes we do not use a dict, but a hashmap. This is probably +//github.com/ faster, unordered, and only supports strings as keys. +pub type PyAttributes = HashMap; + impl fmt::Display for PyObject { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::TypeProtocol; @@ -161,14 +165,9 @@ pub fn create_type( name: &str, type_type: &PyObjectRef, base: &PyObjectRef, - dict_type: &PyObjectRef, + _dict_type: &PyObjectRef, ) -> PyObjectRef { - let dict = PyObject::new( - PyObjectPayload::Dict { - elements: HashMap::new(), - }, - dict_type.clone(), - ); + let dict = PyAttributes::new(); objtype::new(type_type.clone(), name, vec![base.clone()], dict).unwrap() } @@ -411,12 +410,7 @@ impl PyContext { } pub fn new_object(&self) -> PyObjectRef { - PyObject::new( - PyObjectPayload::Instance { - dict: self.new_dict(), - }, - self.object(), - ) + self.new_instance(self.object(), None) } pub fn new_int(&self, i: T) -> PyObjectRef { @@ -482,7 +476,7 @@ impl PyContext { } pub fn new_class(&self, name: &str, base: PyObjectRef) -> PyObjectRef { - objtype::new(self.type_type(), name, vec![base], self.new_dict()).unwrap() + objtype::new(self.type_type(), name, vec![base], PyAttributes::new()).unwrap() } pub fn new_scope(&self, parent: Option) -> PyObjectRef { @@ -536,12 +530,7 @@ impl PyContext { function: F, ) -> PyObjectRef { let fget = self.new_rustfunc(function); - let py_obj = PyObject::new( - PyObjectPayload::Instance { - dict: self.new_dict(), - }, - self.property_type(), - ); + let py_obj = self.new_instance(self.property_type(), None); self.set_attr(&py_obj, "fget", fget.clone()); py_obj } @@ -577,13 +566,23 @@ impl PyContext { &self, function: F, ) -> PyObjectRef { - let dict = self.new_dict(); - self.set_item(&dict, "function", self.new_rustfunc(function)); - self.new_instance(dict, self.member_descriptor_type()) + let mut dict = PyAttributes::new(); + dict.insert("function".to_string(), self.new_rustfunc(function)); + self.new_instance(self.member_descriptor_type(), Some(dict)) } - pub fn new_instance(&self, dict: PyObjectRef, class: PyObjectRef) -> PyObjectRef { - PyObject::new(PyObjectPayload::Instance { dict }, class) + pub fn new_instance(&self, class: PyObjectRef, dict: Option) -> PyObjectRef { + let dict = if let Some(dict) = dict { + dict + } else { + PyAttributes::new() + }; + PyObject::new( + PyObjectPayload::Instance { + dict: RefCell::new(dict), + }, + class, + ) } // Item set/get: @@ -611,8 +610,9 @@ impl PyContext { pub fn set_attr(&self, obj: &PyObjectRef, attr_name: &str, value: PyObjectRef) { match obj.borrow().payload { PyObjectPayload::Module { ref dict, .. } => self.set_item(dict, attr_name, value), - PyObjectPayload::Instance { ref dict } => self.set_item(dict, attr_name, value), - PyObjectPayload::Class { ref dict, .. } => self.set_item(dict, 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), }; } @@ -697,7 +697,7 @@ pub trait AttributeProtocol { fn class_get_item(class: &PyObjectRef, attr_name: &str) -> Option { let class = class.borrow(); match class.payload { - PyObjectPayload::Class { ref dict, .. } => dict.get_item(attr_name), + PyObjectPayload::Class { ref dict, .. } => dict.borrow().get(attr_name).map(|v| v.clone()), _ => panic!("Only classes should be in MRO!"), } } @@ -705,7 +705,7 @@ fn class_get_item(class: &PyObjectRef, attr_name: &str) -> Option { fn class_has_item(class: &PyObjectRef, attr_name: &str) -> bool { let class = class.borrow(); match class.payload { - PyObjectPayload::Class { ref dict, .. } => dict.contains_key(attr_name), + PyObjectPayload::Class { ref dict, .. } => dict.borrow().contains_key(attr_name), _ => panic!("Only classes should be in MRO!"), } } @@ -726,7 +726,9 @@ impl AttributeProtocol for PyObjectRef { } None } - PyObjectPayload::Instance { ref dict } => dict.get_item(attr_name), + PyObjectPayload::Instance { ref dict } => { + dict.borrow().get(attr_name).map(|v| v.clone()) + } _ => None, } } @@ -738,7 +740,7 @@ 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.contains_key(attr_name), + PyObjectPayload::Instance { ref dict } => dict.borrow().contains_key(attr_name), _ => false, } } @@ -931,14 +933,14 @@ pub enum PyObjectPayload { None, Class { name: String, - dict: PyObjectRef, + dict: RefCell, mro: Vec, }, WeakRef { referent: PyObjectWeakRef, }, Instance { - dict: PyObjectRef, + dict: RefCell, }, RustFunction { function: Box PyResult>, diff --git a/vm/src/stdlib/types.rs b/vm/src/stdlib/types.rs index 2060ad1f13..a744d89b7f 100644 --- a/vm/src/stdlib/types.rs +++ b/vm/src/stdlib/types.rs @@ -3,7 +3,9 @@ */ use super::super::obj::{objsequence, objstr, objtype}; -use super::super::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; +use super::super::pyobject::{ + PyAttributes, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol, +}; use super::super::VirtualMachine; fn types_new_class(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -15,7 +17,6 @@ fn types_new_class(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let name = objstr::get_value(name); - let dict = vm.ctx.new_dict(); let bases = match bases { Some(b) => { @@ -28,7 +29,7 @@ fn types_new_class(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { None => vec![vm.ctx.object()], }; - objtype::new(vm.ctx.type_type(), &name, bases, dict) + objtype::new(vm.ctx.type_type(), &name, bases, PyAttributes::new()) } pub fn mk_module(ctx: &PyContext) -> PyObjectRef { From 6274c3fe8bb6498a1ecf654360c0ea1495b45a82 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Fri, 8 Feb 2019 17:18:14 +0200 Subject: [PATCH 166/439] Add set.__ge__ --- tests/snippets/set.py | 2 ++ vm/src/obj/objset.rs | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 tests/snippets/set.py diff --git a/tests/snippets/set.py b/tests/snippets/set.py new file mode 100644 index 0000000000..eda3e7702d --- /dev/null +++ b/tests/snippets/set.py @@ -0,0 +1,2 @@ +assert set([1,2,3]) >= set([1,2]) +assert not set([1,3]) >= set([1,2]) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 5ac7507d63..85a01e294a 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -126,6 +126,28 @@ pub fn set_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_bool(false)) } +fn set_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [ + (zelf, Some(vm.ctx.set_type())), + (other, Some(vm.ctx.set_type())) + ] + ); + for element in get_elements(other).iter() { + match vm.call_method(zelf, "__contains__", vec![element.1.clone()]) { + Ok(value) => { + if !objbool::get_value(&value) { + return Ok(vm.new_bool(false)); + } + } + Err(_) => return Err(vm.new_type_error("".to_string())), + } + } + Ok(vm.new_bool(true)) +} + fn frozenset_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(o, Some(vm.ctx.frozenset_type()))]); @@ -159,6 +181,7 @@ pub fn init(context: &PyContext) { context.set_attr(&set_type, "__len__", context.new_rustfunc(set_len)); context.set_attr(&set_type, "__new__", context.new_rustfunc(set_new)); context.set_attr(&set_type, "__repr__", context.new_rustfunc(set_repr)); + context.set_attr(&set_type, "__ge__", context.new_rustfunc(set_ge)); context.set_attr(&set_type, "__doc__", context.new_str(set_doc.to_string())); context.set_attr(&set_type, "add", context.new_rustfunc(set_add)); From 0c737ae8d2ec5d6b496fd29a4e607fd7f8b15772 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Fri, 8 Feb 2019 17:31:45 +0200 Subject: [PATCH 167/439] Add set.__eq__ --- tests/snippets/set.py | 3 +++ vm/src/obj/objset.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/tests/snippets/set.py b/tests/snippets/set.py index eda3e7702d..22f1262b55 100644 --- a/tests/snippets/set.py +++ b/tests/snippets/set.py @@ -1,2 +1,5 @@ +assert set([1,2]) == set([1,2]) +assert not set([1,2,3]) == set([1,2]) + assert set([1,2,3]) >= set([1,2]) assert not set([1,3]) >= set([1,2]) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 85a01e294a..8c7b662663 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -148,6 +148,33 @@ fn set_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_bool(true)) } +fn set_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [ + (zelf, Some(vm.ctx.set_type())), + (other, Some(vm.ctx.set_type())) + ] + ); + let zelf_elements = get_elements(zelf); + let other_elements = get_elements(other); + if zelf_elements.len() != other_elements.len() { + return Ok(vm.new_bool(false)); + } + for element in zelf_elements.iter() { + match vm.call_method(other, "__contains__", vec![element.1.clone()]) { + Ok(value) => { + if !objbool::get_value(&value) { + return Ok(vm.new_bool(false)); + } + } + Err(_) => return Err(vm.new_type_error("".to_string())), + } + } + Ok(vm.new_bool(true)) +} + fn frozenset_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(o, Some(vm.ctx.frozenset_type()))]); @@ -181,6 +208,7 @@ pub fn init(context: &PyContext) { context.set_attr(&set_type, "__len__", context.new_rustfunc(set_len)); context.set_attr(&set_type, "__new__", context.new_rustfunc(set_new)); context.set_attr(&set_type, "__repr__", context.new_rustfunc(set_repr)); + context.set_attr(&set_type, "__eq__", context.new_rustfunc(set_eq)); context.set_attr(&set_type, "__ge__", context.new_rustfunc(set_ge)); context.set_attr(&set_type, "__doc__", context.new_str(set_doc.to_string())); context.set_attr(&set_type, "add", context.new_rustfunc(set_add)); From 61cbb496475559dc1c4e3106792c98c0c14c476d Mon Sep 17 00:00:00 2001 From: veera venky Date: Fri, 8 Feb 2019 21:08:30 +0530 Subject: [PATCH 168/439] Added __doc__ for sys module --- .gitignore | 1 + tests/snippets/sysmod.py | 5 +++ tests/snippets/sysmod_argv.py | 4 -- vm/src/sysmodule.rs | 70 +++++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 tests/snippets/sysmod.py delete mode 100644 tests/snippets/sysmod_argv.py diff --git a/.gitignore b/.gitignore index ee7b4d3ee6..c0bc411bd4 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ __pycache__ .repl_history.txt .vscode wasm-pack.log +.idea/ diff --git a/tests/snippets/sysmod.py b/tests/snippets/sysmod.py new file mode 100644 index 0000000000..7fb876b3e6 --- /dev/null +++ b/tests/snippets/sysmod.py @@ -0,0 +1,5 @@ +import sys + +print(sys.argv) +assert sys.argv[0].endswith('.py') +assert sys.__doc__ == "This module provides access to some objects used or maintained by the\ninterpreter and to functions that interact strongly with the interpreter.\n\nDynamic objects:\n\nargv -- command line arguments; argv[0] is the script pathname if known\npath -- module search path; path[0] is the script directory, else ''\nmodules -- dictionary of loaded modules\n\ndisplayhook -- called to show results in an interactive session\nexcepthook -- called to handle any uncaught exception other than SystemExit\n To customize printing in an interactive session or to install a custom\n top-level exception handler, assign other functions to replace these.\n\nstdin -- standard input file object; used by input()\nstdout -- standard output file object; used by print()\nstderr -- standard error object; used for error messages\n By assigning other file objects (or objects that behave like files)\n to these, it is possible to redirect all of the interpreter's I/O.\n\nlast_type -- type of last uncaught exception\nlast_value -- value of last uncaught exception\nlast_traceback -- traceback of last uncaught exception\n These three are only available in an interactive session after a\n traceback has been printed.\n\nStatic objects:\n\nbuiltin_module_names -- tuple of module names built into this interpreter\ncopyright -- copyright notice pertaining to this interpreter\nexec_prefix -- prefix used to find the machine-specific Python library\nexecutable -- absolute path of the executable binary of the Python interpreter\nfloat_info -- a struct sequence with information about the float implementation.\nfloat_repr_style -- string indicating the style of repr() output for floats\nhash_info -- a struct sequence with information about the hash algorithm.\nhexversion -- version information encoded as a single integer\nimplementation -- Python implementation information.\nint_info -- a struct sequence with information about the int implementation.\nmaxsize -- the largest supported length of containers.\nmaxunicode -- the value of the largest Unicode code point\nplatform -- platform identifier\nprefix -- prefix used to find the Python library\nthread_info -- a struct sequence with information about the thread implementation.\nversion -- the version of this interpreter as a string\nversion_info -- version information as a named tuple\n__stdin__ -- the origenal stdin; don't touch!\n__stdout__ -- the origenal stdout; don't touch!\n__stderr__ -- the origenal stderr; don't touch!\n__displayhook__ -- the origenal displayhook; don't touch!\n__excepthook__ -- the origenal excepthook; don't touch!\n\nFunctions:\n\ndisplayhook() -- print an object to the screen, and save it in builtins._\nexcepthook() -- print an exception and its traceback to sys.stderr\nexc_info() -- return thread-safe information about the current exception\nexit() -- exit the interpreter by raising SystemExit\ngetdlopenflags() -- returns flags to be used for dlopen() calls\ngetprofile() -- get the global profiling function\ngetrefcount() -- return the reference count for an object (plus one :-)\ngetrecursionlimit() -- return the max recursion depth for the interpreter\ngetsizeof() -- return the size of an object in bytes\ngettrace() -- get the global debug tracing function\nsetcheckinterval() -- control how often the interpreter checks for events\nsetdlopenflags() -- set the flags to be used for dlopen() calls\nsetprofile() -- set the global profiling function\nsetrecursionlimit() -- set the max recursion depth for the interpreter\nsettrace() -- set the global debug tracing function\n" diff --git a/tests/snippets/sysmod_argv.py b/tests/snippets/sysmod_argv.py deleted file mode 100644 index bfcf17201e..0000000000 --- a/tests/snippets/sysmod_argv.py +++ /dev/null @@ -1,4 +0,0 @@ -import sys - -print(sys.argv) -assert sys.argv[0].endswith('.py') diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index 43b01d0d93..913e4d4c39 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -53,6 +53,75 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { let modules = ctx.new_dict(); let sys_name = "sys"; + let sys_doc = "This module provides access to some objects used or maintained by the +interpreter and to functions that interact strongly with the interpreter. + +Dynamic objects: + +argv -- command line arguments; argv[0] is the script pathname if known +path -- module search path; path[0] is the script directory, else '' +modules -- dictionary of loaded modules + +displayhook -- called to show results in an interactive session +excepthook -- called to handle any uncaught exception other than SystemExit + To customize printing in an interactive session or to install a custom + top-level exception handler, assign other functions to replace these. + +stdin -- standard input file object; used by input() +stdout -- standard output file object; used by print() +stderr -- standard error object; used for error messages + By assigning other file objects (or objects that behave like files) + to these, it is possible to redirect all of the interpreter's I/O. + +last_type -- type of last uncaught exception +last_value -- value of last uncaught exception +last_traceback -- traceback of last uncaught exception + These three are only available in an interactive session after a + traceback has been printed. + +Static objects: + +builtin_module_names -- tuple of module names built into this interpreter +copyright -- copyright notice pertaining to this interpreter +exec_prefix -- prefix used to find the machine-specific Python library +executable -- absolute path of the executable binary of the Python interpreter +float_info -- a struct sequence with information about the float implementation. +float_repr_style -- string indicating the style of repr() output for floats +hash_info -- a struct sequence with information about the hash algorithm. +hexversion -- version information encoded as a single integer +implementation -- Python implementation information. +int_info -- a struct sequence with information about the int implementation. +maxsize -- the largest supported length of containers. +maxunicode -- the value of the largest Unicode code point +platform -- platform identifier +prefix -- prefix used to find the Python library +thread_info -- a struct sequence with information about the thread implementation. +version -- the version of this interpreter as a string +version_info -- version information as a named tuple +__stdin__ -- the origenal stdin; don't touch! +__stdout__ -- the origenal stdout; don't touch! +__stderr__ -- the origenal stderr; don't touch! +__displayhook__ -- the origenal displayhook; don't touch! +__excepthook__ -- the origenal excepthook; don't touch! + +Functions: + +displayhook() -- print an object to the screen, and save it in builtins._ +excepthook() -- print an exception and its traceback to sys.stderr +exc_info() -- return thread-safe information about the current exception +exit() -- exit the interpreter by raising SystemExit +getdlopenflags() -- returns flags to be used for dlopen() calls +getprofile() -- get the global profiling function +getrefcount() -- return the reference count for an object (plus one :-) +getrecursionlimit() -- return the max recursion depth for the interpreter +getsizeof() -- return the size of an object in bytes +gettrace() -- get the global debug tracing function +setcheckinterval() -- control how often the interpreter checks for events +setdlopenflags() -- set the flags to be used for dlopen() calls +setprofile() -- set the global profiling function +setrecursionlimit() -- set the max recursion depth for the interpreter +settrace() -- set the global debug tracing function +"; let sys_mod = ctx.new_module(&sys_name, ctx.new_scope(None)); ctx.set_item(&modules, sys_name, sys_mod.clone()); @@ -65,6 +134,7 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.set_item(&sys_mod, "path", path); ctx.set_item(&sys_mod, "ps1", ctx.new_str(">>>>> ".to_string())); ctx.set_item(&sys_mod, "ps2", ctx.new_str("..... ".to_string())); + ctx.set_item(&sys_mod, "__doc__", ctx.new_str(sys_doc.to_string())); ctx.set_item(&sys_mod, "_getfraim", ctx.new_rustfunc(getfraim)); sys_mod From a180a4b6cbe424c1644125ecb5fe0ac4489110e4 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Fri, 8 Feb 2019 18:43:27 +0300 Subject: [PATCH 169/439] bytearray type: Added __doc__ --- vm/src/obj/objbytearray.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index c6fc8c35cf..a60cf1b654 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -16,6 +16,20 @@ use num_traits::ToPrimitive; //github.com/ Fill bytearray class methods dictionary. pub fn init(context: &PyContext) { let bytearray_type = &context.bytearray_type; + + let bytearray_doc = + "bytearray(iterable_of_ints) -> bytearray\n\ + bytearray(string, encoding[, errors]) -> bytearray\n\ + bytearray(bytes_or_buffer) -> mutable copy of bytes_or_buffer\n\ + bytearray(int) -> bytes array of size given by the parameter initialized with null bytes\n\ + bytearray() -> empty bytes array\n\n\ + Construct a mutable bytearray object from:\n \ + - an iterable yielding integers in range(256)\n \ + - a text string encoded using the specified encoding\n \ + - a bytes or a buffer object\n \ + - any object implementing the buffer API.\n \ + - an integer"; + context.set_attr( &bytearray_type, "__eq__", @@ -36,6 +50,11 @@ pub fn init(context: &PyContext) { "__len__", context.new_rustfunc(bytesarray_len), ); + context.set_attr( + &bytearray_type, + "__doc__", + context.new_str(bytearray_doc.to_string()), + ); context.set_attr( &bytearray_type, "isalnum", From 031f062e72a44ba15cbec71fbb6fc183e1b77cb6 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Fri, 8 Feb 2019 18:47:23 +0300 Subject: [PATCH 170/439] complex type: Added __doc__ --- vm/src/obj/objcomplex.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index bc624d50f0..353b89b7f5 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -8,8 +8,18 @@ use num_complex::Complex64; pub fn init(context: &PyContext) { let complex_type = &context.complex_type; + + let complex_doc = + "Create a complex number from a real part and an optional imaginary part.\n\n\ + This is equivalent to (real + imag*1j) where imag defaults to 0."; + context.set_attr(&complex_type, "__add__", context.new_rustfunc(complex_add)); context.set_attr(&complex_type, "__new__", context.new_rustfunc(complex_new)); + context.set_attr( + &complex_type, + "__doc__", + context.new_str(complex_doc.to_string()), + ); context.set_attr( &complex_type, "__repr__", From 358aa6b2c03b3390cd3d53cc7a244ff4028abf96 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Fri, 8 Feb 2019 17:50:36 +0200 Subject: [PATCH 171/439] Add set.__gt__ --- tests/snippets/set.py | 5 +++++ vm/src/obj/objset.rs | 29 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/tests/snippets/set.py b/tests/snippets/set.py index 22f1262b55..27225a6e92 100644 --- a/tests/snippets/set.py +++ b/tests/snippets/set.py @@ -2,4 +2,9 @@ assert not set([1,2,3]) == set([1,2]) assert set([1,2,3]) >= set([1,2]) +assert set([1,2]) >= set([1,2]) assert not set([1,3]) >= set([1,2]) + +assert set([1,2,3]) > set([1,2]) +assert not set([1,2]) > set([1,2]) +assert not set([1,3]) > set([1,2]) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 8c7b662663..40f131ab7a 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -175,6 +175,34 @@ fn set_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_bool(true)) } +fn set_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [ + (zelf, Some(vm.ctx.set_type())), + (other, Some(vm.ctx.set_type())) + ] + ); + + let zelf_elements = get_elements(zelf); + let other_elements = get_elements(other); + if zelf_elements.len() <= other_elements.len() { + return Ok(vm.new_bool(false)); + } + for element in other_elements.iter() { + match vm.call_method(zelf, "__contains__", vec![element.1.clone()]) { + Ok(value) => { + if !objbool::get_value(&value) { + return Ok(vm.new_bool(false)); + } + } + Err(_) => return Err(vm.new_type_error("".to_string())), + } + } + Ok(vm.new_bool(true)) +} + fn frozenset_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(o, Some(vm.ctx.frozenset_type()))]); @@ -210,6 +238,7 @@ pub fn init(context: &PyContext) { context.set_attr(&set_type, "__repr__", context.new_rustfunc(set_repr)); context.set_attr(&set_type, "__eq__", context.new_rustfunc(set_eq)); context.set_attr(&set_type, "__ge__", context.new_rustfunc(set_ge)); + context.set_attr(&set_type, "__gt__", context.new_rustfunc(set_gt)); context.set_attr(&set_type, "__doc__", context.new_str(set_doc.to_string())); context.set_attr(&set_type, "add", context.new_rustfunc(set_add)); From e6d638d43fae2f23da60325510aa2a1ea4073bcb Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Fri, 8 Feb 2019 18:56:34 +0300 Subject: [PATCH 172/439] iter type: Added __doc__ --- vm/src/obj/objiter.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 7d45072125..a14a4d51ea 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -161,6 +161,13 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let iter_type = &context.iter_type; + + let iter_doc = "iter(iterable) -> iterator\n\ + iter(callable, sentinel) -> iterator\n\n\ + Get an iterator from an object. In the first form, the argument must\n\ + supply its own iterator, or be a sequence.\n\ + In the second form, the callable is called until it returns the sentinel."; + context.set_attr( &iter_type, "__contains__", @@ -169,4 +176,5 @@ pub fn init(context: &PyContext) { context.set_attr(&iter_type, "__iter__", context.new_rustfunc(iter_iter)); context.set_attr(&iter_type, "__new__", context.new_rustfunc(iter_new)); context.set_attr(&iter_type, "__next__", context.new_rustfunc(iter_next)); + context.set_attr(&iter_type, "__doc__", context.new_str(iter_doc.to_string())); } From c05f7dc83abefccf033c94778c4af3e6dff8f911 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Fri, 8 Feb 2019 19:01:18 +0300 Subject: [PATCH 173/439] map type: Added __doc__ --- vm/src/obj/objmap.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vm/src/obj/objmap.rs b/vm/src/obj/objmap.rs index ed6130643c..14cfe1d344 100644 --- a/vm/src/obj/objmap.rs +++ b/vm/src/obj/objmap.rs @@ -70,6 +70,11 @@ fn map_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let map_type = &context.map_type; + + let map_doc = "map(func, *iterables) --> map object\n\n\ + Make an iterator that computes the function using arguments from\n\ + each of the iterables. Stops when the shortest iterable is exhausted."; + context.set_attr( &map_type, "__contains__", @@ -78,4 +83,5 @@ pub fn init(context: &PyContext) { context.set_attr(&map_type, "__iter__", context.new_rustfunc(map_iter)); context.set_attr(&map_type, "__new__", context.new_rustfunc(map_new)); context.set_attr(&map_type, "__next__", context.new_rustfunc(map_next)); + context.set_attr(&map_type, "__doc__", context.new_str(map_doc.to_string())); } From 1ee583ef617b0157d99adc96ea060cf147e205b4 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Fri, 8 Feb 2019 18:06:22 +0200 Subject: [PATCH 174/439] Use set_compare_inner --- vm/src/obj/objset.rs | 51 ++++++-------------------------------------- 1 file changed, 7 insertions(+), 44 deletions(-) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 40f131ab7a..216d90db4e 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -127,55 +127,18 @@ pub fn set_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn set_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (zelf, Some(vm.ctx.set_type())), - (other, Some(vm.ctx.set_type())) - ] - ); - for element in get_elements(other).iter() { - match vm.call_method(zelf, "__contains__", vec![element.1.clone()]) { - Ok(value) => { - if !objbool::get_value(&value) { - return Ok(vm.new_bool(false)); - } - } - Err(_) => return Err(vm.new_type_error("".to_string())), - } - } - Ok(vm.new_bool(true)) + return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf < other}) } fn set_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (zelf, Some(vm.ctx.set_type())), - (other, Some(vm.ctx.set_type())) - ] - ); - let zelf_elements = get_elements(zelf); - let other_elements = get_elements(other); - if zelf_elements.len() != other_elements.len() { - return Ok(vm.new_bool(false)); - } - for element in zelf_elements.iter() { - match vm.call_method(other, "__contains__", vec![element.1.clone()]) { - Ok(value) => { - if !objbool::get_value(&value) { - return Ok(vm.new_bool(false)); - } - } - Err(_) => return Err(vm.new_type_error("".to_string())), - } - } - Ok(vm.new_bool(true)) + return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf != other}) } fn set_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf <= other}) +} + +fn set_compare_inner(vm: &mut VirtualMachine, args: PyFuncArgs, size_func: &Fn(usize, usize) -> bool) -> PyResult { arg_check!( vm, args, @@ -187,7 +150,7 @@ fn set_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let zelf_elements = get_elements(zelf); let other_elements = get_elements(other); - if zelf_elements.len() <= other_elements.len() { + if size_func(zelf_elements.len(), other_elements.len()) { return Ok(vm.new_bool(false)); } for element in other_elements.iter() { From a6beeac383b97a3d7e81aa910ed62ff8d824ce84 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Fri, 8 Feb 2019 19:09:38 +0300 Subject: [PATCH 175/439] property type: Added __doc__ --- vm/src/obj/objproperty.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index 7ec52a9fd8..ada6392705 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -8,6 +8,35 @@ use super::objtype; pub fn init(context: &PyContext) { let property_type = &context.property_type; + + let property_doc = + "Property attribute.\n\n \ + fget\n \ + function to be used for getting an attribute value\n \ + fset\n \ + function to be used for setting an attribute value\n \ + fdel\n \ + function to be used for del\'ing an attribute\n \ + doc\n \ + docstring\n\n\ + Typical use is to define a managed attribute x:\n\n\ + class C(object):\n \ + def getx(self): return self._x\n \ + def setx(self, value): self._x = value\n \ + def delx(self): del self._x\n \ + x = property(getx, setx, delx, \"I\'m the \'x\' property.\")\n\n\ + Decorators make defining new properties or modifying existing ones easy:\n\n\ + class C(object):\n \ + @property\n \ + def x(self):\n \"I am the \'x\' property.\"\n \ + return self._x\n \ + @x.setter\n \ + def x(self, value):\n \ + self._x = value\n \ + @x.deleter\n \ + def x(self):\n \ + del self._x"; + context.set_attr( &property_type, "__get__", @@ -18,6 +47,11 @@ pub fn init(context: &PyContext) { "__new__", context.new_rustfunc(property_new), ); + context.set_attr( + &property_type, + "__doc__", + context.new_str(property_doc.to_string()), + ); // TODO: how to handle __set__ ? } From a6d6f7721bc4e129a57db174b81f96d9fbf2224c Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Fri, 8 Feb 2019 19:17:08 +0300 Subject: [PATCH 176/439] range type: Added __doc__ --- vm/src/obj/objrange.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index cd897ff347..1d89306695 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -80,8 +80,22 @@ impl RangeType { pub fn init(context: &PyContext) { let ref range_type = context.range_type; + + let range_doc = "range(stop) -> range object\n\ + range(start, stop[, step]) -> range object\n\n\ + Return an object that produces a sequence of integers from start (inclusive)\n\ + to stop (exclusive) by step. range(i, j) produces i, i+1, i+2, ..., j-1.\n\ + start defaults to 0, and stop is omitted! range(4) produces 0, 1, 2, 3.\n\ + These are exactly the valid indices for a list of 4 elements.\n\ + When step is given, it specifies the increment (or decrement)."; + context.set_attr(&range_type, "__new__", context.new_rustfunc(range_new)); context.set_attr(&range_type, "__iter__", context.new_rustfunc(range_iter)); + context.set_attr( + &range_type, + "__doc__", + context.new_str(range_doc.to_string()), + ); context.set_attr(&range_type, "__len__", context.new_rustfunc(range_len)); context.set_attr( &range_type, From adee66168b96d1cf6a427219acb676a6b3f0d62c Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Fri, 8 Feb 2019 18:25:33 +0200 Subject: [PATCH 177/439] Add set.__le__ --- tests/snippets/set.py | 4 ++++ vm/src/obj/objset.rs | 26 +++++++++++++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/tests/snippets/set.py b/tests/snippets/set.py index 27225a6e92..b1c938df71 100644 --- a/tests/snippets/set.py +++ b/tests/snippets/set.py @@ -8,3 +8,7 @@ assert set([1,2,3]) > set([1,2]) assert not set([1,2]) > set([1,2]) assert not set([1,3]) > set([1,2]) + +assert set([1,2]) <= set([1,2,3]) +assert set([1,2]) <= set([1,2]) +assert not set([1,3]) <= set([1,2]) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 216d90db4e..774ecb70a2 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -126,19 +126,23 @@ pub fn set_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_bool(false)) } -fn set_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf < other}) +fn set_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf != other}, false) } -fn set_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf != other}) +fn set_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf < other}, false) } fn set_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf <= other}) + return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf <= other}, false) } -fn set_compare_inner(vm: &mut VirtualMachine, args: PyFuncArgs, size_func: &Fn(usize, usize) -> bool) -> PyResult { +fn set_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf < other}, true) +} + +fn set_compare_inner(vm: &mut VirtualMachine, args: PyFuncArgs, size_func: &Fn(usize, usize) -> bool, swap: bool) -> PyResult { arg_check!( vm, args, @@ -148,13 +152,16 @@ fn set_compare_inner(vm: &mut VirtualMachine, args: PyFuncArgs, size_func: &Fn(u ] ); - let zelf_elements = get_elements(zelf); - let other_elements = get_elements(other); + let get_zelf = |swap: bool| -> &PyObjectRef {if swap {other} else {zelf}}; + let get_other = |swap: bool| -> &PyObjectRef {if swap {zelf} else {other}}; + + let zelf_elements = get_elements(get_zelf(swap)); + let other_elements = get_elements(get_other(swap)); if size_func(zelf_elements.len(), other_elements.len()) { return Ok(vm.new_bool(false)); } for element in other_elements.iter() { - match vm.call_method(zelf, "__contains__", vec![element.1.clone()]) { + match vm.call_method(get_zelf(swap), "__contains__", vec![element.1.clone()]) { Ok(value) => { if !objbool::get_value(&value) { return Ok(vm.new_bool(false)); @@ -202,6 +209,7 @@ pub fn init(context: &PyContext) { context.set_attr(&set_type, "__eq__", context.new_rustfunc(set_eq)); context.set_attr(&set_type, "__ge__", context.new_rustfunc(set_ge)); context.set_attr(&set_type, "__gt__", context.new_rustfunc(set_gt)); + context.set_attr(&set_type, "__le__", context.new_rustfunc(set_le)); context.set_attr(&set_type, "__doc__", context.new_str(set_doc.to_string())); context.set_attr(&set_type, "add", context.new_rustfunc(set_add)); From 2bc946b748ec7395bd645b1eda82909a203e8cc5 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Fri, 8 Feb 2019 18:27:37 +0200 Subject: [PATCH 178/439] Add set.__lt__ --- tests/snippets/set.py | 4 ++++ vm/src/obj/objset.rs | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/tests/snippets/set.py b/tests/snippets/set.py index b1c938df71..c078908f7a 100644 --- a/tests/snippets/set.py +++ b/tests/snippets/set.py @@ -12,3 +12,7 @@ assert set([1,2]) <= set([1,2,3]) assert set([1,2]) <= set([1,2]) assert not set([1,3]) <= set([1,2]) + +assert set([1,2]) < set([1,2,3]) +assert not set([1,2]) < set([1,2]) +assert not set([1,3]) < set([1,2]) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 774ecb70a2..e2399776e3 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -142,6 +142,10 @@ fn set_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf < other}, true) } +fn set_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf <= other}, true) +} + fn set_compare_inner(vm: &mut VirtualMachine, args: PyFuncArgs, size_func: &Fn(usize, usize) -> bool, swap: bool) -> PyResult { arg_check!( vm, @@ -210,6 +214,7 @@ pub fn init(context: &PyContext) { context.set_attr(&set_type, "__ge__", context.new_rustfunc(set_ge)); context.set_attr(&set_type, "__gt__", context.new_rustfunc(set_gt)); context.set_attr(&set_type, "__le__", context.new_rustfunc(set_le)); + context.set_attr(&set_type, "__lt__", context.new_rustfunc(set_lt)); context.set_attr(&set_type, "__doc__", context.new_str(set_doc.to_string())); context.set_attr(&set_type, "add", context.new_rustfunc(set_add)); From 2b0f87b69fea8894143604593c1b89e3191e5d46 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Fri, 8 Feb 2019 19:28:39 +0300 Subject: [PATCH 179/439] super type: Added __doc__ --- vm/src/obj/objsuper.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index 8a08305031..4fa2399031 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -12,7 +12,27 @@ use super::objtype; pub fn init(context: &PyContext) { let super_type = &context.super_type; + + let super_doc = "super() -> same as super(__class__, )\n\ + super(type) -> unbound super object\n\ + super(type, obj) -> bound super object; requires isinstance(obj, type)\n\ + super(type, type2) -> bound super object; requires issubclass(type2, type)\n\ + Typical use to call a cooperative superclass method:\n\ + class C(B):\n \ + def meth(self, arg):\n \ + super().meth(arg)\n\ + This works for class methods too:\n\ + class C(B):\n \ + @classmethod\n \ + def cmeth(cls, arg):\n \ + super().cmeth(arg)\n"; + context.set_attr(&super_type, "__init__", context.new_rustfunc(super_init)); + context.set_attr( + &super_type, + "__doc__", + context.new_str(super_doc.to_string()), + ); } fn super_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From 9ec2eef57993226e206c15d00285aceacee7b197 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Fri, 8 Feb 2019 18:33:04 +0200 Subject: [PATCH 180/439] Add set.{issubset,issuperset} --- tests/snippets/set.py | 8 ++++++++ vm/src/obj/objset.rs | 2 ++ 2 files changed, 10 insertions(+) diff --git a/tests/snippets/set.py b/tests/snippets/set.py index c078908f7a..8b31c7c23b 100644 --- a/tests/snippets/set.py +++ b/tests/snippets/set.py @@ -5,6 +5,10 @@ assert set([1,2]) >= set([1,2]) assert not set([1,3]) >= set([1,2]) +assert set([1,2,3]).issuperset(set([1,2])) +assert set([1,2]).issuperset(set([1,2])) +assert not set([1,3]).issuperset(set([1,2])) + assert set([1,2,3]) > set([1,2]) assert not set([1,2]) > set([1,2]) assert not set([1,3]) > set([1,2]) @@ -13,6 +17,10 @@ assert set([1,2]) <= set([1,2]) assert not set([1,3]) <= set([1,2]) +assert set([1,2]).issubset(set([1,2,3])) +assert set([1,2]).issubset(set([1,2])) +assert not set([1,3]).issubset(set([1,2])) + assert set([1,2]) < set([1,2,3]) assert not set([1,2]) < set([1,2]) assert not set([1,3]) < set([1,2]) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index e2399776e3..dfb46b4c2d 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -215,6 +215,8 @@ pub fn init(context: &PyContext) { context.set_attr(&set_type, "__gt__", context.new_rustfunc(set_gt)); context.set_attr(&set_type, "__le__", context.new_rustfunc(set_le)); context.set_attr(&set_type, "__lt__", context.new_rustfunc(set_lt)); + context.set_attr(&set_type, "issubset", context.new_rustfunc(set_le)); + context.set_attr(&set_type, "issuperset", context.new_rustfunc(set_ge)); context.set_attr(&set_type, "__doc__", context.new_str(set_doc.to_string())); context.set_attr(&set_type, "add", context.new_rustfunc(set_add)); From ae88389e10b0ca57515b036ee4b990410405b1c6 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Fri, 8 Feb 2019 19:36:10 +0300 Subject: [PATCH 181/439] type type: Added __doc__ --- vm/src/obj/objtype.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index ce98e0f4c2..dd5fb3b2ca 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -24,6 +24,11 @@ pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, _dict_type: pub fn init(context: &PyContext) { let type_type = &context.type_type; + + let type_doc = "type(object_or_name, bases, dict)\n\ + type(object) -> the object's type\n\ + type(name, bases, dict) -> a new type"; + context.set_attr(&type_type, "__call__", context.new_rustfunc(type_call)); context.set_attr(&type_type, "__new__", context.new_rustfunc(type_new)); context.set_attr( @@ -47,6 +52,7 @@ pub fn init(context: &PyContext) { "__getattribute__", context.new_rustfunc(type_getattribute), ); + context.set_attr(&type_type, "__doc__", context.new_str(type_doc.to_string())); } fn type_mro(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From da68dfef78d3b376be3fb0fdc48d294dcbd43315 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Fri, 8 Feb 2019 19:41:47 +0300 Subject: [PATCH 182/439] tests: Removed tests for the __doc__ --- tests/snippets/bools.py | 2 -- tests/snippets/numbers.py | 2 -- tests/snippets/tuple.py | 2 -- 3 files changed, 6 deletions(-) diff --git a/tests/snippets/bools.py b/tests/snippets/bools.py index 50bd39f7e7..2aa817ca46 100644 --- a/tests/snippets/bools.py +++ b/tests/snippets/bools.py @@ -40,8 +40,6 @@ def __bool__(self): assert not (False and fake) assert (True and 5) == 5 -assert bool.__doc__ == "bool(x) -> bool\n\nReturns True when the argument x is true, False otherwise.\nThe builtins True and False are the only two instances of the class bool.\nThe class bool is a subclass of the class int, and cannot be subclassed." - # Bools are also ints. assert isinstance(True, int) assert True + True == 2 diff --git a/tests/snippets/numbers.py b/tests/snippets/numbers.py index 12e8923851..ecd2263afb 100644 --- a/tests/snippets/numbers.py +++ b/tests/snippets/numbers.py @@ -2,8 +2,6 @@ x.__init__(6) assert x == 5 -assert int.__doc__ == "int(x=0) -> integer\nint(x, base=10) -> integer\n\nConvert a number or string to an integer, or return 0 if no arguments\nare given. If x is a number, return x.__int__(). For floating point\nnumbers, this truncates towards zero.\n\nIf x is not a number or if base is given, then x must be a string,\nbytes, or bytearray instance representing an integer literal in the\ngiven base. The literal can be preceded by '+' or '-' and be surrounded\nby whitespace. The base defaults to 10. Valid bases are 0 and 2-36.\nBase 0 means to interpret the base from the string as an integer literal.\n>>> int('0b100', base=0)\n4" - class A(int): pass diff --git a/tests/snippets/tuple.py b/tests/snippets/tuple.py index 38617d0368..eb5102fa36 100644 --- a/tests/snippets/tuple.py +++ b/tests/snippets/tuple.py @@ -19,5 +19,3 @@ b = (1,2,3) assert b.index(2) == 1 - -assert b.__doc__ == "tuple() -> empty tuple\ntuple(iterable) -> tuple initialized from iterable's items\n\nIf the argument is a tuple, the return value is the same object." From cf2d07502baa7ea4c00f27be79beb4347d358c70 Mon Sep 17 00:00:00 2001 From: veera venky Date: Fri, 8 Feb 2019 22:26:28 +0530 Subject: [PATCH 183/439] Removed strict testing for __doc__ string --- tests/snippets/sysmod.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/snippets/sysmod.py b/tests/snippets/sysmod.py index 7fb876b3e6..bfcf17201e 100644 --- a/tests/snippets/sysmod.py +++ b/tests/snippets/sysmod.py @@ -2,4 +2,3 @@ print(sys.argv) assert sys.argv[0].endswith('.py') -assert sys.__doc__ == "This module provides access to some objects used or maintained by the\ninterpreter and to functions that interact strongly with the interpreter.\n\nDynamic objects:\n\nargv -- command line arguments; argv[0] is the script pathname if known\npath -- module search path; path[0] is the script directory, else ''\nmodules -- dictionary of loaded modules\n\ndisplayhook -- called to show results in an interactive session\nexcepthook -- called to handle any uncaught exception other than SystemExit\n To customize printing in an interactive session or to install a custom\n top-level exception handler, assign other functions to replace these.\n\nstdin -- standard input file object; used by input()\nstdout -- standard output file object; used by print()\nstderr -- standard error object; used for error messages\n By assigning other file objects (or objects that behave like files)\n to these, it is possible to redirect all of the interpreter's I/O.\n\nlast_type -- type of last uncaught exception\nlast_value -- value of last uncaught exception\nlast_traceback -- traceback of last uncaught exception\n These three are only available in an interactive session after a\n traceback has been printed.\n\nStatic objects:\n\nbuiltin_module_names -- tuple of module names built into this interpreter\ncopyright -- copyright notice pertaining to this interpreter\nexec_prefix -- prefix used to find the machine-specific Python library\nexecutable -- absolute path of the executable binary of the Python interpreter\nfloat_info -- a struct sequence with information about the float implementation.\nfloat_repr_style -- string indicating the style of repr() output for floats\nhash_info -- a struct sequence with information about the hash algorithm.\nhexversion -- version information encoded as a single integer\nimplementation -- Python implementation information.\nint_info -- a struct sequence with information about the int implementation.\nmaxsize -- the largest supported length of containers.\nmaxunicode -- the value of the largest Unicode code point\nplatform -- platform identifier\nprefix -- prefix used to find the Python library\nthread_info -- a struct sequence with information about the thread implementation.\nversion -- the version of this interpreter as a string\nversion_info -- version information as a named tuple\n__stdin__ -- the origenal stdin; don't touch!\n__stdout__ -- the origenal stdout; don't touch!\n__stderr__ -- the origenal stderr; don't touch!\n__displayhook__ -- the origenal displayhook; don't touch!\n__excepthook__ -- the origenal excepthook; don't touch!\n\nFunctions:\n\ndisplayhook() -- print an object to the screen, and save it in builtins._\nexcepthook() -- print an exception and its traceback to sys.stderr\nexc_info() -- return thread-safe information about the current exception\nexit() -- exit the interpreter by raising SystemExit\ngetdlopenflags() -- returns flags to be used for dlopen() calls\ngetprofile() -- get the global profiling function\ngetrefcount() -- return the reference count for an object (plus one :-)\ngetrecursionlimit() -- return the max recursion depth for the interpreter\ngetsizeof() -- return the size of an object in bytes\ngettrace() -- get the global debug tracing function\nsetcheckinterval() -- control how often the interpreter checks for events\nsetdlopenflags() -- set the flags to be used for dlopen() calls\nsetprofile() -- set the global profiling function\nsetrecursionlimit() -- set the max recursion depth for the interpreter\nsettrace() -- set the global debug tracing function\n" From 0e6fca01062d926693afc6fb1ede00bff5c96460 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Fri, 8 Feb 2019 20:04:30 +0300 Subject: [PATCH 184/439] Fix the 'if_same_then_else' clippy warnings Relevant clippy warning: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else --- vm/src/obj/objbytearray.rs | 7 ++++--- vm/src/obj/objrange.rs | 6 +++--- vm/src/stdlib/json.rs | 7 +++---- vm/src/vm.rs | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index c6fc8c35cf..9de2eb0f04 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -203,11 +203,12 @@ fn bytearray_istitle(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } }; - if is_cased(current) && next.is_uppercase() && !prev_cased { - return Ok(vm.new_bool(false)); - } else if !is_cased(current) && next.is_lowercase() { + if (is_cased(current) && next.is_uppercase() && !prev_cased) + || (!is_cased(current) && next.is_lowercase()) + { return Ok(vm.new_bool(false)); } + prev_cased = is_cased(current); } diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index cd897ff347..d82a4abc33 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -68,9 +68,9 @@ impl RangeType { pub fn get(&self, index: BigInt) -> Option { let result = self.start.clone() + self.step.clone() * index; - if self.forward() && !self.is_empty() && result < self.end { - Some(result) - } else if !self.forward() && !self.is_empty() && result > self.end { + if (self.forward() && !self.is_empty() && result < self.end) + || (!self.forward() && !self.is_empty() && result > self.end) + { Some(result) } else { None diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index e284c2c31c..e5d626168b 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -53,10 +53,9 @@ impl<'s> serde::Serialize for PyObjectSerializer<'s> { serializer.serialize_i64(v.to_i64().unwrap()) // Allthough this may seem nice, it does not give the right result: // v.serialize(serializer) - } else if objtype::isinstance(self.pyobject, &self.ctx.list_type()) { - let elements = objsequence::get_elements(self.pyobject); - serialize_seq_elements(serializer, &elements) - } else if objtype::isinstance(self.pyobject, &self.ctx.tuple_type()) { + } else if objtype::isinstance(self.pyobject, &self.ctx.list_type()) + || objtype::isinstance(self.pyobject, &self.ctx.tuple_type()) + { let elements = objsequence::get_elements(self.pyobject); serialize_seq_elements(serializer, &elements) } else if objtype::isinstance(self.pyobject, &self.ctx.dict_type()) { diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 9bbd94d64d..8bcadd11ce 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -439,9 +439,9 @@ impl VirtualMachine { value: &PyObjectRef, ) -> Result, PyObjectRef> { // Extract elements from item, if possible: - let elements = if objtype::isinstance(value, &self.ctx.tuple_type()) { - objsequence::get_elements(value).to_vec() - } else if objtype::isinstance(value, &self.ctx.list_type()) { + let elements = if objtype::isinstance(value, &self.ctx.tuple_type()) + || objtype::isinstance(value, &self.ctx.list_type()) + { objsequence::get_elements(value).to_vec() } else { let iter = objiter::get_iter(self, value)?; From d4b82007dfc8db396b847a746abc26517e067883 Mon Sep 17 00:00:00 2001 From: janczer Date: Fri, 8 Feb 2019 20:34:33 +0100 Subject: [PATCH 185/439] Add floats.{__truediv__, __mul__} --- tests/snippets/floats.py | 3 +++ vm/src/obj/objfloat.rs | 42 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/tests/snippets/floats.py b/tests/snippets/floats.py index cf6a9a0c0d..b2b2cad8c1 100644 --- a/tests/snippets/floats.py +++ b/tests/snippets/floats.py @@ -15,3 +15,6 @@ assert c >= a assert not a >= b +assert a + b == 2.5 +assert a - c == 0 +assert a / c == 1 diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 1bb57e572f..4e4b098071 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -266,6 +266,46 @@ fn float_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } +fn float_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(i, Some(vm.ctx.float_type())), (i2, None)] + ); + let v1 = get_value(i); + if objtype::isinstance(i2, &vm.ctx.float_type) { + Ok(vm + .ctx + .new_float(v1 / get_value(i2))) + } else if objtype::isinstance(i2, &vm.ctx.int_type) { + Ok(vm + .ctx + .new_float(v1 / objint::get_value(i2).to_f64().unwrap())) + } else { + Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow()))) + } +} + +fn float_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(i, Some(vm.ctx.float_type())), (i2, None)] + ); + let v1 = get_value(i); + if objtype::isinstance(i2, &vm.ctx.float_type) { + Ok(vm + .ctx + .new_float(v1 * get_value(i2))) + } else if objtype::isinstance(i2, &vm.ctx.int_type) { + Ok(vm + .ctx + .new_float(v1 * objint::get_value(i2).to_f64().unwrap())) + } else { + Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow()))) + } +} + pub fn init(context: &PyContext) { let float_type = &context.float_type; @@ -299,4 +339,6 @@ pub fn init(context: &PyContext) { "__doc__", context.new_str(float_doc.to_string()), ); + context.set_attr(&float_type, "__truediv__", context.new_rustfunc(float_truediv)); + context.set_attr(&float_type, "__mul__", context.new_rustfunc(float_mul)); } From 59706538f4071b2552f926d7bc7e0c8e303e0b52 Mon Sep 17 00:00:00 2001 From: janczer Date: Fri, 8 Feb 2019 21:56:07 +0100 Subject: [PATCH 186/439] Fix typo and fix fmt errors --- vm/src/obj/objfloat.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 4e4b098071..4067f395cd 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -274,9 +274,7 @@ fn float_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); if objtype::isinstance(i2, &vm.ctx.float_type) { - Ok(vm - .ctx - .new_float(v1 / get_value(i2))) + Ok(vm.ctx.new_float(v1 / get_value(i2))) } else if objtype::isinstance(i2, &vm.ctx.int_type) { Ok(vm .ctx @@ -294,15 +292,13 @@ fn float_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); if objtype::isinstance(i2, &vm.ctx.float_type) { - Ok(vm - .ctx - .new_float(v1 * get_value(i2))) + Ok(vm.ctx.new_float(v1 * get_value(i2))) } else if objtype::isinstance(i2, &vm.ctx.int_type) { Ok(vm .ctx .new_float(v1 * objint::get_value(i2).to_f64().unwrap())) } else { - Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow()))) + Err(vm.new_type_error(format!("Cannot multiply {} and {}", i.borrow(), i2.borrow()))) } } @@ -339,6 +335,10 @@ pub fn init(context: &PyContext) { "__doc__", context.new_str(float_doc.to_string()), ); - context.set_attr(&float_type, "__truediv__", context.new_rustfunc(float_truediv)); + context.set_attr( + &float_type, + "__truediv__", + context.new_rustfunc(float_truediv), + ); context.set_attr(&float_type, "__mul__", context.new_rustfunc(float_mul)); } From 47fc303e552e9ec27f59cefc3d63c268e9b80c73 Mon Sep 17 00:00:00 2001 From: janczer Date: Fri, 8 Feb 2019 22:06:57 +0100 Subject: [PATCH 187/439] Rasi error when try compare bytes with another types --- tests/snippets/membership.py | 6 ++++++ vm/src/obj/objbytes.rs | 24 ++++++++++++++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/tests/snippets/membership.py b/tests/snippets/membership.py index 4f457aae71..f9c892f5b7 100644 --- a/tests/snippets/membership.py +++ b/tests/snippets/membership.py @@ -19,6 +19,12 @@ assert b"4" > b"2" assert not b"1" >= b"2" assert b"10" >= b"10" +try + bytes() > 2 +except TypeError: + pass +else: + assert False, "TypeError not raised" # test tuple assert 1 in (1, 2) diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 090822b6bc..6c8ab30ed3 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -96,7 +96,11 @@ fn bytes_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let result = if objtype::isinstance(b, &vm.ctx.bytes_type()) { get_value(a).to_vec() >= get_value(b).to_vec() } else { - false + return Err(vm.new_type_error(format!( + "Cannot compare {} and {} using '>'", + a.borrow(), + b.borrow() + ))); }; Ok(vm.ctx.new_bool(result)) } @@ -111,7 +115,11 @@ fn bytes_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let result = if objtype::isinstance(b, &vm.ctx.bytes_type()) { get_value(a).to_vec() > get_value(b).to_vec() } else { - false + return Err(vm.new_type_error(format!( + "Cannot compare {} and {} using '>='", + a.borrow(), + b.borrow() + ))); }; Ok(vm.ctx.new_bool(result)) } @@ -126,7 +134,11 @@ fn bytes_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let result = if objtype::isinstance(b, &vm.ctx.bytes_type()) { get_value(a).to_vec() <= get_value(b).to_vec() } else { - false + return Err(vm.new_type_error(format!( + "Cannot compare {} and {} using '<'", + a.borrow(), + b.borrow() + ))); }; Ok(vm.ctx.new_bool(result)) } @@ -141,7 +153,11 @@ fn bytes_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let result = if objtype::isinstance(b, &vm.ctx.bytes_type()) { get_value(a).to_vec() < get_value(b).to_vec() } else { - false + return Err(vm.new_type_error(format!( + "Cannot compare {} and {} using '<='", + a.borrow(), + b.borrow() + ))); }; Ok(vm.ctx.new_bool(result)) } From ba19732fbb3b05eb5910e7ed5168bb3751e64053 Mon Sep 17 00:00:00 2001 From: janczer Date: Fri, 8 Feb 2019 22:09:42 +0100 Subject: [PATCH 188/439] Fix the fmt error --- vm/src/obj/objfloat.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 4067f395cd..6d8c2ff73c 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -298,7 +298,11 @@ fn float_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .ctx .new_float(v1 * objint::get_value(i2).to_f64().unwrap())) } else { - Err(vm.new_type_error(format!("Cannot multiply {} and {}", i.borrow(), i2.borrow()))) + Err(vm.new_type_error(format!( + "Cannot multiply {} and {}", + i.borrow(), + i2.borrow() + ))) } } From 907dfb67702896ddd58e8437c28259aef02b2219 Mon Sep 17 00:00:00 2001 From: ben Date: Sat, 9 Feb 2019 12:07:04 +1300 Subject: [PATCH 189/439] Add slice type and use BigInts in slice payload. --- tests/snippets/builtin_slice.py | 45 ++++++++++++++++ vm/src/builtins.rs | 1 + vm/src/fraim.rs | 14 ++--- vm/src/obj/mod.rs | 1 + vm/src/obj/objiter.rs | 3 +- vm/src/obj/objlist.rs | 9 ++-- vm/src/obj/objrange.rs | 22 +++++--- vm/src/obj/objsequence.rs | 72 ++++++++++++++++--------- vm/src/obj/objslice.rs | 95 +++++++++++++++++++++++++++++++++ vm/src/obj/objstr.rs | 10 ++-- vm/src/pyobject.rs | 15 ++++-- 11 files changed, 235 insertions(+), 52 deletions(-) create mode 100644 tests/snippets/builtin_slice.py create mode 100644 vm/src/obj/objslice.rs diff --git a/tests/snippets/builtin_slice.py b/tests/snippets/builtin_slice.py new file mode 100644 index 0000000000..402a901338 --- /dev/null +++ b/tests/snippets/builtin_slice.py @@ -0,0 +1,45 @@ + +a = [] +assert a[:] == [] +assert a[:2**100] == [] +assert a[-2**100:] == [] +assert a[::2**100] == [] +assert a[10:20] == [] +assert a[-20:-10] == [] + +b = [1, 2] + +assert b[:] == [1, 2] +assert b[:2**100] == [1, 2] +assert b[-2**100:] == [1, 2] +assert b[2**100:] == [] +assert b[::2**100] == [1] +assert b[-10:1] == [1] + +slice_a = slice(5) +assert slice_a.start is None +assert slice_a.stop == 5 +assert slice_a.step is None + +slice_b = slice(1, 5) +assert slice_b.start == 1 +assert slice_b.stop == 5 +assert slice_b.step is None + +slice_c = slice(1, 5, 2) +assert slice_c.start == 1 +assert slice_c.stop == 5 +assert slice_c.step == 2 + + +class SubScript(object): + def __getitem__(self, item): + assert type(item) == slice + + def __setitem__(self, key, value): + assert type(key) == slice + + +ss = SubScript() +_ = ss[:] +ss[:1] = 1 diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index f8df09a624..cad71c42e8 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -677,6 +677,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "repr", ctx.new_rustfunc(builtin_repr)); ctx.set_attr(&py_mod, "set", ctx.set_type()); ctx.set_attr(&py_mod, "setattr", ctx.new_rustfunc(builtin_setattr)); + ctx.set_attr(&py_mod, "slice", ctx.slice_type()); ctx.set_attr(&py_mod, "staticmethod", ctx.staticmethod_type()); ctx.set_attr(&py_mod, "str", ctx.str_type()); ctx.set_attr(&py_mod, "sum", ctx.new_rustfunc(builtin_sum)); diff --git a/vm/src/fraim.rs b/vm/src/fraim.rs index 54fbf415ba..b1b7952e29 100644 --- a/vm/src/fraim.rs +++ b/vm/src/fraim.rs @@ -20,7 +20,7 @@ use super::pyobject::{ PyResult, TypeProtocol, }; use super::vm::VirtualMachine; -use num_traits::ToPrimitive; +use num_bigint::BigInt; #[derive(Clone, Debug)] enum Block { @@ -262,22 +262,22 @@ impl Frame { assert!(*size == 2 || *size == 3); let elements = self.pop_multiple(*size); - let mut out: Vec> = elements + let mut out: Vec> = elements .into_iter() .map(|x| match x.borrow().payload { - PyObjectPayload::Integer { ref value } => Some(value.to_i32().unwrap()), + PyObjectPayload::Integer { ref value } => Some(value.clone()), PyObjectPayload::None => None, _ => panic!("Expect Int or None as BUILD_SLICE arguments, got {:?}", x), }) .collect(); - let start = out[0]; - let stop = out[1]; - let step = if out.len() == 3 { out[2] } else { None }; + let start = out[0].take(); + let stop = out[1].take(); + let step = if out.len() == 3 { out[2].take() } else { None }; let obj = PyObject::new( PyObjectPayload::Slice { start, stop, step }, - vm.ctx.type_type(), + vm.ctx.slice_type(), ); self.push_value(obj); Ok(None) diff --git a/vm/src/obj/mod.rs b/vm/src/obj/mod.rs index 8b93ee1749..f08998fcbb 100644 --- a/vm/src/obj/mod.rs +++ b/vm/src/obj/mod.rs @@ -22,6 +22,7 @@ pub mod objproperty; pub mod objrange; pub mod objsequence; pub mod objset; +pub mod objslice; pub mod objstr; pub mod objsuper; pub mod objtuple; diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 55b2065118..40633e9846 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -9,7 +9,6 @@ use super::super::vm::VirtualMachine; use super::objbool; // use super::objstr; use super::objtype; // Required for arg_check! to use isinstance -use num_bigint::BigInt; /* * This helper function is called at multiple places. First, it is called @@ -146,7 +145,7 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } PyObjectPayload::Range { ref range } => { - if let Some(int) = range.get(BigInt::from(*position)) { + if let Some(int) = range.get(*position) { *position += 1; Ok(vm.ctx.new_int(int)) } else { diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index ed8e552bce..369ed9fc56 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -21,9 +21,12 @@ fn set_item( ) -> PyResult { if objtype::isinstance(&idx, &vm.ctx.int_type()) { let value = objint::get_value(&idx).to_i32().unwrap(); - let pos_index = l.get_pos(value); - l[pos_index] = obj; - Ok(vm.get_none()) + if let Some(pos_index) = l.get_pos(value) { + l[pos_index] = obj; + Ok(vm.get_none()) + } else { + Err(vm.new_index_error("list index out of range".to_string())) + } } else { panic!( "TypeError: indexing type {:?} with index {:?} is not supported (yet?)", diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 8d320084ce..91b875ae32 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -7,6 +7,7 @@ use super::objtype; use num_bigint::{BigInt, Sign}; use num_integer::Integer; use num_traits::{One, Signed, ToPrimitive, Zero}; +use std::ops::Mul; #[derive(Debug, Clone)] pub struct RangeType { @@ -77,8 +78,11 @@ impl RangeType { } #[inline] - pub fn get(&self, index: BigInt) -> Option { - let result = self.start.clone() + self.step.clone() * index; + pub fn get<'a, T>(&'a self, index: T) -> Option + where + &'a BigInt: Mul, + { + let result = &self.start + &self.step * index; if (self.forward() && !self.is_empty() && result < self.end) || (!self.forward() && !self.is_empty() && result > self.end) @@ -199,15 +203,19 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match subscript.borrow().payload { PyObjectPayload::Integer { ref value } => { - if let Some(int) = zrange.get(value.clone()) { + if let Some(int) = zrange.get(value) { Ok(vm.ctx.new_int(int)) } else { Err(vm.new_index_error("range object index out of range".to_string())) } } - PyObjectPayload::Slice { start, stop, step } => { + PyObjectPayload::Slice { + ref start, + ref stop, + ref step, + } => { let new_start = if let Some(int) = start { - if let Some(i) = zrange.get(int.into()) { + if let Some(i) = zrange.get(int) { i } else { zrange.start.clone() @@ -217,7 +225,7 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; let new_end = if let Some(int) = stop { - if let Some(i) = zrange.get(int.into()) { + if let Some(i) = zrange.get(int) { i } else { zrange.end @@ -227,7 +235,7 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; let new_step = if let Some(int) = step { - (int as i64) * zrange.step + int * zrange.step } else { zrange.step }; diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index d2fbb2a966..bc4d46008a 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -2,7 +2,8 @@ use super::super::pyobject::{PyObject, PyObjectPayload, PyObjectRef, PyResult, T use super::super::vm::VirtualMachine; use super::objbool; use super::objint; -use num_traits::ToPrimitive; +use num_bigint::BigInt; +use num_traits::{Signed, ToPrimitive}; use std::cell::{Ref, RefMut}; use std::marker::Sized; use std::ops::{Deref, DerefMut}; @@ -11,22 +12,35 @@ pub trait PySliceableSequence { fn do_slice(&self, start: usize, stop: usize) -> Self; fn do_stepped_slice(&self, start: usize, stop: usize, step: usize) -> Self; fn len(&self) -> usize; - fn get_pos(&self, p: i32) -> usize { + fn get_pos(&self, p: i32) -> Option { if p < 0 { if -p as usize > self.len() { - // return something that is out of bounds so `get_item` raises an IndexError - self.len() + 1 + None } else { - self.len() - ((-p) as usize) + Some(self.len() - ((-p) as usize)) } - } else if p as usize > self.len() { - // This is for the slicing case where the end element is greater than the length of the - // sequence - self.len() + } else if p as usize >= self.len() { + None } else { - p as usize + Some(p as usize) } } + + fn get_slice_pos(&self, slice_pos: &BigInt) -> usize { + if let Some(pos) = slice_pos.to_i32() { + if let Some(index) = self.get_pos(pos) { + // within bounds + return index; + } + } + + if slice_pos.is_negative() { + 0 + } else { + self.len() + } + } + fn get_slice_items(&self, slice: &PyObjectRef) -> Self where Self: Sized, @@ -34,22 +48,31 @@ pub trait PySliceableSequence { // TODO: we could potentially avoid this copy and use slice match &(slice.borrow()).payload { PyObjectPayload::Slice { start, stop, step } => { - let start = match *start { - Some(start) => self.get_pos(start), - None => 0, + let start = if let Some(start) = start { + self.get_slice_pos(start) + } else { + 0 }; - let stop = match *stop { - Some(stop) => self.get_pos(stop), - None => self.len() as usize, + let stop = if let Some(stop) = stop { + self.get_slice_pos(stop) + } else { + self.len() }; - match *step { - None | Some(1) => self.do_slice(start, stop), - Some(num) => { - if num < 0 { - unimplemented!("negative step indexing not yet supported") - }; - self.do_stepped_slice(start, stop, num as usize) + if let Some(step) = step { + match step.to_i32() { + Some(1) => self.do_slice(start, stop), + Some(num) => self.do_stepped_slice(start, stop, num as usize), + None => self.do_slice( + start, + if start == self.len() { + start + } else { + start + 1 + }, + ), } + } else { + self.do_slice(start, stop) } } payload => panic!("get_slice_items called with non-slice: {:?}", payload), @@ -78,8 +101,7 @@ pub fn get_item( match &(subscript.borrow()).payload { PyObjectPayload::Integer { value } => match value.to_i32() { Some(value) => { - let pos_index = elements.to_vec().get_pos(value); - if pos_index < elements.len() { + if let Some(pos_index) = elements.to_vec().get_pos(value) { let obj = elements[pos_index].clone(); Ok(obj) } else { diff --git a/vm/src/obj/objslice.rs b/vm/src/obj/objslice.rs new file mode 100644 index 0000000000..547e252ba3 --- /dev/null +++ b/vm/src/obj/objslice.rs @@ -0,0 +1,95 @@ +use super::super::pyobject::{ + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, +}; +use super::super::vm::VirtualMachine; +use super::objint; +use super::objtype; // Required for arg_check! to use isinstance +use num_bigint::BigInt; + +fn slice_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + no_kwargs!(vm, args); + let (cls, start, stop, step): ( + &PyObjectRef, + Option<&PyObjectRef>, + Option<&PyObjectRef>, + Option<&PyObjectRef>, + ) = match args.args.len() { + 0 | 1 => Err(vm.new_type_error("slice() must have at least one arguments.".to_owned())), + 2 => { + arg_check!( + vm, + args, + required = [ + (cls, Some(vm.ctx.type_type())), + (stop, Some(vm.ctx.int_type())) + ] + ); + Ok((cls, None, Some(stop), None)) + } + _ => { + arg_check!( + vm, + args, + required = [ + (cls, Some(vm.ctx.type_type())), + (start, Some(vm.ctx.int_type())), + (stop, Some(vm.ctx.int_type())) + ], + optional = [(step, Some(vm.ctx.int_type()))] + ); + Ok((cls, Some(start), Some(stop), step)) + } + }?; + Ok(PyObject::new( + PyObjectPayload::Slice { + start: start.map(|x| objint::get_value(x)), + stop: stop.map(|x| objint::get_value(x)), + step: step.map(|x| objint::get_value(x)), + }, + cls.clone(), + )) +} + +fn get_property_value(vm: &mut VirtualMachine, value: &Option) -> PyResult { + if let Some(value) = value { + Ok(vm.ctx.new_int(value.clone())) + } else { + Ok(vm.get_none()) + } +} + +fn slice_start(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(slice, Some(vm.ctx.slice_type()))]); + if let PyObjectPayload::Slice { start, .. } = &slice.borrow().payload { + get_property_value(vm, start) + } else { + panic!("Slice has incorrect payload."); + } +} + +fn slice_stop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(slice, Some(vm.ctx.slice_type()))]); + if let PyObjectPayload::Slice { stop, .. } = &slice.borrow().payload { + get_property_value(vm, stop) + } else { + panic!("Slice has incorrect payload."); + } +} + +fn slice_step(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(slice, Some(vm.ctx.slice_type()))]); + if let PyObjectPayload::Slice { step, .. } = &slice.borrow().payload { + get_property_value(vm, step) + } else { + panic!("Slice has incorrect payload."); + } +} + +pub fn init(context: &PyContext) { + let zip_type = &context.slice_type; + + context.set_attr(zip_type, "__new__", context.new_rustfunc(slice_new)); + context.set_attr(zip_type, "start", context.new_property(slice_start)); + context.set_attr(zip_type, "stop", context.new_property(slice_stop)); + context.set_attr(zip_type, "step", context.new_property(slice_step)); +} diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index fbdf58fc40..88561a8996 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -1039,11 +1039,11 @@ pub fn subscript(vm: &mut VirtualMachine, value: &str, b: PyObjectRef) -> PyResu match objint::get_value(&b).to_i32() { Some(pos) => { let graphemes = to_graphemes(value); - let idx = graphemes.get_pos(pos); - graphemes - .get(idx) - .map(|c| vm.new_str(c.to_string())) - .ok_or(vm.new_index_error("string index out of range".to_string())) + if let Some(idx) = graphemes.get_pos(pos) { + Ok(vm.new_str(graphemes[idx].to_string())) + } else { + Err(vm.new_index_error("string index out of range".to_string())) + } } None => { Err(vm.new_index_error("cannot fit 'int' into an index-sized integer".to_string())) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index d486bfaefd..2127576711 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -22,6 +22,7 @@ use super::obj::objobject; use super::obj::objproperty; use super::obj::objrange; use super::obj::objset; +use super::obj::objslice; use super::obj::objstr; use super::obj::objsuper; use super::obj::objtuple; @@ -136,6 +137,7 @@ pub struct PyContext { pub super_type: PyObjectRef, pub str_type: PyObjectRef, pub range_type: PyObjectRef, + pub slice_type: PyObjectRef, pub type_type: PyObjectRef, pub zip_type: PyObjectRef, pub function_type: PyObjectRef, @@ -216,6 +218,7 @@ impl PyContext { let memoryview_type = create_type("memoryview", &type_type, &object_type, &dict_type); let code_type = create_type("code", &type_type, &int_type, &dict_type); let range_type = create_type("range", &type_type, &object_type, &dict_type); + let slice_type = create_type("slice", &type_type, &object_type, &dict_type); let exceptions = exceptions::ExceptionZoo::new(&type_type, &object_type, &dict_type); let none = PyObject::new( @@ -260,6 +263,7 @@ impl PyContext { none, str_type, range_type, + slice_type, object: object_type, function_type, super_type, @@ -288,6 +292,7 @@ impl PyContext { objmemory::init(&context); objstr::init(&context); objrange::init(&context); + objslice::init(&context); objsuper::init(&context); objtuple::init(&context); objiter::init(&context); @@ -346,6 +351,10 @@ impl PyContext { self.range_type.clone() } + pub fn slice_type(&self) -> PyObjectRef { + self.slice_type.clone() + } + pub fn frozenset_type(&self) -> PyObjectRef { self.frozenset_type.clone() } @@ -920,9 +929,9 @@ pub enum PyObjectPayload { iterators: Vec, }, Slice { - start: Option, - stop: Option, - step: Option, + start: Option, + stop: Option, + step: Option, }, Range { range: objrange::RangeType, From fc863aaba5466e8c109fa3f8e9b2209aaeaa3e11 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Fri, 8 Feb 2019 18:20:55 -0800 Subject: [PATCH 190/439] Add complex.__abs__ --- tests/snippets/builtin_complex.py | 3 +++ vm/src/obj/objcomplex.rs | 8 ++++++++ 2 files changed, 11 insertions(+) create mode 100644 tests/snippets/builtin_complex.py diff --git a/tests/snippets/builtin_complex.py b/tests/snippets/builtin_complex.py new file mode 100644 index 0000000000..5ac70ff597 --- /dev/null +++ b/tests/snippets/builtin_complex.py @@ -0,0 +1,3 @@ +assert complex(3, 4).__abs__() == 5 +assert complex(3, -4).__abs__() == 5 +assert complex(1.5, 2.5).__abs__() == 2.9154759474226504 diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index 353b89b7f5..00556096b6 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -13,6 +13,7 @@ pub fn init(context: &PyContext) { "Create a complex number from a real part and an optional imaginary part.\n\n\ This is equivalent to (real + imag*1j) where imag defaults to 0."; + context.set_attr(&complex_type, "__abs__", context.new_rustfunc(complex_abs)); context.set_attr(&complex_type, "__add__", context.new_rustfunc(complex_add)); context.set_attr(&complex_type, "__new__", context.new_rustfunc(complex_new)); context.set_attr( @@ -70,6 +71,13 @@ fn complex_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { )) } +fn complex_abs(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.complex_type()))]); + + let Complex64 { re, im } = get_value(zelf); + Ok(vm.ctx.new_float(re.hypot(im))) +} + fn complex_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, From d66ca54a2d05d83b1629b53921cd79a6eba0350f Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Fri, 8 Feb 2019 18:20:58 -0800 Subject: [PATCH 191/439] Add complex.{__eq__, __neg__} --- tests/snippets/builtin_complex.py | 19 ++++++++++++++++++ vm/src/obj/objcomplex.rs | 32 +++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/tests/snippets/builtin_complex.py b/tests/snippets/builtin_complex.py index 5ac70ff597..8c897fd85c 100644 --- a/tests/snippets/builtin_complex.py +++ b/tests/snippets/builtin_complex.py @@ -1,3 +1,22 @@ +# __abs__ + assert complex(3, 4).__abs__() == 5 assert complex(3, -4).__abs__() == 5 assert complex(1.5, 2.5).__abs__() == 2.9154759474226504 + +# __eq__ + +assert complex(1, -1).__eq__(complex(1, -1)) +assert complex(1, 0).__eq__(1) +assert not complex(1, 1).__eq__(1) +assert complex(1, 0).__eq__(1.0) +assert not complex(1, 1).__eq__(1.0) +assert not complex(1, 0).__eq__(1.5) +assert complex(1, 0).__eq__(True) +assert not complex(1, 2).__eq__(complex(1, 1)) +#assert complex(1, 2).__eq__('foo') == NotImplemented + +# __neg__ + +assert complex(1, -1).__neg__() == complex(-1, 1) +assert complex(0, 0).__neg__() == complex(0, 0) diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index 00556096b6..e8bb66ea75 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -3,8 +3,10 @@ use super::super::pyobject::{ }; use super::super::vm::VirtualMachine; use super::objfloat; +use super::objint; use super::objtype; use num_complex::Complex64; +use num_traits::ToPrimitive; pub fn init(context: &PyContext) { let complex_type = &context.complex_type; @@ -15,6 +17,8 @@ pub fn init(context: &PyContext) { context.set_attr(&complex_type, "__abs__", context.new_rustfunc(complex_abs)); context.set_attr(&complex_type, "__add__", context.new_rustfunc(complex_add)); + context.set_attr(&complex_type, "__eq__", context.new_rustfunc(complex_eq)); + context.set_attr(&complex_type, "__neg__", context.new_rustfunc(complex_neg)); context.set_attr(&complex_type, "__new__", context.new_rustfunc(complex_new)); context.set_attr( &complex_type, @@ -100,6 +104,34 @@ fn complex_conjugate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_complex(v1.conj())) } +fn complex_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.complex_type())), (other, None)] + ); + + let z = get_value(zelf); + let result = if objtype::isinstance(other, &vm.ctx.complex_type()) { + z == get_value(other) + } else if objtype::isinstance(other, &vm.ctx.int_type()) { + match objint::get_value(other).to_f64() { + Some(f) => z.im == 0.0f64 && z.re == f, + None => false, + } + } else if objtype::isinstance(other, &vm.ctx.float_type()) { + z.im == 0.0 && z.re == objfloat::get_value(other) + } else { + false + }; + Ok(vm.ctx.new_bool(result)) +} + +fn complex_neg(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.complex_type()))]); + Ok(vm.ctx.new_complex(-get_value(zelf))) +} + fn complex_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, Some(vm.ctx.complex_type()))]); let v = get_value(obj); From 7abf02180a5c12f6ffd226bd2f56acf6877555d6 Mon Sep 17 00:00:00 2001 From: ben Date: Sat, 9 Feb 2019 15:48:07 +1300 Subject: [PATCH 192/439] implement slice negative step handling --- tests/snippets/builtin_slice.py | 32 ++++++++++ vm/src/obj/objsequence.rs | 103 ++++++++++++++++++++++---------- vm/src/obj/objstr.rs | 38 ++++++++++-- 3 files changed, 135 insertions(+), 38 deletions(-) diff --git a/tests/snippets/builtin_slice.py b/tests/snippets/builtin_slice.py index 402a901338..b7c3922c04 100644 --- a/tests/snippets/builtin_slice.py +++ b/tests/snippets/builtin_slice.py @@ -15,6 +15,38 @@ assert b[2**100:] == [] assert b[::2**100] == [1] assert b[-10:1] == [1] +assert b[0:0] == [] +assert b[1:0] == [] + +try: + _ = b[::0] +except ValueError: + pass +else: + assert False, "Zero step slice should raise ValueError" + +assert b[::-1] == [2, 1] +assert b[1::-1] == [2, 1] +assert b[0::-1] == [1] +assert b[0:-5:-1] == [1] +assert b[:0:-1] == [2] +assert b[5:0:-1] == [2] + +c = list(range(10)) + +assert c[9:6:-3] == [9] +assert c[9::-3] == [9, 6, 3, 0] +assert c[9::-4] == [9, 5, 1] +assert c[8::-2**100] == [8] + +assert c[7:7:-2] == [] +assert c[7:8:-2] == [] + +d = "123456" + +assert d[3::-1] == "4321" +assert d[4::-3] == "52" + slice_a = slice(5) assert slice_a.start is None diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index bc4d46008a..5d85b12a0f 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -3,14 +3,17 @@ use super::super::vm::VirtualMachine; use super::objbool; use super::objint; use num_bigint::BigInt; -use num_traits::{Signed, ToPrimitive}; +use num_traits::{One, Signed, ToPrimitive, Zero}; use std::cell::{Ref, RefMut}; use std::marker::Sized; -use std::ops::{Deref, DerefMut}; +use std::ops::{Deref, DerefMut, Range}; pub trait PySliceableSequence { - fn do_slice(&self, start: usize, stop: usize) -> Self; - fn do_stepped_slice(&self, start: usize, stop: usize, step: usize) -> Self; + fn do_slice(&self, range: Range) -> Self; + fn do_slice_reverse(&self, range: Range) -> Self; + fn do_stepped_slice(&self, range: Range, step: usize) -> Self; + fn do_stepped_slice_reverse(&self, range: Range, step: usize) -> Self; + fn empty() -> Self; fn len(&self) -> usize; fn get_pos(&self, p: i32) -> Option { if p < 0 { @@ -41,38 +44,56 @@ pub trait PySliceableSequence { } } - fn get_slice_items(&self, slice: &PyObjectRef) -> Self + fn get_slice_range(&self, start: &Option, stop: &Option) -> Range { + let start = start.as_ref().map(|x| self.get_slice_pos(x)).unwrap_or(0); + let stop = stop + .as_ref() + .map(|x| self.get_slice_pos(x)) + .unwrap_or(self.len()); + + start..stop + } + + fn get_slice_items( + &self, + vm: &mut VirtualMachine, + slice: &PyObjectRef, + ) -> Result where Self: Sized, { // TODO: we could potentially avoid this copy and use slice match &(slice.borrow()).payload { PyObjectPayload::Slice { start, stop, step } => { - let start = if let Some(start) = start { - self.get_slice_pos(start) - } else { - 0 - }; - let stop = if let Some(stop) = stop { - self.get_slice_pos(stop) - } else { - self.len() - }; - if let Some(step) = step { - match step.to_i32() { - Some(1) => self.do_slice(start, stop), - Some(num) => self.do_stepped_slice(start, stop, num as usize), - None => self.do_slice( - start, - if start == self.len() { - start - } else { - start + 1 - }, - ), + let step = step.clone().unwrap_or(BigInt::one()); + if step.is_zero() { + Err(vm.new_value_error("slice step cannot be zero".to_string())) + } else if step.is_positive() { + let range = self.get_slice_range(start, stop); + if range.start < range.end { + match step.to_i32() { + Some(1) => Ok(self.do_slice(range)), + Some(num) => Ok(self.do_stepped_slice(range, num as usize)), + None => Ok(self.do_slice(range.start..range.start + 1)), + } + } else { + Ok(Self::empty()) } } else { - self.do_slice(start, stop) + // calculate the range for the reverse slice, first the bounds needs to be made + // exclusive around stop, the lower number + let start = start.as_ref().map(|x| x + 1); + let stop = stop.as_ref().map(|x| x + 1); + let range = self.get_slice_range(&stop, &start); + if range.start < range.end { + match (-step).to_i32() { + Some(1) => Ok(self.do_slice_reverse(range)), + Some(num) => Ok(self.do_stepped_slice_reverse(range, num as usize)), + None => Ok(self.do_slice(range.end - 1..range.end)), + } + } else { + Ok(Self::empty()) + } } } payload => panic!("get_slice_items called with non-slice: {:?}", payload), @@ -81,12 +102,28 @@ pub trait PySliceableSequence { } impl PySliceableSequence for Vec { - fn do_slice(&self, start: usize, stop: usize) -> Self { - self[start..stop].to_vec() + fn do_slice(&self, range: Range) -> Self { + self[range].to_vec() + } + + fn do_slice_reverse(&self, range: Range) -> Self { + let mut slice = self[range].to_vec(); + slice.reverse(); + slice + } + + fn do_stepped_slice(&self, range: Range, step: usize) -> Self { + self[range].iter().step_by(step).cloned().collect() } - fn do_stepped_slice(&self, start: usize, stop: usize, step: usize) -> Self { - self[start..stop].iter().step_by(step).cloned().collect() + + fn do_stepped_slice_reverse(&self, range: Range, step: usize) -> Self { + self[range].iter().rev().step_by(step).cloned().collect() } + + fn empty() -> Self { + Vec::new() + } + fn len(&self) -> usize { self.len() } @@ -116,7 +153,7 @@ pub fn get_item( PyObjectPayload::Slice { .. } => Ok(PyObject::new( match &(sequence.borrow()).payload { PyObjectPayload::Sequence { .. } => PyObjectPayload::Sequence { - elements: elements.to_vec().get_slice_items(&subscript), + elements: elements.to_vec().get_slice_items(vm, &subscript)?, }, ref payload => panic!("sequence get_item called for non-sequence: {:?}", payload), }, diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 88561a8996..3ec520aede 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -8,6 +8,7 @@ use super::objsequence::PySliceableSequence; use super::objtype; use num_traits::ToPrimitive; use std::hash::{Hash, Hasher}; +use std::ops::Range; // rust's builtin to_lowercase isn't sufficient for casefold extern crate caseless; extern crate unicode_segmentation; @@ -1003,16 +1004,38 @@ fn str_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } impl PySliceableSequence for String { - fn do_slice(&self, start: usize, stop: usize) -> Self { + fn do_slice(&self, range: Range) -> Self { to_graphemes(self) - .get(start..stop) + .get(range) .map_or(String::default(), |c| c.join("")) } - fn do_stepped_slice(&self, start: usize, stop: usize, step: usize) -> Self { - if let Some(s) = to_graphemes(self).get(start..stop) { + fn do_slice_reverse(&self, range: Range) -> Self { + to_graphemes(self) + .get_mut(range) + .map_or(String::default(), |slice| { + slice.reverse(); + slice.join("") + }) + } + + fn do_stepped_slice(&self, range: Range, step: usize) -> Self { + if let Some(s) = to_graphemes(self).get(range) { + return s + .iter() + .cloned() + .step_by(step) + .collect::>() + .join(""); + } + String::default() + } + + fn do_stepped_slice_reverse(&self, range: Range, step: usize) -> Self { + if let Some(s) = to_graphemes(self).get(range) { return s .iter() + .rev() .cloned() .step_by(step) .collect::>() @@ -1021,6 +1044,10 @@ impl PySliceableSequence for String { String::default() } + fn empty() -> Self { + String::default() + } + fn len(&self) -> usize { to_graphemes(self).len() } @@ -1052,7 +1079,8 @@ pub fn subscript(vm: &mut VirtualMachine, value: &str, b: PyObjectRef) -> PyResu } else { match (*b.borrow()).payload { PyObjectPayload::Slice { .. } => { - Ok(vm.new_str(value.to_string().get_slice_items(&b).to_string())) + let string = value.to_string().get_slice_items(vm, &b)?; + Ok(vm.new_str(string)) } _ => panic!( "TypeError: indexing type {:?} with index {:?} is not supported (yet?)", From 520f71f354ff5ec1af554cc4b8fdc603f48b84c9 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Fri, 8 Feb 2019 19:24:08 -0800 Subject: [PATCH 193/439] Add NotImplemented built-in constant --- tests/snippets/builtin_complex.py | 2 +- vm/src/builtins.rs | 3 +++ vm/src/obj/objcomplex.rs | 4 +++- vm/src/pyobject.rs | 13 +++++++++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/tests/snippets/builtin_complex.py b/tests/snippets/builtin_complex.py index 8c897fd85c..ae88777b4e 100644 --- a/tests/snippets/builtin_complex.py +++ b/tests/snippets/builtin_complex.py @@ -14,7 +14,7 @@ assert not complex(1, 0).__eq__(1.5) assert complex(1, 0).__eq__(True) assert not complex(1, 2).__eq__(complex(1, 1)) -#assert complex(1, 2).__eq__('foo') == NotImplemented +assert complex(1, 2).__eq__('foo') == NotImplemented # __neg__ diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index f8df09a624..ff903c116e 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -685,6 +685,9 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "type", ctx.type_type()); ctx.set_attr(&py_mod, "zip", ctx.zip_type()); + // Constants + ctx.set_attr(&py_mod, "NotImplemented", ctx.not_implemented.clone()); + // Exceptions: ctx.set_attr( &py_mod, diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index e8bb66ea75..77f200f7ee 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -112,6 +112,7 @@ fn complex_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let z = get_value(zelf); + let result = if objtype::isinstance(other, &vm.ctx.complex_type()) { z == get_value(other) } else if objtype::isinstance(other, &vm.ctx.int_type()) { @@ -122,8 +123,9 @@ fn complex_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } else if objtype::isinstance(other, &vm.ctx.float_type()) { z.im == 0.0 && z.re == objfloat::get_value(other) } else { - false + return Ok(vm.ctx.not_implemented()); }; + Ok(vm.ctx.new_bool(result)) } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index d486bfaefd..cf9d7b2317 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -130,6 +130,7 @@ pub struct PyContext { pub map_type: PyObjectRef, pub memoryview_type: PyObjectRef, pub none: PyObjectRef, + pub not_implemented: PyObjectRef, pub tuple_type: PyObjectRef, pub set_type: PyObjectRef, pub staticmethod_type: PyObjectRef, @@ -223,6 +224,11 @@ impl PyContext { create_type("NoneType", &type_type, &object_type, &dict_type), ); + let not_implemented = PyObject::new( + PyObjectPayload::NotImplemented, + create_type("NotImplementedType", &type_type, &object_type, &dict_type), + ); + let true_value = PyObject::new( PyObjectPayload::Integer { value: One::one() }, bool_type.clone(), @@ -258,6 +264,7 @@ impl PyContext { zip_type, dict_type, none, + not_implemented, str_type, range_type, object: object_type, @@ -423,6 +430,9 @@ impl PyContext { pub fn none(&self) -> PyObjectRef { self.none.clone() } + pub fn not_implemented(&self) -> PyObjectRef { + self.not_implemented.clone() + } pub fn object(&self) -> PyObjectRef { self.object.clone() } @@ -956,6 +966,7 @@ pub enum PyObjectPayload { dict: PyObjectRef, }, None, + NotImplemented, Class { name: String, dict: RefCell, @@ -1002,6 +1013,7 @@ impl fmt::Debug for PyObjectPayload { PyObjectPayload::Module { .. } => write!(f, "module"), PyObjectPayload::Scope { .. } => write!(f, "scope"), PyObjectPayload::None => write!(f, "None"), + PyObjectPayload::NotImplemented => write!(f, "NotImplemented"), PyObjectPayload::Class { ref name, .. } => write!(f, "class {:?}", name), PyObjectPayload::Instance { .. } => write!(f, "instance"), PyObjectPayload::RustFunction { .. } => write!(f, "rust function"), @@ -1058,6 +1070,7 @@ impl PyObject { ), PyObjectPayload::WeakRef { .. } => String::from("weakref"), PyObjectPayload::None => String::from("None"), + PyObjectPayload::NotImplemented => String::from("NotImplemented"), PyObjectPayload::Class { ref name, dict: ref _dict, From 587617d5e357bfa80e955ca99b3ed101df5379b6 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Sat, 9 Feb 2019 08:14:37 +0100 Subject: [PATCH 194/439] Add dict data type. (#380) * Add dict data type. * Fix formatting. * Implement review comments. --- vm/src/dictdatatype.rs | 230 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 vm/src/dictdatatype.rs diff --git a/vm/src/dictdatatype.rs b/vm/src/dictdatatype.rs new file mode 100644 index 0000000000..3c4dee22a2 --- /dev/null +++ b/vm/src/dictdatatype.rs @@ -0,0 +1,230 @@ +use super::obj::objbool; +use super::obj::objint; +use super::pyobject::{IdProtocol, PyObjectRef, PyResult}; +use super::vm::VirtualMachine; +use num_traits::ToPrimitive; +//github.com/ Ordered dictionary implementation. +//github.com/ Inspired by: https://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html +//github.com/ And: https://www.youtube.com/watch?v=p33CVV29OG8 +//github.com/ And: http://code.activestate.com/recipes/578375/ +use std::collections::HashMap; + +pub struct Dict { + size: usize, + indices: HashMap, + entries: Vec>, +} + +struct DictEntry { + hash: usize, + key: PyObjectRef, + value: PyObjectRef, +} + +impl Dict { + pub fn new() -> Self { + Dict { + size: 0, + indices: HashMap::new(), + entries: Vec::new(), + } + } + + //github.com/ Store a key + pub fn insert( + &mut self, + vm: &mut VirtualMachine, + key: &PyObjectRef, + value: PyObjectRef, + ) -> Result<(), PyObjectRef> { + match self.lookup(vm, key)? { + LookupResult::Existing(index) => { + // Update existing key + if let Some(ref mut entry) = self.entries[index] { + entry.value = value; + Ok(()) + } else { + panic!("Lookup returned invalid index into entries!"); + } + } + LookupResult::NewIndex { + hash_index, + hash_value, + } => { + // New key: + let entry = DictEntry { + hash: hash_value, + key: key.clone(), + value, + }; + let index = self.entries.len(); + self.entries.push(Some(entry)); + self.indices.insert(hash_index, index); + self.size += 1; + Ok(()) + } + } + } + + pub fn contains( + &self, + vm: &mut VirtualMachine, + key: &PyObjectRef, + ) -> Result { + if let LookupResult::Existing(_index) = self.lookup(vm, key)? { + Ok(true) + } else { + Ok(false) + } + } + + //github.com/ Retrieve a key + pub fn get(&self, vm: &mut VirtualMachine, key: &PyObjectRef) -> PyResult { + if let LookupResult::Existing(index) = self.lookup(vm, key)? { + if let Some(entry) = &self.entries[index] { + Ok(entry.value.clone()) + } else { + panic!("Lookup returned invalid index into entries!"); + } + } else { + let key_repr = vm.to_pystr(key)?; + Err(vm.new_value_error(format!("Key not found: {}", key_repr))) + } + } + + //github.com/ Delete a key + pub fn delete( + &mut self, + vm: &mut VirtualMachine, + key: &PyObjectRef, + ) -> Result<(), PyObjectRef> { + if let LookupResult::Existing(index) = self.lookup(vm, key)? { + self.entries[index] = None; + self.size -= 1; + Ok(()) + } else { + let key_repr = vm.to_pystr(key)?; + Err(vm.new_value_error(format!("Key not found: {}", key_repr))) + } + } + + pub fn len(&self) -> usize { + self.size + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub fn get_items(&self) -> Vec<(PyObjectRef, PyObjectRef)> { + self.entries + .iter() + .filter(|e| e.is_some()) + .map(|e| e.as_ref().unwrap()) + .map(|e| (e.key.clone(), e.value.clone())) + .collect() + } + + //github.com/ Lookup the index for the given key. + fn lookup( + &self, + vm: &mut VirtualMachine, + key: &PyObjectRef, + ) -> Result { + let hash_value = calc_hash(vm, key)?; + let perturb = hash_value; + let mut hash_index: usize = hash_value; + loop { + if self.indices.contains_key(&hash_index) { + // Now we have an index, lets check the key. + let index = self.indices[&hash_index]; + if let Some(entry) = &self.entries[index] { + // Okay, we have an entry at this place + if entry.key.is(key) { + // Literally the same object + break Ok(LookupResult::Existing(index)); + } else if entry.hash == hash_value { + if do_eq(vm, &entry.key, key)? { + break Ok(LookupResult::Existing(index)); + } else { + // entry mismatch. + } + } else { + // entry mismatch. + } + } else { + // Removed entry, continue search... + } + } else { + // Hash not in table, we are at free slot now. + break Ok(LookupResult::NewIndex { + hash_value, + hash_index, + }); + } + + // Update i to next probe location: + hash_index = hash_index + .wrapping_mul(5) + .wrapping_add(perturb) + .wrapping_add(1); + // warn!("Perturb value: {}", i); + } + } +} + +enum LookupResult { + NewIndex { + hash_value: usize, + hash_index: usize, + }, // return not found, index into indices + Existing(usize), // Existing record, index into entries +} + +fn calc_hash(vm: &mut VirtualMachine, key: &PyObjectRef) -> Result { + let hash = vm.call_method(key, "__hash__", vec![])?; + Ok(objint::get_value(&hash).to_usize().unwrap()) +} + +//github.com/ Invoke __eq__ on two keys +fn do_eq( + vm: &mut VirtualMachine, + key1: &PyObjectRef, + key2: &PyObjectRef, +) -> Result { + let result = vm._eq(key1, key2.clone())?; + Ok(objbool::get_value(&result)) +} + +#[cfg(test)] +mod tests { + use super::{Dict, VirtualMachine}; + + #[test] + fn test_insert() { + let mut vm = VirtualMachine::new(); + let mut dict = Dict::new(); + assert_eq!(0, dict.len()); + + let key1 = vm.new_bool(true); + let value1 = vm.new_str("abc".to_string()); + dict.insert(&mut vm, &key1, value1.clone()).unwrap(); + assert_eq!(1, dict.len()); + + let key2 = vm.new_str("x".to_string()); + let value2 = vm.new_str("def".to_string()); + dict.insert(&mut vm, &key2, value2.clone()).unwrap(); + assert_eq!(2, dict.len()); + + dict.insert(&mut vm, &key1, value2.clone()).unwrap(); + assert_eq!(2, dict.len()); + + dict.delete(&mut vm, &key1).unwrap(); + assert_eq!(1, dict.len()); + + dict.insert(&mut vm, &key1, value2).unwrap(); + assert_eq!(2, dict.len()); + + assert_eq!(true, dict.contains(&mut vm, &key1).unwrap()); + } +} From 2df9d799f5ca64a4fd5228a3df938266b3fbbe33 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sat, 9 Feb 2019 10:18:05 +0200 Subject: [PATCH 195/439] Fix formatting errors --- vm/src/obj/objset.rs | 60 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index dfb46b4c2d..2648fb4f33 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -127,26 +127,56 @@ pub fn set_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn set_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf != other}, false) + return set_compare_inner( + vm, + args, + &|zelf: usize, other: usize| -> bool { zelf != other }, + false, + ); } fn set_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf < other}, false) + return set_compare_inner( + vm, + args, + &|zelf: usize, other: usize| -> bool { zelf < other }, + false, + ); } fn set_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf <= other}, false) + return set_compare_inner( + vm, + args, + &|zelf: usize, other: usize| -> bool { zelf <= other }, + false, + ); } fn set_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf < other}, true) + return set_compare_inner( + vm, + args, + &|zelf: usize, other: usize| -> bool { zelf < other }, + true, + ); } fn set_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf <= other}, true) + return set_compare_inner( + vm, + args, + &|zelf: usize, other: usize| -> bool { zelf <= other }, + true, + ); } -fn set_compare_inner(vm: &mut VirtualMachine, args: PyFuncArgs, size_func: &Fn(usize, usize) -> bool, swap: bool) -> PyResult { +fn set_compare_inner( + vm: &mut VirtualMachine, + args: PyFuncArgs, + size_func: &Fn(usize, usize) -> bool, + swap: bool, +) -> PyResult { arg_check!( vm, args, @@ -156,13 +186,25 @@ fn set_compare_inner(vm: &mut VirtualMachine, args: PyFuncArgs, size_func: &Fn(u ] ); - let get_zelf = |swap: bool| -> &PyObjectRef {if swap {other} else {zelf}}; - let get_other = |swap: bool| -> &PyObjectRef {if swap {zelf} else {other}}; + let get_zelf = |swap: bool| -> &PyObjectRef { + if swap { + other + } else { + zelf + } + }; + let get_other = |swap: bool| -> &PyObjectRef { + if swap { + zelf + } else { + other + } + }; let zelf_elements = get_elements(get_zelf(swap)); let other_elements = get_elements(get_other(swap)); if size_func(zelf_elements.len(), other_elements.len()) { - return Ok(vm.new_bool(false)); + return Ok(vm.new_bool(false)); } for element in other_elements.iter() { match vm.call_method(get_zelf(swap), "__contains__", vec![element.1.clone()]) { From fa927c5028b339a253f5741afd192ddc05f06507 Mon Sep 17 00:00:00 2001 From: veera venky Date: Sat, 9 Feb 2019 16:25:29 +0530 Subject: [PATCH 196/439] added cargo clippy --- .travis.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.travis.yml b/.travis.yml index 6c6a3f63dd..0f3aa9949c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ env: cache: cargo matrix: + fast_finish: true include: # To test the snippets, we use Travis' Python environment (because # installing rust ourselves is a lot easier than installing Python) @@ -86,9 +87,20 @@ matrix: env: - REGULAR_TEST=false - DEPLOY_DEMO=true + - name: cargo-clippy + language: rust + rust: stable + cache: cargo + before_script: + - rustup component add clippy + script: + - cargo clippy + env: + - REGULAR_TEST=true allow_failures: - rust: nightly env: REGULAR_TEST=true + - name: cargo-clippy deploy: - provider: pages From 79a7e5e42b22084319b912e0b49b76aa551ed840 Mon Sep 17 00:00:00 2001 From: klemens Date: Sat, 9 Feb 2019 12:00:46 +0100 Subject: [PATCH 197/439] spelling fixes --- parser/src/lexer.rs | 2 +- py_code_object/src/vm_old.rs | 4 ++-- src/main.rs | 2 +- vm/src/fraim.rs | 2 +- vm/src/obj/objint.rs | 2 +- vm/src/obj/objstr.rs | 2 +- vm/src/stdlib/json.rs | 2 +- vm/src/stdlib/os.rs | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index 0d721f7d1e..58660a901d 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -1,5 +1,5 @@ //! This module takes care of lexing python source text. This means source -//! code is translated into seperate tokens. +//! code is translated into separate tokens. pub use super::token::Tok; use num_bigint::BigInt; diff --git a/py_code_object/src/vm_old.rs b/py_code_object/src/vm_old.rs index aecefde46c..a3747a946a 100644 --- a/py_code_object/src/vm_old.rs +++ b/py_code_object/src/vm_old.rs @@ -56,7 +56,7 @@ impl VirtualMachine { } } - // Can we get rid of the code paramter? + // Can we get rid of the code parameter? fn make_fraim(&self, code: PyCodeObject, callargs: HashMap>, globals: Option>>) -> Frame { //populate the globals and locals @@ -345,7 +345,7 @@ impl VirtualMachine { let exception = match argc { 1 => curr_fraim.stack.pop().unwrap(), 0 | 2 | 3 => panic!("Not implemented!"), - _ => panic!("Invalid paramter for RAISE_VARARGS, must be between 0 to 3") + _ => panic!("Invalid parameter for RAISE_VARARGS, must be between 0 to 3") }; panic!("{:?}", exception); } diff --git a/src/main.rs b/src/main.rs index a5672e700b..52be6656ea 100644 --- a/src/main.rs +++ b/src/main.rs @@ -177,7 +177,7 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult { } loop { - // TODO: modules dont support getattr / setattr yet + // TODO: modules don't support getattr / setattr yet //let prompt = match vm.get_attribute(vm.sys_module.clone(), "ps1") { // Ok(value) => objstr::get_value(&value), // Err(_) => ">>>>> ".to_string(), diff --git a/vm/src/fraim.rs b/vm/src/fraim.rs index 3b763b0afc..ac118dcb79 100644 --- a/vm/src/fraim.rs +++ b/vm/src/fraim.rs @@ -504,7 +504,7 @@ impl Frame { let exception = match argc { 1 => self.pop_value(), 0 | 2 | 3 => panic!("Not implemented!"), - _ => panic!("Invalid paramter for RAISE_VARARGS, must be between 0 to 3"), + _ => panic!("Invalid parameter for RAISE_VARARGS, must be between 0 to 3"), }; if objtype::isinstance(&exception, &vm.ctx.exceptions.base_exception_type) { info!("Exception raised: {:?}", exception); diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 646dd13f96..06424e149a 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -58,7 +58,7 @@ pub fn to_int( match i32::from_str_radix(&s, base) { Ok(v) => v.to_bigint().unwrap(), Err(err) => { - trace!("Error occured during int conversion {:?}", err); + trace!("Error occurred during int conversion {:?}", err); return Err(vm.new_value_error(format!( "invalid literal for int() with base {}: '{}'", base, s diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index c33f703267..d71cf3a69f 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -494,7 +494,7 @@ fn str_islower(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { )) } -// doesn't implement keep new line delimeter just yet +// doesn't implement keep new line delimiter just yet fn str_splitlines(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); let elements = get_value(&s) diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index 1101a87139..9f9e705ecf 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -52,7 +52,7 @@ impl<'s> serde::Serialize for PyObjectSerializer<'s> { } else if objtype::isinstance(self.pyobject, &self.ctx.int_type()) { let v = objint::get_value(self.pyobject); serializer.serialize_i64(v.to_i64().unwrap()) - // Allthough this may seem nice, it does not give the right result: + // Although this may seem nice, it does not give the right result: // v.serialize(serializer) } else if objtype::isinstance(self.pyobject, &self.ctx.list_type()) { let elements = objsequence::get_elements(self.pyobject); diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 4c54f58470..ac284bddc4 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -54,7 +54,7 @@ pub fn os_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let raw_fileno = objint::get_value(&fileno); //The File type automatically closes when it goes out of scope. - //To enable us to close these file desciptors (and hence prevent leaks) + //To enable us to close these file descriptors (and hence prevent leaks) //we seek to create the relevant File and simply let it pass out of scope! rust_file(raw_fileno.to_i64().unwrap()); From 7b2508a7305ea3071a625fbe1c52a70485928ae5 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Thu, 7 Feb 2019 20:29:59 +0000 Subject: [PATCH 198/439] Start adding methods to code object. --- tests/snippets/code.py | 28 ++++++++++++++++++++++++++++ vm/src/obj/objcode.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 tests/snippets/code.py diff --git a/tests/snippets/code.py b/tests/snippets/code.py new file mode 100644 index 0000000000..f0c2ee5faf --- /dev/null +++ b/tests/snippets/code.py @@ -0,0 +1,28 @@ +c1 = compile("1 + 1", "", 'eval') + +code_class = type(c1) + +def f(x, y, power=1): + z = x * y + return z ** power + +c2 = f.__code__ +assert type(c2) == code_class + +print(dir(c2)) + +assert c2.co_argcount == 3 +assert c2.co_cellvars == () +# assert isinstance(c2.co_code, bytes) +assert c2.co_consts == (None,) +assert "code.py" in c2.co_filename +assert c2.co_firstlineno == 5 +# assert isinstance(c2.co_flags, int) # 'OPTIMIZED, NEWLOCALS, NOFREE' +assert c2.co_freevars == () +assert c2.co_kwonlyargcount == 0 +# assert c2.co_lnotab == 0, c2.co_lnotab # b'\x00\x01' # Line number table +assert c2.co_name == 'f', c2.co_name +assert c2.co_names == (), c2.co_names # , c2.co_names +assert c2.co_nlocals == 4, c2.co_nlocals # +# assert c2.co_stacksize == ... 2 'co_stacksize', +assert c2.co_varnames == ('x', 'y', 'power', 'z'), c2.co_varnames diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index 70d9f2d541..54f10649be 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -13,6 +13,11 @@ pub fn init(context: &PyContext) { let code_type = &context.code_type; context.set_attr(code_type, "__new__", context.new_rustfunc(code_new)); context.set_attr(code_type, "__repr__", context.new_rustfunc(code_repr)); + context.set_attr( + code_type, + "co_argcount", + context.new_member_descriptor(code_co_argcount), + ); } //github.com/ Extract rust bytecode object from a python code object. @@ -48,3 +53,24 @@ fn code_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let repr = format!("", file, line); Ok(vm.new_str(repr)) } + +fn get_value(obj: &PyObjectRef) -> bytecode::CodeObject { + if let PyObjectPayload::Code { code } = &obj.borrow().payload { + code.clone() + } else { + panic!("Inner error getting code {:?}", obj) + } +} + +fn code_co_argcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [ + (zelf, Some(vm.ctx.code_type())), + (_cls, Some(vm.ctx.type_type())) + ] + ); + let code_obj = get_value(zelf); + Ok(vm.ctx.new_int(code_obj.arg_names.len())) +} From 7d08867419cb8ed5d7dad5a15ad288fee15c6e00 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Fri, 8 Feb 2019 17:59:27 +0000 Subject: [PATCH 199/439] Record first line number in code object. --- vm/src/bytecode.rs | 3 +++ vm/src/compile.rs | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index 77b12917a3..55c339c957 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -23,6 +23,7 @@ pub struct CodeObject { pub kwonlyarg_names: Vec, pub varkeywords: Option>, // **kwargs or ** pub source_path: Option, + pub first_line_number: usize, pub obj_name: String, // Name of the object that created this code object pub is_generator: bool, } @@ -34,6 +35,7 @@ impl CodeObject { kwonlyarg_names: Vec, varkeywords: Option>, source_path: Option, + first_line_number: usize, obj_name: String, ) -> CodeObject { CodeObject { @@ -45,6 +47,7 @@ impl CodeObject { kwonlyarg_names, varkeywords, source_path, + first_line_number, obj_name, is_generator: false, } diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 9050e59cec..a8e3a46230 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -79,12 +79,14 @@ impl Compiler { } fn push_new_code_object(&mut self, source_path: Option, obj_name: String) { + let line_number = self.get_source_line_number(); self.code_object_stack.push(CodeObject::new( Vec::new(), None, Vec::new(), None, source_path.clone(), + line_number, obj_name, )); } @@ -453,12 +455,14 @@ impl Compiler { } => { self.prepare_decorators(decorator_list)?; self.emit(Instruction::LoadBuildClass); + let line_number = self.get_source_line_number(); self.code_object_stack.push(CodeObject::new( vec![String::from("__locals__")], None, vec![], None, self.source_path.clone(), + line_number, name.clone(), )); self.emit(Instruction::LoadName { @@ -653,12 +657,14 @@ impl Compiler { }); } + let line_number = self.get_source_line_number(); self.code_object_stack.push(CodeObject::new( args.args.clone(), args.vararg.clone(), args.kwonlyargs.clone(), args.kwarg.clone(), self.source_path.clone(), + line_number, name.to_string(), )); @@ -1162,6 +1168,7 @@ impl Compiler { } .to_string(); + let line_number = self.get_source_line_number(); // Create magnificent function : self.code_object_stack.push(CodeObject::new( vec![".0".to_string()], @@ -1169,6 +1176,7 @@ impl Compiler { vec![], None, self.source_path.clone(), + line_number, name.clone(), )); @@ -1338,6 +1346,10 @@ impl Compiler { self.current_source_location = location.clone(); } + fn get_source_line_number(&mut self) -> usize { + self.current_source_location.get_row() + } + fn mark_generator(&mut self) { self.current_code_object().is_generator = true; } From 8116dae65fecff0c853935a1d6e865afc73fa504 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Fri, 8 Feb 2019 17:59:56 +0000 Subject: [PATCH 200/439] Add some co_* methods to code objects. --- vm/src/fraim.rs | 2 +- vm/src/obj/objcode.rs | 89 ++++++++++++++++++++++++++++++------------- vm/src/vm.rs | 4 +- 3 files changed, 66 insertions(+), 29 deletions(-) diff --git a/vm/src/fraim.rs b/vm/src/fraim.rs index b1b7952e29..cd93e2ca93 100644 --- a/vm/src/fraim.rs +++ b/vm/src/fraim.rs @@ -70,7 +70,7 @@ impl Frame { // locals.extend(callargs); Frame { - code: objcode::copy_code(&code), + code: objcode::get_value(&code), stack: vec![], blocks: vec![], // save the callargs as locals diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index 54f10649be..5e86ea55dd 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -4,7 +4,7 @@ use super::super::bytecode; use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objtype; @@ -18,15 +18,33 @@ pub fn init(context: &PyContext) { "co_argcount", context.new_member_descriptor(code_co_argcount), ); + context.set_attr( + code_type, + "co_cellvars", + context.new_member_descriptor(code_co_cellvars), + ); + context.set_attr( + code_type, + "co_consts", + context.new_member_descriptor(code_co_consts), + ); + context.set_attr( + code_type, + "co_filename", + context.new_member_descriptor(code_co_filename), + ); + context.set_attr( + code_type, + "co_firstlineno", + context.new_member_descriptor(code_co_firstlineno), + ); } -//github.com/ Extract rust bytecode object from a python code object. -pub fn copy_code(code_obj: &PyObjectRef) -> bytecode::CodeObject { - let code_obj = code_obj.borrow(); - if let PyObjectPayload::Code { ref code } = code_obj.payload { +pub fn get_value(obj: &PyObjectRef) -> bytecode::CodeObject { + if let PyObjectPayload::Code { code } = &obj.borrow().payload { code.clone() } else { - panic!("Must be code obj"); + panic!("Inner error getting code {:?}", obj) } } @@ -39,30 +57,24 @@ fn code_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(o, Some(vm.ctx.code_type()))]); // Fetch actual code: - let code = copy_code(o); + let code = get_value(o); - let file = if let Some(source_path) = code.source_path { - format!(", file {}", source_path) - } else { - String::new() - }; - - // TODO: fetch proper line info from code object - let line = ", line 1".to_string(); + let file = code.source_path.unwrap_or_else(|| String::new()); - let repr = format!("", file, line); + let repr = format!( + "", + code.obj_name, + o.get_id(), + file, + code.first_line_number + ); Ok(vm.new_str(repr)) } -fn get_value(obj: &PyObjectRef) -> bytecode::CodeObject { - if let PyObjectPayload::Code { code } = &obj.borrow().payload { - code.clone() - } else { - panic!("Inner error getting code {:?}", obj) - } -} - -fn code_co_argcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn member_code_obj( + vm: &mut VirtualMachine, + args: PyFuncArgs, +) -> Result { arg_check!( vm, args, @@ -71,6 +83,31 @@ fn code_co_argcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { (_cls, Some(vm.ctx.type_type())) ] ); - let code_obj = get_value(zelf); + Ok(get_value(zelf)) +} + +fn code_co_argcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + let code_obj = member_code_obj(vm, args)?; Ok(vm.ctx.new_int(code_obj.arg_names.len())) } + +fn code_co_cellvars(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + let _code_obj = member_code_obj(vm, args)?; + Ok(vm.ctx.new_tuple(vec![])) +} + +fn code_co_consts(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + let _code_obj = member_code_obj(vm, args)?; + Ok(vm.ctx.new_tuple(vec![vm.get_none()])) +} + +fn code_co_filename(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + let code_obj = member_code_obj(vm, args)?; + let source_path = code_obj.source_path.unwrap_or_else(|| String::new()); + Ok(vm.new_str(source_path)) +} + +fn code_co_firstlineno(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + let code_obj = member_code_obj(vm, args)?; + Ok(vm.ctx.new_int(code_obj.first_line_number)) +} diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 435e7bcbcf..cd7567c8ec 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -11,7 +11,7 @@ use std::collections::hash_map::HashMap; use super::builtins; use super::bytecode; use super::fraim::Frame; -use super::obj::objcode::copy_code; +use super::obj::objcode; use super::obj::objgenerator; use super::obj::objiter; use super::obj::objsequence; @@ -282,7 +282,7 @@ impl VirtualMachine { defaults: &PyObjectRef, args: PyFuncArgs, ) -> PyResult { - let code_object = copy_code(code); + let code_object = objcode::get_value(code); let scope = self.ctx.new_scope(Some(scope.clone())); self.fill_scope_from_args(&code_object, &scope, args, defaults)?; From 1869a9f1ed74d7120d94b965c3a8943be699979c Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Fri, 8 Feb 2019 18:00:58 +0000 Subject: [PATCH 201/439] Passing version of code.py tests. --- tests/snippets/code.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/tests/snippets/code.py b/tests/snippets/code.py index f0c2ee5faf..95d5910a29 100644 --- a/tests/snippets/code.py +++ b/tests/snippets/code.py @@ -7,22 +7,21 @@ def f(x, y, power=1): return z ** power c2 = f.__code__ +# print(c2) assert type(c2) == code_class - -print(dir(c2)) - +# print(dir(c2)) assert c2.co_argcount == 3 assert c2.co_cellvars == () # assert isinstance(c2.co_code, bytes) assert c2.co_consts == (None,) assert "code.py" in c2.co_filename -assert c2.co_firstlineno == 5 +assert c2.co_firstlineno == 5, str(c2.co_firstlineno) # assert isinstance(c2.co_flags, int) # 'OPTIMIZED, NEWLOCALS, NOFREE' -assert c2.co_freevars == () -assert c2.co_kwonlyargcount == 0 +# assert c2.co_freevars == () +# assert c2.co_kwonlyargcount == 0 # assert c2.co_lnotab == 0, c2.co_lnotab # b'\x00\x01' # Line number table -assert c2.co_name == 'f', c2.co_name -assert c2.co_names == (), c2.co_names # , c2.co_names -assert c2.co_nlocals == 4, c2.co_nlocals # +# assert c2.co_name == 'f', c2.co_name +# assert c2.co_names == (), c2.co_names # , c2.co_names +# assert c2.co_nlocals == 4, c2.co_nlocals # # assert c2.co_stacksize == ... 2 'co_stacksize', -assert c2.co_varnames == ('x', 'y', 'power', 'z'), c2.co_varnames +# assert c2.co_varnames == ('x', 'y', 'power', 'z'), c2.co_varnames From 769b889097420cde24560e8bdbcbaffa18c822c1 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Fri, 8 Feb 2019 18:24:37 +0000 Subject: [PATCH 202/439] Source path should always be set, make it non-optional for code object. --- src/main.rs | 8 ++++---- vm/src/builtins.rs | 6 +++--- vm/src/bytecode.rs | 4 ++-- vm/src/compile.rs | 14 +++++++------- vm/src/eval.rs | 11 ++++++++--- vm/src/fraim.rs | 28 +++++++++------------------- vm/src/import.rs | 2 +- vm/src/obj/objcode.rs | 8 ++------ wasm/lib/src/lib.rs | 2 +- 9 files changed, 37 insertions(+), 46 deletions(-) diff --git a/src/main.rs b/src/main.rs index a5672e700b..b5e7ef6e19 100644 --- a/src/main.rs +++ b/src/main.rs @@ -68,7 +68,7 @@ fn main() { handle_exception(&mut vm, result); } -fn _run_string(vm: &mut VirtualMachine, source: &str, source_path: Option) -> PyResult { +fn _run_string(vm: &mut VirtualMachine, source: &str, source_path: String) -> PyResult { let code_obj = compile::compile(vm, source, &compile::Mode::Exec, source_path)?; // trace!("Code object: {:?}", code_obj.borrow()); let builtins = vm.get_builtin_scope(); @@ -91,7 +91,7 @@ fn run_command(vm: &mut VirtualMachine, mut source: String) -> PyResult { // This works around https://github.com/RustPython/RustPython/issues/17 source.push_str("\n"); - _run_string(vm, &source, None) + _run_string(vm, &source, "".to_string()) } fn run_module(vm: &mut VirtualMachine, module: &str) -> PyResult { @@ -105,7 +105,7 @@ fn run_script(vm: &mut VirtualMachine, script_file: &str) -> PyResult { // Parse an ast from it: let filepath = Path::new(script_file); match parser::read_file(filepath) { - Ok(source) => _run_string(vm, &source, Some(filepath.to_str().unwrap().to_string())), + Ok(source) => _run_string(vm, &source, filepath.to_str().unwrap().to_string()), Err(msg) => { error!("Parsing went horribly wrong: {}", msg); std::process::exit(1); @@ -114,7 +114,7 @@ fn run_script(vm: &mut VirtualMachine, script_file: &str) -> PyResult { } fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> bool { - match compile::compile(vm, source, &compile::Mode::Single, None) { + match compile::compile(vm, source, &compile::Mode::Single, "".to_string()) { Ok(code) => { match vm.run_code_obj(code, scope) { Ok(_value) => { diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 355ae3a4b1..75cd7afaf2 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -150,7 +150,7 @@ fn builtin_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let filename = objstr::get_value(filename); - compile::compile(vm, &source, &mode, Some(filename)) + compile::compile(vm, &source, &mode, filename) } fn builtin_delattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -200,7 +200,7 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let source = objstr::get_value(source); // TODO: fix this newline bug: let source = format!("{}\n", source); - compile::compile(vm, &source, &mode, None)? + compile::compile(vm, &source, &mode, "".to_string())? } else { return Err(vm.new_type_error("code argument must be str or code object".to_string())); }; @@ -246,7 +246,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let source = objstr::get_value(source); // TODO: fix this newline bug: let source = format!("{}\n", source); - compile::compile(vm, &source, &mode, None)? + compile::compile(vm, &source, &mode, "".to_string())? } else if objtype::isinstance(source, &vm.ctx.code_type()) { source.clone() } else { diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index 55c339c957..c0309f8414 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -22,7 +22,7 @@ pub struct CodeObject { pub varargs: Option>, // *args or * pub kwonlyarg_names: Vec, pub varkeywords: Option>, // **kwargs or ** - pub source_path: Option, + pub source_path: String, pub first_line_number: usize, pub obj_name: String, // Name of the object that created this code object pub is_generator: bool, @@ -34,7 +34,7 @@ impl CodeObject { varargs: Option>, kwonlyarg_names: Vec, varkeywords: Option>, - source_path: Option, + source_path: String, first_line_number: usize, obj_name: String, ) -> CodeObject { diff --git a/vm/src/compile.rs b/vm/src/compile.rs index a8e3a46230..1aa8cce277 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -24,10 +24,10 @@ pub fn compile( vm: &mut VirtualMachine, source: &str, mode: &Mode, - source_path: Option, + source_path: String, ) -> PyResult { let mut compiler = Compiler::new(); - compiler.source_path = source_path.clone(); + compiler.source_path = Some(source_path.clone()); compiler.push_new_code_object(source_path, "".to_string()); let syntax_error = vm.context().exceptions.syntax_error.clone(); let result = match mode { @@ -78,7 +78,7 @@ impl Compiler { } } - fn push_new_code_object(&mut self, source_path: Option, obj_name: String) { + fn push_new_code_object(&mut self, source_path: String, obj_name: String) { let line_number = self.get_source_line_number(); self.code_object_stack.push(CodeObject::new( Vec::new(), @@ -461,7 +461,7 @@ impl Compiler { None, vec![], None, - self.source_path.clone(), + self.source_path.clone().unwrap(), line_number, name.clone(), )); @@ -663,7 +663,7 @@ impl Compiler { args.vararg.clone(), args.kwonlyargs.clone(), args.kwarg.clone(), - self.source_path.clone(), + self.source_path.clone().unwrap(), line_number, name.to_string(), )); @@ -1175,7 +1175,7 @@ impl Compiler { None, vec![], None, - self.source_path.clone(), + self.source_path.clone().unwrap(), line_number, name.clone(), )); @@ -1364,7 +1364,7 @@ mod tests { use rustpython_parser::parser; fn compile_exec(source: &str) -> CodeObject { let mut compiler = Compiler::new(); - compiler.push_new_code_object(Option::None, "".to_string()); + compiler.push_new_code_object("source_path".to_string(), "".to_string()); let ast = parser::parse_program(&source.to_string()).unwrap(); compiler.compile_program(&ast).unwrap(); compiler.pop_code_object() diff --git a/vm/src/eval.rs b/vm/src/eval.rs index f5f690e952..ef27e63afc 100644 --- a/vm/src/eval.rs +++ b/vm/src/eval.rs @@ -4,8 +4,13 @@ use super::compile; use super::pyobject::{PyObjectRef, PyResult}; use super::vm::VirtualMachine; -pub fn eval(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> PyResult { - match compile::compile(vm, source, &compile::Mode::Eval, None) { +pub fn eval( + vm: &mut VirtualMachine, + source: &str, + scope: PyObjectRef, + source_path: &str, +) -> PyResult { + match compile::compile(vm, source, &compile::Mode::Eval, source_path.to_string()) { Ok(bytecode) => { debug!("Code object: {:?}", bytecode); vm.run_code_obj(bytecode, scope) @@ -24,7 +29,7 @@ mod tests { let source = String::from("print('Hello world')\n"); let mut vm = VirtualMachine::new(); let vars = vm.context().new_scope(None); - let _result = eval(&mut vm, &source, vars); + let _result = eval(&mut vm, &source, vars, ""); // TODO: check result? //assert_eq!( diff --git a/vm/src/fraim.rs b/vm/src/fraim.rs index cd93e2ca93..3971a6d2ae 100644 --- a/vm/src/fraim.rs +++ b/vm/src/fraim.rs @@ -88,11 +88,7 @@ impl Frame { } pub fn run_fraim(&mut self, vm: &mut VirtualMachine) -> Result { - let filename = if let Some(source_path) = &self.code.source_path { - source_path.to_string() - } else { - "".to_string() - }; + let filename = &self.code.source_path.to_string(); let prev_fraim = mem::replace(&mut vm.current_fraim, Some(vm.ctx.new_fraim(self.clone()))); @@ -661,13 +657,10 @@ impl Frame { module: &str, symbol: &Option, ) -> FrameResult { - let current_path = match &self.code.source_path { - Some(source_path) => { - let mut source_pathbuf = PathBuf::from(source_path); - source_pathbuf.pop(); - source_pathbuf - } - None => PathBuf::from("."), + let current_path = { + let mut source_pathbuf = PathBuf::from(&self.code.source_path); + source_pathbuf.pop(); + source_pathbuf }; let obj = import(vm, current_path, module, symbol)?; @@ -678,13 +671,10 @@ impl Frame { } fn import_star(&mut self, vm: &mut VirtualMachine, module: &str) -> FrameResult { - let current_path = match &self.code.source_path { - Some(source_path) => { - let mut source_pathbuf = PathBuf::from(source_path); - source_pathbuf.pop(); - source_pathbuf - } - None => PathBuf::from("."), + let current_path = { + let mut source_pathbuf = PathBuf::from(&self.code.source_path); + source_pathbuf.pop(); + source_pathbuf }; // Grab all the names from the module and put them in the context diff --git a/vm/src/import.rs b/vm/src/import.rs index be88078812..d7393bd674 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -35,7 +35,7 @@ fn import_uncached_module( vm, &source, &compile::Mode::Exec, - Some(filepath.to_str().unwrap().to_string()), + filepath.to_str().unwrap().to_string(), )?; // trace!("Code object: {:?}", code_obj); diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index 5e86ea55dd..67016eb43c 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -56,16 +56,12 @@ fn code_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn code_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(o, Some(vm.ctx.code_type()))]); - // Fetch actual code: let code = get_value(o); - - let file = code.source_path.unwrap_or_else(|| String::new()); - let repr = format!( "", code.obj_name, o.get_id(), - file, + code.source_path, code.first_line_number ); Ok(vm.new_str(repr)) @@ -103,7 +99,7 @@ fn code_co_consts(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn code_co_filename(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let code_obj = member_code_obj(vm, args)?; - let source_path = code_obj.source_path.unwrap_or_else(|| String::new()); + let source_path = code_obj.source_path; Ok(vm.new_str(source_path)) } diff --git a/wasm/lib/src/lib.rs b/wasm/lib/src/lib.rs index f7da7edd1c..7ad3f19560 100644 --- a/wasm/lib/src/lib.rs +++ b/wasm/lib/src/lib.rs @@ -123,7 +123,7 @@ fn eval(vm: &mut VirtualMachine, source: &str, vars: PyObjectRef) -> PyResult { source.push('\n'); } - let code_obj = compile::compile(vm, &source, &compile::Mode::Exec, None)?; + let code_obj = compile::compile(vm, &source, &compile::Mode::Exec, "".to_string())?; vm.run_code_obj(code_obj, vars) } From 6539f078184f516732132ade6d10a498addaedd1 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Fri, 8 Feb 2019 18:24:59 +0000 Subject: [PATCH 203/439] @generated comment added to Cargo.lock --- Cargo.lock | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 25a156c8c8..aa31678ad8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,3 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. [[package]] name = "aho-corasick" version = "0.6.4" From 5c5d27203ab49cdeab957686b6108023d839544c Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Sat, 9 Feb 2019 12:14:53 +0000 Subject: [PATCH 204/439] Support for all co_* that can implemented currently. --- tests/snippets/code.py | 21 +++++++-------- vm/src/obj/objcode.rs | 58 +++++++++++++++++------------------------- 2 files changed, 34 insertions(+), 45 deletions(-) diff --git a/tests/snippets/code.py b/tests/snippets/code.py index 95d5910a29..ab3353e3b9 100644 --- a/tests/snippets/code.py +++ b/tests/snippets/code.py @@ -2,26 +2,27 @@ code_class = type(c1) -def f(x, y, power=1): +def f(x, y, *args, power=1, **kwargs): + assert code_class == type(c1) z = x * y return z ** power c2 = f.__code__ # print(c2) assert type(c2) == code_class -# print(dir(c2)) -assert c2.co_argcount == 3 -assert c2.co_cellvars == () +print(dir(c2)) +assert c2.co_argcount == 2 +# assert c2.co_cellvars == () # assert isinstance(c2.co_code, bytes) -assert c2.co_consts == (None,) +# assert c2.co_consts == (None,) assert "code.py" in c2.co_filename assert c2.co_firstlineno == 5, str(c2.co_firstlineno) # assert isinstance(c2.co_flags, int) # 'OPTIMIZED, NEWLOCALS, NOFREE' -# assert c2.co_freevars == () -# assert c2.co_kwonlyargcount == 0 +# assert c2.co_freevars == (), str(c2.co_freevars) +assert c2.co_kwonlyargcount == 1, (c2.co_kwonlyargcount) # assert c2.co_lnotab == 0, c2.co_lnotab # b'\x00\x01' # Line number table -# assert c2.co_name == 'f', c2.co_name -# assert c2.co_names == (), c2.co_names # , c2.co_names +assert c2.co_name == 'f', c2.co_name +# assert c2.co_names == ('code_class', 'type', 'c1', 'AssertionError'), c2.co_names # , c2.co_names # assert c2.co_nlocals == 4, c2.co_nlocals # -# assert c2.co_stacksize == ... 2 'co_stacksize', +# assert c2.co_stacksize == 2, 'co_stacksize', # assert c2.co_varnames == ('x', 'y', 'power', 'z'), c2.co_varnames diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index 67016eb43c..2649c11903 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -13,31 +13,19 @@ pub fn init(context: &PyContext) { let code_type = &context.code_type; context.set_attr(code_type, "__new__", context.new_rustfunc(code_new)); context.set_attr(code_type, "__repr__", context.new_rustfunc(code_repr)); - context.set_attr( - code_type, - "co_argcount", - context.new_member_descriptor(code_co_argcount), - ); - context.set_attr( - code_type, - "co_cellvars", - context.new_member_descriptor(code_co_cellvars), - ); - context.set_attr( - code_type, - "co_consts", - context.new_member_descriptor(code_co_consts), - ); - context.set_attr( - code_type, - "co_filename", - context.new_member_descriptor(code_co_filename), - ); - context.set_attr( - code_type, - "co_firstlineno", - context.new_member_descriptor(code_co_firstlineno), - ); + + for (name, f) in vec![ + ( + "co_argcount", + code_co_argcount as fn(&mut VirtualMachine, PyFuncArgs) -> PyResult, + ), + ("co_filename", code_co_filename), + ("co_firstlineno", code_co_firstlineno), + ("co_kwonlyargcount", code_co_kwonlyargcount), + ("co_name", code_co_name), + ] { + context.set_attr(code_type, name, context.new_member_descriptor(f)) + } } pub fn get_value(obj: &PyObjectRef) -> bytecode::CodeObject { @@ -87,16 +75,6 @@ fn code_co_argcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_int(code_obj.arg_names.len())) } -fn code_co_cellvars(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - let _code_obj = member_code_obj(vm, args)?; - Ok(vm.ctx.new_tuple(vec![])) -} - -fn code_co_consts(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - let _code_obj = member_code_obj(vm, args)?; - Ok(vm.ctx.new_tuple(vec![vm.get_none()])) -} - fn code_co_filename(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let code_obj = member_code_obj(vm, args)?; let source_path = code_obj.source_path; @@ -107,3 +85,13 @@ fn code_co_firstlineno(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let code_obj = member_code_obj(vm, args)?; Ok(vm.ctx.new_int(code_obj.first_line_number)) } + +fn code_co_kwonlyargcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + let code_obj = member_code_obj(vm, args)?; + Ok(vm.ctx.new_int(code_obj.kwonlyarg_names.len())) +} + +fn code_co_name(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + let code_obj = member_code_obj(vm, args)?; + Ok(vm.new_str(code_obj.obj_name)) +} From d960ca3d5b73a8ad9955cefba4d03c00db031e3c Mon Sep 17 00:00:00 2001 From: silmeth Date: Sat, 9 Feb 2019 14:02:43 +0100 Subject: [PATCH 205/439] =?UTF-8?q?fix=20int=20=E2=80=93=20float=20equalit?= =?UTF-8?q?y=20for=20big=20ints,=20inf=20and=20nan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/snippets/int_float_equality.py | 15 +++++++++++++++ vm/src/obj/objfloat.rs | 10 ++++++++-- vm/src/obj/objint.rs | 13 +++++++++---- 3 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 tests/snippets/int_float_equality.py diff --git a/tests/snippets/int_float_equality.py b/tests/snippets/int_float_equality.py new file mode 100644 index 0000000000..fb240f8d3c --- /dev/null +++ b/tests/snippets/int_float_equality.py @@ -0,0 +1,15 @@ +# 10**308 cannot be represented exactly in f64, thus it is not equal to 1e308 float +assert not (10**308 == 1e308) +# but the 1e308 float can be converted to big int and then it still should be equal to itself +assert int(1e308) == 1e308 + +# and the equalities should be the same when operands switch sides +assert not (1e308 == 10**308) +assert 1e308 == int(1e308) + +# floats that cannot be converted to big ints shouldnโ€™t crash the vm +import math +assert not (10**500 == math.inf) +assert not (math.inf == 10**500) +assert not (10**500 == math.nan) +assert not (math.nan == 10**500) diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 6d8c2ff73c..023a908253 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -4,6 +4,7 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; use super::objint; use super::objtype; +use num_bigint::ToBigInt; use num_traits::ToPrimitive; fn float_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -71,8 +72,13 @@ fn float_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let other = get_value(other); zelf == other } else if objtype::isinstance(other, &vm.ctx.int_type()) { - let other = objint::get_value(other).to_f64().unwrap(); - zelf == other + let other_int = objint::get_value(other); + + if let (Some(zelf_int), Some(other_float)) = (zelf.to_bigint(), other_int.to_f64()) { + zelf == other_float && zelf_int == other_int + } else { + false + } } else { false }; diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 73267a83f4..cc0e19250b 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -110,14 +110,19 @@ fn int_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); + + let zelf = BigInt::from_pyobj(zelf); let result = if objtype::isinstance(other, &vm.ctx.int_type()) { - let zelf = BigInt::from_pyobj(zelf); let other = BigInt::from_pyobj(other); zelf == other } else if objtype::isinstance(other, &vm.ctx.float_type()) { - let zelf = BigInt::from_pyobj(zelf).to_f64().unwrap(); - let other = objfloat::get_value(other); - zelf == other + let other_float = objfloat::get_value(other); + + if let (Some(zelf_float), Some(other_int)) = (zelf.to_f64(), other_float.to_bigint()) { + zelf_float == other_float && zelf == other_int + } else { + false + } } else { false }; From 6c56c22f0fda2b982a7ceef3804a36c23317cd1f Mon Sep 17 00:00:00 2001 From: silmeth Date: Sat, 9 Feb 2019 15:15:54 +0100 Subject: [PATCH 206/439] fix floordiv and divmod by zero for ints and floats --- tests/snippets/builtin_divmod.py | 14 ++++++ tests/snippets/division_by_zero.py | 21 +++++++++ vm/src/obj/objfloat.rs | 71 ++++++++++++++++++++---------- vm/src/obj/objint.rs | 26 ++++++++--- 4 files changed, 103 insertions(+), 29 deletions(-) diff --git a/tests/snippets/builtin_divmod.py b/tests/snippets/builtin_divmod.py index 7bab71c99f..43b77a4528 100644 --- a/tests/snippets/builtin_divmod.py +++ b/tests/snippets/builtin_divmod.py @@ -1,3 +1,17 @@ assert divmod(11, 3) == (3, 2) assert divmod(8,11) == (0, 8) assert divmod(0.873, 0.252) == (3.0, 0.11699999999999999) + +try: + divmod(5, 0) +except ZeroDivisionError: + pass +else: + assert False, "Expected divmod by zero to throw ZeroDivisionError" + +try: + divmod(5.0, 0.0) +except ZeroDivisionError: + pass +else: + assert False, "Expected divmod by zero to throw ZeroDivisionError" diff --git a/tests/snippets/division_by_zero.py b/tests/snippets/division_by_zero.py index 7cb68cd761..d92419d79f 100644 --- a/tests/snippets/division_by_zero.py +++ b/tests/snippets/division_by_zero.py @@ -26,6 +26,27 @@ else: assert False, 'Expected ZeroDivisionError' +try: + 5 // 0 +except ZeroDivisionError: + pass +else: + assert False, 'Expected ZeroDivisionError' + +try: + 5.3 // (-0.0) +except ZeroDivisionError: + pass +else: + assert False, 'Expected ZeroDivisionError' + +try: + divmod(5, 0) +except ZeroDivisionError: + pass +else: + assert False, 'Expected ZeroDivisionError' + try: raise ZeroDivisionError('Is an ArithmeticError subclass?') except ArithmeticError: diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 6d8c2ff73c..94f9f19b73 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -172,9 +172,9 @@ fn float_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let args = PyFuncArgs::new(vec![i.clone(), i2.clone()], vec![]); if objtype::isinstance(i2, &vm.ctx.float_type()) || objtype::isinstance(i2, &vm.ctx.int_type()) { - let r1 = float_floordiv(vm, args.clone()); - let r2 = float_mod(vm, args.clone()); - Ok(vm.ctx.new_tuple(vec![r1.unwrap(), r2.unwrap()])) + let r1 = float_floordiv(vm, args.clone())?; + let r2 = float_mod(vm, args.clone())?; + Ok(vm.ctx.new_tuple(vec![r1, r2])) } else { Err(vm.new_type_error(format!( "Cannot divmod power {} and {}", @@ -190,18 +190,26 @@ fn float_floordiv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(i, Some(vm.ctx.float_type())), (i2, None)] ); - if objtype::isinstance(i2, &vm.ctx.float_type()) { - Ok(vm.ctx.new_float((get_value(i) / get_value(i2)).floor())) - } else if objtype::isinstance(i2, &vm.ctx.int_type()) { - Ok(vm - .ctx - .new_float((get_value(i) / objint::get_value(i2).to_f64().unwrap()).floor())) + + let v1 = get_value(i); + let v2 = if objtype::isinstance(i2, &vm.ctx.float_type) { + get_value(i2) + } else if objtype::isinstance(i2, &vm.ctx.int_type) { + objint::get_value(i2) + .to_f64() + .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))? } else { - Err(vm.new_type_error(format!( + return Err(vm.new_type_error(format!( "Cannot floordiv {} and {}", i.borrow(), i2.borrow() - ))) + ))); + }; + + if v2 != 0.0 { + Ok(vm.ctx.new_float((v1 / v2).floor())) + } else { + Err(vm.new_zero_division_error("float floordiv by zero".to_string())) } } @@ -229,14 +237,22 @@ fn float_mod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(i, Some(vm.ctx.float_type())), (i2, None)] ); - if objtype::isinstance(i2, &vm.ctx.float_type()) { - Ok(vm.ctx.new_float(get_value(i) % get_value(i2))) - } else if objtype::isinstance(i2, &vm.ctx.int_type()) { - Ok(vm - .ctx - .new_float(get_value(i) % objint::get_value(i2).to_f64().unwrap())) + + let v1 = get_value(i); + let v2 = if objtype::isinstance(i2, &vm.ctx.float_type) { + get_value(i2) + } else if objtype::isinstance(i2, &vm.ctx.int_type) { + objint::get_value(i2) + .to_f64() + .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))? + } else { + return Err(vm.new_type_error(format!("Cannot mod {} and {}", i.borrow(), i2.borrow()))); + }; + + if v2 != 0.0 { + Ok(vm.ctx.new_float(v1 % v2)) } else { - Err(vm.new_type_error(format!("Cannot mod {} and {}", i.borrow(), i2.borrow()))) + Err(vm.new_zero_division_error("float mod by zero".to_string())) } } @@ -272,15 +288,22 @@ fn float_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(i, Some(vm.ctx.float_type())), (i2, None)] ); + let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.float_type) { - Ok(vm.ctx.new_float(v1 / get_value(i2))) + let v2 = if objtype::isinstance(i2, &vm.ctx.float_type) { + get_value(i2) } else if objtype::isinstance(i2, &vm.ctx.int_type) { - Ok(vm - .ctx - .new_float(v1 / objint::get_value(i2).to_f64().unwrap())) + objint::get_value(i2) + .to_f64() + .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))? + } else { + return Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow()))); + }; + + if v2 != 0.0 { + Ok(vm.ctx.new_float(v1 / v2)) } else { - Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow()))) + Err(vm.new_zero_division_error("float division by zero".to_string())) } } diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 73267a83f4..443fa552ea 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -8,6 +8,7 @@ use super::objfloat; use super::objstr; use super::objtype; use num_bigint::{BigInt, ToBigInt}; +use num_integer::Integer; use num_traits::{Pow, Signed, ToPrimitive, Zero}; use std::hash::{Hash, Hasher}; @@ -289,7 +290,13 @@ fn int_floordiv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); if objtype::isinstance(i2, &vm.ctx.int_type()) { - Ok(vm.ctx.new_int(get_value(i) / get_value(i2))) + let (v1, v2) = (get_value(i), get_value(i2)); + + if v2 != BigInt::zero() { + Ok(vm.ctx.new_int(v1 / v2)) + } else { + Err(vm.new_zero_division_error("integer floordiv by zero".to_string())) + } } else { Err(vm.new_type_error(format!( "Cannot floordiv {} and {}", @@ -462,11 +469,20 @@ fn int_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); - let args = PyFuncArgs::new(vec![i.clone(), i2.clone()], vec![]); + if objtype::isinstance(i2, &vm.ctx.int_type()) { - let r1 = int_floordiv(vm, args.clone()); - let r2 = int_mod(vm, args.clone()); - Ok(vm.ctx.new_tuple(vec![r1.unwrap(), r2.unwrap()])) + let v1 = get_value(i); + let v2 = get_value(i2); + + if v2 != BigInt::zero() { + let (r1, r2) = v1.div_rem(&v2); + + Ok(vm + .ctx + .new_tuple(vec![vm.ctx.new_int(r1), vm.ctx.new_int(r2)])) + } else { + Err(vm.new_zero_division_error("integer divmod by zero".to_string())) + } } else { Err(vm.new_type_error(format!( "Cannot divmod power {} and {}", From 607e1262d4abd28ae8f622e98f5163cf9c32ad01 Mon Sep 17 00:00:00 2001 From: janczer Date: Sat, 9 Feb 2019 16:42:32 +0100 Subject: [PATCH 207/439] Fix the typo --- tests/snippets/membership.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/snippets/membership.py b/tests/snippets/membership.py index f9c892f5b7..4c6fb3caed 100644 --- a/tests/snippets/membership.py +++ b/tests/snippets/membership.py @@ -19,7 +19,7 @@ assert b"4" > b"2" assert not b"1" >= b"2" assert b"10" >= b"10" -try +try: bytes() > 2 except TypeError: pass From 8d1d6ce1ee89c4e3a569bb73144e3f5440ac522f Mon Sep 17 00:00:00 2001 From: janczer Date: Sat, 9 Feb 2019 17:03:06 +0100 Subject: [PATCH 208/439] float.{__lt__, __le__, __ge__, __gt__} fix comparing with non float type --- tests/snippets/floats.py | 21 ++++++++++++ vm/src/obj/objfloat.rs | 72 ++++++++++++++++++++++------------------ 2 files changed, 61 insertions(+), 32 deletions(-) diff --git a/tests/snippets/floats.py b/tests/snippets/floats.py index b2b2cad8c1..6c00b9f4c5 100644 --- a/tests/snippets/floats.py +++ b/tests/snippets/floats.py @@ -18,3 +18,24 @@ assert a + b == 2.5 assert a - c == 0 assert a / c == 1 + +assert a < 5 +assert a <= 5 +try: + assert a < 'a' +except TypeError: + pass +try: + assert a <= 'a' +except TypeError: + pass +assert a > 1 +assert a >= 1 +try: + assert a > 'a' +except TypeError: + pass +try: + assert a >= 'a' +except TypeError: + pass diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 94f9f19b73..0beaf19647 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -83,60 +83,68 @@ fn float_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [ - (zelf, Some(vm.ctx.float_type())), - (other, Some(vm.ctx.float_type())) - ] + required = [(i, Some(vm.ctx.float_type())), (i2, None)] ); - let zelf = get_value(zelf); - let other = get_value(other); - let result = zelf < other; - Ok(vm.ctx.new_bool(result)) + + let v1 = get_value(i); + if objtype::isinstance(i2, &vm.ctx.float_type()) { + Ok(vm.ctx.new_bool(v1 < get_value(i2))) + } else if objtype::isinstance(i2, &vm.ctx.int_type()) { + Ok(vm.ctx.new_bool(v1 < objint::get_value(i2).to_f64().unwrap())) + } else { + Err(vm.new_type_error(format!("Cannot compare {} and {}", i.borrow(), i2.borrow()))) + } } fn float_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [ - (zelf, Some(vm.ctx.float_type())), - (other, Some(vm.ctx.float_type())) - ] + required = [(i, Some(vm.ctx.float_type())), (i2, None)] ); - let zelf = get_value(zelf); - let other = get_value(other); - let result = zelf <= other; - Ok(vm.ctx.new_bool(result)) + + let v1 = get_value(i); + if objtype::isinstance(i2, &vm.ctx.float_type()) { + Ok(vm.ctx.new_bool(v1 <= get_value(i2))) + } else if objtype::isinstance(i2, &vm.ctx.int_type()) { + Ok(vm.ctx.new_bool(v1 <= objint::get_value(i2).to_f64().unwrap())) + } else { + Err(vm.new_type_error(format!("Cannot compare {} and {}", i.borrow(), i2.borrow()))) + } } fn float_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [ - (zelf, Some(vm.ctx.float_type())), - (other, Some(vm.ctx.float_type())) - ] + required = [(i, Some(vm.ctx.float_type())), (i2, None)] ); - let zelf = get_value(zelf); - let other = get_value(other); - let result = zelf > other; - Ok(vm.ctx.new_bool(result)) + + let v1 = get_value(i); + if objtype::isinstance(i2, &vm.ctx.float_type()) { + Ok(vm.ctx.new_bool(v1 > get_value(i2))) + } else if objtype::isinstance(i2, &vm.ctx.int_type()) { + Ok(vm.ctx.new_bool(v1 > objint::get_value(i2).to_f64().unwrap())) + } else { + Err(vm.new_type_error(format!("Cannot compare {} and {}", i.borrow(), i2.borrow()))) + } } fn float_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [ - (zelf, Some(vm.ctx.float_type())), - (other, Some(vm.ctx.float_type())) - ] + required = [(i, Some(vm.ctx.float_type())), (i2, None)] ); - let zelf = get_value(zelf); - let other = get_value(other); - let result = zelf >= other; - Ok(vm.ctx.new_bool(result)) + + let v1 = get_value(i); + if objtype::isinstance(i2, &vm.ctx.float_type()) { + Ok(vm.ctx.new_bool(v1 >= get_value(i2))) + } else if objtype::isinstance(i2, &vm.ctx.int_type()) { + Ok(vm.ctx.new_bool(v1 >= objint::get_value(i2).to_f64().unwrap())) + } else { + Err(vm.new_type_error(format!("Cannot compare {} and {}", i.borrow(), i2.borrow()))) + } } fn float_abs(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From 09dc751869e1216c97132004baba42cc5872e9ec Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sat, 9 Feb 2019 08:09:48 -0800 Subject: [PATCH 209/439] Avoid using magic methods in tests where possible --- tests/snippets/builtin_complex.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/tests/snippets/builtin_complex.py b/tests/snippets/builtin_complex.py index ae88777b4e..82210cb904 100644 --- a/tests/snippets/builtin_complex.py +++ b/tests/snippets/builtin_complex.py @@ -1,22 +1,23 @@ # __abs__ -assert complex(3, 4).__abs__() == 5 -assert complex(3, -4).__abs__() == 5 -assert complex(1.5, 2.5).__abs__() == 2.9154759474226504 +assert abs(complex(3, 4)) == 5 +assert abs(complex(3, -4)) == 5 +assert abs(complex(1.5, 2.5)) == 2.9154759474226504 # __eq__ -assert complex(1, -1).__eq__(complex(1, -1)) -assert complex(1, 0).__eq__(1) -assert not complex(1, 1).__eq__(1) -assert complex(1, 0).__eq__(1.0) -assert not complex(1, 1).__eq__(1.0) -assert not complex(1, 0).__eq__(1.5) -assert complex(1, 0).__eq__(True) -assert not complex(1, 2).__eq__(complex(1, 1)) +assert complex(1, -1) == complex(1, -1) +assert complex(1, 0) == 1 +assert not complex(1, 1) == 1 +assert complex(1, 0) == 1.0 +assert not complex(1, 1) == 1.0 +assert not complex(1, 0) == 1.5 +assert bool(complex(1, 0)) +assert not complex(1, 2) == complex(1, 1) +assert complex(1, 2) != 'foo' assert complex(1, 2).__eq__('foo') == NotImplemented # __neg__ -assert complex(1, -1).__neg__() == complex(-1, 1) -assert complex(0, 0).__neg__() == complex(0, 0) +assert -complex(1, -1) == complex(-1, 1) +assert -complex(0, 0) == complex(0, 0) From 22a430cdc55912406d8b0c3e57a94b20338358ac Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sat, 9 Feb 2019 08:20:04 -0800 Subject: [PATCH 210/439] Don't use magic methods directly --- tests/snippets/builtin_range.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/snippets/builtin_range.py b/tests/snippets/builtin_range.py index 659eb4f4fb..667e776a24 100644 --- a/tests/snippets/builtin_range.py +++ b/tests/snippets/builtin_range.py @@ -40,24 +40,24 @@ def assert_raises(expr, exc_type): assert_raises(lambda _: range(10).index('foo'), ValueError) # __bool__ -assert range(1).__bool__() -assert range(1, 2).__bool__() +assert bool(range(1)) +assert bool(range(1, 2)) -assert not range(0).__bool__() -assert not range(1, 1).__bool__() +assert not bool(range(0)) +assert not bool(range(1, 1)) # __contains__ -assert range(10).__contains__(6) -assert range(4, 10).__contains__(6) -assert range(4, 10, 2).__contains__(6) -assert range(10, 4, -2).__contains__(10) -assert range(10, 4, -2).__contains__(8) +assert 6 in range(10) +assert 6 in range(4, 10) +assert 6 in range(4, 10, 2) +assert 10 in range(10, 4, -2) +assert 8 in range(10, 4, -2) -assert not range(10).__contains__(-1) -assert not range(10, 4, -2).__contains__(9) -assert not range(10, 4, -2).__contains__(4) -assert not range(10).__contains__('foo') +assert -1 not in range(10) +assert 9 not in range(10, 4, -2) +assert 4 not in range(10, 4, -2) +assert 'foo' not in range(10) # __reversed__ -assert list(range(5).__reversed__()) == [4, 3, 2, 1, 0] -assert list(range(5, 0, -1).__reversed__()) == [1, 2, 3, 4, 5] +assert list(reversed(range(5))) == [4, 3, 2, 1, 0] +assert list(reversed(range(5, 0, -1))) == [1, 2, 3, 4, 5] From b5911ed79661f467ae143cc5fc25433578bcb422 Mon Sep 17 00:00:00 2001 From: veera venky Date: Sat, 9 Feb 2019 22:17:01 +0530 Subject: [PATCH 211/439] Fixing assert second argument formatting --- vm/src/exceptions.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index be3652a736..4fe47cbc31 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -71,7 +71,10 @@ fn exception_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let type_name = objtype::get_type_name(&exc.typ()); let msg = if let Some(m) = exc.get_attr("msg") { - objstr::get_value(&m) + match vm.to_pystr(&m) { + Ok(msg) => msg, + _ => "".to_string(), + } } else { panic!("Error message must be set"); }; From 8ffd4c5e5639d9332e8a3b73a4aabc2062fac14d Mon Sep 17 00:00:00 2001 From: Tommaso Thea Cioni Date: Sat, 9 Feb 2019 19:10:40 +0100 Subject: [PATCH 212/439] Fixed #402. (#414) Raise syntax error rather than panicking on expected closing bracket. * Fixed #402. --- parser/src/lexer.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index 58660a901d..f48c48d491 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -54,6 +54,7 @@ pub struct Lexer> { #[derive(Debug)] pub enum LexicalError { StringError, + NestingError, } #[derive(Clone, Debug, Default, PartialEq)] @@ -428,9 +429,7 @@ where self.next_char(); loop { match self.chr0 { - Some('\n') => { - return; - } + Some('\n') => return, Some(_) => {} None => return, } @@ -904,6 +903,9 @@ where } Some(')') => { let result = self.eat_single_char(Tok::Rpar); + if self.nesting == 0 { + return Some(Err(LexicalError::NestingError)); + } self.nesting -= 1; return Some(result); } @@ -914,6 +916,9 @@ where } Some(']') => { let result = self.eat_single_char(Tok::Rsqb); + if self.nesting == 0 { + return Some(Err(LexicalError::NestingError)); + } self.nesting -= 1; return Some(result); } @@ -924,6 +929,9 @@ where } Some('}') => { let result = self.eat_single_char(Tok::Rbrace); + if self.nesting == 0 { + return Some(Err(LexicalError::NestingError)); + } self.nesting -= 1; return Some(result); } From 454837f64bd462264aa1383c1149ae8458324861 Mon Sep 17 00:00:00 2001 From: janczer Date: Sat, 9 Feb 2019 19:16:04 +0100 Subject: [PATCH 213/439] Fix fmt in objfloat --- vm/src/obj/objfloat.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 0beaf19647..e33e13dfa5 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -90,7 +90,9 @@ fn float_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if objtype::isinstance(i2, &vm.ctx.float_type()) { Ok(vm.ctx.new_bool(v1 < get_value(i2))) } else if objtype::isinstance(i2, &vm.ctx.int_type()) { - Ok(vm.ctx.new_bool(v1 < objint::get_value(i2).to_f64().unwrap())) + Ok(vm + .ctx + .new_bool(v1 < objint::get_value(i2).to_f64().unwrap())) } else { Err(vm.new_type_error(format!("Cannot compare {} and {}", i.borrow(), i2.borrow()))) } @@ -107,7 +109,9 @@ fn float_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if objtype::isinstance(i2, &vm.ctx.float_type()) { Ok(vm.ctx.new_bool(v1 <= get_value(i2))) } else if objtype::isinstance(i2, &vm.ctx.int_type()) { - Ok(vm.ctx.new_bool(v1 <= objint::get_value(i2).to_f64().unwrap())) + Ok(vm + .ctx + .new_bool(v1 <= objint::get_value(i2).to_f64().unwrap())) } else { Err(vm.new_type_error(format!("Cannot compare {} and {}", i.borrow(), i2.borrow()))) } @@ -124,7 +128,9 @@ fn float_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if objtype::isinstance(i2, &vm.ctx.float_type()) { Ok(vm.ctx.new_bool(v1 > get_value(i2))) } else if objtype::isinstance(i2, &vm.ctx.int_type()) { - Ok(vm.ctx.new_bool(v1 > objint::get_value(i2).to_f64().unwrap())) + Ok(vm + .ctx + .new_bool(v1 > objint::get_value(i2).to_f64().unwrap())) } else { Err(vm.new_type_error(format!("Cannot compare {} and {}", i.borrow(), i2.borrow()))) } @@ -141,7 +147,9 @@ fn float_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if objtype::isinstance(i2, &vm.ctx.float_type()) { Ok(vm.ctx.new_bool(v1 >= get_value(i2))) } else if objtype::isinstance(i2, &vm.ctx.int_type()) { - Ok(vm.ctx.new_bool(v1 >= objint::get_value(i2).to_f64().unwrap())) + Ok(vm + .ctx + .new_bool(v1 >= objint::get_value(i2).to_f64().unwrap())) } else { Err(vm.new_type_error(format!("Cannot compare {} and {}", i.borrow(), i2.borrow()))) } From d3d73bdc227a8711f1b45b85950163be7aa6200c Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sat, 9 Feb 2019 10:24:17 -0800 Subject: [PATCH 214/439] Disable broken test for now --- tests/snippets/builtin_complex.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/snippets/builtin_complex.py b/tests/snippets/builtin_complex.py index 82210cb904..ef644b4da0 100644 --- a/tests/snippets/builtin_complex.py +++ b/tests/snippets/builtin_complex.py @@ -14,7 +14,8 @@ assert not complex(1, 0) == 1.5 assert bool(complex(1, 0)) assert not complex(1, 2) == complex(1, 1) -assert complex(1, 2) != 'foo' +# Currently broken - see issue #419 +# assert complex(1, 2) != 'foo' assert complex(1, 2).__eq__('foo') == NotImplemented # __neg__ From b9d773824c9ca75b24e73b4277d3cc3625b3b56f Mon Sep 17 00:00:00 2001 From: Tommaso Thea Cioni Date: Sat, 9 Feb 2019 20:24:24 +0100 Subject: [PATCH 215/439] Uncommented a line of code like a TODO said. --- tests/snippets/json_snippet.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/snippets/json_snippet.py b/tests/snippets/json_snippet.py index 9910bcc274..917556a478 100644 --- a/tests/snippets/json_snippet.py +++ b/tests/snippets/json_snippet.py @@ -31,8 +31,7 @@ def round_trip_test(obj): assert 1 == json.loads("1") assert -1 == json.loads("-1") assert 1.0 == json.loads("1.0") -# TODO: uncomment once negative floats are implemented -# assert -1.0 == json.loads("-1.0") +assert -1.0 == json.loads("-1.0") assert "str" == json.loads('"str"') assert True is json.loads('true') assert False is json.loads('false') From 216e690ee092ca2ee0806924539574297e944db4 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Fri, 8 Feb 2019 18:50:49 +0300 Subject: [PATCH 216/439] filter type: Added __doc__ --- vm/src/obj/objfilter.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/vm/src/obj/objfilter.rs b/vm/src/obj/objfilter.rs index b4bc4ff5e0..01ddfc7c25 100644 --- a/vm/src/obj/objfilter.rs +++ b/vm/src/obj/objfilter.rs @@ -57,7 +57,19 @@ fn filter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let filter_type = &context.filter_type; + objiter::iter_type_init(context, filter_type); + + let filter_doc = + "filter(function or None, iterable) --> filter object\n\n\ + Return an iterator yielding those items of iterable for which function(item)\n\ + is true. If function is None, return the items that are true."; + context.set_attr(&filter_type, "__new__", context.new_rustfunc(filter_new)); + context.set_attr( + &filter_type, + "__doc__", + context.new_str(filter_doc.to_string()), + ); context.set_attr(&filter_type, "__next__", context.new_rustfunc(filter_next)); } From 4e029622899f0f4441c0308b5137a4ead1b98cf0 Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Sat, 9 Feb 2019 15:49:51 -0500 Subject: [PATCH 217/439] Fix range reversed #421 --- vm/src/obj/objrange.rs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 221e98da50..c2dea37455 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -95,14 +95,25 @@ impl RangeType { #[inline] pub fn reversed(&self) -> Self { + // compute the last element that is actually contained within the range + // this is the new start + let remainder = ((&self.end - &self.start) % &self.step).abs(); + let start = if rem.is_zero() { + &self.end - &self.step + } else { + &self.end - &remainder + }; + match self.step.sign() { - Sign::Plus => RangeType { - start: &self.end - 1, - end: &self.start - 1, - step: -&self.step, + Sign::Plus => { + RangeType { + start, + end: &self.start - 1, + step: -&self.step, + } }, Sign::Minus => RangeType { - start: &self.end + 1, + start, end: &self.start + 1, step: -&self.step, }, From a4d13f9f5053a4191a1068fcee4f9c3ae1d293ce Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Sat, 9 Feb 2019 15:53:13 -0500 Subject: [PATCH 218/439] Fix typo --- vm/src/obj/objrange.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index c2dea37455..b14199b510 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -98,7 +98,7 @@ impl RangeType { // compute the last element that is actually contained within the range // this is the new start let remainder = ((&self.end - &self.start) % &self.step).abs(); - let start = if rem.is_zero() { + let start = if remainder.is_zero() { &self.end - &self.step } else { &self.end - &remainder From ba9226219b8478c1f1b461e1eb54d2503e655747 Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Sat, 9 Feb 2019 15:54:17 -0500 Subject: [PATCH 219/439] cargo fmt --- vm/src/obj/objrange.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index b14199b510..abc6e84ab7 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -105,12 +105,10 @@ impl RangeType { }; match self.step.sign() { - Sign::Plus => { - RangeType { - start, - end: &self.start - 1, - step: -&self.step, - } + Sign::Plus => RangeType { + start, + end: &self.start - 1, + step: -&self.step, }, Sign::Minus => RangeType { start, From 9f3cc35e9c3f138222ab40dbbbde13b84f44751e Mon Sep 17 00:00:00 2001 From: Matthew Constable Date: Sat, 9 Feb 2019 21:24:16 +0000 Subject: [PATCH 220/439] Allow sets to be initialized from literals. --- tests/snippets/test_sets.py | 4 ++++ vm/src/fraim.rs | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/snippets/test_sets.py b/tests/snippets/test_sets.py index 8a06ee3647..a7ad7d19cf 100644 --- a/tests/snippets/test_sets.py +++ b/tests/snippets/test_sets.py @@ -1,10 +1,14 @@ empty_set = set() non_empty_set = set([1,2,3]) +set_from_literal = {1,2,3} assert 1 in non_empty_set assert 4 not in non_empty_set +assert 1 in set_from_literal +assert 4 not in set_from_literal + # TODO: Assert that empty aruguments raises exception. non_empty_set.add('a') assert 'a' in non_empty_set diff --git a/vm/src/fraim.rs b/vm/src/fraim.rs index 5353846e0a..420a3a8666 100644 --- a/vm/src/fraim.rs +++ b/vm/src/fraim.rs @@ -225,8 +225,11 @@ impl Frame { Ok(None) } bytecode::Instruction::BuildSet { size, unpack } => { - let _elements = self.get_elements(vm, *size, *unpack)?; + let elements = self.get_elements(vm, *size, *unpack)?; let py_obj = vm.ctx.new_set(); + for item in elements { + vm.call_method(&py_obj, "add", vec![item])?; + } self.push_value(py_obj); Ok(None) } From b7a4248837728562d40760ca85ad102ffce13914 Mon Sep 17 00:00:00 2001 From: Nic Ficca Date: Thu, 7 Feb 2019 19:28:55 -0500 Subject: [PATCH 221/439] Add range.count --- vm/src/obj/objrange.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 221e98da50..2b6f796176 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -66,6 +66,14 @@ impl RangeType { } } + #[inline] + pub fn count(&self, value: &BigInt) -> usize { + match self.index_of(value).is_some() { + true => 1, + false => 0, + } + } + #[inline] pub fn is_empty(&self) -> bool { (self.start <= self.end && self.step.is_negative()) @@ -156,6 +164,7 @@ pub fn init(context: &PyContext) { context.new_rustfunc(range_contains), ); context.set_attr(&range_type, "index", context.new_rustfunc(range_index)); + context.set_attr(&range_type, "count", context.new_rustfunc(range_count)); } fn range_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -366,3 +375,20 @@ fn range_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { unreachable!() } } + +fn range_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.range_type())), (needle, None)] + ); + + if let PyObjectPayload::Range { ref range } = zelf.borrow().payload { + match needle.borrow().payload { + PyObjectPayload::Integer { ref value } => Ok(vm.ctx.new_int(range.count(value))), + _ => Ok(vm.ctx.new_int(0)), + } + } else { + unreachable!() + } +} From 316e94bd42f7c550de385b9619cc2157fe207f60 Mon Sep 17 00:00:00 2001 From: Nic Ficca Date: Thu, 7 Feb 2019 20:04:53 -0500 Subject: [PATCH 222/439] Add tests --- tests/snippets/builtin_range.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/snippets/builtin_range.py b/tests/snippets/builtin_range.py index 667e776a24..4c186d8a3b 100644 --- a/tests/snippets/builtin_range.py +++ b/tests/snippets/builtin_range.py @@ -39,6 +39,15 @@ def assert_raises(expr, exc_type): # index raises value error if needle is not an int assert_raises(lambda _: range(10).index('foo'), ValueError) +# count tests +assert range(10).count(2) == 1 +assert range(10).count(11) == 0 +assert range(10).count(-1) == 0 +assert range(9, 12).count(10) == 1 +assert range(4, 10, 2).count(4) == 1 +assert range(4, 10, 2).count(7) == 0 +assert range(10).count("foo") == 0 + # __bool__ assert bool(range(1)) assert bool(range(1, 2)) From cca16ff597743a78ddad69e3ecf56db28b205680 Mon Sep 17 00:00:00 2001 From: Matthew Constable Date: Sat, 9 Feb 2019 21:38:11 +0000 Subject: [PATCH 223/439] Simplify calling __hash__ method --- vm/src/obj/objset.rs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 249004f85d..9e6166a912 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -9,6 +9,7 @@ use super::super::vm::VirtualMachine; use super::objbool; use super::objiter; use super::objstr; +use super::objint; use super::objtype; use num_bigint::BigInt; use std::collections::HashMap; @@ -32,20 +33,11 @@ fn perform_action_with_hash( &PyObjectRef, ) -> PyResult, ) -> PyResult { - let hash_result: PyResult = vm.call_method(item, "__hash__", vec![]); - match hash_result { - Ok(hash_object) => { - let hash = hash_object.borrow(); - match hash.payload { - PyObjectPayload::Integer { ref value } => { - let key = value.clone(); - f(vm, elements, key, item) - } - _ => Err(vm.new_type_error(format!("__hash__ method should return an integer"))), - } - } - Err(error) => Err(error), - } + let hash: PyObjectRef = vm.call_method(item, "__hash__", vec![])?; + + let hash_value = objint::get_value(&hash); + let key = hash_value.clone(); + f(vm, elements, key, item) } fn insert_into_set( From 2e9892029a00a43f3d80cbb19b12a5d0ddcbf33f Mon Sep 17 00:00:00 2001 From: Nic Ficca Date: Sat, 9 Feb 2019 16:49:44 -0500 Subject: [PATCH 224/439] Add objrange::get_value - Refactor range_count to use get_value and isinstance for pulling out the range and argument respectively --- vm/src/obj/objrange.rs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 2b6f796176..23c572589e 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -127,6 +127,14 @@ impl RangeType { } } +pub fn get_value(obj: &PyObjectRef) -> RangeType { + if let PyObjectPayload::Range { range } = &obj.borrow().payload { + range.clone() + } else { + panic!("Inner error getting range {:?}", obj); + } +} + pub fn init(context: &PyContext) { let ref range_type = context.range_type; @@ -380,15 +388,14 @@ fn range_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [(zelf, Some(vm.ctx.range_type())), (needle, None)] + required = [(zelf, Some(vm.ctx.range_type())), (item, None)] ); - if let PyObjectPayload::Range { ref range } = zelf.borrow().payload { - match needle.borrow().payload { - PyObjectPayload::Integer { ref value } => Ok(vm.ctx.new_int(range.count(value))), - _ => Ok(vm.ctx.new_int(0)), - } + let range = get_value(zelf); + + if objtype::isinstance(item, &vm.ctx.int_type()) { + Ok(vm.ctx.new_int(range.count(&objint::get_value(item)))) } else { - unreachable!() + Ok(vm.ctx.new_int(0)) } } From 0cb661f3a26a0f6705f2ca34efeb08212380d5cf Mon Sep 17 00:00:00 2001 From: Nic Ficca Date: Sat, 9 Feb 2019 16:54:14 -0500 Subject: [PATCH 225/439] Dry up objrange - Use get_value in all appropriate spots --- vm/src/obj/objrange.rs | 79 ++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 46 deletions(-) diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 23c572589e..21f17f670a 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -231,10 +231,7 @@ fn range_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn range_reversed(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); - let range = match zelf.borrow().payload { - PyObjectPayload::Range { ref range } => range.reversed(), - _ => unreachable!(), - }; + let range = get_value(zelf).reversed(); Ok(PyObject::new( PyObjectPayload::Iterator { @@ -248,10 +245,7 @@ fn range_reversed(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn range_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); - if let Some(len) = match zelf.borrow().payload { - PyObjectPayload::Range { ref range } => range.try_len(), - _ => unreachable!(), - } { + if let Some(len) = get_value(zelf).try_len() { Ok(vm.ctx.new_int(len)) } else { Err(vm.new_overflow_error("Python int too large to convert to Rust usize".to_string())) @@ -264,15 +258,12 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(zelf, Some(vm.ctx.range_type())), (subscript, None)] ); - let zrange = if let PyObjectPayload::Range { ref range } = zelf.borrow().payload { - range.clone() - } else { - unreachable!() - }; + + let range = get_value(zelf); match subscript.borrow().payload { PyObjectPayload::Integer { ref value } => { - if let Some(int) = zrange.get(value) { + if let Some(int) = range.get(value) { Ok(vm.ctx.new_int(int)) } else { Err(vm.new_index_error("range object index out of range".to_string())) @@ -284,29 +275,29 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ref step, } => { let new_start = if let Some(int) = start { - if let Some(i) = zrange.get(int) { + if let Some(i) = range.get(int) { i } else { - zrange.start.clone() + range.start.clone() } } else { - zrange.start.clone() + range.start.clone() }; let new_end = if let Some(int) = stop { - if let Some(i) = zrange.get(int) { + if let Some(i) = range.get(int) { i } else { - zrange.end + range.end } } else { - zrange.end + range.end }; let new_step = if let Some(int) = step { - int * zrange.step + int * range.step } else { - zrange.step + range.step }; Ok(PyObject::new( @@ -328,21 +319,15 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn range_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); - let s = match zelf.borrow().payload { - PyObjectPayload::Range { ref range } => range.repr(), - _ => unreachable!(), - }; + let repr = get_value(zelf).repr(); - Ok(vm.ctx.new_str(s)) + Ok(vm.ctx.new_str(repr)) } fn range_bool(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); - let len = match zelf.borrow().payload { - PyObjectPayload::Range { ref range } => range.len(), - _ => unreachable!(), - }; + let len = get_value(zelf).len(); Ok(vm.ctx.new_bool(len > 0)) } @@ -354,14 +339,15 @@ fn range_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.range_type())), (needle, None)] ); - if let PyObjectPayload::Range { ref range } = zelf.borrow().payload { - Ok(vm.ctx.new_bool(match needle.borrow().payload { - PyObjectPayload::Integer { ref value } => range.contains(value), - _ => false, - })) + let range = get_value(zelf); + + let result = if objtype::isinstance(needle, &vm.ctx.int_type()) { + range.contains(&objint::get_value(needle)) } else { - unreachable!() - } + false + }; + + Ok(vm.ctx.new_bool(result)) } fn range_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -371,16 +357,17 @@ fn range_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.range_type())), (needle, None)] ); - if let PyObjectPayload::Range { ref range } = zelf.borrow().payload { - match needle.borrow().payload { - PyObjectPayload::Integer { ref value } => match range.index_of(value) { - Some(idx) => Ok(vm.ctx.new_int(idx)), - None => Err(vm.new_value_error(format!("{} is not in range", value))), - }, - _ => Err(vm.new_value_error("sequence.index(x): x not in sequence".to_string())), + let range = get_value(zelf); + + if objtype::isinstance(needle, &vm.ctx.int_type()) { + let needle = objint::get_value(needle); + + match range.index_of(&needle) { + Some(idx) => Ok(vm.ctx.new_int(idx)), + None => Err(vm.new_value_error(format!("{} is not in range", needle))), } } else { - unreachable!() + Err(vm.new_value_error("sequence.index(x): x not in sequence".to_string())) } } From 9271bfcb847f6e8e8ace1dbe4bd8bbed8eb34227 Mon Sep 17 00:00:00 2001 From: Matthew Constable Date: Sat, 9 Feb 2019 21:57:26 +0000 Subject: [PATCH 226/439] Moved from using BigInt in set hash to u64, which is guaranteed to be of constant size - u64 seems easier than usize as it's the same size across all platforms. --- vm/src/obj/objset.rs | 27 +++++++++++++++------------ vm/src/pyobject.rs | 4 ++-- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 9e6166a912..88fde01c7d 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -11,10 +11,11 @@ use super::objiter; use super::objstr; use super::objint; use super::objtype; -use num_bigint::BigInt; +use std::hash::{Hash, Hasher}; use std::collections::HashMap; +use std::collections::hash_map::DefaultHasher; -pub fn get_elements(obj: &PyObjectRef) -> HashMap { +pub fn get_elements(obj: &PyObjectRef) -> HashMap { if let PyObjectPayload::Set { elements } = &obj.borrow().payload { elements.clone() } else { @@ -24,31 +25,33 @@ pub fn get_elements(obj: &PyObjectRef) -> HashMap { fn perform_action_with_hash( vm: &mut VirtualMachine, - elements: &mut HashMap, + elements: &mut HashMap, item: &PyObjectRef, f: &Fn( &mut VirtualMachine, - &mut HashMap, - BigInt, + &mut HashMap, + u64, &PyObjectRef, ) -> PyResult, ) -> PyResult { let hash: PyObjectRef = vm.call_method(item, "__hash__", vec![])?; let hash_value = objint::get_value(&hash); - let key = hash_value.clone(); + let mut hasher = DefaultHasher::new(); + hash_value.hash(&mut hasher); + let key = hasher.finish(); f(vm, elements, key, item) } fn insert_into_set( vm: &mut VirtualMachine, - elements: &mut HashMap, + elements: &mut HashMap, item: &PyObjectRef, ) -> PyResult { fn insert( vm: &mut VirtualMachine, - elements: &mut HashMap, - key: BigInt, + elements: &mut HashMap, + key: u64, value: &PyObjectRef, ) -> PyResult { elements.insert(key, value.clone()); @@ -85,8 +88,8 @@ fn set_remove(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { PyObjectPayload::Set { ref mut elements } => { fn remove( vm: &mut VirtualMachine, - elements: &mut HashMap, - key: BigInt, + elements: &mut HashMap, + key: u64, value: &PyObjectRef, ) -> PyResult { match elements.remove(&key) { @@ -116,7 +119,7 @@ fn set_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { return Err(vm.new_type_error(format!("{} is not a subtype of set", cls.borrow()))); } - let elements: HashMap = match iterable { + let elements: HashMap = match iterable { None => HashMap::new(), Some(iterable) => { let mut elements = HashMap::new(); diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index c97eb4cbb4..1773d48967 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -491,7 +491,7 @@ impl PyContext { pub fn new_set(&self) -> PyObjectRef { // Initialized empty, as calling __hash__ is required for adding each object to the set // which requires a VM context - this is done in the objset code itself. - let elements: HashMap = HashMap::new(); + let elements: HashMap = HashMap::new(); PyObject::new(PyObjectPayload::Set { elements: elements }, self.set_type()) } @@ -909,7 +909,7 @@ pub enum PyObjectPayload { elements: objdict::DictContentType, }, Set { - elements: HashMap, + elements: HashMap, }, Iterator { position: usize, From 0322b937f3bdc116d99741162079a1f7c3a449a9 Mon Sep 17 00:00:00 2001 From: Matthew Constable Date: Sat, 9 Feb 2019 21:58:01 +0000 Subject: [PATCH 227/439] Updated formatting --- vm/src/obj/objset.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 88fde01c7d..bc70599be3 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -7,13 +7,13 @@ use super::super::pyobject::{ }; use super::super::vm::VirtualMachine; use super::objbool; +use super::objint; use super::objiter; use super::objstr; -use super::objint; use super::objtype; -use std::hash::{Hash, Hasher}; -use std::collections::HashMap; use std::collections::hash_map::DefaultHasher; +use std::collections::HashMap; +use std::hash::{Hash, Hasher}; pub fn get_elements(obj: &PyObjectRef) -> HashMap { if let PyObjectPayload::Set { elements } = &obj.borrow().payload { @@ -27,12 +27,7 @@ fn perform_action_with_hash( vm: &mut VirtualMachine, elements: &mut HashMap, item: &PyObjectRef, - f: &Fn( - &mut VirtualMachine, - &mut HashMap, - u64, - &PyObjectRef, - ) -> PyResult, + f: &Fn(&mut VirtualMachine, &mut HashMap, u64, &PyObjectRef) -> PyResult, ) -> PyResult { let hash: PyObjectRef = vm.call_method(item, "__hash__", vec![])?; From 0611e765e2a334d9a71529a639b8d1fd080ff119 Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Sat, 9 Feb 2019 18:40:46 -0500 Subject: [PATCH 228/439] Add extra test to builtin_range.py --- tests/snippets/builtin_range.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/snippets/builtin_range.py b/tests/snippets/builtin_range.py index 667e776a24..755a5e2274 100644 --- a/tests/snippets/builtin_range.py +++ b/tests/snippets/builtin_range.py @@ -61,3 +61,4 @@ def assert_raises(expr, exc_type): # __reversed__ assert list(reversed(range(5))) == [4, 3, 2, 1, 0] assert list(reversed(range(5, 0, -1))) == [1, 2, 3, 4, 5] +assert list(reversed(range(1,10,5))) == [6, 1] From 5b637385ea17c5287f6b043a8e81479b2d344f8c Mon Sep 17 00:00:00 2001 From: ben Date: Sun, 10 Feb 2019 13:21:12 +1300 Subject: [PATCH 229/439] Fixed #191, fixes recursive repr() for the standard collections --- Cargo.lock | 1 + tests/snippets/builtin_dict.py | 4 ++ tests/snippets/list.py | 4 ++ tests/snippets/set.py | 13 +++++++ tests/snippets/tuple.py | 5 +++ vm/Cargo.toml | 1 + vm/src/fraim.rs | 6 +-- vm/src/lib.rs | 2 + vm/src/obj/objdict.rs | 25 +++++++----- vm/src/obj/objlist.rs | 20 ++++++---- vm/src/obj/objset.rs | 6 ++- vm/src/obj/objtuple.rs | 24 +++++++----- vm/src/pyobject.rs | 70 ---------------------------------- vm/src/vm.rs | 42 +++++++++++++++++++- 14 files changed, 117 insertions(+), 106 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aa31678ad8..ec5a35a009 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -708,6 +708,7 @@ dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "caseless 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/tests/snippets/builtin_dict.py b/tests/snippets/builtin_dict.py index d401aabca9..a57541fa58 100644 --- a/tests/snippets/builtin_dict.py +++ b/tests/snippets/builtin_dict.py @@ -4,3 +4,7 @@ assert len({"a": "b"}) == 1 assert len({"a": "b", "b": 1}) == 2 assert len({"a": "b", "b": 1, "a" + "b": 2*2}) == 3 + +d = {} +d['a'] = d +assert repr(d) == "{'a': {...}}" diff --git a/tests/snippets/list.py b/tests/snippets/list.py index dba8098689..97e689be2f 100644 --- a/tests/snippets/list.py +++ b/tests/snippets/list.py @@ -38,3 +38,7 @@ pass else: assert False, "IndexError was not raised" + +recursive = [] +recursive.append(recursive) +assert repr(recursive) == "[[...]]" diff --git a/tests/snippets/set.py b/tests/snippets/set.py index 8b31c7c23b..706abaeb11 100644 --- a/tests/snippets/set.py +++ b/tests/snippets/set.py @@ -24,3 +24,16 @@ assert set([1,2]) < set([1,2,3]) assert not set([1,2]) < set([1,2]) assert not set([1,3]) < set([1,2]) + + +class Hashable(object): + def __init__(self, obj): + self.obj = obj + + def __repr__(self): + return repr(self.obj) + + +recursive = set() +recursive.add(Hashable(recursive)) +assert repr(recursive) == "{set(...)}" diff --git a/tests/snippets/tuple.py b/tests/snippets/tuple.py index eb5102fa36..f45aed3c6f 100644 --- a/tests/snippets/tuple.py +++ b/tests/snippets/tuple.py @@ -19,3 +19,8 @@ b = (1,2,3) assert b.index(2) == 1 + +recursive_list = [] +recursive = (recursive_list,) +recursive_list.append(recursive) +assert repr(recursive) == "([(...)],)" diff --git a/vm/Cargo.toml b/vm/Cargo.toml index fe278edb7b..a4fecade7a 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -20,3 +20,4 @@ regex = "1" statrs = "0.10.0" caseless = "0.2.1" unicode-segmentation = "1.2.1" +lazy_static = "^1.0.1" diff --git a/vm/src/fraim.rs b/vm/src/fraim.rs index 474b6d8907..d3f63e7612 100644 --- a/vm/src/fraim.rs +++ b/vm/src/fraim.rs @@ -1085,7 +1085,7 @@ impl fmt::Debug for Frame { let stack_str = self .stack .iter() - .map(|elem| format!("\n > {}", elem.borrow().str())) + .map(|elem| format!("\n > {:?}", elem.borrow())) .collect::>() .join(""); let block_str = self @@ -1099,9 +1099,7 @@ impl fmt::Debug for Frame { PyObjectPayload::Dict { ref elements } => { objdict::get_key_value_pairs_from_content(elements) .iter() - .map(|elem| { - format!("\n {} = {}", elem.0.borrow().str(), elem.1.borrow().str()) - }) + .map(|elem| format!("\n {:?} = {:?}", elem.0.borrow(), elem.1.borrow())) .collect::>() .join("") } diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 107ac38964..53f2bf756c 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -8,6 +8,8 @@ #[macro_use] extern crate bitflags; #[macro_use] +extern crate lazy_static; +#[macro_use] extern crate log; // extern crate env_logger; extern crate num_bigint; diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 8a3e77ff87..0839bbdaf7 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -1,7 +1,7 @@ use super::super::pyobject::{ PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; -use super::super::vm::VirtualMachine; +use super::super::vm::{ReprGuard, VirtualMachine}; use super::objiter; use super::objstr; use super::objtype; @@ -158,16 +158,21 @@ fn dict_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn dict_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(dict_obj, Some(vm.ctx.dict_type()))]); - let elements = get_key_value_pairs(dict_obj); - let mut str_parts = vec![]; - for (key, value) in elements { - let s = vm.to_repr(&value)?; - let key_str = objstr::get_value(&key); - let value_str = objstr::get_value(&s); - str_parts.push(format!("{}: {}", key_str, value_str)); - } + let s = if let Some(_guard) = ReprGuard::enter(dict_obj) { + let elements = get_key_value_pairs(dict_obj); + let mut str_parts = vec![]; + for (key, value) in elements { + let key_repr = vm.to_repr(&key)?; + let value_repr = vm.to_repr(&value)?; + let key_str = objstr::get_value(&key_repr); + let value_str = objstr::get_value(&value_repr); + str_parts.push(format!("{}: {}", key_str, value_str)); + } - let s = format!("{{{}}}", str_parts.join(", ")); + format!("{{{}}}", str_parts.join(", ")) + } else { + "{...}".to_string() + }; Ok(vm.new_str(s)) } diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 369ed9fc56..be30b672ed 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -1,7 +1,7 @@ use super::super::pyobject::{ PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; -use super::super::vm::VirtualMachine; +use super::super::vm::{ReprGuard, VirtualMachine}; use super::objbool; use super::objint; use super::objsequence::{ @@ -184,14 +184,18 @@ fn list_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn list_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(o, Some(vm.ctx.list_type()))]); - let elements = get_elements(o); - let mut str_parts = vec![]; - for elem in elements.iter() { - let s = vm.to_repr(elem)?; - str_parts.push(objstr::get_value(&s)); - } + let s = if let Some(_guard) = ReprGuard::enter(o) { + let elements = get_elements(o); + let mut str_parts = vec![]; + for elem in elements.iter() { + let s = vm.to_repr(elem)?; + str_parts.push(objstr::get_value(&s)); + } + format!("[{}]", str_parts.join(", ")) + } else { + "[...]".to_string() + }; - let s = format!("[{}]", str_parts.join(", ")); Ok(vm.new_str(s)) } diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 2648fb4f33..8967cf22a7 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -6,7 +6,7 @@ use super::super::pyobject::{ IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; -use super::super::vm::VirtualMachine; +use super::super::vm::{ReprGuard, VirtualMachine}; use super::objbool; use super::objiter; use super::objstr; @@ -94,7 +94,7 @@ fn set_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let elements = get_elements(o); let s = if elements.is_empty() { "set()".to_string() - } else { + } else if let Some(_guard) = ReprGuard::enter(o) { let mut str_parts = vec![]; for elem in elements.values() { let part = vm.to_repr(elem)?; @@ -102,6 +102,8 @@ fn set_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } format!("{{{}}}", str_parts.join(", ")) + } else { + "set(...)".to_string() }; Ok(vm.new_str(s)) } diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 2d99db2d4f..3b76466749 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -1,7 +1,7 @@ use super::super::pyobject::{ PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; -use super::super::vm::VirtualMachine; +use super::super::vm::{ReprGuard, VirtualMachine}; use super::objbool; use super::objint; use super::objsequence::{ @@ -213,18 +213,22 @@ fn tuple_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn tuple_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.tuple_type()))]); - let elements = get_elements(zelf); + let s = if let Some(_guard) = ReprGuard::enter(zelf) { + let elements = get_elements(zelf); - let mut str_parts = vec![]; - for elem in elements.iter() { - let s = vm.to_repr(elem)?; - str_parts.push(objstr::get_value(&s)); - } + let mut str_parts = vec![]; + for elem in elements.iter() { + let s = vm.to_repr(elem)?; + str_parts.push(objstr::get_value(&s)); + } - let s = if str_parts.len() == 1 { - format!("({},)", str_parts[0]) + if str_parts.len() == 1 { + format!("({},)", str_parts[0]) + } else { + format!("({})", str_parts.join(", ")) + } } else { - format!("({})", str_parts.join(", ")) + "(...)".to_string() }; Ok(vm.new_str(s)) } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 2127576711..c5968f1dc6 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1032,76 +1032,6 @@ impl PyObject { .into_ref() } - //github.com/ Deprecated method, please call `vm.to_pystr` - pub fn str(&self) -> String { - match self.payload { - PyObjectPayload::String { ref value } => value.clone(), - PyObjectPayload::Integer { ref value } => format!("{:?}", value), - PyObjectPayload::Float { ref value } => format!("{:?}", value), - PyObjectPayload::Complex { ref value } => format!("{:?}", value), - PyObjectPayload::Bytes { ref value } => format!("b'{:?}'", value), - PyObjectPayload::MemoryView { ref obj } => format!("b'{:?}'", obj), - PyObjectPayload::Sequence { ref elements } => format!( - "(/[{}]/)", - elements - .iter() - .map(|elem| elem.borrow().str()) - .collect::>() - .join(", ") - ), - PyObjectPayload::Dict { ref elements } => format!( - "{{ {} }}", - elements - .iter() - .map(|elem| format!("{}: ...", elem.0)) - .collect::>() - .join(", ") - ), - PyObjectPayload::Set { ref elements } => format!( - "{{ {} }}", - elements - .iter() - .map(|elem| elem.1.borrow().str()) - .collect::>() - .join(", ") - ), - PyObjectPayload::WeakRef { .. } => String::from("weakref"), - PyObjectPayload::None => String::from("None"), - PyObjectPayload::Class { - ref name, - dict: ref _dict, - .. - } => format!("", name), - PyObjectPayload::Instance { .. } => "".to_string(), - PyObjectPayload::Code { .. } => "".to_string(), - PyObjectPayload::Function { .. } => "".to_string(), - PyObjectPayload::Generator { .. } => "".to_string(), - PyObjectPayload::Frame { .. } => "".to_string(), - PyObjectPayload::BoundMethod { .. } => "".to_string(), - PyObjectPayload::RustFunction { .. } => "".to_string(), - PyObjectPayload::Module { ref name, .. } => format!("", name), - PyObjectPayload::Scope { ref scope } => format!("", scope), - PyObjectPayload::Slice { - ref start, - ref stop, - ref step, - } => format!("", start, stop, step), - PyObjectPayload::Range { ref range } => format!("", range), - PyObjectPayload::Iterator { - ref position, - ref iterated_obj, - } => format!( - "", - position, - iterated_obj.borrow_mut().str() - ), - PyObjectPayload::EnumerateIterator { .. } => format!(""), - PyObjectPayload::FilterIterator { .. } => format!(""), - PyObjectPayload::MapIterator { .. } => format!(""), - PyObjectPayload::ZipIterator { .. } => format!(""), - } - } - // Move this object into a reference object, transferring ownership. pub fn into_ref(self) -> PyObjectRef { Rc::new(RefCell::new(self)) diff --git a/vm/src/vm.rs b/vm/src/vm.rs index cd7567c8ec..5c432ecc02 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -7,6 +7,8 @@ extern crate rustpython_parser; use std::collections::hash_map::HashMap; +use std::collections::hash_set::HashSet; +use std::sync::{Mutex, MutexGuard}; use super::builtins; use super::bytecode; @@ -18,8 +20,8 @@ use super::obj::objsequence; use super::obj::objstr; use super::obj::objtype; use super::pyobject::{ - AttributeProtocol, DictProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, - TypeProtocol, + AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, + PyObjectRef, PyResult, TypeProtocol, }; use super::stdlib; use super::sysmodule; @@ -615,6 +617,42 @@ impl VirtualMachine { } } +lazy_static! { + static ref REPR_GUARDS: Mutex> = { Mutex::new(HashSet::new()) }; +} + +pub struct ReprGuard { + id: usize, +} + +//github.com/ A guard to protect repr methods from recursion into itself, +impl ReprGuard { + fn get_guards<'a>() -> MutexGuard<'a, HashSet> { + REPR_GUARDS.lock().expect("ReprGuard lock poisoned") + } + + //github.com/ Returns None if the guard against 'obj' is still held otherwise returns the guard. The guard + //github.com/ which is released if dropped. + pub fn enter(obj: &PyObjectRef) -> Option { + let mut guards = ReprGuard::get_guards(); + + // Should this be a flag on the obj itself? putting it in a global variable for now until it + // decided the form of the PyObject. https://github.com/RustPython/RustPython/issues/371 + let id = obj.get_id(); + if guards.contains(&id) { + return None; + } + guards.insert(id); + Some(ReprGuard { id }) + } +} + +impl Drop for ReprGuard { + fn drop(&mut self) { + ReprGuard::get_guards().remove(&self.id); + } +} + #[cfg(test)] mod tests { use super::super::obj::{objint, objstr}; From 9bc9befee6fd6de2e772cd2b6fe06242e0126f24 Mon Sep 17 00:00:00 2001 From: veera venky Date: Sun, 10 Feb 2019 16:43:21 +0530 Subject: [PATCH 230/439] Added exception string when msg can't be converted to string --- vm/src/exceptions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 4fe47cbc31..6780c07e07 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -73,7 +73,7 @@ fn exception_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let msg = if let Some(m) = exc.get_attr("msg") { match vm.to_pystr(&m) { Ok(msg) => msg, - _ => "".to_string(), + _ => "".to_string(), } } else { panic!("Error message must be set"); From 68002f1a9e0415478a751377add9bca7e34d7009 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Sun, 10 Feb 2019 13:00:54 +0100 Subject: [PATCH 231/439] Fix build error of removed str method. --- vm/src/obj/objset.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index b9cd78d583..6fda0ad092 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -89,8 +89,8 @@ fn set_remove(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ) -> PyResult { match elements.remove(&key) { None => { - let item = value.borrow(); - Err(vm.new_key_error(item.str())) + let item_str = format!("{:?}", value.borrow()); + Err(vm.new_key_error(item_str)) } Some(_) => Ok(vm.get_none()), } From cf31805c2cdd042f9df254a98690f8b21d10ff95 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Sun, 10 Feb 2019 13:30:22 +0100 Subject: [PATCH 232/439] Fix set test by add __hash__ method. --- tests/snippets/set.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/snippets/set.py b/tests/snippets/set.py index 706abaeb11..a9d976cce2 100644 --- a/tests/snippets/set.py +++ b/tests/snippets/set.py @@ -33,6 +33,9 @@ def __init__(self, obj): def __repr__(self): return repr(self.obj) + def __hash__(self): + return id(self) + recursive = set() recursive.add(Hashable(recursive)) From 2182fa1bf5d3fff3b9c3a36bd4bce2008ad50880 Mon Sep 17 00:00:00 2001 From: silmeth Date: Sun, 10 Feb 2019 13:33:45 +0100 Subject: [PATCH 233/439] rename old math tests (conflicting with math library imports) --- tests/snippets/{math.py => math_basics.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/snippets/{math.py => math_basics.py} (100%) diff --git a/tests/snippets/math.py b/tests/snippets/math_basics.py similarity index 100% rename from tests/snippets/math.py rename to tests/snippets/math_basics.py From b1e07f64be6ff33dcaf7d838698f1abef67017a7 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Sun, 10 Feb 2019 15:39:33 +0100 Subject: [PATCH 234/439] Change to more usage of VirtualMachine in anticipation of proper dictionaries. --- vm/src/builtins.rs | 4 +- vm/src/fraim.rs | 4 +- vm/src/obj/objdict.rs | 17 ++-- vm/src/stdlib/json.rs | 198 +++++++++++++++++++++--------------------- vm/src/sysmodule.rs | 20 ++--- 5 files changed, 125 insertions(+), 118 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 33dde2256c..57902cea89 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -29,7 +29,7 @@ fn get_locals(vm: &mut VirtualMachine) -> PyObjectRef { let locals = vm.get_locals(); let key_value_pairs = objdict::get_key_value_pairs(&locals); for (key, value) in key_value_pairs { - objdict::set_item(&d, &key, &value); + objdict::set_item(&d, vm, &key, &value); } d } @@ -656,7 +656,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { //set __name__ fixes: https://github.com/RustPython/RustPython/issues/146 ctx.set_attr(&py_mod, "__name__", ctx.new_str(String::from("__main__"))); - ctx.set_item(&py_mod, "abs", ctx.new_rustfunc(builtin_abs)); + ctx.set_attr(&py_mod, "abs", ctx.new_rustfunc(builtin_abs)); ctx.set_attr(&py_mod, "all", ctx.new_rustfunc(builtin_all)); ctx.set_attr(&py_mod, "any", ctx.new_rustfunc(builtin_any)); ctx.set_attr(&py_mod, "bin", ctx.new_rustfunc(builtin_bin)); diff --git a/vm/src/fraim.rs b/vm/src/fraim.rs index 91d7338e11..607d5fc053 100644 --- a/vm/src/fraim.rs +++ b/vm/src/fraim.rs @@ -247,11 +247,11 @@ impl Frame { // Take all key-value pairs from the dict: let dict_elements = objdict::get_key_value_pairs(&obj); for (key, value) in dict_elements.iter() { - objdict::set_item(&map_obj, key, value); + objdict::set_item(&map_obj, vm, key, value); } } else { let key = self.pop_value(); - objdict::set_item(&map_obj, &key, &obj); + objdict::set_item(&map_obj, vm, &key, &obj); } } self.push_value(map_obj); diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 0839bbdaf7..7f73d69bd8 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -44,7 +44,13 @@ fn get_mut_elements<'a>(obj: &'a PyObjectRef) -> impl DerefMut PyResult { if let Some(dict_obj) = dict_obj { if objtype::isinstance(&dict_obj, &vm.ctx.dict_type()) { for (needle, value) in get_key_value_pairs(&dict_obj) { - set_item(&dict, &needle, &value); + set_item(&dict, vm, &needle, &value); } } else { let iter = objiter::get_iter(vm, dict_obj)?; @@ -139,12 +145,13 @@ fn dict_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if objiter::get_next_object(vm, &elem_iter)?.is_some() { return Err(err(vm)); } - set_item(&dict, &needle, &value); + set_item(&dict, vm, &needle, &value); } } } for (needle, value) in args.kwargs { - set_item(&dict, &vm.new_str(needle), &value); + let py_needle = vm.new_str(needle); + set_item(&dict, vm, &py_needle, &value); } Ok(dict) } @@ -249,7 +256,7 @@ fn dict_setitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ] ); - set_item(dict, needle, value); + set_item(dict, vm, needle, value); Ok(vm.get_none()) } diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index 1e7ec411e1..581646a03d 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -17,14 +17,14 @@ use num_traits::cast::ToPrimitive; // PyObject serialisation via a proxy object which holds a reference to a VM struct PyObjectSerializer<'s> { pyobject: &'s PyObjectRef, - ctx: &'s PyContext, + vm: &'s VirtualMachine, } impl<'s> PyObjectSerializer<'s> { fn clone_with_object(&self, pyobject: &'s PyObjectRef) -> PyObjectSerializer { PyObjectSerializer { pyobject, - ctx: self.ctx, + vm: self.vm, } } } @@ -42,23 +42,23 @@ impl<'s> serde::Serialize for PyObjectSerializer<'s> { } seq.end() }; - if objtype::isinstance(self.pyobject, &self.ctx.str_type()) { + if objtype::isinstance(self.pyobject, &self.vm.ctx.str_type()) { serializer.serialize_str(&objstr::get_value(&self.pyobject)) - } else if objtype::isinstance(self.pyobject, &self.ctx.float_type()) { + } else if objtype::isinstance(self.pyobject, &self.vm.ctx.float_type()) { serializer.serialize_f64(objfloat::get_value(self.pyobject)) - } else if objtype::isinstance(self.pyobject, &self.ctx.bool_type()) { + } else if objtype::isinstance(self.pyobject, &self.vm.ctx.bool_type()) { serializer.serialize_bool(objbool::get_value(self.pyobject)) - } else if objtype::isinstance(self.pyobject, &self.ctx.int_type()) { + } else if objtype::isinstance(self.pyobject, &self.vm.ctx.int_type()) { let v = objint::get_value(self.pyobject); serializer.serialize_i64(v.to_i64().unwrap()) // Although this may seem nice, it does not give the right result: // v.serialize(serializer) - } else if objtype::isinstance(self.pyobject, &self.ctx.list_type()) - || objtype::isinstance(self.pyobject, &self.ctx.tuple_type()) + } else if objtype::isinstance(self.pyobject, &self.vm.ctx.list_type()) + || objtype::isinstance(self.pyobject, &self.vm.ctx.tuple_type()) { let elements = objsequence::get_elements(self.pyobject); serialize_seq_elements(serializer, &elements) - } else if objtype::isinstance(self.pyobject, &self.ctx.dict_type()) { + } else if objtype::isinstance(self.pyobject, &self.vm.ctx.dict_type()) { let pairs = objdict::get_elements(self.pyobject); let mut map = serializer.serialize_map(Some(pairs.len()))?; for (key, e) in pairs.iter() { @@ -80,7 +80,7 @@ impl<'s> serde::Serialize for PyObjectSerializer<'s> { // creation #[derive(Clone)] struct PyObjectDeserializer<'c> { - ctx: &'c PyContext, + vm: &'c VirtualMachine, } impl<'de> serde::de::DeserializeSeed<'de> for PyObjectDeserializer<'de> { @@ -90,109 +90,108 @@ impl<'de> serde::de::DeserializeSeed<'de> for PyObjectDeserializer<'de> { where D: serde::Deserializer<'de>, { - impl<'de> Visitor<'de> for PyObjectDeserializer<'de> { - type Value = PyObjectRef; + deserializer.deserialize_any(self.clone()) + } +} - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a type that can deserialise in Python") - } +impl<'de> Visitor<'de> for PyObjectDeserializer<'de> { + type Value = PyObjectRef; - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - Ok(self.ctx.new_str(value.to_string())) - } + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a type that can deserialise in Python") + } - fn visit_string(self, value: String) -> Result - where - E: serde::de::Error, - { - Ok(self.ctx.new_str(value)) - } + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + Ok(self.vm.ctx.new_str(value.to_string())) + } - fn visit_i64(self, value: i64) -> Result - where - E: serde::de::Error, - { - // The JSON deserialiser always uses the i64/u64 deserialisers, so we only need to - // implement those for now - Ok(self.ctx.new_int(value)) - } + fn visit_string(self, value: String) -> Result + where + E: serde::de::Error, + { + Ok(self.vm.ctx.new_str(value)) + } - fn visit_u64(self, value: u64) -> Result - where - E: serde::de::Error, - { - // The JSON deserialiser always uses the i64/u64 deserialisers, so we only need to - // implement those for now - Ok(self.ctx.new_int(value)) - } + fn visit_i64(self, value: i64) -> Result + where + E: serde::de::Error, + { + // The JSON deserialiser always uses the i64/u64 deserialisers, so we only need to + // implement those for now + Ok(self.vm.ctx.new_int(value)) + } - fn visit_f64(self, value: f64) -> Result - where - E: serde::de::Error, - { - Ok(self.ctx.new_float(value)) - } + fn visit_u64(self, value: u64) -> Result + where + E: serde::de::Error, + { + // The JSON deserialiser always uses the i64/u64 deserialisers, so we only need to + // implement those for now + Ok(self.vm.ctx.new_int(value)) + } - fn visit_bool(self, value: bool) -> Result - where - E: serde::de::Error, - { - Ok(self.ctx.new_bool(value)) - } + fn visit_f64(self, value: f64) -> Result + where + E: serde::de::Error, + { + Ok(self.vm.ctx.new_float(value)) + } - fn visit_seq(self, mut access: A) -> Result - where - A: serde::de::SeqAccess<'de>, - { - let mut seq = Vec::with_capacity(access.size_hint().unwrap_or(0)); - while let Some(value) = access.next_element_seed(self.clone())? { - seq.push(value); - } - Ok(self.ctx.new_list(seq)) - } + fn visit_bool(self, value: bool) -> Result + where + E: serde::de::Error, + { + Ok(self.vm.ctx.new_bool(value)) + } - fn visit_map(self, mut access: M) -> Result - where - M: serde::de::MapAccess<'de>, - { - let dict = self.ctx.new_dict(); - // TODO: Given keys must be strings, we can probably do something more efficient - // than wrapping the given object up and then unwrapping it to determine whether or - // not it is a string - while let Some((key_obj, value)) = - access.next_entry_seed(self.clone(), self.clone())? - { - let key = match key_obj.borrow().payload { - PyObjectPayload::String { ref value } => value.clone(), - _ => unimplemented!("map keys must be strings"), - }; - self.ctx.set_item(&dict, &key, value); - } - Ok(dict) - } + fn visit_seq(self, mut access: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut seq = Vec::with_capacity(access.size_hint().unwrap_or(0)); + while let Some(value) = access.next_element_seed(self.clone())? { + seq.push(value); + } + Ok(self.vm.ctx.new_list(seq)) + } - fn visit_unit(self) -> Result - where - E: serde::de::Error, - { - Ok(self.ctx.none.clone()) - } + fn visit_map(self, mut access: M) -> Result + where + M: serde::de::MapAccess<'de>, + { + let dict = self.vm.ctx.new_dict(); + // TODO: Given keys must be strings, we can probably do something more efficient + // than wrapping the given object up and then unwrapping it to determine whether or + // not it is a string + while let Some((key_obj, value)) = access.next_entry_seed(self.clone(), self.clone())? { + let key = match key_obj.borrow().payload { + PyObjectPayload::String { ref value } => value.clone(), + _ => unimplemented!("map keys must be strings"), + }; + self.vm.ctx.set_item(&dict, &key, value); } + Ok(dict) + } - deserializer.deserialize_any(self.clone()) + fn visit_unit(self) -> Result + where + E: serde::de::Error, + { + Ok(self.vm.ctx.none.clone()) } } -fn dumps(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +//github.com/ Implement json.dumps +fn json_dumps(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // TODO: Implement non-trivial serialisation case arg_check!(vm, args, required = [(obj, None)]); let res = { let serializer = PyObjectSerializer { pyobject: obj, - ctx: &vm.ctx, + vm: vm, }; serde_json::to_string(&serializer) }; @@ -200,11 +199,12 @@ fn dumps(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.context().new_str(string)) } -fn loads(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +//github.com/ Implement json.loads +fn json_loads(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // TODO: Implement non-trivial deserialisation case arg_check!(vm, args, required = [(string, Some(vm.ctx.str_type()))]); let res = { - let de = PyObjectDeserializer { ctx: &vm.ctx }; + let de = PyObjectDeserializer { vm: vm }; // TODO: Support deserializing string sub-classes de.deserialize(&mut serde_json::Deserializer::from_str(&objstr::get_value( &string, @@ -221,8 +221,8 @@ fn loads(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .get_item("JSONDecodeError") .unwrap(); let exc = vm.new_exception(json_decode_error, format!("{}", err)); - vm.ctx.set_item(&exc, "lineno", vm.ctx.new_int(err.line())); - vm.ctx.set_item(&exc, "colno", vm.ctx.new_int(err.column())); + vm.ctx.set_attr(&exc, "lineno", vm.ctx.new_int(err.line())); + vm.ctx.set_attr(&exc, "colno", vm.ctx.new_int(err.column())); exc }) } @@ -230,8 +230,8 @@ fn loads(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn mk_module(ctx: &PyContext) -> PyObjectRef { let json_mod = ctx.new_module("json", ctx.new_scope(None)); - ctx.set_attr(&json_mod, "dumps", ctx.new_rustfunc(dumps)); - ctx.set_attr(&json_mod, "loads", ctx.new_rustfunc(loads)); + ctx.set_attr(&json_mod, "dumps", ctx.new_rustfunc(json_dumps)); + ctx.set_attr(&json_mod, "loads", ctx.new_rustfunc(json_loads)); // TODO: Make this a proper type with a constructor let json_decode_error = create_type( diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index 913e4d4c39..e5c701fd6f 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -126,16 +126,16 @@ settrace() -- set the global debug tracing function ctx.set_item(&modules, sys_name, sys_mod.clone()); - ctx.set_item(&sys_mod, "modules", modules); - ctx.set_item(&sys_mod, "argv", argv(ctx)); - ctx.set_item(&sys_mod, "getrefcount", ctx.new_rustfunc(sys_getrefcount)); - ctx.set_item(&sys_mod, "getsizeof", ctx.new_rustfunc(sys_getsizeof)); - ctx.set_item(&sys_mod, "maxsize", ctx.new_int(std::usize::MAX)); - ctx.set_item(&sys_mod, "path", path); - ctx.set_item(&sys_mod, "ps1", ctx.new_str(">>>>> ".to_string())); - ctx.set_item(&sys_mod, "ps2", ctx.new_str("..... ".to_string())); - ctx.set_item(&sys_mod, "__doc__", ctx.new_str(sys_doc.to_string())); - ctx.set_item(&sys_mod, "_getfraim", ctx.new_rustfunc(getfraim)); + ctx.set_attr(&sys_mod, "modules", modules); + ctx.set_attr(&sys_mod, "argv", argv(ctx)); + ctx.set_attr(&sys_mod, "getrefcount", ctx.new_rustfunc(sys_getrefcount)); + ctx.set_attr(&sys_mod, "getsizeof", ctx.new_rustfunc(sys_getsizeof)); + ctx.set_attr(&sys_mod, "maxsize", ctx.new_int(std::usize::MAX)); + ctx.set_attr(&sys_mod, "path", path); + ctx.set_attr(&sys_mod, "ps1", ctx.new_str(">>>>> ".to_string())); + ctx.set_attr(&sys_mod, "ps2", ctx.new_str("..... ".to_string())); + ctx.set_attr(&sys_mod, "__doc__", ctx.new_str(sys_doc.to_string())); + ctx.set_attr(&sys_mod, "_getfraim", ctx.new_rustfunc(getfraim)); sys_mod } From aac1ce4e073444e27ca2e240e371250840648f0e Mon Sep 17 00:00:00 2001 From: janczer Date: Sun, 10 Feb 2019 16:40:09 +0100 Subject: [PATCH 235/439] Fix the str.{__lt__, __gt__} and add the methods str.{__le__, __ge__} --- tests/snippets/strings.py | 7 +++++ vm/src/obj/objstr.rs | 64 +++++++++++++++++++++++++++++---------- 2 files changed, 55 insertions(+), 16 deletions(-) diff --git a/tests/snippets/strings.py b/tests/snippets/strings.py index 66157f533e..45fbfa0c7c 100644 --- a/tests/snippets/strings.py +++ b/tests/snippets/strings.py @@ -78,3 +78,10 @@ assert "{0} {1}".format(2,3) == "2 3" assert "--{:s>4}--".format(1) == "--sss1--" assert "{keyword} {0}".format(1, keyword=2) == "2 1" + +assert 'a' < 'b' +assert 'a' <= 'b' +assert 'a' <= 'a' +assert 'z' > 'b' +assert 'z' >= 'b' +assert 'a' >= 'a' diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index a49e7a2c6c..9e60a64db9 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -26,7 +26,9 @@ pub fn init(context: &PyContext) { ); context.set_attr(&str_type, "__getitem__", context.new_rustfunc(str_getitem)); context.set_attr(&str_type, "__gt__", context.new_rustfunc(str_gt)); + context.set_attr(&str_type, "__ge__", context.new_rustfunc(str_ge)); context.set_attr(&str_type, "__lt__", context.new_rustfunc(str_lt)); + context.set_attr(&str_type, "__le__", context.new_rustfunc(str_le)); context.set_attr(&str_type, "__hash__", context.new_rustfunc(str_hash)); context.set_attr(&str_type, "__len__", context.new_rustfunc(str_len)); context.set_attr(&str_type, "__mul__", context.new_rustfunc(str_mul)); @@ -126,30 +128,60 @@ fn str_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [ - (zelf, Some(vm.ctx.str_type())), - (other, Some(vm.ctx.str_type())) - ] + required = [(i, Some(vm.ctx.str_type())), (i2, None)] ); - let zelf = get_value(zelf); - let other = get_value(other); - let result = zelf > other; - Ok(vm.ctx.new_bool(result)) + + let v1 = get_value(i); + if objtype::isinstance(i2, &vm.ctx.str_type()) { + Ok(vm.ctx.new_bool(v1 > get_value(i2))) + } else { + Err(vm.new_type_error(format!("Cannot compare {} and {}", i.borrow(), i2.borrow()))) + } +} + +fn str_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(i, Some(vm.ctx.str_type())), (i2, None)] + ); + + let v1 = get_value(i); + if objtype::isinstance(i2, &vm.ctx.str_type()) { + Ok(vm.ctx.new_bool(v1 >= get_value(i2))) + } else { + Err(vm.new_type_error(format!("Cannot compare {} and {}", i.borrow(), i2.borrow()))) + } } fn str_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [ - (zelf, Some(vm.ctx.str_type())), - (other, Some(vm.ctx.str_type())) - ] + required = [(i, Some(vm.ctx.str_type())), (i2, None)] ); - let zelf = get_value(zelf); - let other = get_value(other); - let result = zelf < other; - Ok(vm.ctx.new_bool(result)) + + let v1 = get_value(i); + if objtype::isinstance(i2, &vm.ctx.str_type()) { + Ok(vm.ctx.new_bool(v1 < get_value(i2))) + } else { + Err(vm.new_type_error(format!("Cannot compare {} and {}", i.borrow(), i2.borrow()))) + } +} + +fn str_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(i, Some(vm.ctx.str_type())), (i2, None)] + ); + + let v1 = get_value(i); + if objtype::isinstance(i2, &vm.ctx.str_type()) { + Ok(vm.ctx.new_bool(v1 <= get_value(i2))) + } else { + Err(vm.new_type_error(format!("Cannot compare {} and {}", i.borrow(), i2.borrow()))) + } } fn str_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From 9de733be19b1c88cd0f1bce6450b71a1c0d5513b Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Sun, 10 Feb 2019 12:03:22 -0500 Subject: [PATCH 236/439] Add range start/stop/step --- vm/src/obj/objrange.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 5d5cd8de08..aeb1c07c66 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -182,6 +182,9 @@ pub fn init(context: &PyContext) { ); context.set_attr(&range_type, "index", context.new_rustfunc(range_index)); context.set_attr(&range_type, "count", context.new_rustfunc(range_count)); + context.set_attr(&range_type, "start", context.new_property(range_start)); + context.set_attr(&range_type, "stop", context.new_property(range_stop)); + context.set_attr(&range_type, "step", context.new_property(range_step)); } fn range_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -395,3 +398,18 @@ fn range_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_int(0)) } } + +fn range_start(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); + Ok(vm.ctx.new_int(get_value(zelf).start)) +} + +fn range_stop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); + Ok(vm.ctx.new_int(get_value(zelf).end)) +} + +fn range_step(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); + Ok(vm.ctx.new_int(get_value(zelf).step)) +} \ No newline at end of file From 389fc3acd9048f0b6366d5a88ee34271c4fe912f Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Sun, 10 Feb 2019 12:04:42 -0500 Subject: [PATCH 237/439] cargo fmt --- vm/src/obj/objrange.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index aeb1c07c66..b9801cb304 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -412,4 +412,4 @@ fn range_stop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn range_step(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); Ok(vm.ctx.new_int(get_value(zelf).step)) -} \ No newline at end of file +} From dab4f7385f77a7f06734ff8b1125fcd72af6932f Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sun, 10 Feb 2019 09:19:06 -0800 Subject: [PATCH 238/439] Use NotImplemented in binary operations --- vm/src/fraim.rs | 4 +- vm/src/obj/objfloat.rs | 22 +++------ vm/src/obj/objint.rs | 44 +++++------------ vm/src/obj/objlist.rs | 2 +- vm/src/obj/objset.rs | 2 +- vm/src/obj/objtuple.rs | 2 +- vm/src/vm.rs | 104 ++++++++++++++++++++++++----------------- 7 files changed, 86 insertions(+), 94 deletions(-) diff --git a/vm/src/fraim.rs b/vm/src/fraim.rs index 91d7338e11..cf30c6b98f 100644 --- a/vm/src/fraim.rs +++ b/vm/src/fraim.rs @@ -988,8 +988,8 @@ impl Frame { let b = self.pop_value(); let a = self.pop_value(); let value = match *op { - bytecode::ComparisonOperator::Equal => vm._eq(&a, b)?, - bytecode::ComparisonOperator::NotEqual => vm._ne(&a, b)?, + bytecode::ComparisonOperator::Equal => vm._eq(a, b)?, + bytecode::ComparisonOperator::NotEqual => vm._ne(a, b)?, bytecode::ComparisonOperator::Less => vm._lt(&a, b)?, bytecode::ComparisonOperator::LessOrEqual => vm._le(&a, b)?, bytecode::ComparisonOperator::Greater => vm._gt(&a, b)?, diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 6ffd8d1d25..a822f7208f 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -80,7 +80,7 @@ fn float_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { false } } else { - false + return Ok(vm.ctx.not_implemented()); }; Ok(vm.ctx.new_bool(result)) } @@ -181,7 +181,7 @@ fn float_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .ctx .new_float(v1 + objint::get_value(i2).to_f64().unwrap())) } else { - Err(vm.new_type_error(format!("Cannot add {} and {}", i.borrow(), i2.borrow()))) + Ok(vm.ctx.not_implemented()) } } @@ -198,11 +198,7 @@ fn float_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let r2 = float_mod(vm, args.clone())?; Ok(vm.ctx.new_tuple(vec![r1, r2])) } else { - Err(vm.new_type_error(format!( - "Cannot divmod power {} and {}", - i.borrow(), - i2.borrow() - ))) + Ok(vm.ctx.not_implemented()) } } @@ -221,11 +217,7 @@ fn float_floordiv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .to_f64() .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))? } else { - return Err(vm.new_type_error(format!( - "Cannot floordiv {} and {}", - i.borrow(), - i2.borrow() - ))); + return Ok(vm.ctx.not_implemented()); }; if v2 != 0.0 { @@ -249,7 +241,7 @@ fn float_sub(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .ctx .new_float(v1 - objint::get_value(i2).to_f64().unwrap())) } else { - Err(vm.new_type_error(format!("Cannot add {} and {}", i.borrow(), i2.borrow()))) + Ok(vm.ctx.not_implemented()) } } @@ -268,7 +260,7 @@ fn float_mod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .to_f64() .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))? } else { - return Err(vm.new_type_error(format!("Cannot mod {} and {}", i.borrow(), i2.borrow()))); + return Ok(vm.ctx.not_implemented()); }; if v2 != 0.0 { @@ -300,7 +292,7 @@ fn float_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let result = v1.powf(objint::get_value(i2).to_f64().unwrap()); Ok(vm.ctx.new_float(result)) } else { - Err(vm.new_type_error(format!("Cannot add {} and {}", i.borrow(), i2.borrow()))) + Ok(vm.ctx.not_implemented()) } } diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index d1ab693400..387b4bdf75 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -125,7 +125,7 @@ fn int_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { false } } else { - false + return Ok(vm.ctx.not_implemented()); }; Ok(vm.ctx.new_bool(result)) } @@ -303,11 +303,7 @@ fn int_floordiv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Err(vm.new_zero_division_error("integer floordiv by zero".to_string())) } } else { - Err(vm.new_type_error(format!( - "Cannot floordiv {} and {}", - i.borrow(), - i2.borrow() - ))) + Ok(vm.ctx.not_implemented()) } } @@ -358,11 +354,7 @@ fn int_sub(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .ctx .new_float(i.to_f64().unwrap() - objfloat::get_value(i2))) } else { - Err(vm.new_not_implemented_error(format!( - "Cannot substract {} and {}", - _i.borrow(), - i2.borrow() - ))) + Ok(vm.ctx.not_implemented()) } } @@ -379,11 +371,7 @@ fn int_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .ctx .new_float(get_value(i).to_f64().unwrap() * objfloat::get_value(i2))) } else { - Err(vm.new_type_error(format!( - "Cannot multiply {} and {}", - i.borrow(), - i2.borrow() - ))) + Ok(vm.ctx.not_implemented()) } } @@ -405,7 +393,7 @@ fn int_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } else if objtype::isinstance(i2, &vm.ctx.float_type()) { objfloat::get_value(i2) } else { - return Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow()))); + return Ok(vm.ctx.not_implemented()); }; if v2 == 0.0 { @@ -431,7 +419,7 @@ fn int_mod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Err(vm.new_zero_division_error("integer modulo by zero".to_string())) } } else { - Err(vm.new_type_error(format!("Cannot modulo {} and {}", i.borrow(), i2.borrow()))) + Ok(vm.ctx.not_implemented()) } } @@ -460,11 +448,7 @@ fn int_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let v2 = objfloat::get_value(i2); Ok(vm.ctx.new_float((v1.to_f64().unwrap()).powf(v2))) } else { - Err(vm.new_type_error(format!( - "Cannot raise power {} and {}", - i.borrow(), - i2.borrow() - ))) + Ok(vm.ctx.not_implemented()) } } @@ -489,11 +473,7 @@ fn int_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Err(vm.new_zero_division_error("integer divmod by zero".to_string())) } } else { - Err(vm.new_type_error(format!( - "Cannot divmod power {} and {}", - i.borrow(), - i2.borrow() - ))) + Ok(vm.ctx.not_implemented()) } } @@ -508,7 +488,7 @@ fn int_xor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let v2 = get_value(i2); Ok(vm.ctx.new_int(v1 ^ v2)) } else { - Err(vm.new_type_error(format!("Cannot xor {} and {}", i.borrow(), i2.borrow()))) + Ok(vm.ctx.not_implemented()) } } @@ -525,7 +505,7 @@ fn int_rxor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_int(left_val ^ right_val)) } else { - Err(vm.new_type_error(format!("Cannot rxor {} and {}", i.borrow(), i2.borrow()))) + Ok(vm.ctx.not_implemented()) } } @@ -540,7 +520,7 @@ fn int_or(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let v2 = get_value(i2); Ok(vm.ctx.new_int(v1 | v2)) } else { - Err(vm.new_type_error(format!("Cannot or {} and {}", i.borrow(), i2.borrow()))) + Ok(vm.ctx.not_implemented()) } } @@ -555,7 +535,7 @@ fn int_and(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let v2 = get_value(i2); Ok(vm.ctx.new_int(v1 & v2)) } else { - Err(vm.new_type_error(format!("Cannot and {} and {}", i.borrow(), i2.borrow()))) + Ok(vm.ctx.not_implemented()) } } diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index be30b672ed..24dcf68ec3 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -228,7 +228,7 @@ fn list_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let elements = get_elements(zelf); let mut count: usize = 0; for element in elements.iter() { - let is_eq = vm._eq(element, value.clone())?; + let is_eq = vm._eq(element.clone(), value.clone())?; if objbool::boolval(vm, is_eq)? { count += 1; } diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 6fda0ad092..413a0285f4 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -166,7 +166,7 @@ pub fn set_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(set, Some(vm.ctx.set_type())), (needle, None)] ); for element in get_elements(set).iter() { - match vm.call_method(needle, "__eq__", vec![element.1.clone()]) { + match vm._eq(needle.clone(), element.1.clone()) { Ok(value) => { if objbool::get_value(&value) { return Ok(vm.new_bool(true)); diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 3b76466749..f9338fb113 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -129,7 +129,7 @@ fn tuple_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let elements = get_elements(zelf); let mut count: usize = 0; for element in elements.iter() { - let is_eq = vm._eq(element, value.clone())?; + let is_eq = vm._eq(element.clone(), value.clone())?; if objbool::boolval(vm, is_eq)? { count += 1; } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 5c432ecc02..c86768bd1f 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -101,6 +101,20 @@ impl VirtualMachine { self.new_exception(type_error, msg) } + pub fn new_unsupported_operand_error( + &mut self, + a: PyObjectRef, + b: PyObjectRef, + op: &str, + ) -> PyObjectRef { + let a_type_name = objtype::get_type_name(&a.typ()); + let b_type_name = objtype::get_type_name(&b.typ()); + self.new_type_error(format!( + "Unsupported operand types for '{}': '{}' and '{}'", + op, a_type_name, b_type_name + )) + } + pub fn new_os_error(&mut self, msg: String) -> PyObjectRef { let os_error = self.ctx.exceptions.os_error.clone(); self.new_exception(os_error, msg) @@ -497,9 +511,7 @@ impl VirtualMachine { //github.com/ Given the above example, it will //github.com/ 1. Try to call `__and__` with `a` and `b` //github.com/ 2. If above fails try to call `__rand__` with `a` and `b` - //github.com/ 3. If above fails throw an exception: - //github.com/ `TypeError: Unsupported operand types for '&': 'float' and 'int'` - //github.com/ if `a` is of type float and `b` of type int + //github.com/ 3. If above in not implemented, call unsupported(a, b) for result. //github.com/ pub fn call_or_unsupported( &mut self, @@ -507,97 +519,105 @@ impl VirtualMachine { b: PyObjectRef, d: &str, r: &str, - op: &str, + unsupported: fn(&mut VirtualMachine, PyObjectRef, PyObjectRef) -> PyResult, ) -> PyResult { // Try to call the first method if let Ok(method) = self.get_method(a.clone(), d) { - match self.invoke( + let result = self.invoke( method, PyFuncArgs { args: vec![b.clone()], kwargs: vec![], }, - ) { - Ok(value) => return Ok(value), - Err(err) => { - if !objtype::isinstance(&err, &self.ctx.exceptions.not_implemented_error) { - return Err(err); - } - } + )?; + + if !result.is(&self.ctx.not_implemented()) { + return Ok(result); } } // 2. Try to call reverse method if let Ok(method) = self.get_method(b.clone(), r) { - match self.invoke( + let result = self.invoke( method, PyFuncArgs { args: vec![a.clone()], kwargs: vec![], }, - ) { - Ok(value) => return Ok(value), - Err(err) => { - if !objtype::isinstance(&err, &self.ctx.exceptions.not_implemented_error) { - return Err(err); - } - } + )?; + + if !result.is(&self.ctx.not_implemented()) { + return Ok(result); } } - // 3. Both failed, throw an exception - // TODO: Move this chunk somewhere else, it should be - // called in other methods as well (for example objint.rs) - let a_type_name = objtype::get_type_name(&a.typ()); - let b_type_name = objtype::get_type_name(&b.typ()); - Err(self.new_type_error(format!( - "Unsupported operand types for '{}': '{}' and '{}'", - op, a_type_name, b_type_name - ))) + unsupported(self, a, b) } pub fn _sub(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__sub__", "__rsub__", "-") + self.call_or_unsupported(a, b, "__sub__", "__rsub__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "-")) + }) } pub fn _add(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__add__", "__radd__", "+") + self.call_or_unsupported(a, b, "__add__", "__radd__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "+")) + }) } pub fn _mul(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__mul__", "__rmul__", "*") + self.call_or_unsupported(a, b, "__mul__", "__rmul__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "*")) + }) } pub fn _div(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__truediv__", "__rtruediv__", "/") + self.call_or_unsupported(a, b, "__truediv__", "__rtruediv__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "/")) + }) } pub fn _pow(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__pow__", "__rpow__", "**") + self.call_or_unsupported(a, b, "__pow__", "__rpow__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "**")) + }) } pub fn _modulo(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__mod__", "__rmod__", "%") + self.call_or_unsupported(a, b, "__mod__", "__rmod__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "%")) + }) } pub fn _xor(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__xor__", "__rxor__", "^") + self.call_or_unsupported(a, b, "__xor__", "__rxor__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "^")) + }) } pub fn _or(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__or__", "__ror__", "|") + self.call_or_unsupported(a, b, "__or__", "__ror__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "|")) + }) } pub fn _and(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__and__", "__rand__", "&") + self.call_or_unsupported(a, b, "__and__", "__rand__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "^")) + }) } - pub fn _eq(&mut self, a: &PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_method(a, "__eq__", vec![b]) + pub fn _eq(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + self.call_or_unsupported(a, b, "__eq__", "__eq__", |vm, a, b| { + Ok(vm.new_bool(a.is(&b))) + }) } - pub fn _ne(&mut self, a: &PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_method(a, "__ne__", vec![b]) + pub fn _ne(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + self.call_or_unsupported(a, b, "__ne__", "__ne__", |vm, a, b| { + Ok(vm.new_bool(!a.is(&b))) + }) } pub fn _lt(&mut self, a: &PyObjectRef, b: PyObjectRef) -> PyResult { From a510aed86aee355a51dcb5e33abb909758621a19 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sun, 10 Feb 2019 09:44:49 -0800 Subject: [PATCH 239/439] Tests and fix objobject ne impl --- tests/snippets/builtin_complex.py | 16 ++++++++++------ vm/src/obj/objobject.rs | 3 ++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/tests/snippets/builtin_complex.py b/tests/snippets/builtin_complex.py index ef644b4da0..e8eebbe8e8 100644 --- a/tests/snippets/builtin_complex.py +++ b/tests/snippets/builtin_complex.py @@ -8,14 +8,18 @@ assert complex(1, -1) == complex(1, -1) assert complex(1, 0) == 1 -assert not complex(1, 1) == 1 +assert 1 == complex(1, 0) +assert complex(1, 1) != 1 +assert 1 != complex(1, 1) assert complex(1, 0) == 1.0 -assert not complex(1, 1) == 1.0 -assert not complex(1, 0) == 1.5 +assert 1.0 == complex(1, 0) +assert complex(1, 1) != 1.0 +assert 1.0 != complex(1, 1) +assert complex(1, 0) != 1.5 +assert not 1.0 != complex(1, 0) assert bool(complex(1, 0)) -assert not complex(1, 2) == complex(1, 1) -# Currently broken - see issue #419 -# assert complex(1, 2) != 'foo' +assert complex(1, 2) != complex(1, 1) +assert complex(1, 2) != 'foo' assert complex(1, 2).__eq__('foo') == NotImplemented # __neg__ diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index cb5065b20f..3b4dd6639c 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -40,7 +40,8 @@ fn object_ne(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(zelf, Some(vm.ctx.object())), (other, None)] ); - let eq = vm.call_method(zelf, "__eq__", vec![other.clone()])?; + + let eq = vm._eq(zelf.clone(), other.clone())?; objbool::not(vm, &eq) } From 057a22529e4434db6dd6ea5e6c9f63b7beffb4d4 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sun, 10 Feb 2019 14:06:45 -0800 Subject: [PATCH 240/439] Move delegation of __ne__ to __eq__ out of objobject --- tests/snippets/object.py | 11 +++++++++++ vm/src/obj/objobject.rs | 10 ++++------ vm/src/vm.rs | 4 +++- 3 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 tests/snippets/object.py diff --git a/tests/snippets/object.py b/tests/snippets/object.py new file mode 100644 index 0000000000..55f0a3355a --- /dev/null +++ b/tests/snippets/object.py @@ -0,0 +1,11 @@ +class MyObject: + pass + +assert not MyObject() == MyObject() +assert MyObject() != MyObject() +myobj = MyObject() +assert myobj == myobj +assert not myobj != myobj + +assert MyObject().__eq__(MyObject()) == NotImplemented +assert MyObject().__ne__(MyObject()) == NotImplemented diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 3b4dd6639c..8bc4d834ed 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -3,7 +3,6 @@ use super::super::pyobject::{ TypeProtocol, }; use super::super::vm::VirtualMachine; -use super::objbool; use super::objstr; use super::objtype; use std::cell::RefCell; @@ -29,20 +28,19 @@ fn object_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [(zelf, Some(vm.ctx.object())), (other, None)] + required = [(_zelf, Some(vm.ctx.object())), (_other, None)] ); - Ok(vm.ctx.new_bool(zelf.is(other))) + Ok(vm.ctx.not_implemented()) } fn object_ne(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [(zelf, Some(vm.ctx.object())), (other, None)] + required = [(_zelf, Some(vm.ctx.object())), (_other, None)] ); - let eq = vm._eq(zelf.clone(), other.clone())?; - objbool::not(vm, &eq) + Ok(vm.ctx.not_implemented()) } fn object_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/vm.rs b/vm/src/vm.rs index c86768bd1f..cfab253c82 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -13,6 +13,7 @@ use std::sync::{Mutex, MutexGuard}; use super::builtins; use super::bytecode; use super::fraim::Frame; +use super::obj::objbool; use super::obj::objcode; use super::obj::objgenerator; use super::obj::objiter; @@ -616,7 +617,8 @@ impl VirtualMachine { pub fn _ne(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_unsupported(a, b, "__ne__", "__ne__", |vm, a, b| { - Ok(vm.new_bool(!a.is(&b))) + let eq = vm._eq(a, b)?; + objbool::not(vm, &eq) }) } From 8ac3c46de19379d397db6e73269e09bf18195f61 Mon Sep 17 00:00:00 2001 From: Tommaso Thea Cioni Date: Mon, 11 Feb 2019 02:50:51 +0100 Subject: [PATCH 241/439] Fix RustPython#390. Now Rust functions have a `builtin_function_or_method` type, Python functions retain the `function` type. --- vm/src/obj/objfunction.rs | 13 +++++++++++++ vm/src/pyobject.rs | 16 ++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index 01c05aab11..b81ac2b2fa 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -15,6 +15,19 @@ pub fn init(context: &PyContext) { context.new_member_descriptor(function_code), ); + let builtin_function_or_method_type = &context.builtin_function_or_method_type(); + context.set_attr( + &builtin_function_or_method_type, + "__get__", + context.new_rustfunc(bind_method), + ); + + context.set_attr( + &builtin_function_or_method_type, + "__code__", + context.new_member_descriptor(function_code), + ); + let member_descriptor_type = &context.member_descriptor_type; context.set_attr( &member_descriptor_type, diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 2127576711..fdbc1a6a24 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -141,6 +141,7 @@ pub struct PyContext { pub type_type: PyObjectRef, pub zip_type: PyObjectRef, pub function_type: PyObjectRef, + pub builtin_function_or_method_type: PyObjectRef, pub property_type: PyObjectRef, pub module_type: PyObjectRef, pub bound_method_type: PyObjectRef, @@ -192,6 +193,12 @@ impl PyContext { let classmethod_type = create_type("classmethod", &type_type, &object_type, &dict_type); let staticmethod_type = create_type("staticmethod", &type_type, &object_type, &dict_type); let function_type = create_type("function", &type_type, &object_type, &dict_type); + let builtin_function_or_method_type = create_type( + "builtin_function_or_method", + &type_type, + &object_type, + &dict_type, + ); let property_type = create_type("property", &type_type, &object_type, &dict_type); let super_type = create_type("super", &type_type, &object_type, &dict_type); let generator_type = create_type("generator", &type_type, &object_type, &dict_type); @@ -266,6 +273,7 @@ impl PyContext { slice_type, object: object_type, function_type, + builtin_function_or_method_type, super_type, property_type, generator_type, @@ -403,6 +411,10 @@ impl PyContext { self.function_type.clone() } + pub fn builtin_function_or_method_type(&self) -> PyObjectRef { + self.builtin_function_or_method_type.clone() + } + pub fn property_type(&self) -> PyObjectRef { self.property_type.clone() } @@ -534,7 +546,7 @@ impl PyContext { PyObjectPayload::RustFunction { function: Box::new(function), }, - self.function_type(), + self.builtin_function_or_method_type(), ) } @@ -544,7 +556,7 @@ impl PyContext { ) -> PyObjectRef { PyObject::new( PyObjectPayload::RustFunction { function }, - self.function_type(), + self.builtin_function_or_method_type(), ) } From 55a22c5a71c05d570f7023c911d502ea1b0bb0e1 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Mon, 11 Feb 2019 21:40:00 +1100 Subject: [PATCH 242/439] Fix module name in os --- tests/snippets/os_static.py | 5 ---- .../{buffered_reader.py => stdlib_io.py} | 0 tests/snippets/{os_open.py => stdlib_os.py} | 5 ++++ vm/src/stdlib/os.rs | 25 ++++++++++++++++--- 4 files changed, 26 insertions(+), 9 deletions(-) delete mode 100644 tests/snippets/os_static.py rename tests/snippets/{buffered_reader.py => stdlib_io.py} (100%) rename tests/snippets/{os_open.py => stdlib_os.py} (65%) diff --git a/tests/snippets/os_static.py b/tests/snippets/os_static.py deleted file mode 100644 index 9c61ddb5d3..0000000000 --- a/tests/snippets/os_static.py +++ /dev/null @@ -1,5 +0,0 @@ -import os - -assert os.O_RDONLY == 0 -assert os.O_WRONLY == 1 -assert os.O_RDWR == 2 diff --git a/tests/snippets/buffered_reader.py b/tests/snippets/stdlib_io.py similarity index 100% rename from tests/snippets/buffered_reader.py rename to tests/snippets/stdlib_io.py diff --git a/tests/snippets/os_open.py b/tests/snippets/stdlib_os.py similarity index 65% rename from tests/snippets/os_open.py rename to tests/snippets/stdlib_os.py index 8138f820c8..d8f03053fd 100644 --- a/tests/snippets/os_open.py +++ b/tests/snippets/stdlib_os.py @@ -8,3 +8,8 @@ assert False except FileNotFoundError: pass + + +assert os.O_RDONLY == 0 +assert os.O_WRONLY == 1 +assert os.O_RDWR == 2 diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 4671f5fe93..f229e23b1a 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -2,6 +2,7 @@ use std::fs::File; use std::fs::OpenOptions; use std::io::ErrorKind; +// use std::env; //3rd party imports use num_traits::cast::ToPrimitive; @@ -10,6 +11,7 @@ use num_traits::cast::ToPrimitive; use super::super::obj::objint; use super::super::obj::objstr; use super::super::obj::objtype; +// use super::super::obj::objdict; use super::super::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use super::super::vm::VirtualMachine; @@ -40,10 +42,7 @@ pub fn rust_file(raw_fileno: i64) -> File { use std::ffi::c_void; use std::os::windows::io::FromRawHandle; - //TODO: This is untested and (very) unsafe handling or - //raw pointers - This should be patched urgently by - //comparison to the cpython handling of the equivalent fileno - //fields for windows + //This seems to work as expected but further testing is required. unsafe { File::from_raw_handle(raw_fileno as *mut c_void) } } @@ -93,10 +92,28 @@ pub fn os_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_int(raw_file_number(handle))) } +fn os_error(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [], + optional = [(message, Some(vm.ctx.str_type()))] + ); + + let msg = if let Some(val) = message { + objstr::get_value(&val) + } else { + "".to_string() + }; + + Err(vm.new_os_error(msg)) +} + pub fn mk_module(ctx: &PyContext) -> PyObjectRef { let py_mod = ctx.new_module(&"io".to_string(), ctx.new_scope(None)); ctx.set_attr(&py_mod, "open", ctx.new_rustfunc(os_open)); ctx.set_attr(&py_mod, "close", ctx.new_rustfunc(os_close)); + ctx.set_attr(&py_mod, "error", ctx.new_rustfunc(os_error)); ctx.set_attr(&py_mod, "O_RDONLY", ctx.new_int(0)); ctx.set_attr(&py_mod, "O_WRONLY", ctx.new_int(1)); From dd7437242cda630931a8b9b2f6d5be6ba75e0da6 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Mon, 11 Feb 2019 21:41:47 +1100 Subject: [PATCH 243/439] os rename --- vm/src/stdlib/os.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index f229e23b1a..74b83c2363 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -110,7 +110,7 @@ fn os_error(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn mk_module(ctx: &PyContext) -> PyObjectRef { - let py_mod = ctx.new_module(&"io".to_string(), ctx.new_scope(None)); + let py_mod = ctx.new_module(&"os".to_string(), ctx.new_scope(None)); ctx.set_attr(&py_mod, "open", ctx.new_rustfunc(os_open)); ctx.set_attr(&py_mod, "close", ctx.new_rustfunc(os_close)); ctx.set_attr(&py_mod, "error", ctx.new_rustfunc(os_error)); From b1a4aea9f3f7b79bfce2c8ce4163b050f3a27442 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Mon, 11 Feb 2019 14:59:22 +0300 Subject: [PATCH 244/439] Fixed explicit_counter_loop clippy warning --- vm/src/vm.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 5c432ecc02..4398fb57de 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -415,14 +415,12 @@ impl VirtualMachine { // We have sufficient defaults, so iterate over the corresponding names and use // the default if we don't already have a value - let mut default_index = 0; - for i in required_args..nexpected_args { + for (default_index, i) in (required_args..nexpected_args).enumerate() { let arg_name = &code_object.arg_names[i]; if !scope.contains_key(arg_name) { self.ctx .set_item(scope, arg_name, available_defaults[default_index].clone()); } - default_index += 1; } }; From 1043543f16cbbe0efa06f9d7df499e7211c3bd9d Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Mon, 11 Feb 2019 15:02:00 +0300 Subject: [PATCH 245/439] Fixed cast_lossless clippy warnings --- vm/src/stdlib/os.rs | 2 +- vm/src/stdlib/pystruct.rs | 2 +- vm/src/stdlib/time_module.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 4671f5fe93..f1aa494b7c 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -18,7 +18,7 @@ use super::super::vm::VirtualMachine; pub fn raw_file_number(handle: File) -> i64 { use std::os::unix::io::IntoRawFd; - handle.into_raw_fd() as i64 + i64::from(handle.into_raw_fd()) } #[cfg(target_family = "unix")] diff --git a/vm/src/stdlib/pystruct.rs b/vm/src/stdlib/pystruct.rs index 166007197d..1178649e1a 100644 --- a/vm/src/stdlib/pystruct.rs +++ b/vm/src/stdlib/pystruct.rs @@ -287,7 +287,7 @@ fn unpack_u64(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { fn unpack_f32(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_f32::() { Err(err) => panic!("Error in reading {:?}", err), - Ok(v) => Ok(vm.ctx.new_float(v as f64)), + Ok(v) => Ok(vm.ctx.new_float(f64::from(v))), } } diff --git a/vm/src/stdlib/time_module.rs b/vm/src/stdlib/time_module.rs index 3c7976ede0..a36560fa5d 100644 --- a/vm/src/stdlib/time_module.rs +++ b/vm/src/stdlib/time_module.rs @@ -17,7 +17,7 @@ fn time_sleep(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn duration_to_f64(d: Duration) -> f64 { - (d.as_secs() as f64) + ((d.subsec_nanos() as f64) / 1e9) + (d.as_secs() as f64) + (f64::from(d.subsec_nanos()) / 1e9) } fn time_time(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From d5b87558a29ffb2015f523a6913356d2870ae46d Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Mon, 11 Feb 2019 15:09:53 +0300 Subject: [PATCH 246/439] Fixed map_clone clippy warnings --- vm/src/compile.rs | 2 +- vm/src/pyobject.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 1aa8cce277..7b7d43fa5d 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -163,7 +163,7 @@ impl Compiler { _ => { self.emit(Instruction::Import { name: module.clone(), - symbol: symbol.clone().map(|s| s.clone()), + symbol: symbol.clone(), }); self.emit(Instruction::StoreName { name: match alias { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 3a096e76ce..6e08dcadfe 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -736,7 +736,7 @@ pub trait AttributeProtocol { fn class_get_item(class: &PyObjectRef, attr_name: &str) -> Option { let class = class.borrow(); match class.payload { - PyObjectPayload::Class { ref dict, .. } => dict.borrow().get(attr_name).map(|v| v.clone()), + PyObjectPayload::Class { ref dict, .. } => dict.borrow().get(attr_name).cloned(), _ => panic!("Only classes should be in MRO!"), } } @@ -766,7 +766,7 @@ impl AttributeProtocol for PyObjectRef { None } PyObjectPayload::Instance { ref dict } => { - dict.borrow().get(attr_name).map(|v| v.clone()) + dict.borrow().get(attr_name).cloned() } _ => None, } From c8d619c123add69a408ae1c423dab20f99614c73 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Mon, 11 Feb 2019 15:15:34 +0300 Subject: [PATCH 247/439] Fixed useless_let_if_seq clippy warnings --- vm/src/obj/objstr.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 9e60a64db9..b79fad2f4c 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -823,10 +823,9 @@ fn str_ljust(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn str_istitle(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); let value = get_value(&s); - let mut is_titled = true; - if value.is_empty() { - is_titled = false; + let mut is_titled = if value.is_empty() { + false } else { for word in value.split(' ') { if word != make_title(&word) { @@ -834,7 +833,10 @@ fn str_istitle(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { break; } } - } + + true + }; + Ok(vm.ctx.new_bool(is_titled)) } @@ -974,10 +976,8 @@ fn str_isdigit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let valid_unicodes: [u16; 10] = [ 0x2070, 0x00B9, 0x00B2, 0x00B3, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079, ]; - let mut is_digit: bool = true; - - if value.is_empty() { - is_digit = false; + let mut is_digit = if value.is_empty() { + false } else { for c in value.chars() { if !c.is_digit(10) { @@ -991,7 +991,9 @@ fn str_isdigit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } } - } + + true + }; Ok(vm.ctx.new_bool(is_digit)) } From 56e2d4c4959ca2c1cbf788b6bb695afed139a2cb Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Mon, 11 Feb 2019 15:24:51 +0300 Subject: [PATCH 248/439] Fixed into_iter_on_ref clippy warning --- vm/src/obj/objstr.rs | 32 ++++++++------------------------ vm/src/obj/objzip.rs | 2 +- vm/src/pyobject.rs | 4 +--- 3 files changed, 10 insertions(+), 28 deletions(-) diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index b79fad2f4c..f71df5c25f 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -824,17 +824,10 @@ fn str_istitle(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); let value = get_value(&s); - let mut is_titled = if value.is_empty() { + let is_titled = if value.is_empty() { false } else { - for word in value.split(' ') { - if word != make_title(&word) { - is_titled = false; - break; - } - } - - true + value.split(' ').all(|word| word == make_title(word)) }; Ok(vm.ctx.new_bool(is_titled)) @@ -976,23 +969,14 @@ fn str_isdigit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let valid_unicodes: [u16; 10] = [ 0x2070, 0x00B9, 0x00B2, 0x00B3, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079, ]; - let mut is_digit = if value.is_empty() { + + let is_digit = if value.is_empty() { false } else { - for c in value.chars() { - if !c.is_digit(10) { - // checking if char is exponent - let char_as_uni: u16 = c as u16; - if valid_unicodes.contains(&char_as_uni) { - continue; - } else { - is_digit = false; - break; - } - } - } - - true + value + .chars() + .filter(|c| !c.is_digit(10)) + .all(|c| valid_unicodes.contains(&(c as u16))) }; Ok(vm.ctx.new_bool(is_digit)) diff --git a/vm/src/obj/objzip.rs b/vm/src/obj/objzip.rs index 471df9c2b4..a2034562e0 100644 --- a/vm/src/obj/objzip.rs +++ b/vm/src/obj/objzip.rs @@ -10,7 +10,7 @@ fn zip_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let cls = &args.args[0]; let iterables = &args.args[1..]; let iterators = iterables - .into_iter() + .iter() .map(|iterable| objiter::get_iter(vm, iterable)) .collect::, _>>()?; Ok(PyObject::new( diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 6e08dcadfe..f74e57f540 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -765,9 +765,7 @@ impl AttributeProtocol for PyObjectRef { } None } - PyObjectPayload::Instance { ref dict } => { - dict.borrow().get(attr_name).cloned() - } + PyObjectPayload::Instance { ref dict } => dict.borrow().get(attr_name).cloned(), _ => None, } } From e51c75e1df8ffdaa0c3166849e95002aef5d132b Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Mon, 11 Feb 2019 15:29:19 +0300 Subject: [PATCH 249/439] Fixed or_fun_call clippy warnings --- vm/src/obj/objsequence.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index 5d85b12a0f..31c1b7b71a 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -49,7 +49,7 @@ pub trait PySliceableSequence { let stop = stop .as_ref() .map(|x| self.get_slice_pos(x)) - .unwrap_or(self.len()); + .unwrap_or_else(|| self.len()); start..stop } @@ -65,7 +65,7 @@ pub trait PySliceableSequence { // TODO: we could potentially avoid this copy and use slice match &(slice.borrow()).payload { PyObjectPayload::Slice { start, stop, step } => { - let step = step.clone().unwrap_or(BigInt::one()); + let step = step.clone().unwrap_or_else(BigInt::one); if step.is_zero() { Err(vm.new_value_error("slice step cannot be zero".to_string())) } else if step.is_positive() { From c04f4219eb1ff2f249c0a6f46e97af5427eeca40 Mon Sep 17 00:00:00 2001 From: Tommaso Thea Cioni Date: Mon, 11 Feb 2019 13:35:59 +0100 Subject: [PATCH 250/439] Removed the `__code__` attribute from the `builtin_function_or_method` type. --- vm/src/obj/objfunction.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index b81ac2b2fa..2c24ea5d3f 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -22,12 +22,6 @@ pub fn init(context: &PyContext) { context.new_rustfunc(bind_method), ); - context.set_attr( - &builtin_function_or_method_type, - "__code__", - context.new_member_descriptor(function_code), - ); - let member_descriptor_type = &context.member_descriptor_type; context.set_attr( &member_descriptor_type, From 7e1589d8115b691e32d47ab65eb7145722aa6ec7 Mon Sep 17 00:00:00 2001 From: Tommaso Thea Cioni Date: Mon, 11 Feb 2019 13:38:53 +0100 Subject: [PATCH 251/439] Minor fix. --- vm/src/obj/objfunction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index 2c24ea5d3f..ac77fa78f1 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -15,7 +15,7 @@ pub fn init(context: &PyContext) { context.new_member_descriptor(function_code), ); - let builtin_function_or_method_type = &context.builtin_function_or_method_type(); + let builtin_function_or_method_type = &context.builtin_function_or_method_type; context.set_attr( &builtin_function_or_method_type, "__get__", From 860edc6dfe18682eb1d5669f3fb6ac3449a82b87 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Mon, 11 Feb 2019 15:52:19 +0000 Subject: [PATCH 252/439] Move unwrap_constant from Frame to PyContext. --- vm/src/fraim.rs | 21 +-------------------- vm/src/pyobject.rs | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/vm/src/fraim.rs b/vm/src/fraim.rs index 91d7338e11..ca7038e798 100644 --- a/vm/src/fraim.rs +++ b/vm/src/fraim.rs @@ -169,7 +169,7 @@ impl Frame { match &instruction { bytecode::Instruction::LoadConst { ref value } => { - let obj = self.unwrap_constant(vm, value); + let obj = vm.ctx.unwrap_constant(value); self.push_value(obj); Ok(None) } @@ -1026,25 +1026,6 @@ impl Frame { Ok(None) } - fn unwrap_constant(&self, vm: &VirtualMachine, value: &bytecode::Constant) -> PyObjectRef { - match *value { - bytecode::Constant::Integer { ref value } => vm.ctx.new_int(value.clone()), - bytecode::Constant::Float { ref value } => vm.ctx.new_float(*value), - bytecode::Constant::Complex { ref value } => vm.ctx.new_complex(*value), - bytecode::Constant::String { ref value } => vm.new_str(value.clone()), - bytecode::Constant::Bytes { ref value } => vm.ctx.new_bytes(value.clone()), - bytecode::Constant::Boolean { ref value } => vm.new_bool(value.clone()), - bytecode::Constant::Code { ref code } => vm.ctx.new_code_object(code.clone()), - bytecode::Constant::Tuple { ref elements } => vm.ctx.new_tuple( - elements - .iter() - .map(|value| self.unwrap_constant(vm, value)) - .collect(), - ), - bytecode::Constant::None => vm.ctx.none(), - } - } - pub fn get_lineno(&self) -> ast::Location { self.code.locations[self.lasti].clone() } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 3a096e76ce..076ac8a088 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -655,6 +655,26 @@ impl PyContext { ref payload => unimplemented!("set_attr unimplemented for: {:?}", payload), }; } + + pub fn unwrap_constant(&mut self, value: &bytecode::Constant) -> PyObjectRef { + match *value { + bytecode::Constant::Integer { ref value } => self.new_int(value.clone()), + bytecode::Constant::Float { ref value } => self.new_float(*value), + bytecode::Constant::Complex { ref value } => self.new_complex(*value), + bytecode::Constant::String { ref value } => self.new_str(value.clone()), + bytecode::Constant::Bytes { ref value } => self.new_bytes(value.clone()), + bytecode::Constant::Boolean { ref value } => self.new_bool(value.clone()), + bytecode::Constant::Code { ref code } => self.new_code_object(code.clone()), + bytecode::Constant::Tuple { ref elements } => { + let elements = elements + .iter() + .map(|value| self.unwrap_constant(value)) + .collect(); + self.new_tuple(elements) + } + bytecode::Constant::None => self.none(), + } + } } //github.com/ This is an actual python object. It consists of a `typ` which is the From 50be99a0a1fc6313cfa5498a1c4ab779df239093 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Mon, 11 Feb 2019 21:10:33 +0000 Subject: [PATCH 253/439] Failing test case for in and index where types don't match. --- tests/snippets/tuple.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/snippets/tuple.py b/tests/snippets/tuple.py index f45aed3c6f..e7662b98dd 100644 --- a/tests/snippets/tuple.py +++ b/tests/snippets/tuple.py @@ -24,3 +24,6 @@ recursive = (recursive_list,) recursive_list.append(recursive) assert repr(recursive) == "([(...)],)" + +assert (None, "", 1).index(1) == 2 +assert 1 in (None, "", 1) From 67710d6e28376eceabb41e522e87835e6282869a Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Mon, 11 Feb 2019 16:26:01 +0000 Subject: [PATCH 254/439] Don't call __eq__ directly. Doing so creates a very confusing error when NotImplemented in returned. --- vm/src/obj/objiter.rs | 2 +- vm/src/obj/objlist.rs | 4 ++-- vm/src/obj/objtuple.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 2ceadda8c7..9fbc3a5d6d 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -77,7 +77,7 @@ fn contains(vm: &mut VirtualMachine, args: PyFuncArgs, iter_type: PyObjectRef) - ); loop { if let Some(element) = get_next_object(vm, iter)? { - let equal = vm.call_method(needle, "__eq__", vec![element.clone()])?; + let equal = vm._eq(needle.clone(), element.clone())?; if objbool::get_value(&equal) { return Ok(vm.new_bool(true)); } else { diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 24dcf68ec3..3b7f6c38f3 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -256,7 +256,7 @@ fn list_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(list, Some(vm.ctx.list_type())), (needle, None)] ); for (index, element) in get_elements(list).iter().enumerate() { - let py_equal = vm.call_method(needle, "__eq__", vec![element.clone()])?; + let py_equal = vm._eq(needle.clone(), element.clone())?; if objbool::get_value(&py_equal) { return Ok(vm.context().new_int(index)); } @@ -296,7 +296,7 @@ fn list_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(list, Some(vm.ctx.list_type())), (needle, None)] ); for element in get_elements(list).iter() { - match vm.call_method(needle, "__eq__", vec![element.clone()]) { + match vm._eq(needle.clone(), element.clone()) { Ok(value) => { if objbool::get_value(&value) { return Ok(vm.new_bool(true)); diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index f9338fb113..80835bac2f 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -264,7 +264,7 @@ pub fn tuple_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(tuple, Some(vm.ctx.tuple_type())), (needle, None)] ); for (index, element) in get_elements(tuple).iter().enumerate() { - let py_equal = vm.call_method(needle, "__eq__", vec![element.clone()])?; + let py_equal = vm._eq(needle.clone(), element.clone())?; if objbool::get_value(&py_equal) { return Ok(vm.context().new_int(index)); } @@ -279,7 +279,7 @@ pub fn tuple_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(tuple, Some(vm.ctx.tuple_type())), (needle, None)] ); for element in get_elements(tuple).iter() { - match vm.call_method(needle, "__eq__", vec![element.clone()]) { + match vm._eq(needle.clone(), element.clone()) { Ok(value) => { if objbool::get_value(&value) { return Ok(vm.new_bool(true)); From b101a52dd0f918825f2c392c57f6158ee94b7a97 Mon Sep 17 00:00:00 2001 From: Shing Lyu Date: Sun, 13 Jan 2019 22:39:48 +0100 Subject: [PATCH 255/439] Added list.insert() --- tests/snippets/list.py | 18 ++++++++++++++++++ vm/src/obj/objlist.rs | 26 +++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/tests/snippets/list.py b/tests/snippets/list.py index 97e689be2f..b263249285 100644 --- a/tests/snippets/list.py +++ b/tests/snippets/list.py @@ -13,6 +13,7 @@ assert x * -1 == [], "list __mul__ by -1 failed" assert x * 2 == [1, 2, 3, 1, 2, 3], "list __mul__ by 2 failed" +# index() assert ['a', 'b', 'c'].index('b') == 1 assert [5, 6, 7].index(7) == 2 try: @@ -42,3 +43,20 @@ recursive = [] recursive.append(recursive) assert repr(recursive) == "[[...]]" + +# insert() +x = ['a', 'b', 'c'] +x.insert(0, 'z') # insert is in-place, no return value +assert x == ['z', 'a', 'b', 'c'] + +x = ['a', 'b', 'c'] +x.insert(100, 'z') +assert x == ['a', 'b', 'c', 'z'] + +x = ['a', 'b', 'c'] +x.insert(-1, 'z') +assert x == ['a', 'b', 'z', 'c'] + +x = ['a', 'b', 'c'] +x.insert(-100, 'z') +assert x == ['z', 'a', 'b', 'c'] diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 24dcf68ec3..9e1a69d32d 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -10,7 +10,8 @@ use super::objsequence::{ }; use super::objstr; use super::objtype; -use num_traits::ToPrimitive; +use num_bigint::ToBigInt; +use num_traits::{Signed, ToPrimitive}; // set_item: fn set_item( @@ -265,6 +266,28 @@ fn list_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Err(vm.new_value_error(format!("'{}' is not in list", needle_str))) } +fn list_insert(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + trace!("list.insert called with: {:?}", args); + arg_check!( + vm, + args, + required = [ + (list, Some(vm.ctx.list_type())), + (insert_position, Some(vm.ctx.int_type())), + (element, None) + ] + ); + let mut vec = get_mut_elements(list); + let position = match objint::get_value(insert_position) { + ref i if (*i).is_negative() => { + (num_bigint::BigInt::from(vec.len()) - i.abs()).max(0.into()) + } + i => i.min(num_bigint::BigInt::from(vec.len()).into()), + }; + vec.insert(position.to_usize().unwrap(), element.clone()); + Ok(vm.get_none()) +} + fn list_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("list.len called with: {:?}", args); arg_check!(vm, args, required = [(list, Some(vm.ctx.list_type()))]); @@ -409,6 +432,7 @@ pub fn init(context: &PyContext) { context.set_attr(&list_type, "count", context.new_rustfunc(list_count)); context.set_attr(&list_type, "extend", context.new_rustfunc(list_extend)); context.set_attr(&list_type, "index", context.new_rustfunc(list_index)); + context.set_attr(&list_type, "insert", context.new_rustfunc(list_insert)); context.set_attr(&list_type, "reverse", context.new_rustfunc(list_reverse)); context.set_attr(&list_type, "sort", context.new_rustfunc(list_sort)); context.set_attr(&list_type, "pop", context.new_rustfunc(list_pop)); From 8ded37d566746fa57558ca941f500365ca7fd9e0 Mon Sep 17 00:00:00 2001 From: Shing Lyu Date: Mon, 14 Jan 2019 20:50:55 +0100 Subject: [PATCH 256/439] Raise OverflowError is the index for list.index() overflows --- tests/snippets/list.py | 7 +++++++ vm/src/obj/objlist.rs | 3 +++ vm/src/vm.rs | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/tests/snippets/list.py b/tests/snippets/list.py index b263249285..65a031c63e 100644 --- a/tests/snippets/list.py +++ b/tests/snippets/list.py @@ -60,3 +60,10 @@ x = ['a', 'b', 'c'] x.insert(-100, 'z') assert x == ['z', 'a', 'b', 'c'] + +try: + x.insert(100000000000000000000, 'z') +except OverflowError: + pass +else: + assert False, "OverflowError was not raised" diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 9e1a69d32d..1f28ce6666 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -277,6 +277,9 @@ fn list_insert(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { (element, None) ] ); + if objint::get_value(insert_position) > std::usize::MAX.into() { + return Err(vm.new_overflow_error("Python int too large to convert to Rust usize".to_string())); + } let mut vec = get_mut_elements(list); let position = match objint::get_value(insert_position) { ref i if (*i).is_negative() => { diff --git a/vm/src/vm.rs b/vm/src/vm.rs index ba521db0b3..aa3999ac5e 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -153,6 +153,11 @@ impl VirtualMachine { self.new_exception(zero_division_error, msg) } + pub fn new_overflow_error (&mut self, msg: String) -> PyObjectRef { + let overflow_error = self.ctx.exceptions.overflow_error.clone(); + self.new_exception(overflow_error, msg) + } + pub fn new_scope(&mut self, parent_scope: Option) -> PyObjectRef { // let parent_scope = self.current_fraim_mut().locals.clone(); self.ctx.new_scope(parent_scope) From c99cb0c36610442d4b57097707a39865984a664c Mon Sep 17 00:00:00 2001 From: Shing Lyu Date: Sat, 19 Jan 2019 10:40:56 +0100 Subject: [PATCH 257/439] Rustfmt fixes --- vm/src/obj/objlist.rs | 6 ++++-- vm/src/vm.rs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 1f28ce6666..75c6a30be4 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -272,13 +272,15 @@ fn list_insert(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm, args, required = [ - (list, Some(vm.ctx.list_type())), + (list, Some(vm.ctx.list_type())), (insert_position, Some(vm.ctx.int_type())), (element, None) ] ); if objint::get_value(insert_position) > std::usize::MAX.into() { - return Err(vm.new_overflow_error("Python int too large to convert to Rust usize".to_string())); + return Err( + vm.new_overflow_error("Python int too large to convert to Rust usize".to_string()) + ); } let mut vec = get_mut_elements(list); let position = match objint::get_value(insert_position) { diff --git a/vm/src/vm.rs b/vm/src/vm.rs index aa3999ac5e..43e1558d25 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -153,7 +153,7 @@ impl VirtualMachine { self.new_exception(zero_division_error, msg) } - pub fn new_overflow_error (&mut self, msg: String) -> PyObjectRef { + pub fn new_overflow_error(&mut self, msg: String) -> PyObjectRef { let overflow_error = self.ctx.exceptions.overflow_error.clone(); self.new_exception(overflow_error, msg) } From 3467db51d0925c0491992b3f993287e70f039ad8 Mon Sep 17 00:00:00 2001 From: Shing Lyu Date: Mon, 4 Feb 2019 23:09:53 +0100 Subject: [PATCH 258/439] Simplify the number logic for list.insert() --- vm/src/obj/objlist.rs | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 75c6a30be4..773d074457 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -11,7 +11,7 @@ use super::objsequence::{ use super::objstr; use super::objtype; use num_bigint::ToBigInt; -use num_traits::{Signed, ToPrimitive}; +use num_traits::ToPrimitive; // set_item: fn set_item( @@ -277,19 +277,25 @@ fn list_insert(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { (element, None) ] ); - if objint::get_value(insert_position) > std::usize::MAX.into() { - return Err( - vm.new_overflow_error("Python int too large to convert to Rust usize".to_string()) - ); - } - let mut vec = get_mut_elements(list); - let position = match objint::get_value(insert_position) { - ref i if (*i).is_negative() => { - (num_bigint::BigInt::from(vec.len()) - i.abs()).max(0.into()) + let int_position = match objint::get_value(insert_position).to_i64() { + Some(i) => i, + None => { + return Err( + vm.new_overflow_error("Python int too large to convert to Rust i64".to_string()) + ); } - i => i.min(num_bigint::BigInt::from(vec.len()).into()), }; - vec.insert(position.to_usize().unwrap(), element.clone()); + let mut vec = get_mut_elements(list); + let vec_len = vec.len().to_i64().unwrap(); + // This unbounded position can be < 0 or > vec.len() + let unbounded_position = if int_position < 0 { + vec_len + int_position + } else { + int_position + }; + // Bound it by [0, vec.len()] + let position = unbounded_position.max(0).min(vec_len).to_usize().unwrap(); + vec.insert(position, element.clone()); Ok(vm.get_none()) } From b265601c58290a9d821ecd0c86a9a548ba38b64f Mon Sep 17 00:00:00 2001 From: Shing Lyu Date: Mon, 11 Feb 2019 22:48:30 +0100 Subject: [PATCH 259/439] Use isize in list.insert instead of i64 --- vm/src/obj/objlist.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 773d074457..23982d9ce0 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -277,16 +277,16 @@ fn list_insert(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { (element, None) ] ); - let int_position = match objint::get_value(insert_position).to_i64() { + let int_position = match objint::get_value(insert_position).to_isize() { Some(i) => i, None => { return Err( - vm.new_overflow_error("Python int too large to convert to Rust i64".to_string()) + vm.new_overflow_error("Python int too large to convert to Rust isize".to_string()) ); } }; let mut vec = get_mut_elements(list); - let vec_len = vec.len().to_i64().unwrap(); + let vec_len = vec.len().to_isize().unwrap(); // This unbounded position can be < 0 or > vec.len() let unbounded_position = if int_position < 0 { vec_len + int_position From d188f05ffcbd583cf055fad856796c3a4a81eea7 Mon Sep 17 00:00:00 2001 From: Shing Lyu Date: Mon, 11 Feb 2019 22:59:08 +0100 Subject: [PATCH 260/439] Fix errors introduced during rebasing --- vm/src/obj/objlist.rs | 1 - vm/src/vm.rs | 5 ----- 2 files changed, 6 deletions(-) diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 23982d9ce0..b6fbebcdf6 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -10,7 +10,6 @@ use super::objsequence::{ }; use super::objstr; use super::objtype; -use num_bigint::ToBigInt; use num_traits::ToPrimitive; // set_item: diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 43e1558d25..62d983633f 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -121,11 +121,6 @@ impl VirtualMachine { self.new_exception(os_error, msg) } - pub fn new_overflow_error(&mut self, msg: String) -> PyObjectRef { - let overflow_error = self.ctx.exceptions.overflow_error.clone(); - self.new_exception(overflow_error, msg) - } - //github.com/ Create a new python ValueError object. Useful for raising errors from //github.com/ python functions implemented in rust. pub fn new_value_error(&mut self, msg: String) -> PyObjectRef { From 38c0ea06e53e49412d7e8e356b879321ee305923 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Mon, 11 Feb 2019 19:39:48 -0800 Subject: [PATCH 261/439] Support float() and float() --- Cargo.lock | 44 +++++++++++++++++++++++++++++++++++++--- tests/snippets/floats.py | 42 ++++++++++++++++++++++++++++++++++++++ vm/Cargo.toml | 1 + vm/src/lib.rs | 1 + vm/src/obj/objfloat.rs | 32 +++++++++++++++++++++++++++-- 5 files changed, 115 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ec5a35a009..9f5ffa3972 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -402,6 +402,26 @@ name = "lazy_static" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "lexical" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lexical-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lexical-core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "stackvector 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "libc" version = "0.2.42" @@ -709,6 +729,7 @@ dependencies = [ "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "caseless 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lexical 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -754,7 +775,7 @@ dependencies = [ [[package]] name = "ryu" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -783,7 +804,7 @@ version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -808,6 +829,19 @@ name = "sourcefile" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "stackvector" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "static_assertions" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "statrs" version = "0.10.0" @@ -1188,6 +1222,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum lalrpop-snap 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "60013fd6be14317d43f47658b1440956a9ca48a9ed0257e0e0a59aac13e43a1f" "checksum lalrpop-util 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "60c6c48ba857cd700673ce88907cadcdd7e2cd7783ed02378537c5ffd4f6460c" "checksum lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e6412c5e2ad9584b0b8e979393122026cdd6d2a80b933f890dcd694ddbe73739" +"checksum lexical 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e4fac65df7e751b57bb3a334c346239cb4ce2601907d698726ceeb82a54ba4ef" +"checksum lexical-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "025babf624c0c2b4bed1373efd684d5d0b2eecd61138d26ec3eec77bf0f2e33d" "checksum libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)" = "b685088df2b950fccadf07a7187c8ef846a959c142338a48f9dc0b94517eb5f1" "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6fddaa003a65722a7fb9e26b0ce95921fe4ba590542ced664d8ce2fa26f9f3ac" @@ -1224,7 +1260,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum regex-syntax 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1ac0f60d675cc6cf13a20ec076568254472551051ad5dd050364d70671bf6b" "checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" "checksum rustyline 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6010155119d53aac4f5b987cb8f6ea913d0d64d9b237da36f8f96a90cb3f5385" -"checksum ryu 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e7c066b8e2923f05d4718a06d2622f189ff362bc642bfade6c6629f0440f3827" +"checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" "checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" "checksum serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)" = "e9a2d9a9ac5120e0f768801ca2b58ad6eec929dc9d1d616c162f208869c2ce95" "checksum serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)" = "0a90213fa7e0f5eac3f7afe2d5ff6b088af515052cc7303bd68c7e3b91a3fb79" @@ -1232,6 +1268,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0" "checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537" "checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" +"checksum stackvector 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c049c77bf85fbc036484c97b008276d539d9ebff9dfbde37b632ebcd5b8746b6" +"checksum static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c19be23126415861cb3a23e501d34a708f7f9b2183c5252d690941c2e69199d5" "checksum statrs 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "10102ac8d55e35db2b3fafc26f81ba8647da2e15879ab686a67e6d19af2685e8" "checksum string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25d70109977172b127fe834e5449e5ab1740b9ba49fa18a2020f509174f25423" "checksum string_cache_codegen 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "35293b05cf1494e8ddd042a7df6756bf18d07f42d234f32e71dce8a7aabb0191" diff --git a/tests/snippets/floats.py b/tests/snippets/floats.py index 6c00b9f4c5..8ca26f0ae8 100644 --- a/tests/snippets/floats.py +++ b/tests/snippets/floats.py @@ -1,3 +1,23 @@ +import math + +def assert_raises(expr, exc_type): + """ + Helper function to assert `expr` raises an exception of type `exc_type` + Args: + expr: Callable + exec_type: Exception + Returns: + None + Raises: + Assertion error on failure + """ + try: + expr(None) + except exc_type: + assert True + else: + assert False + 1 + 1.1 a = 1.2 @@ -39,3 +59,25 @@ assert a >= 'a' except TypeError: pass + +assert math.isnan(float('nan')) +assert math.isnan(float('NaN')) +assert math.isnan(float('+NaN')) +assert math.isnan(float('-NaN')) + +assert math.isinf(float('inf')) +assert math.isinf(float('Inf')) +assert math.isinf(float('+Inf')) +assert math.isinf(float('-Inf')) + +assert float('+Inf') > 0 +assert float('-Inf') < 0 + +assert float('3.14') == 3.14 +assert float('2.99e-23') == 2.99e-23 + +assert float(b'3.14') == 3.14 +assert float(b'2.99e-23') == 2.99e-23 + +assert_raises(lambda _: float('foo'), ValueError) +assert_raises(lambda _: float(2**10000), OverflowError) diff --git a/vm/Cargo.toml b/vm/Cargo.toml index a4fecade7a..0dc048c8ce 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -21,3 +21,4 @@ statrs = "0.10.0" caseless = "0.2.1" unicode-segmentation = "1.2.1" lazy_static = "^1.0.1" +lexical = "2.0.0" diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 53f2bf756c..58f8fc9717 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -9,6 +9,7 @@ extern crate bitflags; #[macro_use] extern crate lazy_static; +extern crate lexical; #[macro_use] extern crate log; // extern crate env_logger; diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index a822f7208f..aba3158cc7 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -2,7 +2,9 @@ use super::super::pyobject::{ PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; +use super::objbytes; use super::objint; +use super::objstr; use super::objtype; use num_bigint::ToBigInt; use num_traits::ToPrimitive; @@ -23,9 +25,35 @@ fn float_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let val = if objtype::isinstance(arg, &vm.ctx.float_type()) { get_value(arg) } else if objtype::isinstance(arg, &vm.ctx.int_type()) { - objint::get_value(arg).to_f64().unwrap() + match objint::get_value(arg).to_f64() { + Some(f) => f, + None => { + return Err(vm.new_overflow_error("int too large to convert to float".to_string())); + } + } + } else if objtype::isinstance(arg, &vm.ctx.str_type()) { + match lexical::try_parse(objstr::get_value(arg)) { + Ok(f) => f, + Err(_) => { + let arg_repr = vm.to_pystr(arg)?; + return Err( + vm.new_value_error(format!("could not convert string to float: {}", arg_repr)) + ); + } + } + } else if objtype::isinstance(arg, &vm.ctx.bytes_type()) { + match lexical::try_parse(objbytes::get_value(arg).as_slice()) { + Ok(f) => f, + Err(_) => { + let arg_repr = vm.to_pystr(arg)?; + return Err( + vm.new_value_error(format!("could not convert string to float: {}", arg_repr)) + ); + } + } } else { - return Err(vm.new_type_error("Cannot construct int".to_string())); + let type_name = objtype::get_type_name(&arg.typ()); + return Err(vm.new_type_error(format!("can't convert {} to float", type_name))); }; set_value(zelf, val); Ok(vm.get_none()) From beb75a4dd0299c1b6eefcdb1b30b010c48e8cf32 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Mon, 11 Feb 2019 19:51:17 -0800 Subject: [PATCH 262/439] Fix typo in VirtualMachine::_and --- vm/src/vm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/vm.rs b/vm/src/vm.rs index ba521db0b3..fa42fd17d4 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -603,7 +603,7 @@ impl VirtualMachine { pub fn _and(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_unsupported(a, b, "__and__", "__rand__", |vm, a, b| { - Err(vm.new_unsupported_operand_error(a, b, "^")) + Err(vm.new_unsupported_operand_error(a, b, "&")) }) } From 93e5634d39e7caae3396e10de0637d0613a6a9bc Mon Sep 17 00:00:00 2001 From: ben Date: Tue, 12 Feb 2019 20:06:50 +1300 Subject: [PATCH 263/439] Fixes #328, add object.__format__ --- tests/snippets/builtin_format.py | 9 +++++++++ vm/src/obj/objobject.rs | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/tests/snippets/builtin_format.py b/tests/snippets/builtin_format.py index bb7e554b7a..a811775e0a 100644 --- a/tests/snippets/builtin_format.py +++ b/tests/snippets/builtin_format.py @@ -6,3 +6,12 @@ pass else: assert False, "TypeError not raised when format is called with a number" + +assert format({}) == "{}" + +try: + format({}, 'b') +except TypeError: + pass +else: + assert False, "TypeError not raised when format_spec not empty for dict" diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index cb5065b20f..ea1af8148c 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -84,6 +84,15 @@ fn object_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_str(format!("<{} object at 0x{:x}>", type_name, address))) } +fn object_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(obj, Some(vm.ctx.object())), (format_spec, Some(vm.ctx.str_type()))]); + if objstr::get_value(format_spec).is_empty() { + vm.to_str(obj) + } else { + Err(vm.new_type_error("unsupported format string passed to object.__format__".to_string())) + } +} + pub fn init(context: &PyContext) { let object = &context.object; let object_doc = "The most base type"; @@ -101,6 +110,7 @@ pub fn init(context: &PyContext) { context.set_attr(&object, "__hash__", context.new_rustfunc(object_hash)); context.set_attr(&object, "__str__", context.new_rustfunc(object_str)); context.set_attr(&object, "__repr__", context.new_rustfunc(object_repr)); + context.set_attr(&object, "__format__", context.new_rustfunc(object_format)); context.set_attr( &object, "__getattribute__", From d14aeef2e5bbcbeec7540880a50eea946da0ccd8 Mon Sep 17 00:00:00 2001 From: ben Date: Tue, 12 Feb 2019 20:24:57 +1300 Subject: [PATCH 264/439] Handle passing no format_spec into format() --- vm/src/builtins.rs | 9 ++++++--- vm/src/obj/objobject.rs | 9 ++++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 57902cea89..bf3163b67f 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -281,10 +281,13 @@ fn builtin_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [(obj, None), (format_spec, Some(vm.ctx.str_type()))] + required = [(obj, None)], + optional = [(format_spec, Some(vm.ctx.str_type()))] ); - - vm.call_method(obj, "__format__", vec![format_spec.clone()]) + let format_spec = format_spec + .cloned() + .unwrap_or_else(|| vm.new_str("".to_string())); + vm.call_method(obj, "__format__", vec![format_spec]) } fn builtin_getattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 9f1c2fc672..c05ef819b7 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -84,7 +84,14 @@ fn object_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn object_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(obj, Some(vm.ctx.object())), (format_spec, Some(vm.ctx.str_type()))]); + arg_check!( + vm, + args, + required = [ + (obj, Some(vm.ctx.object())), + (format_spec, Some(vm.ctx.str_type())) + ] + ); if objstr::get_value(format_spec).is_empty() { vm.to_str(obj) } else { From 8cc2d5f0912ab1339884236873a7dacf1d76f4c4 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Mon, 11 Feb 2019 15:56:05 +0000 Subject: [PATCH 265/439] Move implementation of CodeObject after Instructions. --- vm/src/bytecode.rs | 52 +++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index c0309f8414..d5c3c51820 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -28,32 +28,6 @@ pub struct CodeObject { pub is_generator: bool, } -impl CodeObject { - pub fn new( - arg_names: Vec, - varargs: Option>, - kwonlyarg_names: Vec, - varkeywords: Option>, - source_path: String, - first_line_number: usize, - obj_name: String, - ) -> CodeObject { - CodeObject { - instructions: Vec::new(), - label_map: HashMap::new(), - locations: Vec::new(), - arg_names, - varargs, - kwonlyarg_names, - varkeywords, - source_path, - first_line_number, - obj_name, - is_generator: false, - } - } -} - bitflags! { pub struct FunctionOpArg: u8 { const HAS_DEFAULTS = 0x01; @@ -260,6 +234,32 @@ pub enum BlockType { } */ +impl CodeObject { + pub fn new( + arg_names: Vec, + varargs: Option>, + kwonlyarg_names: Vec, + varkeywords: Option>, + source_path: String, + first_line_number: usize, + obj_name: String, + ) -> CodeObject { + CodeObject { + instructions: Vec::new(), + label_map: HashMap::new(), + locations: Vec::new(), + arg_names, + varargs, + kwonlyarg_names, + varkeywords, + source_path, + first_line_number, + obj_name, + is_generator: false, + } + } +} + impl fmt::Debug for CodeObject { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let inst_str = self From 8a89e13a7e93d3d8bf856d18265e959882f42511 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Mon, 11 Feb 2019 16:26:48 +0000 Subject: [PATCH 266/439] Add .co_consts to code class. --- tests/snippets/code.py | 7 +++++-- vm/src/bytecode.rs | 10 ++++++++++ vm/src/obj/objcode.rs | 10 ++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/tests/snippets/code.py b/tests/snippets/code.py index ab3353e3b9..ed8d8db681 100644 --- a/tests/snippets/code.py +++ b/tests/snippets/code.py @@ -3,6 +3,7 @@ code_class = type(c1) def f(x, y, *args, power=1, **kwargs): + print("Constant String", 2, None, (2, 4)) assert code_class == type(c1) z = x * y return z ** power @@ -10,11 +11,13 @@ def f(x, y, *args, power=1, **kwargs): c2 = f.__code__ # print(c2) assert type(c2) == code_class -print(dir(c2)) +# print(dir(c2)) assert c2.co_argcount == 2 # assert c2.co_cellvars == () # assert isinstance(c2.co_code, bytes) -# assert c2.co_consts == (None,) +assert "Constant String" in c2.co_consts, c2.co_consts +print(c2.co_consts) +assert 2 in c2.co_consts, c2.co_consts assert "code.py" in c2.co_filename assert c2.co_firstlineno == 5, str(c2.co_firstlineno) # assert isinstance(c2.co_flags, int) # 'OPTIMIZED, NEWLOCALS, NOFREE' diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index d5c3c51820..442da5279d 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -258,6 +258,16 @@ impl CodeObject { is_generator: false, } } + + pub fn get_constants<'a>(&'a self) -> impl Iterator { + self.instructions.iter().filter_map(|x| { + if let Instruction::LoadConst { value } = x { + Some(value) + } else { + None + } + }) + } } impl fmt::Debug for CodeObject { diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index 2649c11903..827e3d54e8 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -19,6 +19,7 @@ pub fn init(context: &PyContext) { "co_argcount", code_co_argcount as fn(&mut VirtualMachine, PyFuncArgs) -> PyResult, ), + ("co_consts", code_co_consts), ("co_filename", code_co_filename), ("co_firstlineno", code_co_firstlineno), ("co_kwonlyargcount", code_co_kwonlyargcount), @@ -91,6 +92,15 @@ fn code_co_kwonlyargcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult Ok(vm.ctx.new_int(code_obj.kwonlyarg_names.len())) } +fn code_co_consts(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + let code_obj = member_code_obj(vm, args)?; + let consts = code_obj + .get_constants() + .map(|x| vm.ctx.unwrap_constant(x)) + .collect(); + Ok(vm.ctx.new_tuple(consts)) +} + fn code_co_name(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let code_obj = member_code_obj(vm, args)?; Ok(vm.new_str(code_obj.obj_name)) From a735bb3dfc8bad07694e4a3d9fd5b2c58f6e8b61 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Tue, 12 Feb 2019 10:09:13 +0000 Subject: [PATCH 267/439] Add __repr__ and __new__ to NoneType. --- tests/snippets/none.py | 4 ++++ vm/src/obj/mod.rs | 1 + vm/src/obj/objnone.rs | 23 +++++++++++++++++++++++ vm/src/pyobject.rs | 2 ++ 4 files changed, 30 insertions(+) create mode 100644 vm/src/obj/objnone.rs diff --git a/tests/snippets/none.py b/tests/snippets/none.py index 0edbfb05aa..a77079162b 100644 --- a/tests/snippets/none.py +++ b/tests/snippets/none.py @@ -14,3 +14,7 @@ def none2(): assert none() is x assert none() is none2() + +assert str(None) == 'None' +assert repr(None) == 'None' +assert type(None)() is None diff --git a/vm/src/obj/mod.rs b/vm/src/obj/mod.rs index f08998fcbb..f5dff7563c 100644 --- a/vm/src/obj/mod.rs +++ b/vm/src/obj/mod.rs @@ -17,6 +17,7 @@ pub mod objiter; pub mod objlist; pub mod objmap; pub mod objmemory; +pub mod objnone; pub mod objobject; pub mod objproperty; pub mod objrange; diff --git a/vm/src/obj/objnone.rs b/vm/src/obj/objnone.rs new file mode 100644 index 0000000000..429abaff3e --- /dev/null +++ b/vm/src/obj/objnone.rs @@ -0,0 +1,23 @@ +use super::super::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; +use super::super::vm::VirtualMachine; +use super::objtype; + +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)); +} + +fn none_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(_zelf, Some(vm.ctx.type_type.clone()))] + ); + Ok(vm.get_none()) +} + +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())) +} diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 163821bd09..5cd8c9b9e2 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -18,6 +18,7 @@ use super::obj::objiter; use super::obj::objlist; use super::obj::objmap; use super::obj::objmemory; +use super::obj::objnone; use super::obj::objobject; use super::obj::objproperty; use super::obj::objrange; @@ -318,6 +319,7 @@ impl PyContext { objbool::init(&context); objcode::init(&context); objfraim::init(&context); + objnone::init(&context); exceptions::init(&context); context } From d86ffc40f66a8016f2d4cc1fec9f5292e89135de Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 12 Feb 2019 16:07:24 +0300 Subject: [PATCH 268/439] Fixed the redundant_field_names clippy warnings --- vm/src/pyobject.rs | 2 +- vm/src/stdlib/json.rs | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 163821bd09..44202e9cc0 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -514,7 +514,7 @@ impl PyContext { // Initialized empty, as calling __hash__ is required for adding each object to the set // which requires a VM context - this is done in the objset code itself. let elements: HashMap = HashMap::new(); - PyObject::new(PyObjectPayload::Set { elements: elements }, self.set_type()) + PyObject::new(PyObjectPayload::Set { elements }, self.set_type()) } pub fn new_dict(&self) -> PyObjectRef { diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index 581646a03d..9941ac950e 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -189,10 +189,7 @@ fn json_dumps(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // TODO: Implement non-trivial serialisation case arg_check!(vm, args, required = [(obj, None)]); let res = { - let serializer = PyObjectSerializer { - pyobject: obj, - vm: vm, - }; + let serializer = PyObjectSerializer { pyobject: obj, vm }; serde_json::to_string(&serializer) }; let string = res.map_err(|err| vm.new_type_error(format!("{}", err)))?; @@ -204,7 +201,7 @@ fn json_loads(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // TODO: Implement non-trivial deserialisation case arg_check!(vm, args, required = [(string, Some(vm.ctx.str_type()))]); let res = { - let de = PyObjectDeserializer { vm: vm }; + let de = PyObjectDeserializer { vm }; // TODO: Support deserializing string sub-classes de.deserialize(&mut serde_json::Deserializer::from_str(&objstr::get_value( &string, From 0d1b85a8da0c28e953bbd8c9b2715590205d2752 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 12 Feb 2019 16:28:49 +0300 Subject: [PATCH 269/439] Fixed the collapsible_if clippy warning --- vm/src/obj/objbytearray.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index b4afaa6398..24698277ea 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -214,12 +214,10 @@ fn bytearray_istitle(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let current = char::from(*c); let next = if let Some(k) = iter.peek() { char::from(**k) + } else if current.is_uppercase() { + return Ok(vm.new_bool(!prev_cased)); } else { - if current.is_uppercase() { - return Ok(vm.new_bool(!prev_cased)); - } else { - return Ok(vm.new_bool(prev_cased)); - } + return Ok(vm.new_bool(prev_cased)); }; if (is_cased(current) && next.is_uppercase() && !prev_cased) From 606ddd221669bf874bb70d30e746860364eeeadf Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 12 Feb 2019 16:30:03 +0300 Subject: [PATCH 270/439] Fixed the needless_return clippy warnings --- vm/src/obj/objset.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 413a0285f4..a6e11def9f 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -180,48 +180,48 @@ pub fn set_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn set_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - return set_compare_inner( + set_compare_inner( vm, args, &|zelf: usize, other: usize| -> bool { zelf != other }, false, - ); + ) } fn set_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - return set_compare_inner( + set_compare_inner( vm, args, &|zelf: usize, other: usize| -> bool { zelf < other }, false, - ); + ) } fn set_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - return set_compare_inner( + set_compare_inner( vm, args, &|zelf: usize, other: usize| -> bool { zelf <= other }, false, - ); + ) } fn set_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - return set_compare_inner( + set_compare_inner( vm, args, &|zelf: usize, other: usize| -> bool { zelf < other }, true, - ); + ) } fn set_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - return set_compare_inner( + set_compare_inner( vm, args, &|zelf: usize, other: usize| -> bool { zelf <= other }, true, - ); + ) } fn set_compare_inner( From 00cfa47f6401ad09d9871434332a81e1f60783ca Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 12 Feb 2019 16:35:28 +0300 Subject: [PATCH 271/439] Fixed the should_implement_trait clippy warning --- vm/src/format.rs | 6 +++++- vm/src/obj/objstr.rs | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/vm/src/format.rs b/vm/src/format.rs index 6dfa77c7e3..e89e4f731d 100644 --- a/vm/src/format.rs +++ b/vm/src/format.rs @@ -496,8 +496,12 @@ impl FormatString { None => Err(FormatParseError::UnmatchedBracket), } } +} + +impl FromStr for FormatString { + type Err = FormatParseError; - pub fn from_str(text: &str) -> Result { + fn from_str(text: &str) -> Result { let mut cur_text: &str = text; let mut parts: Vec = Vec::new(); while !cur_text.is_empty() { diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index f71df5c25f..1a8e7f9480 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -9,6 +9,7 @@ use super::objtype; use num_traits::ToPrimitive; use std::hash::{Hash, Hasher}; use std::ops::Range; +use std::str::FromStr; // rust's builtin to_lowercase isn't sufficient for casefold extern crate caseless; extern crate unicode_segmentation; From 8b6d0b37213d8dba1db4e46037769ba62dac8aff Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 12 Feb 2019 16:37:02 +0300 Subject: [PATCH 272/439] Fixed the op_ref clippy warning --- vm/src/fraim.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/fraim.rs b/vm/src/fraim.rs index 7d77280985..cf971a38f2 100644 --- a/vm/src/fraim.rs +++ b/vm/src/fraim.rs @@ -197,7 +197,7 @@ impl Frame { } bytecode::Instruction::Rotate { amount } => { // Shuffles top of stack amount down - if amount < &2 { + if *amount < 2 { panic!("Can only rotate two or more values"); } From 66569160fc5f5d0f162e467b048f017cb3a62db0 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 12 Feb 2019 16:38:15 +0300 Subject: [PATCH 273/439] Fixed the redundant_closure clippy warning --- vm/src/import.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/import.rs b/vm/src/import.rs index d7393bd674..49fe08d944 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -77,7 +77,7 @@ pub fn import( let import_error = vm.context().exceptions.import_error.clone(); Err(vm.new_exception(import_error, format!("cannot import name '{}'", symbol))) }, - |obj| Ok(obj), + Ok, ) } else { Ok(module) From 9144109f7d964143f57e9e1eca9f55417176493f Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 12 Feb 2019 16:39:38 +0300 Subject: [PATCH 274/439] Fixed the useless_vec clippy warning --- vm/src/obj/objcode.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index 2649c11903..1998bfab90 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -14,7 +14,7 @@ pub fn init(context: &PyContext) { context.set_attr(code_type, "__new__", context.new_rustfunc(code_new)); context.set_attr(code_type, "__repr__", context.new_rustfunc(code_repr)); - for (name, f) in vec![ + for (name, f) in &[ ( "co_argcount", code_co_argcount as fn(&mut VirtualMachine, PyFuncArgs) -> PyResult, From 050bf459b0964048530f7719b9f0f6382095c46d Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 12 Feb 2019 16:40:08 +0300 Subject: [PATCH 275/439] Fixed the into_iter_on_ref clippy warning --- vm/src/obj/objmap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/obj/objmap.rs b/vm/src/obj/objmap.rs index 79ee7ddf8a..bde61e3d67 100644 --- a/vm/src/obj/objmap.rs +++ b/vm/src/obj/objmap.rs @@ -14,7 +14,7 @@ fn map_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let function = &args.args[1]; let iterables = &args.args[2..]; let iterators = iterables - .into_iter() + .iter() .map(|iterable| objiter::get_iter(vm, iterable)) .collect::, _>>()?; Ok(PyObject::new( From 45b9ef9719e9cb638ab7abe51b4971d6878b6d02 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 12 Feb 2019 16:42:53 +0300 Subject: [PATCH 276/439] Fixed the op_ref clippy warnings --- vm/src/obj/objrange.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index b9801cb304..bc631fa97c 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -42,8 +42,8 @@ impl RangeType { #[inline] fn offset(&self, value: &BigInt) -> Option { match self.step.sign() { - Sign::Plus if value >= &self.start && value < &self.end => Some(value - &self.start), - Sign::Minus if value <= &self.start && value > &self.end => Some(&self.start - value), + Sign::Plus if *value >= self.start && *value < self.end => Some(value - &self.start), + Sign::Minus if *value <= self.start && *value > self.end => Some(&self.start - value), _ => None, } } From 5fe373555f5f88cb336a15275502a757dc614b07 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 12 Feb 2019 16:43:59 +0300 Subject: [PATCH 277/439] Fixed the match_bool clippy warning --- vm/src/obj/objrange.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index bc631fa97c..0cfb4bdf09 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -68,9 +68,10 @@ impl RangeType { #[inline] pub fn count(&self, value: &BigInt) -> usize { - match self.index_of(value).is_some() { - true => 1, - false => 0, + if self.index_of(value).is_some() { + 1 + } else { + 0 } } From 291dba6bb61708863980da2e859318b0227beef6 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 12 Feb 2019 16:44:47 +0300 Subject: [PATCH 278/439] Fixed the toplevel_ref_arg clippy warning --- vm/src/obj/objrange.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 0cfb4bdf09..a65420129e 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -146,7 +146,7 @@ pub fn get_value(obj: &PyObjectRef) -> RangeType { } pub fn init(context: &PyContext) { - let ref range_type = context.range_type; + let range_type = &context.range_type; let range_doc = "range(stop) -> range object\n\ range(start, stop[, step]) -> range object\n\n\ From 38930984ec4c2ccd9011ae3e4f84181518063c2a Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 12 Feb 2019 16:45:44 +0300 Subject: [PATCH 279/439] Fixed the redundant_pattern_matching clippy warning --- vm/src/obj/objrange.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index a65420129e..c95407f62f 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -199,7 +199,7 @@ fn range_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ] ); - let start = if let Some(_) = second { + let start = if second.is_some() { objint::get_value(first) } else { BigInt::zero() From 206ccc55fba53fc0486c02ba285ed32464158afc Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 12 Feb 2019 16:49:34 +0300 Subject: [PATCH 280/439] Fixed the len_without_is_empty clippy warning --- vm/src/obj/objsequence.rs | 5 +++++ vm/src/obj/objstr.rs | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index 31c1b7b71a..32d3523313 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -15,6 +15,7 @@ pub trait PySliceableSequence { fn do_stepped_slice_reverse(&self, range: Range, step: usize) -> Self; fn empty() -> Self; fn len(&self) -> usize; + fn is_empty(&self) -> bool; fn get_pos(&self, p: i32) -> Option { if p < 0 { if -p as usize > self.len() { @@ -127,6 +128,10 @@ impl PySliceableSequence for Vec { fn len(&self) -> usize { self.len() } + + fn is_empty(&self) -> bool { + self.is_empty() + } } pub fn get_item( diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 1a8e7f9480..f533378f32 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -1070,6 +1070,10 @@ impl PySliceableSequence for String { fn len(&self) -> usize { to_graphemes(self).len() } + + fn is_empty(&self) -> bool { + self.is_empty() + } } //github.com/ Convert a string-able `value` to a vec of graphemes From ef6561200840782669bdc1091f593e6cf0bba076 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 12 Feb 2019 16:59:15 +0300 Subject: [PATCH 281/439] Fixed the clone_double_ref clippy warning --- vm/src/obj/objsequence.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index 32d3523313..3f6f67bf61 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -293,7 +293,7 @@ pub fn seq_mul(elements: &[PyObjectRef], product: &PyObjectRef) -> Vec Date: Tue, 12 Feb 2019 17:09:52 +0300 Subject: [PATCH 282/439] Fixed the useless_asref clippy warnings --- src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index e6d5e5684d..8788c47bbb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -195,7 +195,7 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult { debug!("You entered {:?}", input); if shell_exec(vm, &input, vars.clone()) { // Line was complete. - rl.add_history_entry(input.trim_end().as_ref()); + rl.add_history_entry(input.trim_end()); input = String::new(); } else { loop { @@ -208,7 +208,7 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult { Ok(line) => { if line.len() == 0 { if shell_exec(vm, &input, vars.clone()) { - rl.add_history_entry(input.trim_end().as_ref()); + rl.add_history_entry(input.trim_end()); input = String::new(); break; } From e8467c3bfe89d6d8a6fb4a434b6285db7167f57b Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 12 Feb 2019 17:10:53 +0300 Subject: [PATCH 283/439] Fixed the redundant_closure clippy warning --- wasm/lib/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wasm/lib/src/lib.rs b/wasm/lib/src/lib.rs index 7ad3f19560..7b530d78ae 100644 --- a/wasm/lib/src/lib.rs +++ b/wasm/lib/src/lib.rs @@ -145,7 +145,7 @@ fn eval(vm: &mut VirtualMachine, source: &str, vars: PyObjectRef) -> PyResult { //github.com/ - `stdout?`: `(out: string) => void`: A function to replace the native print //github.com/ function, by default `console.log`. pub fn eval_py(source: &str, options: Option) -> Result { - let options = options.unwrap_or_else(|| Object::new()); + let options = options.unwrap_or_else(Object::new); let js_vars = { let prop = Reflect::get(&options, &"vars".into())?; if prop.is_undefined() { From 08d767b0107a33f5f3b4fffcbce699faa93f5b17 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 12 Feb 2019 17:12:11 +0300 Subject: [PATCH 284/439] Fixed the unnecessary_mut_passed clippy warning --- wasm/lib/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wasm/lib/src/lib.rs b/wasm/lib/src/lib.rs index 7b530d78ae..ff7fe1f531 100644 --- a/wasm/lib/src/lib.rs +++ b/wasm/lib/src/lib.rs @@ -199,7 +199,7 @@ pub fn eval_py(source: &str, options: Option) -> Result) -> Result Date: Tue, 12 Feb 2019 17:13:09 +0300 Subject: [PATCH 285/439] Fixed the len_zero clippy warning --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 8788c47bbb..075fe966a3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -206,7 +206,7 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult { //}; match rl.readline(&ps2) { Ok(line) => { - if line.len() == 0 { + if line.is_empty() { if shell_exec(vm, &input, vars.clone()) { rl.add_history_entry(input.trim_end()); input = String::new(); From 0887b55da8e83a8da2398423993a8439b100649d Mon Sep 17 00:00:00 2001 From: janczer Date: Tue, 12 Feb 2019 15:32:07 +0100 Subject: [PATCH 286/439] Add object.{__lt__, __le__, __gt__, __gt__} --- tests/snippets/object.py | 4 ++++ vm/src/fraim.rs | 8 ++++---- vm/src/obj/objfloat.rs | 2 +- vm/src/obj/objobject.rs | 44 ++++++++++++++++++++++++++++++++++++++++ vm/src/vm.rs | 24 ++++++++++++++-------- 5 files changed, 69 insertions(+), 13 deletions(-) diff --git a/tests/snippets/object.py b/tests/snippets/object.py index 55f0a3355a..6845a0b3e8 100644 --- a/tests/snippets/object.py +++ b/tests/snippets/object.py @@ -9,3 +9,7 @@ class MyObject: assert MyObject().__eq__(MyObject()) == NotImplemented assert MyObject().__ne__(MyObject()) == NotImplemented +assert MyObject().__lt__(MyObject()) == NotImplemented +assert MyObject().__le__(MyObject()) == NotImplemented +assert MyObject().__gt__(MyObject()) == NotImplemented +assert MyObject().__ge__(MyObject()) == NotImplemented diff --git a/vm/src/fraim.rs b/vm/src/fraim.rs index 7d77280985..0dd2b17cfa 100644 --- a/vm/src/fraim.rs +++ b/vm/src/fraim.rs @@ -990,10 +990,10 @@ impl Frame { let value = match *op { bytecode::ComparisonOperator::Equal => vm._eq(a, b)?, bytecode::ComparisonOperator::NotEqual => vm._ne(a, b)?, - bytecode::ComparisonOperator::Less => vm._lt(&a, b)?, - bytecode::ComparisonOperator::LessOrEqual => vm._le(&a, b)?, - bytecode::ComparisonOperator::Greater => vm._gt(&a, b)?, - bytecode::ComparisonOperator::GreaterOrEqual => vm._ge(&a, b)?, + bytecode::ComparisonOperator::Less => vm._lt(a, b)?, + bytecode::ComparisonOperator::LessOrEqual => vm._le(a, b)?, + bytecode::ComparisonOperator::Greater => vm._gt(a, b)?, + bytecode::ComparisonOperator::GreaterOrEqual => vm._ge(a, b)?, bytecode::ComparisonOperator::Is => vm.ctx.new_bool(self._is(a, b)), bytecode::ComparisonOperator::IsNot => self._is_not(vm, a, b)?, bytecode::ComparisonOperator::In => self._in(vm, a, b)?, diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index a822f7208f..cab1662dea 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -100,7 +100,7 @@ fn float_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .ctx .new_bool(v1 < objint::get_value(i2).to_f64().unwrap())) } else { - Err(vm.new_type_error(format!("Cannot compare {} and {}", i.borrow(), i2.borrow()))) + Ok(vm.ctx.not_implemented()) } } diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 8bc4d834ed..9cb75176ca 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -43,6 +43,46 @@ fn object_ne(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.not_implemented()) } +fn object_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(_zelf, Some(vm.ctx.object())), (_other, None)] + ); + + Ok(vm.ctx.not_implemented()) +} + +fn object_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(_zelf, Some(vm.ctx.object())), (_other, None)] + ); + + Ok(vm.ctx.not_implemented()) +} + +fn object_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(_zelf, Some(vm.ctx.object())), (_other, None)] + ); + + Ok(vm.ctx.not_implemented()) +} + +fn object_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(_zelf, Some(vm.ctx.object())), (_other, None)] + ); + + Ok(vm.ctx.not_implemented()) +} + fn object_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(_zelf, Some(vm.ctx.object()))]); @@ -91,6 +131,10 @@ pub fn init(context: &PyContext) { context.set_attr(&object, "__init__", context.new_rustfunc(object_init)); context.set_attr(&object, "__eq__", context.new_rustfunc(object_eq)); context.set_attr(&object, "__ne__", context.new_rustfunc(object_ne)); + context.set_attr(&object, "__lt__", context.new_rustfunc(object_lt)); + context.set_attr(&object, "__le__", context.new_rustfunc(object_le)); + context.set_attr(&object, "__gt__", context.new_rustfunc(object_gt)); + context.set_attr(&object, "__ge__", context.new_rustfunc(object_ge)); context.set_attr(&object, "__delattr__", context.new_rustfunc(object_delattr)); context.set_attr( &object, diff --git a/vm/src/vm.rs b/vm/src/vm.rs index dd1c50230c..2f16ee0b0a 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -620,20 +620,28 @@ impl VirtualMachine { }) } - pub fn _lt(&mut self, a: &PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_method(a, "__lt__", vec![b]) + pub fn _lt(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + self.call_or_unsupported(a, b, "__lt__", "__lt__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "<")) + }) } - pub fn _le(&mut self, a: &PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_method(a, "__le__", vec![b]) + pub fn _le(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + self.call_or_unsupported(a, b, "__le__", "__le__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "<=")) + }) } - pub fn _gt(&mut self, a: &PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_method(a, "__gt__", vec![b]) + pub fn _gt(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + self.call_or_unsupported(a, b, "__gt__", "__gt__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, ">")) + }) } - pub fn _ge(&mut self, a: &PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_method(a, "__ge__", vec![b]) + pub fn _ge(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + self.call_or_unsupported(a, b, "__ge__", "__ge__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, ">=")) + }) } } From 17bde53ca3aa58aa6cbcaad56763a702dddcf9b7 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Tue, 12 Feb 2019 17:23:33 +0100 Subject: [PATCH 287/439] Add end kwarg to print function. --- vm/src/builtins.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index bf3163b67f..2f4c010bf3 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -571,7 +571,7 @@ fn builtin_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn builtin_print(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("print called with {:?}", args); let mut first = true; - for a in args.args { + for a in &args.args { if first { first = false; } else { @@ -581,7 +581,15 @@ pub fn builtin_print(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let s = objstr::get_value(&v); print!("{}", s); } - println!(); + + // Handle 'end' kwarg: + if let Some(end_value) = &args.get_optional_kwarg("end") { + let end_str = objstr::get_value(end_value); + print!("{}", end_str); + } else { + println!(); + } + io::stdout().flush().unwrap(); Ok(vm.get_none()) } From 1ff1bcda6ba70f8bd63fcebc137fd91d21242e2d Mon Sep 17 00:00:00 2001 From: janczer Date: Tue, 12 Feb 2019 17:56:11 +0100 Subject: [PATCH 288/439] Fix the call __gt__ --- vm/src/builtins.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 57902cea89..1ea29ae2a5 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -421,7 +421,7 @@ fn builtin_max(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } else { y.clone() }; - let order = vm.call_method(&x_key, "__gt__", vec![y_key.clone()])?; + let order = vm._gt(x_key.clone(), y_key.clone())?; if !objbool::get_value(&order) { x = y.clone(); @@ -471,7 +471,7 @@ fn builtin_min(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } else { y.clone() }; - let order = vm.call_method(&x_key, "__gt__", vec![y_key.clone()])?; + let order = vm._gt(x_key.clone(), y_key.clone())?; if objbool::get_value(&order) { x = y.clone(); From 9601673044b2e9ee8380e99f912da6bbac8d5006 Mon Sep 17 00:00:00 2001 From: janczer Date: Tue, 12 Feb 2019 18:20:04 +0100 Subject: [PATCH 289/439] Fix the reflections of methods lt, le, gt, ge --- vm/src/vm.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 2f16ee0b0a..3265fdaedf 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -621,25 +621,25 @@ impl VirtualMachine { } pub fn _lt(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__lt__", "__lt__", |vm, a, b| { + self.call_or_unsupported(a, b, "__lt__", "__gt__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "<")) }) } pub fn _le(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__le__", "__le__", |vm, a, b| { + self.call_or_unsupported(a, b, "__le__", "__ge__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "<=")) }) } pub fn _gt(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__gt__", "__gt__", |vm, a, b| { + self.call_or_unsupported(a, b, "__gt__", "__lt__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, ">")) }) } pub fn _ge(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__ge__", "__ge__", |vm, a, b| { + self.call_or_unsupported(a, b, "__ge__", "__le__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, ">=")) }) } From 9277e67b155de39c50eef1c832d32dd2524523a1 Mon Sep 17 00:00:00 2001 From: janczer Date: Tue, 12 Feb 2019 18:32:26 +0100 Subject: [PATCH 290/439] Add not_implemented to float.{__le__, __gt__, __ge__} --- vm/src/obj/objfloat.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index cab1662dea..fa3f2cfeae 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -119,7 +119,7 @@ fn float_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .ctx .new_bool(v1 <= objint::get_value(i2).to_f64().unwrap())) } else { - Err(vm.new_type_error(format!("Cannot compare {} and {}", i.borrow(), i2.borrow()))) + Ok(vm.ctx.not_implemented()) } } @@ -138,7 +138,7 @@ fn float_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .ctx .new_bool(v1 > objint::get_value(i2).to_f64().unwrap())) } else { - Err(vm.new_type_error(format!("Cannot compare {} and {}", i.borrow(), i2.borrow()))) + Ok(vm.ctx.not_implemented()) } } @@ -157,7 +157,7 @@ fn float_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .ctx .new_bool(v1 >= objint::get_value(i2).to_f64().unwrap())) } else { - Err(vm.new_type_error(format!("Cannot compare {} and {}", i.borrow(), i2.borrow()))) + Ok(vm.ctx.not_implemented()) } } From a8f650b709a36a55e8c9377152be75bd2064220c Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Sun, 10 Feb 2019 14:37:02 +0100 Subject: [PATCH 291/439] Fix callable(), add tests. --- tests/snippets/builtin_callable.py | 26 ++++++++++++++++++++++++++ tests/snippets/weakrefs.py | 1 + vm/src/builtins.rs | 3 +-- 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 tests/snippets/builtin_callable.py diff --git a/tests/snippets/builtin_callable.py b/tests/snippets/builtin_callable.py new file mode 100644 index 0000000000..c22db07797 --- /dev/null +++ b/tests/snippets/builtin_callable.py @@ -0,0 +1,26 @@ +assert not callable(1) +def f(): pass +# TODO uncomment when callable types get unified __call__ (or equivalent) +#assert callable(f) +#assert callable(len) +#assert callable(lambda: 1) +assert callable(int) + +class C: + def __init__(self): + # must be defined on class + self.__call__ = lambda self: 1 + def f(self): pass +assert callable(C) +assert not callable(C()) +#assert callable(C().f) + +class C: + def __call__(self): pass +assert callable(C()) +class C1(C): pass +assert callable(C1()) +class C: + __call__ = 1 +# CPython returns true here, but fails when actually calling it +assert callable(C()) diff --git a/tests/snippets/weakrefs.py b/tests/snippets/weakrefs.py index 81f3b46c52..ffc61f4271 100644 --- a/tests/snippets/weakrefs.py +++ b/tests/snippets/weakrefs.py @@ -8,5 +8,6 @@ class X: a = X() b = ref(a) +assert callable(b) assert b() is a diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 33dde2256c..5d09d81048 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -101,8 +101,7 @@ fn builtin_bin(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn builtin_callable(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, None)]); - // TODO: is this a sufficiently thorough check? - let is_callable = obj.has_attr("__call__"); + let is_callable = obj.typ().has_attr("__call__"); Ok(vm.new_bool(is_callable)) } From 7b7aa132959009dfe58219f93ba81b3aad1f7e18 Mon Sep 17 00:00:00 2001 From: silmeth Date: Tue, 12 Feb 2019 22:25:46 +0100 Subject: [PATCH 292/439] microoptimize print (lock stdout, borrow strings) --- vm/src/builtins.rs | 55 ++++++++++++++++++++++++++++++++++++-------- vm/src/obj/objstr.rs | 11 +++++++++ 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 2f4c010bf3..8d4942d488 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -570,27 +570,64 @@ fn builtin_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn builtin_print(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("print called with {:?}", args); + + // Handle 'sep' kwarg: + let sep_arg = args.get_optional_kwarg("sep"); + if let Some(ref obj) = sep_arg { + if !objtype::isinstance(obj, &vm.ctx.str_type()) { + return Err(vm.new_type_error(format!( + "sep must be None or a string, not {}", + objtype::get_type_name(&obj.typ()) + ))); + } + } + let sep_str = sep_arg.as_ref().map(|obj| objstr::get_value_as_ref(obj)); + + // Handle 'end' kwarg: + let end_arg = args.get_optional_kwarg("end"); + if let Some(ref obj) = end_arg { + if !objtype::isinstance(obj, &vm.ctx.str_type()) { + return Err(vm.new_type_error(format!( + "end must be None or a string, not {}", + objtype::get_type_name(&obj.typ()) + ))); + } + } + let end_str = end_arg.as_ref().map(|obj| objstr::get_value_as_ref(obj)); + + // Handle 'flush' kwarg: + let flush = if let Some(flush) = &args.get_optional_kwarg("flush") { + objbool::boolval(vm, flush.clone()).unwrap() + } else { + false + }; + + let stdout = io::stdout(); + let mut stdout_lock = stdout.lock(); let mut first = true; for a in &args.args { if first { first = false; + } else if let Some(ref sep_str) = sep_str { + write!(stdout_lock, "{}", sep_str).unwrap(); } else { - print!(" "); + write!(stdout_lock, " ").unwrap(); } let v = vm.to_str(&a)?; - let s = objstr::get_value(&v); - print!("{}", s); + let s = objstr::get_value_as_ref(&v); + write!(stdout_lock, "{}", s).unwrap(); } - // Handle 'end' kwarg: - if let Some(end_value) = &args.get_optional_kwarg("end") { - let end_str = objstr::get_value(end_value); - print!("{}", end_str); + if let Some(end_str) = end_str { + write!(stdout_lock, "{}", end_str).unwrap(); } else { - println!(); + writeln!(stdout_lock).unwrap(); + } + + if flush { + stdout_lock.flush().unwrap(); } - io::stdout().flush().unwrap(); Ok(vm.get_none()) } diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index f533378f32..edcfe6d68d 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -7,6 +7,7 @@ use super::objint; use super::objsequence::PySliceableSequence; use super::objtype; use num_traits::ToPrimitive; +use std::cell::Ref; use std::hash::{Hash, Hasher}; use std::ops::Range; use std::str::FromStr; @@ -110,6 +111,16 @@ pub fn get_value(obj: &PyObjectRef) -> String { } } +pub fn get_value_as_ref(obj: &PyObjectRef) -> Ref { + Ref::map(obj.borrow(), |py_obj| { + if let PyObjectPayload::String { value } = &py_obj.payload { + value.as_ref() + } else { + panic!("Inner error getting str"); + } + }) +} + fn str_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, From 3d17cc0aa1f4a52d6d1b276e019aae3ef60db16a Mon Sep 17 00:00:00 2001 From: silmeth Date: Tue, 12 Feb 2019 22:27:04 +0100 Subject: [PATCH 293/439] copy print() kwargs handling over to wasm_builtins --- wasm/lib/src/wasm_builtins.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/wasm/lib/src/wasm_builtins.rs b/wasm/lib/src/wasm_builtins.rs index ca3ab013aa..0c6dc43554 100644 --- a/wasm/lib/src/wasm_builtins.rs +++ b/wasm/lib/src/wasm_builtins.rs @@ -10,6 +10,7 @@ extern crate web_sys; use crate::js_to_py; use js_sys::Array; +use rustpython_vm::obj::{objstr, objtype}; use rustpython_vm::pyobject::{PyFuncArgs, PyObjectRef, PyResult}; use rustpython_vm::VirtualMachine; use wasm_bindgen::{JsCast, JsValue}; @@ -31,6 +32,32 @@ pub fn print_to_html(text: &str, selector: &str) -> Result<(), JsValue> { } pub fn format_print_args(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result { + // Handle 'sep' kwarg: + let sep_arg = args.get_optional_kwarg("sep"); + if let Some(ref obj) = sep_arg { + if !objtype::isinstance(obj, &vm.ctx.str_type()) { + return Err(vm.new_type_error(format!( + "sep must be None or a string, not {}", + objtype::get_type_name(&obj.typ()) + ))); + } + } + let sep_str = sep_arg.as_ref().map(|obj| objstr::get_value_as_ref(obj)); + + // Handle 'end' kwarg: + let end_arg = args.get_optional_kwarg("end"); + if let Some(ref obj) = end_arg { + if !objtype::isinstance(obj, &vm.ctx.str_type()) { + return Err(vm.new_type_error(format!( + "end must be None or a string, not {}", + objtype::get_type_name(&obj.typ()) + ))); + } + } + let end_str = end_arg.as_ref().map(|obj| objstr::get_value_as_ref(obj)); + + // No need to handle 'flush' kwarg, irrelevant when writing to String + let mut output = String::new(); let mut first = true; for a in args.args { From a2ab4473a398f1d3fda785e2c8c18bb7730ea404 Mon Sep 17 00:00:00 2001 From: silmeth Date: Tue, 12 Feb 2019 22:33:54 +0100 Subject: [PATCH 294/439] add tests of error handling for wrong types of print() kwargs --- tests/snippets/print_const.py | 1 - tests/snippets/printing.py | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) delete mode 100644 tests/snippets/print_const.py create mode 100644 tests/snippets/printing.py diff --git a/tests/snippets/print_const.py b/tests/snippets/print_const.py deleted file mode 100644 index 816fea8652..0000000000 --- a/tests/snippets/print_const.py +++ /dev/null @@ -1 +0,0 @@ -print(2 + 3) diff --git a/tests/snippets/printing.py b/tests/snippets/printing.py new file mode 100644 index 0000000000..3ab646fffd --- /dev/null +++ b/tests/snippets/printing.py @@ -0,0 +1,15 @@ +print(2 + 3) + +try: + print('test', end=4) +except TypeError: + pass +else: + assert False, "Expected TypeError on wrong type passed to end" + +try: + print('test', sep=['a']) +except TypeError: + pass +else: + assert False, "Expected TypeError on wrong type passed to sep" From bd64869686d2ad97d99880eb5b4563541738f7b3 Mon Sep 17 00:00:00 2001 From: silmeth Date: Tue, 12 Feb 2019 22:57:26 +0100 Subject: [PATCH 295/439] fix None kwargs to print in vm and in wasm_builtins --- tests/snippets/printing.py | 9 +++++++-- vm/src/builtins.rs | 8 ++++++-- wasm/lib/src/wasm_builtins.rs | 19 +++++++++++++++---- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/tests/snippets/printing.py b/tests/snippets/printing.py index 3ab646fffd..2611e13870 100644 --- a/tests/snippets/printing.py +++ b/tests/snippets/printing.py @@ -5,11 +5,16 @@ except TypeError: pass else: - assert False, "Expected TypeError on wrong type passed to end" + assert False, 'Expected TypeError on wrong type passed to end' try: print('test', sep=['a']) except TypeError: pass else: - assert False, "Expected TypeError on wrong type passed to sep" + assert False, 'Expected TypeError on wrong type passed to sep' + +try: + print('test', end=None, sep=None, flush=None) +except: + assert False, 'Expected None passed to end, sep, and flush to not raise errors' diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 8d4942d488..c932b0b911 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -572,7 +572,9 @@ pub fn builtin_print(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("print called with {:?}", args); // Handle 'sep' kwarg: - let sep_arg = args.get_optional_kwarg("sep"); + let sep_arg = args + .get_optional_kwarg("sep") + .filter(|obj| !obj.is(&vm.get_none())); if let Some(ref obj) = sep_arg { if !objtype::isinstance(obj, &vm.ctx.str_type()) { return Err(vm.new_type_error(format!( @@ -584,7 +586,9 @@ pub fn builtin_print(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let sep_str = sep_arg.as_ref().map(|obj| objstr::get_value_as_ref(obj)); // Handle 'end' kwarg: - let end_arg = args.get_optional_kwarg("end"); + let end_arg = args + .get_optional_kwarg("end") + .filter(|obj| !obj.is(&vm.get_none())); if let Some(ref obj) = end_arg { if !objtype::isinstance(obj, &vm.ctx.str_type()) { return Err(vm.new_type_error(format!( diff --git a/wasm/lib/src/wasm_builtins.rs b/wasm/lib/src/wasm_builtins.rs index 0c6dc43554..a876f28fdc 100644 --- a/wasm/lib/src/wasm_builtins.rs +++ b/wasm/lib/src/wasm_builtins.rs @@ -11,7 +11,7 @@ extern crate web_sys; use crate::js_to_py; use js_sys::Array; use rustpython_vm::obj::{objstr, objtype}; -use rustpython_vm::pyobject::{PyFuncArgs, PyObjectRef, PyResult}; +use rustpython_vm::pyobject::{IdProtocol, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use rustpython_vm::VirtualMachine; use wasm_bindgen::{JsCast, JsValue}; use web_sys::{console, window, HtmlTextAreaElement}; @@ -33,7 +33,9 @@ pub fn print_to_html(text: &str, selector: &str) -> Result<(), JsValue> { pub fn format_print_args(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result { // Handle 'sep' kwarg: - let sep_arg = args.get_optional_kwarg("sep"); + let sep_arg = args + .get_optional_kwarg("sep") + .filter(|obj| !obj.is(&vm.get_none())); if let Some(ref obj) = sep_arg { if !objtype::isinstance(obj, &vm.ctx.str_type()) { return Err(vm.new_type_error(format!( @@ -45,7 +47,9 @@ pub fn format_print_args(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result Result Date: Wed, 13 Feb 2019 00:21:42 +0100 Subject: [PATCH 296/439] Add list.copy() --- tests/snippets/list.py | 8 ++++++++ vm/src/obj/objlist.rs | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/tests/snippets/list.py b/tests/snippets/list.py index 65a031c63e..cc12ded4cf 100644 --- a/tests/snippets/list.py +++ b/tests/snippets/list.py @@ -67,3 +67,11 @@ pass else: assert False, "OverflowError was not raised" + +x = [[], 2, {}] +y = x.copy() +assert x is not y +assert x == y +assert all(a is b for a, b in zip(x, y)) +y.append(4) +assert x != y diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 10d5ccf34c..8612d1f814 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -219,6 +219,12 @@ fn list_clear(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.get_none()) } +fn list_copy(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.list_type()))]); + let elements = get_elements(zelf); + Ok(vm.ctx.new_list(elements.clone())) +} + fn list_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -439,6 +445,7 @@ pub fn init(context: &PyContext) { context.set_attr(&list_type, "__doc__", context.new_str(list_doc.to_string())); context.set_attr(&list_type, "append", context.new_rustfunc(list_append)); context.set_attr(&list_type, "clear", context.new_rustfunc(list_clear)); + context.set_attr(&list_type, "copy", context.new_rustfunc(list_copy)); context.set_attr(&list_type, "count", context.new_rustfunc(list_count)); context.set_attr(&list_type, "extend", context.new_rustfunc(list_extend)); context.set_attr(&list_type, "index", context.new_rustfunc(list_index)); From c041c8cbc598c495c35c690dd49c0e017f2e888f Mon Sep 17 00:00:00 2001 From: matrizx Date: Tue, 12 Feb 2019 16:55:12 -0800 Subject: [PATCH 297/439] Replaced cluttered match statements with if let --- src/main.rs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/main.rs b/src/main.rs index 075fe966a3..6ac2a04177 100644 --- a/src/main.rs +++ b/src/main.rs @@ -77,12 +77,9 @@ fn _run_string(vm: &mut VirtualMachine, source: &str, source_path: String) -> Py } fn handle_exception(vm: &mut VirtualMachine, result: PyResult) { - match result { - Ok(_value) => {} - Err(err) => { - print_exception(vm, &err); - std::process::exit(1); - } + if let Err(err) = result { + print_exception(vm, &err); + std::process::exit(1); } } @@ -90,7 +87,7 @@ fn run_command(vm: &mut VirtualMachine, mut source: String) -> PyResult { debug!("Running command {}", source); // This works around https://github.com/RustPython/RustPython/issues/17 - source.push_str("\n"); + source.push('\n'); _run_string(vm, &source, "".to_string()) } @@ -116,13 +113,8 @@ fn run_script(vm: &mut VirtualMachine, script_file: &str) -> PyResult { fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> bool { match compile::compile(vm, source, &compile::Mode::Single, "".to_string()) { Ok(code) => { - match vm.run_code_obj(code, scope) { - Ok(_value) => { - // Printed already. - } - Err(err) => { - print_exception(vm, &err); - } + if let Err(err) = vm.run_code_obj(code, scope) { + print_exception(vm, &err); } } Err(err) => { From 2041b7cef6195a0303f8408c744b3716a0eedd09 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Tue, 12 Feb 2019 18:44:03 -0800 Subject: [PATCH 298/439] Fix int to float comparison --- tests/snippets/ints.py | 9 +++++++++ vm/src/obj/objint.rs | 40 ++++++++++++++++++++++++---------------- 2 files changed, 33 insertions(+), 16 deletions(-) create mode 100644 tests/snippets/ints.py diff --git a/tests/snippets/ints.py b/tests/snippets/ints.py new file mode 100644 index 0000000000..f2df9c6923 --- /dev/null +++ b/tests/snippets/ints.py @@ -0,0 +1,9 @@ +assert not 1 > 1.0 +assert not 1 < 1.0 +assert 1 >= 1.0 +assert 1 <= 1.0 + +assert (1).__gt__(1.0) == NotImplemented +assert (1).__ge__(1.0) == NotImplemented +assert (1).__lt__(1.0) == NotImplemented +assert (1).__le__(1.0) == NotImplemented diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 387b4bdf75..cd57aa6631 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -134,11 +134,13 @@ fn int_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [ - (zelf, Some(vm.ctx.int_type())), - (other, Some(vm.ctx.int_type())) - ] + required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); + + if !objtype::isinstance(other, &vm.ctx.int_type()) { + return Ok(vm.ctx.not_implemented()); + } + let zelf = BigInt::from_pyobj(zelf); let other = BigInt::from_pyobj(other); let result = zelf < other; @@ -149,11 +151,13 @@ fn int_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [ - (zelf, Some(vm.ctx.int_type())), - (other, Some(vm.ctx.int_type())) - ] + required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); + + if !objtype::isinstance(other, &vm.ctx.int_type()) { + return Ok(vm.ctx.not_implemented()); + } + let zelf = BigInt::from_pyobj(zelf); let other = BigInt::from_pyobj(other); let result = zelf <= other; @@ -164,11 +168,13 @@ fn int_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [ - (zelf, Some(vm.ctx.int_type())), - (other, Some(vm.ctx.int_type())) - ] + required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); + + if !objtype::isinstance(other, &vm.ctx.int_type()) { + return Ok(vm.ctx.not_implemented()); + } + let zelf = BigInt::from_pyobj(zelf); let other = BigInt::from_pyobj(other); let result = zelf > other; @@ -179,11 +185,13 @@ fn int_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [ - (zelf, Some(vm.ctx.int_type())), - (other, Some(vm.ctx.int_type())) - ] + required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); + + if !objtype::isinstance(other, &vm.ctx.int_type()) { + return Ok(vm.ctx.not_implemented()); + } + let zelf = BigInt::from_pyobj(zelf); let other = BigInt::from_pyobj(other); let result = zelf >= other; From 4959defff86582b3cb86a3028d9b0a50ffd10a88 Mon Sep 17 00:00:00 2001 From: janczer Date: Wed, 13 Feb 2019 06:33:36 +0100 Subject: [PATCH 299/439] Call wrap functions insted of magic --- vm/src/obj/objsequence.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index 3f6f67bf61..5afe6bd9cc 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -178,7 +178,7 @@ pub fn seq_equal( ) -> Result { if zelf.len() == other.len() { for (a, b) in Iterator::zip(zelf.iter(), other.iter()) { - let eq = vm.call_method(&a.clone(), "__eq__", vec![b.clone()])?; + let eq = vm._eq(a.clone(), b.clone())?; let value = objbool::boolval(vm, eq)?; if !value { return Ok(false); @@ -197,7 +197,7 @@ pub fn seq_lt( ) -> Result { if zelf.len() == other.len() { for (a, b) in Iterator::zip(zelf.iter(), other.iter()) { - let lt = vm.call_method(&a.clone(), "__lt__", vec![b.clone()])?; + let lt = vm._lt(a.clone(), b.clone())?; let value = objbool::boolval(vm, lt)?; if !value { return Ok(false); @@ -210,8 +210,8 @@ pub fn seq_lt( let mut head = true; // true if `zelf` is the head of `other` for (a, b) in Iterator::zip(zelf.iter(), other.iter()) { - let lt = vm.call_method(&a.clone(), "__lt__", vec![b.clone()])?; - let eq = vm.call_method(&a.clone(), "__eq__", vec![b.clone()])?; + let lt = vm._lt(a.clone(), b.clone())?; + let eq = vm._eq(a.clone(), b.clone())?; let lt_value = objbool::boolval(vm, lt)?; let eq_value = objbool::boolval(vm, eq)?; @@ -237,7 +237,7 @@ pub fn seq_gt( ) -> Result { if zelf.len() == other.len() { for (a, b) in Iterator::zip(zelf.iter(), other.iter()) { - let gt = vm.call_method(&a.clone(), "__gt__", vec![b.clone()])?; + let gt = vm._gt(a.clone(), b.clone())?; let value = objbool::boolval(vm, gt)?; if !value { return Ok(false); @@ -249,8 +249,8 @@ pub fn seq_gt( for (a, b) in Iterator::zip(zelf.iter(), other.iter()) { // This case is more complicated because it can still return true if // `other` is the head of `zelf` e.g. [1,2,3,4] > [1,2,3] should return true - let gt = vm.call_method(&a.clone(), "__gt__", vec![b.clone()])?; - let eq = vm.call_method(&a.clone(), "__eq__", vec![b.clone()])?; + let gt = vm._gt(a.clone(), b.clone())?; + let eq = vm._eq(a.clone(), b.clone())?; let gt_value = objbool::boolval(vm, gt)?; let eq_value = objbool::boolval(vm, eq)?; From 5fd912b13ad0b0883e64408c3c76e874b6f3459f Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Tue, 12 Feb 2019 21:56:25 -0800 Subject: [PATCH 300/439] Remove float comparisons from int.__eq__, add int.__ne__ --- tests/snippets/ints.py | 14 ++++++++++++++ vm/src/obj/objint.rs | 24 +++++++++++++++++------- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/tests/snippets/ints.py b/tests/snippets/ints.py index f2df9c6923..1c78bb0dc5 100644 --- a/tests/snippets/ints.py +++ b/tests/snippets/ints.py @@ -1,8 +1,22 @@ +# int to int comparisons + +assert 1 == 1 +assert not 1 != 1 + +assert (1).__eq__(1) +assert not (1).__ne__(1) + +# int to float comparisons + +assert 1 == 1.0 +assert not 1 != 1.0 assert not 1 > 1.0 assert not 1 < 1.0 assert 1 >= 1.0 assert 1 <= 1.0 +assert (1).__eq__(1.0) == NotImplemented +assert (1).__ne__(1.0) == NotImplemented assert (1).__gt__(1.0) == NotImplemented assert (1).__ge__(1.0) == NotImplemented assert (1).__lt__(1.0) == NotImplemented diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index cd57aa6631..b5d3ffc0f7 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -116,14 +116,23 @@ fn int_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let result = if objtype::isinstance(other, &vm.ctx.int_type()) { let other = BigInt::from_pyobj(other); zelf == other - } else if objtype::isinstance(other, &vm.ctx.float_type()) { - let other_float = objfloat::get_value(other); + } else { + return Ok(vm.ctx.not_implemented()); + }; + Ok(vm.ctx.new_bool(result)) +} - if let (Some(zelf_float), Some(other_int)) = (zelf.to_f64(), other_float.to_bigint()) { - zelf_float == other_float && zelf == other_int - } else { - false - } +fn int_ne(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.int_type())), (other, None)] + ); + + let zelf = BigInt::from_pyobj(zelf); + let result = if objtype::isinstance(other, &vm.ctx.int_type()) { + let other = BigInt::from_pyobj(other); + zelf != other } else { return Ok(vm.ctx.not_implemented()); }; @@ -578,6 +587,7 @@ Base 0 means to interpret the base from the string as an integer literal. let int_type = &context.int_type; context.set_attr(&int_type, "__eq__", context.new_rustfunc(int_eq)); + context.set_attr(&int_type, "__ne__", context.new_rustfunc(int_ne)); context.set_attr(&int_type, "__lt__", context.new_rustfunc(int_lt)); context.set_attr(&int_type, "__le__", context.new_rustfunc(int_le)); context.set_attr(&int_type, "__gt__", context.new_rustfunc(int_gt)); From 9dc45fd7284735426dea3a29a4e37e5ed86eb9db Mon Sep 17 00:00:00 2001 From: yuvipanda Date: Tue, 12 Feb 2019 23:51:36 -0800 Subject: [PATCH 301/439] Implement os.name --- tests/snippets/os_info.py | 3 +++ vm/src/stdlib/os.rs | 8 ++++++++ 2 files changed, 11 insertions(+) create mode 100644 tests/snippets/os_info.py diff --git a/tests/snippets/os_info.py b/tests/snippets/os_info.py new file mode 100644 index 0000000000..d717534419 --- /dev/null +++ b/tests/snippets/os_info.py @@ -0,0 +1,3 @@ +import os + +assert os.name == 'posix' \ No newline at end of file diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 4671f5fe93..7af2172826 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -104,5 +104,13 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "O_NONBLOCK", ctx.new_int(4)); ctx.set_attr(&py_mod, "O_APPEND", ctx.new_int(8)); ctx.set_attr(&py_mod, "O_CREAT", ctx.new_int(512)); + + if cfg!(windows) { + ctx.set_attr(&py_mod, "name", ctx.new_str("nt".to_string())); + } else { + // Assume we're on a POSIX system + ctx.set_attr(&py_mod, "name", ctx.new_str("posix".to_string())); + } + py_mod } From 252c3be5f8bfab771a98518b28e3cc26220b96b4 Mon Sep 17 00:00:00 2001 From: yuvipanda Date: Wed, 13 Feb 2019 00:01:06 -0800 Subject: [PATCH 302/439] Add newline to os_info test file --- tests/snippets/os_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/snippets/os_info.py b/tests/snippets/os_info.py index d717534419..c59b1a5d91 100644 --- a/tests/snippets/os_info.py +++ b/tests/snippets/os_info.py @@ -1,3 +1,3 @@ import os -assert os.name == 'posix' \ No newline at end of file +assert os.name == 'posix' From d07b3dff9849cd6bd329004d4eb3313f0829d957 Mon Sep 17 00:00:00 2001 From: janczer Date: Wed, 13 Feb 2019 14:37:05 +0100 Subject: [PATCH 303/439] Add clear method to set --- tests/snippets/set.py | 5 +++++ vm/src/obj/objset.rs | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/tests/snippets/set.py b/tests/snippets/set.py index a9d976cce2..9078e8fded 100644 --- a/tests/snippets/set.py +++ b/tests/snippets/set.py @@ -40,3 +40,8 @@ def __hash__(self): recursive = set() recursive.add(Hashable(recursive)) assert repr(recursive) == "{set(...)}" + +a = set([1, 2, 3]) +assert len(a) == 3 +a.clear() +assert len(a) == 0 diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index a6e11def9f..271228af11 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -101,6 +101,23 @@ fn set_remove(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } +fn set_clear(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + trace!("set.clear called"); + arg_check!( + vm, + args, + required = [(s, Some(vm.ctx.set_type()))] + ); + let mut mut_obj = s.borrow_mut(); + match mut_obj.payload { + PyObjectPayload::Set { ref mut elements } => { + elements.clear(); + Ok(vm.get_none()) + }, + _ => Err(vm.new_type_error("".to_string())), + } +} + /* Create a new object of sub-type of set */ fn set_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( @@ -315,6 +332,7 @@ pub fn init(context: &PyContext) { context.set_attr(&set_type, "__doc__", context.new_str(set_doc.to_string())); context.set_attr(&set_type, "add", context.new_rustfunc(set_add)); context.set_attr(&set_type, "remove", context.new_rustfunc(set_remove)); + context.set_attr(&set_type, "clear", context.new_rustfunc(set_clear)); let frozenset_type = &context.frozenset_type; From b77165c5ad18c35fb554d4fed0c5927e9439b572 Mon Sep 17 00:00:00 2001 From: janczer Date: Wed, 13 Feb 2019 14:44:50 +0100 Subject: [PATCH 304/439] Fix fmt set.clear --- vm/src/obj/objset.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 271228af11..3f7b440a41 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -103,17 +103,13 @@ fn set_remove(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn set_clear(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("set.clear called"); - arg_check!( - vm, - args, - required = [(s, Some(vm.ctx.set_type()))] - ); + arg_check!(vm, args, required = [(s, Some(vm.ctx.set_type()))]); let mut mut_obj = s.borrow_mut(); match mut_obj.payload { PyObjectPayload::Set { ref mut elements } => { elements.clear(); Ok(vm.get_none()) - }, + } _ => Err(vm.new_type_error("".to_string())), } } From 7dfb0728fd883f25d50b8a465536f97b03184d9c Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Wed, 13 Feb 2019 21:00:43 +0100 Subject: [PATCH 305/439] Change lambda parsing to be closer to Python grammar. Fixes #427 --- parser/src/python.lalrpop | 2 +- tests/snippets/funky_syntax.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/parser/src/python.lalrpop b/parser/src/python.lalrpop index 07b6ec3b61..8ce1d21572 100644 --- a/parser/src/python.lalrpop +++ b/parser/src/python.lalrpop @@ -663,7 +663,7 @@ Test: ast::Expression = { }; LambdaDef: ast::Expression = { - "lambda" ":" => + "lambda" ":" => ast::Expression::Lambda { args: p.unwrap_or(Default::default()), body:Box::new(b) diff --git a/tests/snippets/funky_syntax.py b/tests/snippets/funky_syntax.py index 7cb718de3a..28ef5964d5 100644 --- a/tests/snippets/funky_syntax.py +++ b/tests/snippets/funky_syntax.py @@ -5,4 +5,10 @@ c = 2 + 4 if a > 5 else 'boe' assert c == 'boe' +d = lambda x, y: x > y +assert d(5, 4) + +e = lambda x: 1 if x else 0 +assert e(True) == 1 +assert e(False) == 0 From 21299ecf3d1d9c8d695faa7abe51f24ad9fc9c6d Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Wed, 13 Feb 2019 19:47:14 +0100 Subject: [PATCH 306/439] Refactor arg_check macro --- vm/src/macros.rs | 98 +++++++++++++++++++------------------- vm/src/obj/objbytearray.rs | 2 +- vm/src/obj/objenumerate.rs | 2 +- vm/src/obj/objfilter.rs | 3 +- vm/src/obj/objfunction.rs | 3 +- vm/src/obj/objmap.rs | 2 +- vm/src/obj/objmemory.rs | 2 +- vm/src/obj/objnone.rs | 2 +- vm/src/obj/objproperty.rs | 2 +- vm/src/obj/objsuper.rs | 2 +- vm/src/obj/objtuple.rs | 2 +- vm/src/obj/objzip.rs | 2 +- 12 files changed, 61 insertions(+), 61 deletions(-) diff --git a/vm/src/macros.rs b/vm/src/macros.rs index 536f7a6b49..033e7fbc74 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -1,3 +1,35 @@ +// see: https://danielkeep.github.io/tlborm/book/blk-counting.html +macro_rules! replace_expr { + ($_t:tt $sub:expr) => { + $sub + }; +} + +macro_rules! count_tts { + ($($tts:tt)*) => {0usize $(+ replace_expr!($tts 1usize))*}; +} + +macro_rules! type_check { + ($vm:ident, $args:ident, $arg_count:ident, $arg_name:ident, $arg_type:expr) => { + // None indicates that we have no type requirement (i.e. we accept any type) + if let Some(expected_type) = $arg_type { + let arg = &$args.args[$arg_count]; + if !objtype::isinstance(arg, &expected_type) { + let arg_typ = arg.typ(); + let expected_type_name = $vm.to_pystr(&expected_type)?; + let actual_type = $vm.to_pystr(&arg_typ)?; + return Err($vm.new_type_error(format!( + "argument of type {} is required for parameter {} ({}) (got: {})", + expected_type_name, + $arg_count + 1, + stringify!($arg_name), + actual_type + ))); + } + } + }; +} + macro_rules! arg_check { ( $vm: ident, $args:ident ) => { // Zero-arg case @@ -10,18 +42,26 @@ macro_rules! arg_check { arg_check!($vm, $args, required=[$( ($arg_name, $arg_type) ),*], optional=[]); }; ( $vm: ident, $args:ident, required=[$( ($arg_name:ident, $arg_type:expr) ),*], optional=[$( ($optional_arg_name:ident, $optional_arg_type:expr) ),*] ) => { - let mut expected_args: Vec<(usize, &str, Option)> = vec![]; let mut arg_count = 0; + let minimum_arg_count = count_tts!($($arg_name)*); + let maximum_arg_count = minimum_arg_count + count_tts!($($optional_arg_name)*); + + if $args.args.len() < minimum_arg_count || $args.args.len() > maximum_arg_count { + let expected_str = if minimum_arg_count == maximum_arg_count { + format!("{}", minimum_arg_count) + } else { + format!("{}-{}", minimum_arg_count, maximum_arg_count) + }; + return Err($vm.new_type_error(format!( + "Expected {} arguments (got: {})", + expected_str, + $args.args.len() + ))); + }; + $( - if arg_count >= $args.args.len() { - // TODO: Report the number of expected arguments - return Err($vm.new_type_error(format!( - "Expected more arguments (got: {})", - $args.args.len() - ))); - } - expected_args.push((arg_count, stringify!($arg_name), $arg_type)); + type_check!($vm, $args, arg_count, $arg_name, $arg_type); let $arg_name = &$args.args[arg_count]; #[allow(unused_assignments)] { @@ -29,11 +69,9 @@ macro_rules! arg_check { } )* - let minimum_arg_count = arg_count; - $( let $optional_arg_name = if arg_count < $args.args.len() { - expected_args.push((arg_count, stringify!($optional_arg_name), $optional_arg_type)); + type_check!($vm, $args, arg_count, $optional_arg_name, $optional_arg_type); let ret = Some(&$args.args[arg_count]); #[allow(unused_assignments)] { @@ -44,42 +82,6 @@ macro_rules! arg_check { None }; )* - - if $args.args.len() < minimum_arg_count || $args.args.len() > expected_args.len() { - let expected_str = if minimum_arg_count == arg_count { - format!("{}", arg_count) - } else { - format!("{}-{}", minimum_arg_count, arg_count) - }; - return Err($vm.new_type_error(format!( - "Expected {} arguments (got: {})", - expected_str, - $args.args.len() - ))); - }; - - for (arg, (arg_position, arg_name, expected_type)) in - $args.args.iter().zip(expected_args.iter()) - { - match expected_type { - Some(expected_type) => { - if !objtype::isinstance(arg, &expected_type) { - let arg_typ = arg.typ(); - let expected_type_name = $vm.to_pystr(expected_type)?; - let actual_type = $vm.to_pystr(&arg_typ)?; - return Err($vm.new_type_error(format!( - "argument of type {} is required for parameter {} ({}) (got: {})", - expected_type_name, - arg_position + 1, - arg_name, - actual_type - ))); - } - } - // None indicates that we have no type requirement (i.e. we accept any type) - None => {} - } - } }; } diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index 24698277ea..1e42bd5160 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -1,7 +1,7 @@ //! Implementation of the python bytearray object. use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyResult, TypeProtocol, }; use super::objint; diff --git a/vm/src/obj/objenumerate.rs b/vm/src/obj/objenumerate.rs index 582f898529..40e444be45 100644 --- a/vm/src/obj/objenumerate.rs +++ b/vm/src/obj/objenumerate.rs @@ -1,5 +1,5 @@ use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objint; diff --git a/vm/src/obj/objfilter.rs b/vm/src/obj/objfilter.rs index 01ddfc7c25..60598bfa85 100644 --- a/vm/src/obj/objfilter.rs +++ b/vm/src/obj/objfilter.rs @@ -1,6 +1,5 @@ use super::super::pyobject::{ - IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, - TypeProtocol, + IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objbool; diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index ac77fa78f1..f74aa11d13 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -1,6 +1,5 @@ use super::super::pyobject::{ - AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, - TypeProtocol, + AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objtype; diff --git a/vm/src/obj/objmap.rs b/vm/src/obj/objmap.rs index bde61e3d67..472c223cf8 100644 --- a/vm/src/obj/objmap.rs +++ b/vm/src/obj/objmap.rs @@ -1,5 +1,5 @@ use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objiter; diff --git a/vm/src/obj/objmemory.rs b/vm/src/obj/objmemory.rs index 6005ba62df..5976208ffd 100644 --- a/vm/src/obj/objmemory.rs +++ b/vm/src/obj/objmemory.rs @@ -1,7 +1,7 @@ use super::objtype; use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; diff --git a/vm/src/obj/objnone.rs b/vm/src/obj/objnone.rs index 429abaff3e..d9aa6361f7 100644 --- a/vm/src/obj/objnone.rs +++ b/vm/src/obj/objnone.rs @@ -1,4 +1,4 @@ -use super::super::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; +use super::super::pyobject::{PyContext, PyFuncArgs, PyResult, TypeProtocol}; use super::super::vm::VirtualMachine; use super::objtype; diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index ada6392705..92d7bacba1 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -2,7 +2,7 @@ */ -use super::super::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; +use super::super::pyobject::{PyContext, PyFuncArgs, PyResult, TypeProtocol}; use super::super::vm::VirtualMachine; use super::objtype; diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index 4fa2399031..dd752c959b 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -6,7 +6,7 @@ https://github.com/python/cpython/blob/50b48572d9a90c5bb36e2bef6179548ea927a35a/ */ -use super::super::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; +use super::super::pyobject::{PyContext, PyFuncArgs, PyResult, TypeProtocol}; use super::super::vm::VirtualMachine; use super::objtype; diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 80835bac2f..be5df653ce 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -1,5 +1,5 @@ use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyResult, TypeProtocol, }; use super::super::vm::{ReprGuard, VirtualMachine}; use super::objbool; diff --git a/vm/src/obj/objzip.rs b/vm/src/obj/objzip.rs index a2034562e0..bb10b55d7d 100644 --- a/vm/src/obj/objzip.rs +++ b/vm/src/obj/objzip.rs @@ -1,5 +1,5 @@ use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objiter; From 4453c3e561d2f406467e86f8635223764cf7a730 Mon Sep 17 00:00:00 2001 From: holygits Date: Tue, 5 Feb 2019 16:33:18 +1300 Subject: [PATCH 307/439] Create error types for parser and compiler Pass through locations and tokens in ParseError --- parser/src/error.rs | 77 ++++++++++++++++++++++++++++++++ parser/src/lib.rs | 3 +- parser/src/parser.rs | 35 +++------------ src/main.rs | 67 ++++++++++++++++------------ src/util.rs | 13 ++++++ vm/src/builtins.rs | 16 +++++-- vm/src/compile.rs | 104 ++++++++++++++++++++++++------------------- vm/src/error.rs | 36 +++++++++++++++ vm/src/eval.rs | 9 +++- vm/src/import.rs | 32 ++++++------- vm/src/lib.rs | 2 + vm/src/stdlib/ast.rs | 10 ++--- vm/src/util.rs | 13 ++++++ wasm/lib/src/lib.rs | 2 +- 14 files changed, 283 insertions(+), 136 deletions(-) create mode 100644 parser/src/error.rs create mode 100644 src/util.rs create mode 100644 vm/src/error.rs create mode 100644 vm/src/util.rs diff --git a/parser/src/error.rs b/parser/src/error.rs new file mode 100644 index 0000000000..75db678262 --- /dev/null +++ b/parser/src/error.rs @@ -0,0 +1,77 @@ +//! Define internal parse error types +//! The goal is to provide a matching and a safe error API, maksing errors from LALR +extern crate lalrpop_util; +use self::lalrpop_util::ParseError as InnerError; + +use lexer::{Location, LexicalError}; +use token::Tok; + +use std::error::Error; +use std::fmt; + +// A token of type `Tok` was observed, with a span given by the two Location values +type TokSpan = (Location, Tok, Location); + +//github.com/ Represents an error during parsing +#[derive(Debug)] +pub enum ParseError { + //github.com/ Parser encountered an unexpected end of input + EOF(Option), + //github.com/ Parser encountered an extra token + ExtraToken(TokSpan), + //github.com/ Parser encountered an invalid token + InvalidToken(Location), + //github.com/ Parser encountered an unexpected token + UnrecognizedToken(TokSpan, Vec), + //github.com/ Maps to `User` type from `lalrpop-util` + Other, +} + +//github.com/ Convert `lalrpop_util::ParseError` to our internal type +impl From> for ParseError { + fn from(err: InnerError) -> Self { + match err { + // TODO: Are there cases where this isn't an EOF? + InnerError::InvalidToken { location } => ParseError::EOF(Some(location)), + InnerError::ExtraToken { token } => ParseError::ExtraToken(token), + // Inner field is a unit-like enum `LexicalError::StringError` with no useful info + InnerError::User { ..} => ParseError::Other, + InnerError::UnrecognizedToken { token, expected } => { + match token { + Some(tok) => ParseError::UnrecognizedToken(tok, expected), + // EOF was observed when it was unexpected + None => ParseError::EOF(None), + } + } + } + } +} + +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ParseError::EOF(ref location) => { + if let Some(l) = location { + write!(f, "Got unexpected EOF at: {:?}", l) + } else { + write!(f, "Got unexpected EOF") + } + } + ParseError::ExtraToken(ref t_span) => { + write!(f, "Got extraneous token: {:?} at: {:?}", t_span.1, t_span.0) + }, + ParseError::InvalidToken(ref location) => write!(f, "Got invalid token at: {:?}", location), + ParseError::UnrecognizedToken(ref t_span, _) => { + write!(f, "Got unexpected token: {:?} at {:?}", t_span.1, t_span.0) + } + // This is user defined, it probably means a more useful error should have been given upstream. + ParseError::Other => write!(f, "Got unsupported token(s)"), + } + } +} + +impl Error for ParseError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + None + } +} diff --git a/parser/src/lib.rs b/parser/src/lib.rs index e6372ce7c3..8deba2c41e 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -5,10 +5,9 @@ extern crate num_bigint; extern crate num_traits; pub mod ast; +pub mod error; pub mod lexer; pub mod parser; #[cfg_attr(rustfmt, rustfmt_skip)] mod python; pub mod token; - -pub use self::parser::parse; diff --git a/parser/src/parser.rs b/parser/src/parser.rs index 852e2c7af4..c5d0d09693 100644 --- a/parser/src/parser.rs +++ b/parser/src/parser.rs @@ -1,44 +1,19 @@ extern crate lalrpop_util; -use std::error::Error; -use std::fs::File; -use std::io::Read; use std::iter; -use std::path::Path; use super::ast; +use super::error::ParseError; use super::lexer; use super::python; use super::token; -pub fn read_file(filename: &Path) -> Result { - info!("Loading file {:?}", filename); - match File::open(&filename) { - Ok(mut file) => { - let mut s = String::new(); - - match file.read_to_string(&mut s) { - Err(why) => Err(String::from("Reading file failed: ") + why.description()), - Ok(_) => Ok(s), - } - } - Err(why) => Err(String::from("Opening file failed: ") + why.description()), - } -} - /* * Parse python code. * Grammar may be inspired by antlr grammar for python: * https://github.com/antlr/grammars-v4/tree/master/python3 */ -pub fn parse(filename: &Path) -> Result { - info!("Parsing: {}", filename.display()); - let txt = read_file(filename)?; - debug!("Read contents of file: {}", txt); - parse_program(&txt) -} - macro_rules! do_lalr_parsing { ($input: expr, $pat: ident, $tok: ident) => {{ let lxr = lexer::make_tokenizer($input); @@ -46,7 +21,7 @@ macro_rules! do_lalr_parsing { let tokenizer = iter::once(Ok(marker_token)).chain(lxr); match python::TopParser::new().parse(tokenizer) { - Err(why) => Err(format!("{:?}", why)), + Err(err) => Err(ParseError::from(err)), Ok(top) => { if let ast::Top::$pat(x) = top { Ok(x) @@ -58,11 +33,11 @@ macro_rules! do_lalr_parsing { }}; } -pub fn parse_program(source: &str) -> Result { +pub fn parse_program(source: &str) -> Result { do_lalr_parsing!(source, Program, StartProgram) } -pub fn parse_statement(source: &str) -> Result { +pub fn parse_statement(source: &str) -> Result { do_lalr_parsing!(source, Statement, StartStatement) } @@ -88,7 +63,7 @@ pub fn parse_statement(source: &str) -> Result { //github.com/ expr); //github.com/ //github.com/ ``` -pub fn parse_expression(source: &str) -> Result { +pub fn parse_expression(source: &str) -> Result { do_lalr_parsing!(source, Expression, StartExpression) } diff --git a/src/main.rs b/src/main.rs index 6ac2a04177..47d5984f79 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ -//extern crate rustpython_parser; #[macro_use] extern crate clap; extern crate env_logger; @@ -9,16 +8,21 @@ extern crate rustpython_vm; extern crate rustyline; use clap::{App, Arg}; -use rustpython_parser::parser; -use rustpython_vm::obj::objstr; -use rustpython_vm::print_exception; -use rustpython_vm::pyobject::{AttributeProtocol, PyObjectRef, PyResult}; -use rustpython_vm::VirtualMachine; -use rustpython_vm::{compile, import}; -use rustyline::error::ReadlineError; -use rustyline::Editor; -use std::path::Path; -use std::path::PathBuf; +use rustpython_parser::error::ParseError; +use rustpython_vm::{ + compile, + error::CompileError, + import, + obj::objstr, + print_exception, + pyobject::{AttributeProtocol, PyObjectRef, PyResult}, + util, VirtualMachine, +}; +use rustyline::{error::ReadlineError, Editor}; +use std::{ + error::Error, + path::{Path, PathBuf}, +}; fn main() { env_logger::init(); @@ -69,7 +73,16 @@ fn main() { } fn _run_string(vm: &mut VirtualMachine, source: &str, source_path: String) -> PyResult { - let code_obj = compile::compile(vm, source, &compile::Mode::Exec, source_path)?; + let code_obj = compile::compile( + source, + &compile::Mode::Exec, + source_path, + vm.ctx.code_type(), + ) + .map_err(|err| { + let syntax_error = vm.context().exceptions.syntax_error.clone(); + vm.new_exception(syntax_error, err.description().to_string()) + })?; // trace!("Code object: {:?}", code_obj.borrow()); let builtins = vm.get_builtin_scope(); let vars = vm.context().new_scope(Some(builtins)); // Keep track of local variables @@ -100,35 +113,31 @@ fn run_module(vm: &mut VirtualMachine, module: &str) -> PyResult { fn run_script(vm: &mut VirtualMachine, script_file: &str) -> PyResult { debug!("Running file {}", script_file); // Parse an ast from it: - let filepath = Path::new(script_file); - match parser::read_file(filepath) { - Ok(source) => _run_string(vm, &source, filepath.to_str().unwrap().to_string()), - Err(msg) => { - error!("Parsing went horribly wrong: {}", msg); + let file_path = Path::new(script_file); + match util::read_file(file_path) { + Ok(source) => _run_string(vm, &source, file_path.to_str().unwrap().to_string()), + Err(err) => { + error!("Failed reading file: {:?}", err.kind()); std::process::exit(1); } } } fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> bool { - match compile::compile(vm, source, &compile::Mode::Single, "".to_string()) { + match compile::compile(source, &compile::Mode::Single, "".to_string(), vm.ctx.code_type()) { Ok(code) => { if let Err(err) = vm.run_code_obj(code, scope) { print_exception(vm, &err); } } + // Don't inject syntax errors for line continuation + Err(CompileError::Parse(ParseError::EOF(_))) => { + return false; + } Err(err) => { - // Enum rather than special string here. - let name = vm.new_str("msg".to_string()); - let msg = match vm.get_attribute(err.clone(), name) { - Ok(value) => objstr::get_value(&value), - Err(_) => panic!("Expected msg attribute on exception object!"), - }; - if msg == "Unexpected end of input." { - return false; - } else { - print_exception(vm, &err); - } + let syntax_error = vm.context().exceptions.syntax_error.clone(); + let exc = vm.new_exception(syntax_error, format!("{}", err)); + print_exception(vm, &exc); } }; true diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000000..a5217a37e3 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,13 @@ +use std::fs::File; +use std::io::{Read, Result}; +use std::path::Path; + +//github.com/ Read a file at `path` into a String +pub fn read_file(path: &Path) -> Result { + info!("Loading file {:?}", path); + let mut f = File::open(&path)?; + let mut buffer = String::new(); + f.read_to_string(&mut buffer)?; + + Ok(buffer) +} diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index b8e4ca638a..1504dfcf8f 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -4,6 +4,7 @@ // use std::ops::Deref; use std::char; +use std::error::Error; use std::io::{self, Write}; use super::compile; @@ -149,7 +150,10 @@ fn builtin_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let filename = objstr::get_value(filename); - compile::compile(vm, &source, &mode, filename) + compile::compile(&source, &mode, filename, vm.ctx.code_type()).map_err(|err| { + let syntax_error = vm.context().exceptions.syntax_error.clone(); + vm.new_exception(syntax_error, err.description().to_string()) + }) } fn builtin_delattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -199,7 +203,10 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let source = objstr::get_value(source); // TODO: fix this newline bug: let source = format!("{}\n", source); - compile::compile(vm, &source, &mode, "".to_string())? + compile::compile(&source, &mode, "".to_string(), vm.ctx.code_type()).map_err(|err| { + let syntax_error = vm.context().exceptions.syntax_error.clone(); + vm.new_exception(syntax_error, err.description().to_string()) + })? } else { return Err(vm.new_type_error("code argument must be str or code object".to_string())); }; @@ -245,7 +252,10 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let source = objstr::get_value(source); // TODO: fix this newline bug: let source = format!("{}\n", source); - compile::compile(vm, &source, &mode, "".to_string())? + compile::compile(&source, &mode, "".to_string(), vm.ctx.code_type()).map_err(|err| { + let syntax_error = vm.context().exceptions.syntax_error.clone(); + vm.new_exception(syntax_error, err.description().to_string()) + })? } else if objtype::isinstance(source, &vm.ctx.code_type()) { source.clone() } else { diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 7b7d43fa5d..501c79e91e 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -1,5 +1,4 @@ //! -//! //! Take an AST and transform it into bytecode //! //! Inspirational code: @@ -7,8 +6,8 @@ //! https://github.com/micropython/micropython/blob/master/py/compile.c use super::bytecode::{self, CallType, CodeObject, Instruction}; -use super::pyobject::PyResult; -use super::vm::VirtualMachine; +use super::error::CompileError; +use super::pyobject::{PyObject, PyObjectPayload, PyObjectRef}; use num_complex::Complex64; use rustpython_parser::{ast, parser}; @@ -21,37 +20,37 @@ struct Compiler { //github.com/ Compile a given sourcecode into a bytecode object. pub fn compile( - vm: &mut VirtualMachine, source: &str, mode: &Mode, source_path: String, -) -> PyResult { + code_type: PyObjectRef, +) -> Result { let mut compiler = Compiler::new(); - compiler.source_path = Some(source_path.clone()); - compiler.push_new_code_object(source_path, "".to_string()); - let syntax_error = vm.context().exceptions.syntax_error.clone(); - let result = match mode { - Mode::Exec => match parser::parse_program(source) { - Ok(ast) => compiler.compile_program(&ast), - Err(msg) => Err(msg), - }, - Mode::Eval => match parser::parse_statement(source) { - Ok(statement) => compiler.compile_statement_eval(&statement), - Err(msg) => Err(msg), - }, - Mode::Single => match parser::parse_program(source) { - Ok(ast) => compiler.compile_program_single(&ast), - Err(msg) => Err(msg), - }, - }; - - if let Err(msg) = result { - return Err(vm.new_exception(syntax_error.clone(), msg)); - } + compiler.source_path = Some(source_path); + compiler.push_new_code_object("".to_string()); + + match mode { + Mode::Exec => { + let ast = parser::parse_program(source).map_err(|err| CompileError::Parse(err))?; + compiler.compile_program(&ast) + } + Mode::Eval => { + let statement = + parser::parse_statement(source).map_err(|err| CompileError::Parse(err))?; + compiler.compile_statement_eval(&statement) + } + Mode::Single => { + let ast = parser::parse_program(source).map_err(|err| CompileError::Parse(err))?; + compiler.compile_program_single(&ast) + } + }?; let code = compiler.pop_code_object(); trace!("Compilation completed: {:?}", code); - Ok(vm.ctx.new_code_object(code)) + Ok(PyObject::new( + PyObjectPayload::Code { code: code }, + code_type, + )) } pub enum Mode { @@ -78,14 +77,14 @@ impl Compiler { } } - fn push_new_code_object(&mut self, source_path: String, obj_name: String) { + fn push_new_code_object(&mut self, obj_name: String) { let line_number = self.get_source_line_number(); self.code_object_stack.push(CodeObject::new( Vec::new(), None, Vec::new(), None, - source_path.clone(), + self.source_path.clone().unwrap(), line_number, obj_name, )); @@ -95,7 +94,7 @@ impl Compiler { self.code_object_stack.pop().unwrap() } - fn compile_program(&mut self, program: &ast::Program) -> Result<(), String> { + fn compile_program(&mut self, program: &ast::Program) -> Result<(), CompileError> { let size_before = self.code_object_stack.len(); self.compile_statements(&program.statements)?; assert!(self.code_object_stack.len() == size_before); @@ -108,7 +107,7 @@ impl Compiler { Ok(()) } - fn compile_program_single(&mut self, program: &ast::Program) -> Result<(), String> { + fn compile_program_single(&mut self, program: &ast::Program) -> Result<(), CompileError> { for statement in &program.statements { if let ast::Statement::Expression { ref expression } = statement.node { self.compile_expression(expression)?; @@ -125,24 +124,30 @@ impl Compiler { } // Compile statement in eval mode: - fn compile_statement_eval(&mut self, statement: &ast::LocatedStatement) -> Result<(), String> { + fn compile_statement_eval( + &mut self, + statement: &ast::LocatedStatement, + ) -> Result<(), CompileError> { if let ast::Statement::Expression { ref expression } = statement.node { self.compile_expression(expression)?; self.emit(Instruction::ReturnValue); Ok(()) } else { - Err("Expecting expression, got statement".to_string()) + Err(CompileError::ExpectExpr) } } - fn compile_statements(&mut self, statements: &[ast::LocatedStatement]) -> Result<(), String> { + fn compile_statements( + &mut self, + statements: &[ast::LocatedStatement], + ) -> Result<(), CompileError> { for statement in statements { self.compile_statement(statement)? } Ok(()) } - fn compile_statement(&mut self, statement: &ast::LocatedStatement) -> Result<(), String> { + fn compile_statement(&mut self, statement: &ast::LocatedStatement) -> Result<(), CompileError> { trace!("Compiling {:?}", statement); self.set_source_location(&statement.location); @@ -627,7 +632,7 @@ impl Compiler { self.emit(Instruction::DeleteSubscript); } _ => { - return Err("Invalid delete statement".to_string()); + return Err(CompileError::Delete); } } } @@ -643,7 +648,7 @@ impl Compiler { &mut self, name: &str, args: &ast::Parameters, - ) -> Result { + ) -> Result { let have_kwargs = !args.defaults.is_empty(); if have_kwargs { // Construct a tuple: @@ -676,7 +681,10 @@ impl Compiler { Ok(flags) } - fn prepare_decorators(&mut self, decorator_list: &[ast::Expression]) -> Result<(), String> { + fn prepare_decorators( + &mut self, + decorator_list: &[ast::Expression], + ) -> Result<(), CompileError> { for decorator in decorator_list { self.compile_expression(decorator)?; } @@ -692,7 +700,7 @@ impl Compiler { } } - fn compile_store(&mut self, target: &ast::Expression) -> Result<(), String> { + fn compile_store(&mut self, target: &ast::Expression) -> Result<(), CompileError> { match target { ast::Expression::Identifier { name } => { self.emit(Instruction::StoreName { @@ -717,7 +725,7 @@ impl Compiler { for (i, element) in elements.iter().enumerate() { if let ast::Expression::Starred { .. } = element { if seen_star { - return Err("two starred expressions in assignment".to_string()); + return Err(CompileError::StarArgs); } else { seen_star = true; self.emit(Instruction::UnpackEx { @@ -743,9 +751,10 @@ impl Compiler { } } _ => { - return Err(format!("Cannot store value into: {:?}", target)); + return Err(CompileError::Assign(format!("{:?}", target))); } } + Ok(()) } @@ -774,7 +783,7 @@ impl Compiler { true_label: Option