From cf400501a1ba2dd5a1d9dede3a58dd2be40ece13 Mon Sep 17 00:00:00 2001 From: Ricky Han Date: Mon, 25 Feb 2019 16:32:50 -0500 Subject: [PATCH 01/23] move set_item to DictProtocol --- vm/src/import.rs | 2 +- vm/src/obj/objobject.rs | 4 ++-- vm/src/pyobject.rs | 34 +++++++++++++++++++++------------- vm/src/stdlib/json.rs | 2 +- vm/src/sysmodule.rs | 4 ++-- vm/src/vm.rs | 10 ++++++++-- 6 files changed, 35 insertions(+), 21 deletions(-) diff --git a/vm/src/import.rs b/vm/src/import.rs index b0b53156c..454cf8442 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -60,7 +60,7 @@ pub fn import_module( return Ok(module); } let module = import_uncached_module(vm, current_path, module_name)?; - vm.ctx.set_item(&sys_modules, module_name, module.clone()); + sys_modules.set_item(&vm.ctx, module_name, module.clone()); Ok(module) } diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 62f31b8c2..361952237 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -2,7 +2,7 @@ use super::objstr; use super::objtype; use crate::pyobject::{ AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, - TypeProtocol, + TypeProtocol, DictProtocol, }; use crate::vm::VirtualMachine; use std::cell::RefCell; @@ -178,7 +178,7 @@ fn object_dict(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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()); + new_dict.set_item(&vm.ctx, &attr, value.clone()); } Ok(new_dict) } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 073a5d646..0ce906b24 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -640,17 +640,6 @@ impl PyContext { ) } - // Item set/get: - pub fn set_item(&self, obj: &PyObjectRef, key: &str, v: PyObjectRef) { - 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); - } - ref k => panic!("TODO {:?}", k), - }; - } - pub fn get_attr(&self, obj: &PyObjectRef, attr_name: &str) -> Option { // This does not need to be on the PyContext. // We do not require to make a new key as string for this function @@ -665,7 +654,7 @@ impl PyContext { dict.borrow_mut().insert(attr_name.to_string(), value); } PyObjectPayload::Scope { ref scope } => { - self.set_item(&scope.locals, attr_name, value); + scope.locals.set_item(self, attr_name, value); } ref payload => unimplemented!("set_attr unimplemented for: {:?}", payload), }; @@ -822,6 +811,7 @@ pub trait DictProtocol { fn contains_key(&self, k: &str) -> bool; fn get_item(&self, k: &str) -> Option; fn get_key_value_pairs(&self) -> Vec<(PyObjectRef, PyObjectRef)>; + fn set_item(&self, ctx: &PyContext, key: &str, v: PyObjectRef); } impl DictProtocol for PyObjectRef { @@ -838,8 +828,9 @@ 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 { ref dict, .. } => dict.get_item(k), PyObjectPayload::Scope { ref scope } => scope.locals.get_item(k), - _ => panic!("TODO"), + ref k => panic!("TODO {:?}", k), } } @@ -851,6 +842,23 @@ impl DictProtocol for PyObjectRef { _ => panic!("TODO"), } } + + // Item set/get: + fn set_item(&self, ctx: &PyContext, key: &str, v: PyObjectRef) { + match self.borrow_mut().payload { + PyObjectPayload::Dict { ref mut elements } => { + let key = ctx.new_str(key.to_string()); + objdict::set_item_in_content(elements, &key, &v); + } + PyObjectPayload::Module { ref mut dict, .. } => { + dict.set_item(ctx, key, v); + } + PyObjectPayload::Scope { ref mut scope, .. } => { + scope.locals.set_item(ctx, key, v); + } + ref k => panic!("TODO {:?}", k), + }; + } } pub trait BufferProtocol { diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index 53507e81d..97d85b3df 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -171,7 +171,7 @@ impl<'de> Visitor<'de> for PyObjectDeserializer<'de> { PyObjectPayload::String { ref value } => value.clone(), _ => unimplemented!("map keys must be strings"), }; - self.vm.ctx.set_item(&dict, &key, value); + dict.set_item(&self.vm.ctx, &key, value); } Ok(dict) } diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index 1e07b4d9b..0a93f2c96 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -1,4 +1,4 @@ -use crate::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; +use crate::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol, DictProtocol}; use crate::vm::VirtualMachine; use std::rc::Rc; use std::{env, mem}; @@ -132,7 +132,7 @@ settrace() -- set the global debug tracing function "_getframe" => ctx.new_rustfunc(getframe), }); - ctx.set_item(&modules, sys_name, sys_mod.clone()); + modules.set_item(&ctx, sys_name, sys_mod.clone()); ctx.set_attr(&sys_mod, "modules", modules); sys_mod diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 149a5313c..2953066a6 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -26,6 +26,7 @@ use crate::pyobject::{ }; use crate::stdlib; use crate::sysmodule; +use num_bigint::ToBigInt; // use objects::objects; @@ -53,7 +54,7 @@ impl VirtualMachine { // Add builtins as builtins module: let modules = sysmod.get_attr("modules").unwrap(); - ctx.set_item(&modules, "builtins", builtins.clone()); + modules.set_item(&ctx, "builtins", builtins.clone()); let stdlib_inits = stdlib::get_module_inits(); VirtualMachine { @@ -76,6 +77,11 @@ impl VirtualMachine { self.ctx.new_str(s) } + /// Create a new python int object. + pub fn new_int(&self, i: T) -> PyObjectRef { + self.ctx.new_int(i) + } + /// Create a new python bool object. pub fn new_bool(&self, b: bool) -> PyObjectRef { self.ctx.new_bool(b) @@ -395,7 +401,7 @@ impl VirtualMachine { self.ctx.set_attr(scope, &name, value); } else if let Some(d) = &kwargs { - self.ctx.set_item(d, &name, value); + d.set_item(&self.ctx, &name, value); } else { return Err( self.new_type_error(format!("Got an unexpected keyword argument '{}'", name)) From 6f85a44f7c58350d047f046a834c4c511f5dc2a1 Mon Sep 17 00:00:00 2001 From: Ricky Han Date: Mon, 25 Feb 2019 16:35:19 -0500 Subject: [PATCH 02/23] cargo fmt --- parser/src/lexer.rs | 4 +++- vm/src/obj/objobject.rs | 4 ++-- vm/src/stdlib/mod.rs | 5 ++++- vm/src/sysmodule.rs | 2 +- wasm/lib/src/browser_module.rs | 3 ++- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index 619b2dd1a..bb2b3bcd3 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -687,7 +687,9 @@ where match self.chr0 { Some('0'..='9') => return Some(self.lex_number()), - Some('_') | Some('a'..='z') | Some('A'..='Z') => return Some(self.lex_identifier()), + Some('_') | Some('a'..='z') | Some('A'..='Z') => { + return Some(self.lex_identifier()) + } Some('#') => { self.lex_comment(); continue; diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 361952237..007be8a3b 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -1,8 +1,8 @@ use super::objstr; use super::objtype; use crate::pyobject::{ - AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, - TypeProtocol, DictProtocol, + AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, + PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use std::cell::RefCell; diff --git a/vm/src/stdlib/mod.rs b/vm/src/stdlib/mod.rs index 0dabba9b1..4ec43f9cd 100644 --- a/vm/src/stdlib/mod.rs +++ b/vm/src/stdlib/mod.rs @@ -25,7 +25,10 @@ pub type StdlibInitFunc = Box PyObjectRef>; pub fn get_module_inits() -> HashMap { let mut modules = HashMap::new(); - modules.insert("ast".to_string(), Box::new(ast::mk_module) as StdlibInitFunc); + modules.insert( + "ast".to_string(), + Box::new(ast::mk_module) as StdlibInitFunc, + ); modules.insert("dis".to_string(), Box::new(dis::mk_module)); modules.insert("json".to_string(), Box::new(json::mk_module)); modules.insert("keyword".to_string(), Box::new(keyword::mk_module)); diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index 0a93f2c96..59b88de8d 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -1,4 +1,4 @@ -use crate::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol, DictProtocol}; +use crate::pyobject::{DictProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; use std::rc::Rc; use std::{env, mem}; diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index f1be3033f..621afb214 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -134,5 +134,6 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { } pub fn setup_browser_module(vm: &mut VirtualMachine) { - vm.stdlib_inits.insert(BROWSER_NAME.to_string(), Box::new(mk_module)); + vm.stdlib_inits + .insert(BROWSER_NAME.to_string(), Box::new(mk_module)); } From b1c97ec8774eb74c8692b3e17590d3d75f14c819 Mon Sep 17 00:00:00 2001 From: Ricky Han Date: Mon, 25 Feb 2019 23:28:34 -0500 Subject: [PATCH 03/23] refactor macros --- vm/src/macros.rs | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/vm/src/macros.rs b/vm/src/macros.rs index a05972961..64ce7d3d4 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -14,12 +14,12 @@ macro_rules! count_tts { #[macro_export] macro_rules! type_check { - ($vm:ident, $args:ident, $arg_count:ident, $arg_name:ident, $arg_type:expr) => { + + ($vm:ident, $arg:expr, $arg_count: expr, $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 !$crate::obj::objtype::isinstance(arg, &expected_type) { - let arg_typ = arg.typ(); + if !$crate::obj::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!( @@ -71,7 +71,7 @@ macro_rules! arg_check { // check if the type matches. If not, return with error // assign the arg to a variable $( - type_check!($vm, $args, arg_count, $arg_name, $arg_type); + type_check!($vm, &$args.args[arg_count], arg_count, $arg_name, $arg_type); let $arg_name = &$args.args[arg_count]; #[allow(unused_assignments)] { @@ -83,14 +83,19 @@ macro_rules! arg_check { // check if the type matches. If not, return with error // assign the arg to a variable $( - let $optional_arg_name = if arg_count < $args.args.len() { - type_check!($vm, $args, arg_count, $optional_arg_name, $optional_arg_type); - let ret = Some(&$args.args[arg_count]); - #[allow(unused_assignments)] - { - arg_count += 1; + let len = $args.args.len(); + let $optional_arg_name = if arg_count >= len && (arg_count - len) < $args.kwargs.len() { + type_check!($vm, &$args.kwargs[arg_count-len].1, arg_count-len, $optional_arg_name, $optional_arg_type); + let kwarg = &$args.kwargs[arg_count - len]; + if &kwarg.0 == stringify!($optional_arg_name) { + #[allow(unused_assignments)] + { + arg_count += 1; + } + Some(&kwarg.1) + } else { + None } - ret } else { None }; From 3fc583813ab13f5abbef65db347eee2382e308c5 Mon Sep 17 00:00:00 2001 From: Ricky Han Date: Tue, 26 Feb 2019 00:05:41 -0500 Subject: [PATCH 04/23] fix macro --- vm/src/macros.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/vm/src/macros.rs b/vm/src/macros.rs index 64ce7d3d4..6b725519e 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -84,9 +84,11 @@ macro_rules! arg_check { // assign the arg to a variable $( let len = $args.args.len(); + let klen = $args.kwargs.len(); let $optional_arg_name = if arg_count >= len && (arg_count - len) < $args.kwargs.len() { - type_check!($vm, &$args.kwargs[arg_count-len].1, arg_count-len, $optional_arg_name, $optional_arg_type); - let kwarg = &$args.kwargs[arg_count - len]; + let idx = klen-(arg_count-len) - 1; + type_check!($vm, &$args.kwargs[idx].1, arg_count-len, $optional_arg_name, $optional_arg_type); + let kwarg = &$args.kwargs[idx]; if &kwarg.0 == stringify!($optional_arg_name) { #[allow(unused_assignments)] { From b0bf93e5306ba28e556ef3e2fb5736488af984b2 Mon Sep 17 00:00:00 2001 From: Aaron Date: Mon, 25 Feb 2019 21:13:18 -0800 Subject: [PATCH 05/23] add lexer error --- parser/src/lexer.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index 619b2dd1a..de15771f4 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -55,6 +55,7 @@ pub struct Lexer> { pub enum LexicalError { StringError, NestingError, + UnrecognizedToken { tok: char }, } #[derive(Clone, Debug, Default, PartialEq)] @@ -687,7 +688,9 @@ where match self.chr0 { Some('0'..='9') => return Some(self.lex_number()), - Some('_') | Some('a'..='z') | Some('A'..='Z') => return Some(self.lex_identifier()), + Some('_') | Some('a'..='z') | Some('A'..='Z') => { + return Some(self.lex_identifier()) + } Some('#') => { self.lex_comment(); continue; @@ -1033,7 +1036,7 @@ where None => return None, _ => { let c = self.next_char(); - panic!("Not impl {:?}", c) + return Some(Err(LexicalError::UnrecognizedToken { tok: c.unwrap() })); } // Ignore all the rest.. } } From 93a6b4fef9175fdc1527087019f6b3c4cb9a3864 Mon Sep 17 00:00:00 2001 From: Ricky Han Date: Tue, 26 Feb 2019 00:32:19 -0500 Subject: [PATCH 06/23] reverse kwargs order --- vm/src/macros.rs | 3 +-- vm/src/pyobject.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/vm/src/macros.rs b/vm/src/macros.rs index 6b725519e..fa4ddfd21 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -84,9 +84,8 @@ macro_rules! arg_check { // assign the arg to a variable $( let len = $args.args.len(); - let klen = $args.kwargs.len(); let $optional_arg_name = if arg_count >= len && (arg_count - len) < $args.kwargs.len() { - let idx = klen-(arg_count-len) - 1; + let idx = arg_count-len; type_check!($vm, &$args.kwargs[idx].1, arg_count-len, $optional_arg_name, $optional_arg_type); let kwarg = &$args.kwargs[idx]; if &kwarg.0 == stringify!($optional_arg_name) { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 0ce906b24..3efe4efd8 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -893,7 +893,7 @@ pub struct PyFuncArgs { impl PyFuncArgs { pub fn new(mut args: Vec, kwarg_names: Vec) -> PyFuncArgs { let mut kwargs = vec![]; - for name in kwarg_names.iter().rev() { + for name in kwarg_names.iter() { kwargs.push((name.clone(), args.pop().unwrap())); } PyFuncArgs { args, kwargs } From baf3fe4becb4ec99a730618f40797e961edeb592 Mon Sep 17 00:00:00 2001 From: Ricky Han Date: Tue, 26 Feb 2019 01:15:09 -0500 Subject: [PATCH 07/23] fix build error --- wasm/lib/src/convert.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index df63c959e..187e33c1f 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -134,8 +134,7 @@ pub fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef { for pair in object_entries(&Object::from(js_val)) { let (key, val) = pair.expect("iteration over object to not fail"); let py_val = js_to_py(vm, val); - vm.ctx - .set_item(&dict, &String::from(js_sys::JsString::from(key)), py_val); + dict.set_item(&vm.ctx, &String::from(js_sys::JsString::from(key)), py_val); } dict } From 138de0dfcd4b4f059cbdb367182c2df9e6e68c35 Mon Sep 17 00:00:00 2001 From: Ricky Han Date: Tue, 26 Feb 2019 01:15:41 -0500 Subject: [PATCH 08/23] fix build error --- wasm/lib/src/convert.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index 187e33c1f..a3d976d9f 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -1,7 +1,7 @@ use crate::vm_class::{AccessibleVM, WASMVirtualMachine}; use js_sys::{Array, ArrayBuffer, Object, Reflect, Uint8Array}; use rustpython_vm::obj::{objbytes, objtype}; -use rustpython_vm::pyobject::{self, PyFuncArgs, PyObjectRef, PyResult}; +use rustpython_vm::pyobject::{self, PyFuncArgs, PyObjectRef, PyResult, DictProtocol}; use rustpython_vm::VirtualMachine; use wasm_bindgen::{closure::Closure, prelude::*, JsCast}; From b1088c629c52cf90fc19070d299b0cd6d4a796f6 Mon Sep 17 00:00:00 2001 From: Aaron Date: Tue, 26 Feb 2019 00:55:16 -0800 Subject: [PATCH 09/23] fmt --- parser/src/lexer.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index de15771f4..7061279dc 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -688,9 +688,7 @@ where match self.chr0 { Some('0'..='9') => return Some(self.lex_number()), - Some('_') | Some('a'..='z') | Some('A'..='Z') => { - return Some(self.lex_identifier()) - } + Some('_') | Some('a'..='z') | Some('A'..='Z') => { return Some(self.lex_identifier()); } Some('#') => { self.lex_comment(); continue; From 53eff6c9d7763128712eac4eb5728f69ce54a244 Mon Sep 17 00:00:00 2001 From: Ricky Han Date: Tue, 26 Feb 2019 05:11:38 -0500 Subject: [PATCH 10/23] revert --- vm/src/macros.rs | 3 ++- vm/src/pyobject.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/vm/src/macros.rs b/vm/src/macros.rs index fa4ddfd21..cd6546e5d 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -84,8 +84,9 @@ macro_rules! arg_check { // assign the arg to a variable $( let len = $args.args.len(); + let klen = $args.kwargs.len(); let $optional_arg_name = if arg_count >= len && (arg_count - len) < $args.kwargs.len() { - let idx = arg_count-len; + let idx = klen - (arg_count-len) - 1; type_check!($vm, &$args.kwargs[idx].1, arg_count-len, $optional_arg_name, $optional_arg_type); let kwarg = &$args.kwargs[idx]; if &kwarg.0 == stringify!($optional_arg_name) { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 3efe4efd8..0ce906b24 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -893,7 +893,7 @@ pub struct PyFuncArgs { impl PyFuncArgs { pub fn new(mut args: Vec, kwarg_names: Vec) -> PyFuncArgs { let mut kwargs = vec![]; - for name in kwarg_names.iter() { + for name in kwarg_names.iter().rev() { kwargs.push((name.clone(), args.pop().unwrap())); } PyFuncArgs { args, kwargs } From 267934bb49a2c43c5f731692ced1460f41858b8f Mon Sep 17 00:00:00 2001 From: Aaron Date: Tue, 26 Feb 2019 12:50:18 -0800 Subject: [PATCH 11/23] fmt --- parser/src/lexer.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index 7061279dc..0c3d88780 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -688,7 +688,9 @@ where match self.chr0 { Some('0'..='9') => return Some(self.lex_number()), - Some('_') | Some('a'..='z') | Some('A'..='Z') => { return Some(self.lex_identifier()); } + Some('_') | Some('a'..='z') | Some('A'..='Z') => { + return Some(self.lex_identifier()); + } Some('#') => { self.lex_comment(); continue; From 45da6b8af19c09ff3bc1b91f7b11fc7a5861bc0c Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Tue, 26 Feb 2019 20:27:20 +0100 Subject: [PATCH 12/23] Add list.sort, sorted() --- tests/snippets/list.py | 39 +++++++++++++++ vm/src/builtins.rs | 12 ++++- vm/src/obj/objlist.rs | 99 ++++++++++++++++++++++++++++++++++++--- vm/src/obj/objsequence.rs | 8 ++++ 4 files changed, 151 insertions(+), 7 deletions(-) diff --git a/tests/snippets/list.py b/tests/snippets/list.py index 881279f07..9e385abdc 100644 --- a/tests/snippets/list.py +++ b/tests/snippets/list.py @@ -117,3 +117,42 @@ b.append(c) assert a == b assert [foo] == [foo] + +for size in [1, 2, 3, 4, 5, 8, 10, 100, 1000]: + lst = list(range(size)) + orig = lst[:] + lst.sort() + assert lst == orig + assert sorted(lst) == orig + assert_raises(ZeroDivisionError, lambda: sorted(lst, key=lambda x: 1/x)) + lst.reverse() + assert sorted(lst) == orig + assert sorted(lst, reverse=True) == lst + assert sorted(lst, key=lambda x: -x) == lst + assert sorted(lst, key=lambda x: -x, reverse=True) == orig + +assert sorted([(1, 2, 3), (0, 3, 6)]) == [(0, 3, 6), (1, 2, 3)] +assert sorted([(1, 2, 3), (0, 3, 6)], key=lambda x: x[0]) == [(0, 3, 6), (1, 2, 3)] +assert sorted([(1, 2, 3), (0, 3, 6)], key=lambda x: x[1]) == [(1, 2, 3), (0, 3, 6)] +assert sorted([(1, 2), (), (5,)], key=len) == [(), (5,), (1, 2)] + +lst = [3, 1, 5, 2, 4] +class C: + def __init__(self, x): self.x = x + def __lt__(self, other): return self.x < other.x +lst.sort(key=C) +assert lst == [1, 2, 3, 4, 5] + +lst = [3, 1, 5, 2, 4] +class C: + def __init__(self, x): self.x = x + def __gt__(self, other): return self.x > other.x +lst.sort(key=C) +assert lst == [1, 2, 3, 4, 5] + +lst = [5, 1, 2, 3, 4] +def f(x): + lst.append(1) + return x +assert_raises(ValueError, lambda: lst.sort(key=f)) # "list modified during sort" +assert lst == [1, 2, 3, 4, 5] diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 57f77c81d..870b3573e 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -689,7 +689,16 @@ fn builtin_setattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } // builtin_slice -// builtin_sorted + +fn builtin_sorted(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(iterable, None)]); + let items = vm.extract_elements(iterable)?; + let lst = vm.ctx.new_list(items); + + args.shift(); + vm.call_method_pyargs(&lst, "sort", args)?; + Ok(lst) +} fn builtin_sum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(iterable, None)]); @@ -763,6 +772,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { "round" => ctx.new_rustfunc(builtin_round), "set" => ctx.set_type(), "setattr" => ctx.new_rustfunc(builtin_setattr), + "sorted" => ctx.new_rustfunc(builtin_sorted), "slice" => ctx.slice_type(), "staticmethod" => ctx.staticmethod_type(), "str" => ctx.str_type(), diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 8c72bc9a9..e09f15a8b 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -3,8 +3,8 @@ use std::cell::{Cell, RefCell}; 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, seq_mul, - PySliceableSequence, + get_elements, get_elements_cell, get_item, get_mut_elements, seq_equal, seq_ge, seq_gt, seq_le, + seq_lt, seq_mul, PySliceableSequence, }; use super::objstr; use super::objtype; @@ -334,12 +334,99 @@ fn list_reverse(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.get_none()) } +fn quicksort( + vm: &mut VirtualMachine, + keys: &mut [PyObjectRef], + values: &mut [PyObjectRef], +) -> PyResult<()> { + let len = values.len(); + if len >= 2 { + let pivot = partition(vm, keys, values)?; + quicksort(vm, &mut keys[0..pivot], &mut values[0..pivot])?; + quicksort(vm, &mut keys[pivot + 1..len], &mut values[pivot + 1..len])?; + } + Ok(()) +} + +fn partition( + vm: &mut VirtualMachine, + keys: &mut [PyObjectRef], + values: &mut [PyObjectRef], +) -> PyResult { + let len = values.len(); + let pivot = len / 2; + + values.swap(pivot, len - 1); + keys.swap(pivot, len - 1); + + let mut store_idx = 0; + for i in 0..len - 1 { + let result = vm._lt(keys[i].clone(), keys[len - 1].clone())?; + let boolval = objbool::boolval(vm, result)?; + if boolval { + values.swap(i, store_idx); + keys.swap(i, store_idx); + store_idx += 1; + } + } + + values.swap(store_idx, len - 1); + keys.swap(store_idx, len - 1); + Ok(store_idx) +} + +fn do_sort( + vm: &mut VirtualMachine, + values: &mut Vec, + key_func: Option, + reverse: bool, +) -> PyResult<()> { + // build a list of keys. If no keyfunc is provided, it's a copy of the list. + let mut keys: Vec = vec![]; + for x in values.iter() { + keys.push(match &key_func { + None => x.clone(), + Some(ref func) => vm.invoke( + (*func).clone(), + PyFuncArgs { + args: vec![x.clone()], + kwargs: vec![], + }, + )?, + }); + } + + quicksort(vm, &mut keys, values)?; + + if reverse { + values.reverse(); + } + + Ok(()) +} + fn list_sort(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(list, Some(vm.ctx.list_type()))]); - let mut _elements = get_mut_elements(list); - unimplemented!("TODO: figure out how to invoke `sort_by` on a Vec"); - // elements.sort_by(); - // Ok(vm.get_none()) + let key_func = args.get_optional_kwarg("key"); + let reverse = args.get_optional_kwarg("reverse"); + let reverse = match reverse { + None => false, + Some(val) => objbool::boolval(vm, val)?, + }; + + let elements_cell = get_elements_cell(list); + // replace list contents with [] for duration of sort. + // this prevents keyfunc from messing with the list and makes it easy to + // check if it tries to append elements to it. + let mut elements = elements_cell.replace(vec![]); + do_sort(vm, &mut elements, key_func, reverse)?; + let temp_elements = elements_cell.replace(elements); + + if !temp_elements.is_empty() { + return Err(vm.new_value_error("list modified during sort".to_string())); + } + + Ok(vm.get_none()) } fn list_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index 58437b98e..46912c1b8 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -302,6 +302,14 @@ pub fn seq_mul(elements: &[PyObjectRef], product: &PyObjectRef) -> Vec(obj: &'a PyObjectRef) -> &'a RefCell> { + if let PyObjectPayload::Sequence { ref elements } = obj.payload { + elements + } else { + panic!("Cannot extract elements from non-sequence"); + } +} + pub fn get_elements<'a>(obj: &'a PyObjectRef) -> impl Deref> + 'a { if let PyObjectPayload::Sequence { ref elements } = obj.payload { elements.borrow() From 830cc7e9900cf3149aba69972ef33566de14fc66 Mon Sep 17 00:00:00 2001 From: ricky han Date: Tue, 26 Feb 2019 17:46:06 -0500 Subject: [PATCH 13/23] revert macro --- vm/src/macros.rs | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/vm/src/macros.rs b/vm/src/macros.rs index cd6546e5d..a05972961 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -14,12 +14,12 @@ macro_rules! count_tts { #[macro_export] macro_rules! type_check { - - ($vm:ident, $arg:expr, $arg_count: expr, $arg_name:ident, $arg_type:expr) => { + ($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 { - if !$crate::obj::objtype::isinstance($arg, &expected_type) { - let arg_typ = $arg.typ(); + let arg = &$args.args[$arg_count]; + if !$crate::obj::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!( @@ -71,7 +71,7 @@ macro_rules! arg_check { // check if the type matches. If not, return with error // assign the arg to a variable $( - type_check!($vm, &$args.args[arg_count], arg_count, $arg_name, $arg_type); + type_check!($vm, $args, arg_count, $arg_name, $arg_type); let $arg_name = &$args.args[arg_count]; #[allow(unused_assignments)] { @@ -83,21 +83,14 @@ macro_rules! arg_check { // check if the type matches. If not, return with error // assign the arg to a variable $( - let len = $args.args.len(); - let klen = $args.kwargs.len(); - let $optional_arg_name = if arg_count >= len && (arg_count - len) < $args.kwargs.len() { - let idx = klen - (arg_count-len) - 1; - type_check!($vm, &$args.kwargs[idx].1, arg_count-len, $optional_arg_name, $optional_arg_type); - let kwarg = &$args.kwargs[idx]; - if &kwarg.0 == stringify!($optional_arg_name) { - #[allow(unused_assignments)] - { - arg_count += 1; - } - Some(&kwarg.1) - } else { - None + let $optional_arg_name = if arg_count < $args.args.len() { + type_check!($vm, $args, arg_count, $optional_arg_name, $optional_arg_type); + let ret = Some(&$args.args[arg_count]); + #[allow(unused_assignments)] + { + arg_count += 1; } + ret } else { None }; From 30145ec6e2bbe5050b3c75af0112e65e9a05203b Mon Sep 17 00:00:00 2001 From: ricky han Date: Tue, 26 Feb 2019 17:47:03 -0500 Subject: [PATCH 14/23] cargo fmt --- vm/src/obj/objobject.rs | 4 ++-- vm/src/pyobject.rs | 4 +++- wasm/lib/src/convert.rs | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index a9c44f165..73a4df608 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -1,8 +1,8 @@ use super::objstr; use super::objtype; use crate::pyobject::{ - AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, - PyObjectRef, PyResult, TypeProtocol, PyObject + AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, + PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use std::cell::RefCell; diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 2cf8e9d93..d29650b26 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -849,7 +849,9 @@ impl DictProtocol for PyObjectRef { fn get_item(&self, k: &str) -> Option { match self.payload { - PyObjectPayload::Dict { ref elements } => objdict::content_get_key_str(&elements.borrow(), k), + PyObjectPayload::Dict { ref elements } => { + objdict::content_get_key_str(&elements.borrow(), k) + } PyObjectPayload::Module { ref dict, .. } => dict.get_item(k), PyObjectPayload::Scope { ref scope } => scope.borrow().locals.get_item(k), ref k => panic!("TODO {:?}", k), diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index a3d976d9f..adcbb3173 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -1,7 +1,7 @@ use crate::vm_class::{AccessibleVM, WASMVirtualMachine}; use js_sys::{Array, ArrayBuffer, Object, Reflect, Uint8Array}; use rustpython_vm::obj::{objbytes, objtype}; -use rustpython_vm::pyobject::{self, PyFuncArgs, PyObjectRef, PyResult, DictProtocol}; +use rustpython_vm::pyobject::{self, DictProtocol, PyFuncArgs, PyObjectRef, PyResult}; use rustpython_vm::VirtualMachine; use wasm_bindgen::{closure::Closure, prelude::*, JsCast}; From d5f0e25686c667edba945a84237fff919eaf36c4 Mon Sep 17 00:00:00 2001 From: ricky han Date: Tue, 26 Feb 2019 18:32:27 -0500 Subject: [PATCH 15/23] add rustfmt.toml --- parser/src/lexer.rs | 4 +++- rustfmt.toml | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 rustfmt.toml diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index 619b2dd1a..bb2b3bcd3 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -687,7 +687,9 @@ where match self.chr0 { Some('0'..='9') => return Some(self.lex_number()), - Some('_') | Some('a'..='z') | Some('A'..='Z') => return Some(self.lex_identifier()), + Some('_') | Some('a'..='z') | Some('A'..='Z') => { + return Some(self.lex_identifier()) + } Some('#') => { self.lex_comment(); continue; diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 000000000..ae9df8459 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +version = "One" From ec93c55e6d58f5c30d59085edeb56bd3d3f2dee3 Mon Sep 17 00:00:00 2001 From: ricky han Date: Tue, 26 Feb 2019 18:37:40 -0500 Subject: [PATCH 16/23] rustfmt --- parser/src/lexer.rs | 2 +- parser/src/parser.rs | 4 +++- rustfmt.toml | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index bb2b3bcd3..24fa7c969 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -688,7 +688,7 @@ where match self.chr0 { Some('0'..='9') => return Some(self.lex_number()), Some('_') | Some('a'..='z') | Some('A'..='Z') => { - return Some(self.lex_identifier()) + return Some(self.lex_identifier()); } Some('#') => { self.lex_comment(); diff --git a/parser/src/parser.rs b/parser/src/parser.rs index 2a39a41f8..4587e457e 100644 --- a/parser/src/parser.rs +++ b/parser/src/parser.rs @@ -306,7 +306,9 @@ mod tests { #[test] fn test_parse_class() { - let source = String::from("class Foo(A, B):\n def __init__(self):\n pass\n def method_with_default(self, arg='default'):\n pass\n"); + let source = String::from( + "class Foo(A, B):\n def __init__(self):\n pass\n def method_with_default(self, arg='default'):\n pass\n", + ); assert_eq!( parse_statement(&source), Ok(ast::LocatedStatement { diff --git a/rustfmt.toml b/rustfmt.toml index ae9df8459..32a9786fa 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1 +1 @@ -version = "One" +edition = "2018" From 21ebb03242f6d84381d0c3ebe2c7b154b360229d Mon Sep 17 00:00:00 2001 From: ricky han Date: Tue, 26 Feb 2019 18:38:16 -0500 Subject: [PATCH 17/23] rust fmt --- parser/src/lexer.rs | 4 +--- rustfmt.toml | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index bb2b3bcd3..619b2dd1a 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -687,9 +687,7 @@ where match self.chr0 { Some('0'..='9') => return Some(self.lex_number()), - Some('_') | Some('a'..='z') | Some('A'..='Z') => { - return Some(self.lex_identifier()) - } + Some('_') | Some('a'..='z') | Some('A'..='Z') => return Some(self.lex_identifier()), Some('#') => { self.lex_comment(); continue; diff --git a/rustfmt.toml b/rustfmt.toml index ae9df8459..32a9786fa 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1 +1 @@ -version = "One" +edition = "2018" From 3df8f1c788918edbaadfd51092ebf14d71904627 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 26 Feb 2019 20:55:23 -0600 Subject: [PATCH 18/23] Update the snippet on page load; insert spaces when tab is pressed --- wasm/demo/src/main.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/wasm/demo/src/main.js b/wasm/demo/src/main.js index f18dac7d0..642507cec 100644 --- a/wasm/demo/src/main.js +++ b/wasm/demo/src/main.js @@ -12,7 +12,11 @@ const editor = CodeMirror.fromTextArea(document.getElementById('code'), { 'Cmd-Enter': runCodeFromTextarea, 'Shift-Tab': 'indentLess', 'Ctrl-/': 'toggleComment', - 'Cmd-/': 'toggleComment' + 'Cmd-/': 'toggleComment', + Tab: editor => { + var spaces = Array(editor.getOption('indentUnit') + 1).join(' '); + editor.replaceSelection(spaces); + } }, lineNumbers: true, mode: 'text/x-python', @@ -51,7 +55,7 @@ document const snippets = document.getElementById('snippets'); -snippets.addEventListener('change', () => { +const updateSnippet = () => { const selected = snippets.value; // the require here creates a webpack context; it's fine to use it @@ -60,9 +64,11 @@ snippets.addEventListener('change', () => { const snippet = require(`raw-loader!../snippets/${selected}.py`); editor.setValue(snippet); - runCodeFromTextarea(); -}); +}; -// Run once for demo -runCodeFromTextarea(); +snippets.addEventListener('change', updateSnippet); + +// Run once for demo (updateSnippet b/c the browser might try to keep the same +// option selected for the `select`, but the textarea won't be updated) +updateSnippet(); From 717392c3c594fadf00b01d37d7d74df6ef154660 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 26 Feb 2019 21:21:01 -0600 Subject: [PATCH 19/23] Scroll to the bottom of the demo when it gets updated --- wasm/demo/src/main.js | 6 +++--- wasm/lib/src/wasm_builtins.rs | 9 +++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/wasm/demo/src/main.js b/wasm/demo/src/main.js index 642507cec..0735f8d22 100644 --- a/wasm/demo/src/main.js +++ b/wasm/demo/src/main.js @@ -24,10 +24,10 @@ const editor = CodeMirror.fromTextArea(document.getElementById('code'), { autofocus: true }); -function runCodeFromTextarea() { - const consoleElement = document.getElementById('console'); - const errorElement = document.getElementById('error'); +const consoleElement = document.getElementById('console'); +const errorElement = document.getElementById('error'); +function runCodeFromTextarea() { // Clean the console and errors consoleElement.value = ''; errorElement.textContent = ''; diff --git a/wasm/lib/src/wasm_builtins.rs b/wasm/lib/src/wasm_builtins.rs index b8f4e121f..c91d8c31e 100644 --- a/wasm/lib/src/wasm_builtins.rs +++ b/wasm/lib/src/wasm_builtins.rs @@ -26,8 +26,17 @@ pub fn print_to_html(text: &str, selector: &str) -> Result<(), JsValue> { let textarea = element .dyn_ref::() .ok_or_else(|| js_sys::TypeError::new("Element must be a textarea"))?; + let value = textarea.value(); + + let scroll_height = textarea.scroll_height(); + let scrolled_to_bottom = scroll_height - textarea.scroll_top() == textarea.client_height(); + textarea.set_value(&format!("{}{}", value, text)); + + if scrolled_to_bottom { + textarea.scroll_with_x_and_y(0.0, scroll_height.into()); + } Ok(()) } From b82e829c1ff7b2e63cf67317d9df592c8e4b4ad9 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 26 Feb 2019 21:27:30 -0600 Subject: [PATCH 20/23] newline --- wasm/lib/src/wasm_builtins.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/wasm/lib/src/wasm_builtins.rs b/wasm/lib/src/wasm_builtins.rs index c91d8c31e..7f2b7a622 100644 --- a/wasm/lib/src/wasm_builtins.rs +++ b/wasm/lib/src/wasm_builtins.rs @@ -37,6 +37,7 @@ pub fn print_to_html(text: &str, selector: &str) -> Result<(), JsValue> { if scrolled_to_bottom { textarea.scroll_with_x_and_y(0.0, scroll_height.into()); } + Ok(()) } From c4513a176b44361a00fd2970c68705b21f5b77eb Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 26 Feb 2019 22:20:28 -0600 Subject: [PATCH 21/23] Proper error handling for int to animationFrame id --- Cargo.lock | 1 + wasm/lib/Cargo.toml | 1 + wasm/lib/src/browser_module.rs | 12 +++++++----- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fd613d7f7..2fa56aaa7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -775,6 +775,7 @@ dependencies = [ "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "js-sys 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "rustpython_parser 0.0.1", "rustpython_vm 0.1.0", "wasm-bindgen 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/wasm/lib/Cargo.toml b/wasm/lib/Cargo.toml index 0c7e06b53..5c4e6c86d 100644 --- a/wasm/lib/Cargo.toml +++ b/wasm/lib/Cargo.toml @@ -18,6 +18,7 @@ wasm-bindgen = "0.2" wasm-bindgen-futures = "0.3" js-sys = "0.3" futures = "0.1" +num-traits = "0.2" [dependencies.web-sys] version = "0.3" diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index e3f697838..2824040a6 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -1,6 +1,7 @@ use crate::{convert, vm_class::AccessibleVM, wasm_builtins::window}; use futures::{future, Future}; use js_sys::Promise; +use num_traits::cast::ToPrimitive; use rustpython_vm::obj::{objint, objstr}; use rustpython_vm::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use rustpython_vm::VirtualMachine; @@ -167,11 +168,12 @@ fn browser_request_animation_frame(vm: &mut VirtualMachine, args: PyFuncArgs) -> fn browser_cancel_animation_frame(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(id, Some(vm.ctx.int_type()))]); - // fine because - let id = objint::get_value(id) - .to_string() - .parse() - .expect("bigint.to_string() to be parsable as i32"); + let id = objint::get_value(id).to_i32().ok_or_else(|| { + vm.new_exception( + vm.ctx.exceptions.value_error.clone(), + "Integer too large to convert to i32 for animationFrame id".into(), + ) + })?; window() .cancel_animation_frame(id) From 15e6187583533dbf2893c2dc7390b4e753ae1f51 Mon Sep 17 00:00:00 2001 From: alexpantyukhin Date: Wed, 27 Feb 2019 11:04:16 +0400 Subject: [PATCH 22/23] add overflow errors for int shifts. --- tests/snippets/ints.py | 3 +++ vm/src/obj/objint.rs | 6 ++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/snippets/ints.py b/tests/snippets/ints.py index 9aa83eba4..3bf91491d 100644 --- a/tests/snippets/ints.py +++ b/tests/snippets/ints.py @@ -1,3 +1,5 @@ +from testutils import assert_raises + # int to int comparisons assert 1 == 1 @@ -36,6 +38,7 @@ assert (2).__rtruediv__(1) == 0.5 assert (1).real == 1 assert (1).imag == 0 +assert_raises(OverflowError, lambda: 1 << 10 ** 100000) assert (1).__eq__(1.0) == NotImplemented assert (1).__ne__(1.0) == NotImplemented diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 9f5e48893..018e995fe 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -246,8 +246,7 @@ fn int_lshift(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match get_value(i2) { 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)); + Err(vm.new_overflow_error("the number is too large to convert to float".to_string())) } _ => panic!("Failed converting {} to rust usize", get_value(i2)), } @@ -276,8 +275,7 @@ fn int_rshift(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match get_value(i2) { 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)); + Err(vm.new_overflow_error("the number is too large to convert to float".to_string())) } _ => panic!("Failed converting {} to rust usize", get_value(i2)), } From 70ad78eb21627698a6ae2de109e6331491948004 Mon Sep 17 00:00:00 2001 From: alexpantyukhin Date: Wed, 27 Feb 2019 13:22:27 +0400 Subject: [PATCH 23/23] fix error messages. --- vm/src/obj/objint.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 018e995fe..8c240fba3 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -246,7 +246,7 @@ fn int_lshift(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match get_value(i2) { 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()) => { - Err(vm.new_overflow_error("the number is too large to convert to float".to_string())) + Err(vm.new_overflow_error("the number is too large to convert to int".to_string())) } _ => panic!("Failed converting {} to rust usize", get_value(i2)), } @@ -275,7 +275,7 @@ fn int_rshift(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match get_value(i2) { 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()) => { - Err(vm.new_overflow_error("the number is too large to convert to float".to_string())) + Err(vm.new_overflow_error("the number is too large to convert to int".to_string())) } _ => panic!("Failed converting {} to rust usize", get_value(i2)), }