From 9c0bcc2bd7277627159b3c6fddd2c00d91f2fae6 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Mon, 18 Feb 2019 12:18:53 -0600 Subject: [PATCH 001/380] Re-add WASM fetch builtin --- wasm/lib/src/lib.rs | 2 + wasm/lib/src/wasm_builtins.rs | 116 +++++++++++++++++++++++++++++++++- 2 files changed, 116 insertions(+), 2 deletions(-) diff --git a/wasm/lib/src/lib.rs b/wasm/lib/src/lib.rs index 240e7861c3..0791465e8b 100644 --- a/wasm/lib/src/lib.rs +++ b/wasm/lib/src/lib.rs @@ -4,8 +4,10 @@ pub mod wasm_builtins; extern crate futures; extern crate js_sys; +#[macro_use] extern crate rustpython_vm; extern crate wasm_bindgen; +extern crate wasm_bindgen_futures; extern crate web_sys; use js_sys::{Object, Reflect, TypeError}; diff --git a/wasm/lib/src/wasm_builtins.rs b/wasm/lib/src/wasm_builtins.rs index 211eedb415..e1fee0f8d4 100644 --- a/wasm/lib/src/wasm_builtins.rs +++ b/wasm/lib/src/wasm_builtins.rs @@ -8,14 +8,17 @@ extern crate futures; extern crate js_sys; extern crate wasm_bindgen; +extern crate wasm_bindgen_futures; extern crate web_sys; -use crate::convert; -use js_sys::Array; +use crate::{convert, vm_class::AccessibleVM}; +use futures::{future, Future}; +use js_sys::{Array, Promise}; use rustpython_vm::obj::{objstr, objtype}; use rustpython_vm::pyobject::{IdProtocol, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use rustpython_vm::VirtualMachine; use wasm_bindgen::{prelude::*, JsCast}; +use wasm_bindgen_futures::{future_to_promise, JsFuture}; use web_sys::{console, HtmlTextAreaElement}; fn window() -> web_sys::Window { @@ -104,7 +107,116 @@ pub fn builtin_print_console(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyRes Ok(vm.get_none()) } +enum FetchResponseFormat { + Json, + Text, + ArrayBuffer, +} + +impl FetchResponseFormat { + fn from_str(vm: &mut VirtualMachine, s: &str) -> Result { + match s { + "json" => Ok(FetchResponseFormat::Json), + "text" => Ok(FetchResponseFormat::Text), + "array_buffer" => Ok(FetchResponseFormat::ArrayBuffer), + _ => Err(vm.new_type_error("Unkown fetch response_format".into())), + } + } + fn get_response(&self, response: &web_sys::Response) -> Result { + match self { + FetchResponseFormat::Json => response.json(), + FetchResponseFormat::Text => response.text(), + FetchResponseFormat::ArrayBuffer => response.array_buffer(), + } + } +} + +fn builtin_fetch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [ + (url, Some(vm.ctx.str_type())), + (handler, Some(vm.ctx.function_type())) + ], + // TODO: use named parameters for these + optional = [ + (reject_handler, Some(vm.ctx.function_type())), + (response_format, Some(vm.ctx.str_type())), + (method, Some(vm.ctx.str_type())), + (headers, Some(vm.ctx.dict_type())) + ] + ); + + let response_format = match response_format { + Some(s) => FetchResponseFormat::from_str(vm, &objstr::get_value(s))?, + None => FetchResponseFormat::Text, + }; + + let mut opts = web_sys::RequestInit::new(); + + match method { + Some(s) => opts.method(&objstr::get_value(s)), + None => opts.method("GET"), + }; + + let request = web_sys::Request::new_with_str_and_init(&objstr::get_value(url), &opts) + .map_err(|err| convert::js_py_typeerror(vm, err))?; + + if let Some(headers) = headers { + use rustpython_vm::obj::objdict; + let h = request.headers(); + for (key, value) in objdict::get_key_value_pairs(headers) { + let key = objstr::get_value(&vm.to_str(&key)?); + let value = objstr::get_value(&vm.to_str(&value)?); + h.set(&key, &value) + .map_err(|err| convert::js_py_typeerror(vm, err))?; + } + } + + let window = window(); + let request_prom = window.fetch_with_request(&request); + + let handler = handler.clone(); + let reject_handler = reject_handler.cloned(); + + let acc_vm = AccessibleVM::from_vm(vm); + + let future = JsFuture::from(request_prom) + .and_then(move |val| { + let response = val + .dyn_into::() + .expect("val to be of type Response"); + response_format.get_response(&response) + }) + .and_then(|prom| JsFuture::from(prom)) + .then(move |val| { + let vm = &mut acc_vm + .upgrade() + .expect("that the VM *not* be destroyed while promise is being resolved"); + match val { + Ok(val) => { + let val = convert::js_to_py(vm, val); + let args = PyFuncArgs::new(vec![val], vec![]); + let _ = vm.invoke(handler, args); + } + Err(val) => { + if let Some(reject_handler) = reject_handler { + let val = convert::js_to_py(vm, val); + let args = PyFuncArgs::new(vec![val], vec![]); + let _ = vm.invoke(reject_handler, args); + } + } + } + future::ok(JsValue::UNDEFINED) + }); + future_to_promise(future); + + Ok(vm.get_none()) +} + pub fn setup_wasm_builtins(vm: &mut VirtualMachine, scope: &PyObjectRef) { let ctx = vm.context(); ctx.set_attr(scope, "print", ctx.new_rustfunc(builtin_print_console)); + ctx.set_attr(scope, "fetch", ctx.new_rustfunc(builtin_fetch)); } From 4edca2121d8d20d458aee8e1e1ba9d1f18cc6cfd Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Mon, 18 Feb 2019 15:25:52 -0600 Subject: [PATCH 002/380] Convert some fetch options to kwargs, add PyFuncArgs method --- vm/src/pyobject.rs | 23 +++++++++++++++++++++++ wasm/lib/src/wasm_builtins.rs | 18 ++++++++---------- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 4ec2058e4d..0b14ca874a 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -920,6 +920,29 @@ impl PyFuncArgs { } None } + + pub fn get_optional_kwarg_with_type( + &self, + key: &str, + ty: PyObjectRef, + vm: &mut VirtualMachine, + ) -> Result, PyObjectRef> { + match self.get_optional_kwarg(key) { + Some(kwarg) => { + if objtype::isinstance(&kwarg, &ty) { + Ok(Some(kwarg)) + } else { + let expected_ty_name = vm.to_pystr(&ty)?; + let actual_ty_name = vm.to_pystr(&kwarg.typ())?; + Err(vm.new_type_error(format!( + "argument of type {} is required for named parameter `{}` (got: {})", + expected_ty_name, key, actual_ty_name + ))) + } + } + None => Ok(None), + } + } } /// Rather than determining the type of a python object, this enum is more diff --git a/wasm/lib/src/wasm_builtins.rs b/wasm/lib/src/wasm_builtins.rs index e1fee0f8d4..808200f695 100644 --- a/wasm/lib/src/wasm_builtins.rs +++ b/wasm/lib/src/wasm_builtins.rs @@ -139,24 +139,22 @@ fn builtin_fetch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { (url, Some(vm.ctx.str_type())), (handler, Some(vm.ctx.function_type())) ], - // TODO: use named parameters for these - optional = [ - (reject_handler, Some(vm.ctx.function_type())), - (response_format, Some(vm.ctx.str_type())), - (method, Some(vm.ctx.str_type())), - (headers, Some(vm.ctx.dict_type())) - ] + optional = [(reject_handler, Some(vm.ctx.function_type()))] ); + let response_format = + args.get_optional_kwarg_with_type("response_format", vm.ctx.str_type(), vm)?; + let method = args.get_optional_kwarg_with_type("method", vm.ctx.str_type(), vm)?; + let headers = args.get_optional_kwarg_with_type("headers", vm.ctx.dict_type(), vm)?; let response_format = match response_format { - Some(s) => FetchResponseFormat::from_str(vm, &objstr::get_value(s))?, + Some(s) => FetchResponseFormat::from_str(vm, &objstr::get_value(&s))?, None => FetchResponseFormat::Text, }; let mut opts = web_sys::RequestInit::new(); match method { - Some(s) => opts.method(&objstr::get_value(s)), + Some(s) => opts.method(&objstr::get_value(&s)), None => opts.method("GET"), }; @@ -166,7 +164,7 @@ fn builtin_fetch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if let Some(headers) = headers { use rustpython_vm::obj::objdict; let h = request.headers(); - for (key, value) in objdict::get_key_value_pairs(headers) { + for (key, value) in objdict::get_key_value_pairs(&headers) { let key = objstr::get_value(&vm.to_str(&key)?); let value = objstr::get_value(&vm.to_str(&value)?); h.set(&key, &value) From 92a37335413f48d96f45453abe2512118d40b219 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Mon, 18 Feb 2019 15:34:06 -0600 Subject: [PATCH 003/380] Add body option to fetch --- wasm/lib/src/wasm_builtins.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/wasm/lib/src/wasm_builtins.rs b/wasm/lib/src/wasm_builtins.rs index 808200f695..97abc89976 100644 --- a/wasm/lib/src/wasm_builtins.rs +++ b/wasm/lib/src/wasm_builtins.rs @@ -145,6 +145,7 @@ fn builtin_fetch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args.get_optional_kwarg_with_type("response_format", vm.ctx.str_type(), vm)?; let method = args.get_optional_kwarg_with_type("method", vm.ctx.str_type(), vm)?; let headers = args.get_optional_kwarg_with_type("headers", vm.ctx.dict_type(), vm)?; + let body = args.get_optional_kwarg("body"); let response_format = match response_format { Some(s) => FetchResponseFormat::from_str(vm, &objstr::get_value(&s))?, @@ -158,13 +159,16 @@ fn builtin_fetch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { None => opts.method("GET"), }; + if let Some(body) = body { + opts.body(Some(&convert::py_to_js(vm, body))); + } + let request = web_sys::Request::new_with_str_and_init(&objstr::get_value(url), &opts) .map_err(|err| convert::js_py_typeerror(vm, err))?; if let Some(headers) = headers { - use rustpython_vm::obj::objdict; let h = request.headers(); - for (key, value) in objdict::get_key_value_pairs(&headers) { + for (key, value) in rustpython_vm::obj::objdict::get_key_value_pairs(&headers) { let key = objstr::get_value(&vm.to_str(&key)?); let value = objstr::get_value(&vm.to_str(&value)?); h.set(&key, &value) From d7163132831be7231b52115d74bc8110c71ac85b Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Mon, 18 Feb 2019 15:54:06 -0600 Subject: [PATCH 004/380] Add content_type option to headers --- wasm/lib/src/wasm_builtins.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/wasm/lib/src/wasm_builtins.rs b/wasm/lib/src/wasm_builtins.rs index 97abc89976..586442045c 100644 --- a/wasm/lib/src/wasm_builtins.rs +++ b/wasm/lib/src/wasm_builtins.rs @@ -146,6 +146,7 @@ fn builtin_fetch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let method = args.get_optional_kwarg_with_type("method", vm.ctx.str_type(), vm)?; let headers = args.get_optional_kwarg_with_type("headers", vm.ctx.dict_type(), vm)?; let body = args.get_optional_kwarg("body"); + let content_type = args.get_optional_kwarg_with_type("content_type", vm.ctx.str_type(), vm)?; let response_format = match response_format { Some(s) => FetchResponseFormat::from_str(vm, &objstr::get_value(&s))?, @@ -176,6 +177,13 @@ fn builtin_fetch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } + if let Some(content_type) = content_type { + request + .headers() + .set("Content-Type", &objstr::get_value(&content_type)) + .map_err(|err| convert::js_py_typeerror(vm, err))?; + } + let window = window(); let request_prom = window.fetch_with_request(&request); From fd184a1e68f2d2c91498c111c34fd32eb8595a60 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Sat, 23 Feb 2019 13:58:19 -0600 Subject: [PATCH 005/380] Add a WASM browser module with fetch() available --- vm/src/macros.rs | 1 + wasm/lib/src/browser_module.rs | 138 +++++++++++++++++++++++++++++++ wasm/lib/src/lib.rs | 1 + wasm/lib/src/vm_class.rs | 13 +-- wasm/lib/src/wasm_builtins.rs | 143 ++------------------------------- 5 files changed, 152 insertions(+), 144 deletions(-) create mode 100644 wasm/lib/src/browser_module.rs diff --git a/vm/src/macros.rs b/vm/src/macros.rs index de94fc4415..a05972961a 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -111,6 +111,7 @@ macro_rules! no_kwargs { }; } +#[macro_export] macro_rules! py_module { ( $ctx:expr, $module_name:expr, { $($name:expr => $value:expr),* $(,)* }) => { { diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs new file mode 100644 index 0000000000..ec4800a18d --- /dev/null +++ b/wasm/lib/src/browser_module.rs @@ -0,0 +1,138 @@ +use crate::{convert, vm_class::AccessibleVM, wasm_builtins::window}; +use futures::{future, Future}; +use js_sys::Promise; +use rustpython_vm::obj::objstr; +use rustpython_vm::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; +use rustpython_vm::VirtualMachine; +use wasm_bindgen::{prelude::*, JsCast}; +use wasm_bindgen_futures::{future_to_promise, JsFuture}; + +enum FetchResponseFormat { + Json, + Text, + ArrayBuffer, +} + +impl FetchResponseFormat { + fn from_str(vm: &mut VirtualMachine, s: &str) -> Result { + match s { + "json" => Ok(FetchResponseFormat::Json), + "text" => Ok(FetchResponseFormat::Text), + "array_buffer" => Ok(FetchResponseFormat::ArrayBuffer), + _ => Err(vm.new_type_error("Unkown fetch response_format".into())), + } + } + fn get_response(&self, response: &web_sys::Response) -> Result { + match self { + FetchResponseFormat::Json => response.json(), + FetchResponseFormat::Text => response.text(), + FetchResponseFormat::ArrayBuffer => response.array_buffer(), + } + } +} + +fn browser_fetch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [ + (url, Some(vm.ctx.str_type())), + (handler, Some(vm.ctx.function_type())) + ], + optional = [(reject_handler, Some(vm.ctx.function_type()))] + ); + let response_format = + args.get_optional_kwarg_with_type("response_format", vm.ctx.str_type(), vm)?; + let method = args.get_optional_kwarg_with_type("method", vm.ctx.str_type(), vm)?; + let headers = args.get_optional_kwarg_with_type("headers", vm.ctx.dict_type(), vm)?; + let body = args.get_optional_kwarg("body"); + let content_type = args.get_optional_kwarg_with_type("content_type", vm.ctx.str_type(), vm)?; + + let response_format = match response_format { + Some(s) => FetchResponseFormat::from_str(vm, &objstr::get_value(&s))?, + None => FetchResponseFormat::Text, + }; + + let mut opts = web_sys::RequestInit::new(); + + match method { + Some(s) => opts.method(&objstr::get_value(&s)), + None => opts.method("GET"), + }; + + if let Some(body) = body { + opts.body(Some(&convert::py_to_js(vm, body))); + } + + let request = web_sys::Request::new_with_str_and_init(&objstr::get_value(url), &opts) + .map_err(|err| convert::js_py_typeerror(vm, err))?; + + if let Some(headers) = headers { + let h = request.headers(); + for (key, value) in rustpython_vm::obj::objdict::get_key_value_pairs(&headers) { + let key = objstr::get_value(&vm.to_str(&key)?); + let value = objstr::get_value(&vm.to_str(&value)?); + h.set(&key, &value) + .map_err(|err| convert::js_py_typeerror(vm, err))?; + } + } + + if let Some(content_type) = content_type { + request + .headers() + .set("Content-Type", &objstr::get_value(&content_type)) + .map_err(|err| convert::js_py_typeerror(vm, err))?; + } + + let window = window(); + let request_prom = window.fetch_with_request(&request); + + let handler = handler.clone(); + let reject_handler = reject_handler.cloned(); + + let acc_vm = AccessibleVM::from_vm(vm); + + let future = JsFuture::from(request_prom) + .and_then(move |val| { + let response = val + .dyn_into::() + .expect("val to be of type Response"); + response_format.get_response(&response) + }) + .and_then(|prom| JsFuture::from(prom)) + .then(move |val| { + let vm = &mut acc_vm + .upgrade() + .expect("that the VM *not* be destroyed while promise is being resolved"); + match val { + Ok(val) => { + let val = convert::js_to_py(vm, val); + let args = PyFuncArgs::new(vec![val], vec![]); + let _ = vm.invoke(handler, args); + } + Err(val) => { + if let Some(reject_handler) = reject_handler { + let val = convert::js_to_py(vm, val); + let args = PyFuncArgs::new(vec![val], vec![]); + let _ = vm.invoke(reject_handler, args); + } + } + } + future::ok(JsValue::UNDEFINED) + }); + future_to_promise(future); + + Ok(vm.get_none()) +} + +const BROWSER_NAME: &str = "browser"; + +pub fn mk_module(ctx: &PyContext) -> PyObjectRef { + py_module!(ctx, BROWSER_NAME, { + "fetch" => ctx.new_rustfunc(browser_fetch) + }) +} + +pub fn setup_browser_module(vm: &mut VirtualMachine) { + vm.stdlib_inits.insert(BROWSER_NAME.to_string(), mk_module); +} diff --git a/wasm/lib/src/lib.rs b/wasm/lib/src/lib.rs index 170c31bbf6..1aff0e6de3 100644 --- a/wasm/lib/src/lib.rs +++ b/wasm/lib/src/lib.rs @@ -1,3 +1,4 @@ +pub mod browser_module; pub mod convert; pub mod vm_class; pub mod wasm_builtins; diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index 5af38ef39b..f803d2d776 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -1,5 +1,6 @@ +use crate::browser_module::setup_browser_module; use crate::convert; -use crate::wasm_builtins::{self, setup_wasm_builtins}; +use crate::wasm_builtins; use js_sys::{SyntaxError, TypeError}; use rustpython_vm::{ compile, @@ -17,12 +18,12 @@ pub(crate) struct StoredVirtualMachine { } impl StoredVirtualMachine { - fn new(id: String, inject_builtins: bool) -> StoredVirtualMachine { + fn new(id: String, inject_browser_module: bool) -> StoredVirtualMachine { let mut vm = VirtualMachine::new(); let builtin = vm.get_builtin_scope(); let scope = vm.context().new_scope(Some(builtin)); - if inject_builtins { - setup_wasm_builtins(&mut vm, &scope); + if inject_browser_module { + setup_browser_module(&mut vm); } vm.wasm_id = Some(id); StoredVirtualMachine { vm, scope } @@ -42,12 +43,12 @@ pub struct VMStore; #[wasm_bindgen(js_class = vmStore)] impl VMStore { - pub fn init(id: String, inject_builtins: Option) -> WASMVirtualMachine { + pub fn init(id: String, inject_browser_module: Option) -> WASMVirtualMachine { STORED_VMS.with(|cell| { let mut vms = cell.borrow_mut(); if !vms.contains_key(&id) { let stored_vm = - StoredVirtualMachine::new(id.clone(), inject_builtins.unwrap_or(true)); + StoredVirtualMachine::new(id.clone(), inject_browser_module.unwrap_or(true)); vms.insert(id.clone(), Rc::new(RefCell::new(stored_vm))); } }); diff --git a/wasm/lib/src/wasm_builtins.rs b/wasm/lib/src/wasm_builtins.rs index 586442045c..b8f4e121ff 100644 --- a/wasm/lib/src/wasm_builtins.rs +++ b/wasm/lib/src/wasm_builtins.rs @@ -2,26 +2,17 @@ //! //! This is required because some feature like I/O works differently in the browser comparing to //! desktop. -//! Implements functions listed here: https://docs.python.org/3/library/builtins.html and some -//! others. +//! Implements functions listed here: https://docs.python.org/3/library/builtins.html. -extern crate futures; -extern crate js_sys; -extern crate wasm_bindgen; -extern crate wasm_bindgen_futures; -extern crate web_sys; - -use crate::{convert, vm_class::AccessibleVM}; -use futures::{future, Future}; -use js_sys::{Array, Promise}; +use crate::convert; +use js_sys::{self, Array}; use rustpython_vm::obj::{objstr, objtype}; use rustpython_vm::pyobject::{IdProtocol, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use rustpython_vm::VirtualMachine; use wasm_bindgen::{prelude::*, JsCast}; -use wasm_bindgen_futures::{future_to_promise, JsFuture}; -use web_sys::{console, HtmlTextAreaElement}; +use web_sys::{self, console, HtmlTextAreaElement}; -fn window() -> web_sys::Window { +pub(crate) fn window() -> web_sys::Window { web_sys::window().expect("Window to be available") } @@ -106,127 +97,3 @@ pub fn builtin_print_console(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyRes console::log(&arr); Ok(vm.get_none()) } - -enum FetchResponseFormat { - Json, - Text, - ArrayBuffer, -} - -impl FetchResponseFormat { - fn from_str(vm: &mut VirtualMachine, s: &str) -> Result { - match s { - "json" => Ok(FetchResponseFormat::Json), - "text" => Ok(FetchResponseFormat::Text), - "array_buffer" => Ok(FetchResponseFormat::ArrayBuffer), - _ => Err(vm.new_type_error("Unkown fetch response_format".into())), - } - } - fn get_response(&self, response: &web_sys::Response) -> Result { - match self { - FetchResponseFormat::Json => response.json(), - FetchResponseFormat::Text => response.text(), - FetchResponseFormat::ArrayBuffer => response.array_buffer(), - } - } -} - -fn builtin_fetch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (url, Some(vm.ctx.str_type())), - (handler, Some(vm.ctx.function_type())) - ], - optional = [(reject_handler, Some(vm.ctx.function_type()))] - ); - let response_format = - args.get_optional_kwarg_with_type("response_format", vm.ctx.str_type(), vm)?; - let method = args.get_optional_kwarg_with_type("method", vm.ctx.str_type(), vm)?; - let headers = args.get_optional_kwarg_with_type("headers", vm.ctx.dict_type(), vm)?; - let body = args.get_optional_kwarg("body"); - let content_type = args.get_optional_kwarg_with_type("content_type", vm.ctx.str_type(), vm)?; - - let response_format = match response_format { - Some(s) => FetchResponseFormat::from_str(vm, &objstr::get_value(&s))?, - None => FetchResponseFormat::Text, - }; - - let mut opts = web_sys::RequestInit::new(); - - match method { - Some(s) => opts.method(&objstr::get_value(&s)), - None => opts.method("GET"), - }; - - if let Some(body) = body { - opts.body(Some(&convert::py_to_js(vm, body))); - } - - let request = web_sys::Request::new_with_str_and_init(&objstr::get_value(url), &opts) - .map_err(|err| convert::js_py_typeerror(vm, err))?; - - if let Some(headers) = headers { - let h = request.headers(); - for (key, value) in rustpython_vm::obj::objdict::get_key_value_pairs(&headers) { - let key = objstr::get_value(&vm.to_str(&key)?); - let value = objstr::get_value(&vm.to_str(&value)?); - h.set(&key, &value) - .map_err(|err| convert::js_py_typeerror(vm, err))?; - } - } - - if let Some(content_type) = content_type { - request - .headers() - .set("Content-Type", &objstr::get_value(&content_type)) - .map_err(|err| convert::js_py_typeerror(vm, err))?; - } - - let window = window(); - let request_prom = window.fetch_with_request(&request); - - let handler = handler.clone(); - let reject_handler = reject_handler.cloned(); - - let acc_vm = AccessibleVM::from_vm(vm); - - let future = JsFuture::from(request_prom) - .and_then(move |val| { - let response = val - .dyn_into::() - .expect("val to be of type Response"); - response_format.get_response(&response) - }) - .and_then(|prom| JsFuture::from(prom)) - .then(move |val| { - let vm = &mut acc_vm - .upgrade() - .expect("that the VM *not* be destroyed while promise is being resolved"); - match val { - Ok(val) => { - let val = convert::js_to_py(vm, val); - let args = PyFuncArgs::new(vec![val], vec![]); - let _ = vm.invoke(handler, args); - } - Err(val) => { - if let Some(reject_handler) = reject_handler { - let val = convert::js_to_py(vm, val); - let args = PyFuncArgs::new(vec![val], vec![]); - let _ = vm.invoke(reject_handler, args); - } - } - } - future::ok(JsValue::UNDEFINED) - }); - future_to_promise(future); - - Ok(vm.get_none()) -} - -pub fn setup_wasm_builtins(vm: &mut VirtualMachine, scope: &PyObjectRef) { - let ctx = vm.context(); - ctx.set_attr(scope, "print", ctx.new_rustfunc(builtin_print_console)); - ctx.set_attr(scope, "fetch", ctx.new_rustfunc(builtin_fetch)); -} From 878c131af42b14f2ea2013186fc416f1cfbf88f1 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Sat, 23 Feb 2019 14:52:02 -0600 Subject: [PATCH 006/380] Make panic errors available and display them nicely for the demo --- wasm/demo/src/main.js | 12 ++++++++---- wasm/lib/src/lib.rs | 21 +++++++++++++++++++-- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/wasm/demo/src/main.js b/wasm/demo/src/main.js index d8934b5c10..ad9e0bec7a 100644 --- a/wasm/demo/src/main.js +++ b/wasm/demo/src/main.js @@ -36,9 +36,12 @@ function runCodeFromTextarea() { if (result !== null) { consoleElement.value += `\n${result}\n`; } - } catch (e) { - errorElement.textContent = e; - console.error(e); + } catch (err) { + if (err instanceof WebAssembly.RuntimeError) { + err = window.__RUSTPYTHON_ERROR || err; + } + errorElement.textContent = err; + console.error(err); } } @@ -46,4 +49,5 @@ document .getElementById('run-btn') .addEventListener('click', runCodeFromTextarea); -runCodeFromTextarea(); // Run once for demo +// Run once for demo +runCodeFromTextarea(); diff --git a/wasm/lib/src/lib.rs b/wasm/lib/src/lib.rs index 2c69866e90..8149490fea 100644 --- a/wasm/lib/src/lib.rs +++ b/wasm/lib/src/lib.rs @@ -9,17 +9,34 @@ extern crate wasm_bindgen; extern crate web_sys; use js_sys::{Object, Reflect, TypeError}; +use std::panic; use wasm_bindgen::prelude::*; pub use crate::vm_class::*; const PY_EVAL_VM_ID: &str = "__py_eval_vm"; -extern crate console_error_panic_hook; +fn panic_hook(info: &panic::PanicInfo) { + // If something errors, just ignore it; we don't want to panic in the panic hook + use js_sys::WebAssembly::RuntimeError; + let window = match web_sys::window() { + Some(win) => win, + None => return, + }; + let msg = &info.to_string(); + let _ = Reflect::set(&window, &"__RUSTPYTHON_ERROR_MSG".into(), &msg.into()); + let error = RuntimeError::new(&msg); + let _ = Reflect::set(&window, &"__RUSTPYTHON_ERROR".into(), &error); + let stack = match Reflect::get(&error, &"stack".into()) { + Ok(stack) => stack, + Err(_) => return, + }; + let _ = Reflect::set(&window, &"__RUSTPYTHON_ERROR_STACK".into(), &stack.into()); +} #[wasm_bindgen(start)] pub fn setup_console_error() { - std::panic::set_hook(Box::new(console_error_panic_hook::hook)); + std::panic::set_hook(Box::new(panic_hook)); } // Hack to comment out wasm-bindgen's generated typescript definitons From ff66bb602e44d8460a3f4fef27f4fee6c508688f Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Sat, 23 Feb 2019 15:05:52 -0600 Subject: [PATCH 007/380] Follow clippy's advice --- wasm/lib/src/browser_module.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index ec4800a18d..93b6ec5cde 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -99,7 +99,7 @@ fn browser_fetch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .expect("val to be of type Response"); response_format.get_response(&response) }) - .and_then(|prom| JsFuture::from(prom)) + .and_then(JsFuture::from) .then(move |val| { let vm = &mut acc_vm .upgrade() From 2b9adfea8a35789d5c4dd0056a76dba1c963c08b Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Sat, 23 Feb 2019 15:25:12 -0600 Subject: [PATCH 008/380] Add fetch demo snippet --- wasm/demo/snippets/fetch.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 wasm/demo/snippets/fetch.py diff --git a/wasm/demo/snippets/fetch.py b/wasm/demo/snippets/fetch.py new file mode 100644 index 0000000000..00a64ebe76 --- /dev/null +++ b/wasm/demo/snippets/fetch.py @@ -0,0 +1,12 @@ +from browser import fetch + +def fetch_handler(res): + print(f"headers: {res['headers']}") + +fetch( + "https://httpbin.org/get", + fetch_handler, + lambda err: print(f"error: {err}"), + response_format="json", + headers={"X-Header-Thing": "rustpython is neat"}, +) From 9d99f94a3fd1a671d7e533437bf308193af0c7f7 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Sat, 23 Feb 2019 16:21:10 -0600 Subject: [PATCH 009/380] Add! --- wasm/demo/snippets/fetch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wasm/demo/snippets/fetch.py b/wasm/demo/snippets/fetch.py index 00a64ebe76..63b21b69d8 100644 --- a/wasm/demo/snippets/fetch.py +++ b/wasm/demo/snippets/fetch.py @@ -8,5 +8,5 @@ def fetch_handler(res): fetch_handler, lambda err: print(f"error: {err}"), response_format="json", - headers={"X-Header-Thing": "rustpython is neat"}, + headers={"X-Header-Thing": "rustpython is neat!"}, ) From c14a8302e82e5ae844fb9e89f6a14a2e197698bf Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Fri, 22 Feb 2019 20:14:10 -0800 Subject: [PATCH 010/380] Initial prototype of "extractor pattern" for native funcs --- vm/src/obj/objbool.rs | 8 ++- vm/src/obj/objint.rs | 20 +++++- vm/src/obj/objrange.rs | 31 +++++----- vm/src/pyobject.rs | 134 +++++++++++++++++++++++++++++++++++++++-- 4 files changed, 168 insertions(+), 25 deletions(-) diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index c5c9cb43b1..41681a476f 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -1,10 +1,16 @@ use super::objtype; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + IntoPyObject, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use num_traits::Zero; +impl IntoPyObject for bool { + fn into_pyobject(self, ctx: &PyContext) -> PyResult { + Ok(ctx.new_bool(self)) + } +} + pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result { let result = match obj.borrow().payload { PyObjectPayload::Integer { ref value } => !value.is_zero(), diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 7e3da093e4..5217ab0121 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -3,8 +3,8 @@ use super::objstr; use super::objtype; use crate::format::FormatSpec; use crate::pyobject::{ - FromPyObjectRef, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, - TypeProtocol, + FromPyObject, FromPyObjectRef, IntoPyObject, PyContext, PyFuncArgs, PyObject, PyObjectPayload, + PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use num_bigint::{BigInt, ToBigInt}; @@ -15,6 +15,22 @@ use std::hash::{Hash, Hasher}; // This proxy allows for easy switching between types. type IntType = BigInt; +pub type PyInt = BigInt; + +impl IntoPyObject for PyInt { + fn into_pyobject(self, ctx: &PyContext) -> PyResult { + Ok(ctx.new_int(self)) + } +} + +// TODO: macro to impl for all primitive ints + +impl IntoPyObject for usize { + fn into_pyobject(self, ctx: &PyContext) -> PyResult { + Ok(ctx.new_int(self)) + } +} + fn int_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(int, Some(vm.ctx.int_type()))]); let v = get_value(int); diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index c5075ccbd3..db9b97a080 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -1,7 +1,8 @@ -use super::objint; +use super::objint::{self, PyInt}; use super::objtype; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + FromPyObject, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, + TypeProtocol, }; use crate::vm::VirtualMachine; use num_bigint::{BigInt, Sign}; @@ -18,6 +19,14 @@ pub struct RangeType { pub step: BigInt, } +type PyRange = RangeType; + +impl FromPyObject for PyRange { + fn from_pyobject(obj: PyObjectRef) -> PyResult { + Ok(get_value(&obj)) + } +} + impl RangeType { #[inline] pub fn try_len(&self) -> Option { @@ -345,22 +354,12 @@ fn range_bool(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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)] - ); - - let range = get_value(zelf); - - let result = if objtype::isinstance(needle, &vm.ctx.int_type()) { - range.contains(&objint::get_value(needle)) +fn range_contains(vm: &mut VirtualMachine, zelf: PyRange, needle: PyObjectRef) -> bool { + if objtype::isinstance(&needle, &vm.ctx.int_type()) { + zelf.contains(&objint::get_value(&needle)) } else { false - }; - - Ok(vm.ctx.new_bool(result)) + } } fn range_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 0158028d02..2178cb04b5 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -72,7 +72,7 @@ pub type PyObjectWeakRef = Weak>; /// Use this type for function which return a python object or and exception. /// Both the python object and the python exception are `PyObjectRef` types /// since exceptions are also python objects. -pub type PyResult = Result; // A valid value, or an exception +pub type PyResult = Result; // A valid value, or an exception /// For attributes we do not use a dict, but a hashmap. This is probably /// faster, unordered, and only supports strings as keys. @@ -553,13 +553,15 @@ impl PyContext { ) } - pub fn new_rustfunc PyResult>( - &self, - function: F, - ) -> PyObjectRef { + pub fn new_rustfunc(&self, factory: F) -> PyObjectRef + where + F: PyNativeFuncFactory, + T: FromPyFuncArgs, + R: IntoPyObject, + { PyObject::new( PyObjectPayload::RustFunction { - function: Box::new(function), + function: factory.create(), }, self.builtin_function_or_method_type(), ) @@ -922,6 +924,126 @@ impl PyFuncArgs { } } +pub trait FromPyObject: Sized { + fn from_pyobject(obj: PyObjectRef) -> PyResult; +} + +impl FromPyObject for PyObjectRef { + fn from_pyobject(obj: PyObjectRef) -> PyResult { + Ok(obj) + } +} + +pub trait IntoPyObject { + fn into_pyobject(self, ctx: &PyContext) -> PyResult; +} + +impl IntoPyObject for PyObjectRef { + fn into_pyobject(self, ctx: &PyContext) -> PyResult { + Ok(self) + } +} + +impl IntoPyObject for PyResult { + fn into_pyobject(self, ctx: &PyContext) -> PyResult { + self + } +} + +pub trait FromPyFuncArgs: Sized { + fn from_py_func_args(args: &mut PyFuncArgs) -> PyResult; +} + +impl FromPyFuncArgs for Vec +where + T: FromPyFuncArgs, +{ + fn from_py_func_args(args: &mut PyFuncArgs) -> PyResult { + let mut v = Vec::with_capacity(args.args.len()); + // TODO: This will loop infinitely if T::from_py_func_args doesn't + // consume any positional args. Check for this and panic. + while !args.args.is_empty() { + v.push(T::from_py_func_args(args)?); + } + Ok(v) + } +} + +macro_rules! tuple_from_py_func_args { + ($($T:ident),+) => { + impl<$($T),+> FromPyFuncArgs for ($($T,)+) + where + $($T: FromPyFuncArgs),+ + { + fn from_py_func_args(args: &mut PyFuncArgs) -> PyResult { + Ok(($($T::from_py_func_args(args)?,)+)) + } + } + }; +} + +tuple_from_py_func_args!(A); +tuple_from_py_func_args!(A, B); +tuple_from_py_func_args!(A, B, C); +tuple_from_py_func_args!(A, B, C, D); +tuple_from_py_func_args!(A, B, C, D, E); + +impl FromPyFuncArgs for T +where + T: FromPyObject, +{ + fn from_py_func_args(args: &mut PyFuncArgs) -> PyResult { + Self::from_pyobject(args.shift()) + } +} + +pub type PyNativeFunc = Box PyResult>; + +pub trait PyNativeFuncFactory { + fn create(self) -> PyNativeFunc; +} + +impl PyNativeFuncFactory<(A,), R> for F +where + F: Fn(&mut VirtualMachine, A) -> R + 'static, + A: FromPyFuncArgs, + R: IntoPyObject, +{ + fn create(self) -> PyNativeFunc { + Box::new(move |vm, mut args| { + // TODO: type-checking! + (self)(vm, A::from_py_func_args(&mut args)?).into_pyobject(&vm.ctx) + }) + } +} + +impl PyNativeFuncFactory<(A, B), R> for F +where + F: Fn(&mut VirtualMachine, A, B) -> R + 'static, + A: FromPyFuncArgs, + B: FromPyFuncArgs, + R: IntoPyObject, +{ + fn create(self) -> PyNativeFunc { + Box::new(move |vm, mut args| { + (self)( + vm, + A::from_py_func_args(&mut args)?, + B::from_py_func_args(&mut args)?, + ) + .into_pyobject(&vm.ctx) + }) + } +} + +impl FromPyFuncArgs for PyFuncArgs { + fn from_py_func_args(args: &mut PyFuncArgs) -> PyResult { + // HACK HACK HACK + // TODO: get rid of this clone! + Ok(args.clone()) + } +} + /// Rather than determining the type of a python object, this enum is more /// a holder for the rust payload of a python object. It is more a carrier /// of rust data for a particular python object. Determine the python type From a74cbbbe192a0da4361b31622bae81e86ca52a36 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sat, 23 Feb 2019 08:23:15 -0800 Subject: [PATCH 011/380] Impl PyNativeFuncFactory for up to 5-tuples --- vm/src/pyobject.rs | 51 +++++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 2178cb04b5..402e467419 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1003,38 +1003,29 @@ pub trait PyNativeFuncFactory { fn create(self) -> PyNativeFunc; } -impl PyNativeFuncFactory<(A,), R> for F -where - F: Fn(&mut VirtualMachine, A) -> R + 'static, - A: FromPyFuncArgs, - R: IntoPyObject, -{ - fn create(self) -> PyNativeFunc { - Box::new(move |vm, mut args| { - // TODO: type-checking! - (self)(vm, A::from_py_func_args(&mut args)?).into_pyobject(&vm.ctx) - }) - } +macro_rules! tuple_py_native_func_factory { + ($($T:ident),+) => { + impl PyNativeFuncFactory<($($T,)+), R> for F + where + F: Fn(&mut VirtualMachine, $($T),+) -> R + 'static, + $($T: FromPyFuncArgs,)+ + R: IntoPyObject, + { + fn create(self) -> PyNativeFunc { + Box::new(move |vm, mut args| { + (self)(vm, $($T::from_py_func_args(&mut args)?,)+) + .into_pyobject(&vm.ctx) + }) + } + } + }; } -impl PyNativeFuncFactory<(A, B), R> for F -where - F: Fn(&mut VirtualMachine, A, B) -> R + 'static, - A: FromPyFuncArgs, - B: FromPyFuncArgs, - R: IntoPyObject, -{ - fn create(self) -> PyNativeFunc { - Box::new(move |vm, mut args| { - (self)( - vm, - A::from_py_func_args(&mut args)?, - B::from_py_func_args(&mut args)?, - ) - .into_pyobject(&vm.ctx) - }) - } -} +tuple_py_native_func_factory!(A); +tuple_py_native_func_factory!(A, B); +tuple_py_native_func_factory!(A, B, C); +tuple_py_native_func_factory!(A, B, C, D); +tuple_py_native_func_factory!(A, B, C, D, E); impl FromPyFuncArgs for PyFuncArgs { fn from_py_func_args(args: &mut PyFuncArgs) -> PyResult { From fb0384d24d1774681c2443401cc085279eeb4d91 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sat, 23 Feb 2019 09:36:10 -0800 Subject: [PATCH 012/380] Some prerequisite data types for arg checking --- vm/src/obj/objrange.rs | 4 ++++ vm/src/pyobject.rs | 48 +++++++++++++++++++++++++++++------------- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index db9b97a080..1da01c890d 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -22,6 +22,10 @@ pub struct RangeType { type PyRange = RangeType; impl FromPyObject for PyRange { + fn typ(ctx: &PyContext) -> Option { + Some(ctx.range_type()) + } + fn from_pyobject(obj: PyObjectRef) -> PyResult { Ok(get_value(&obj)) } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 402e467419..d08cb24be9 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -925,10 +925,16 @@ impl PyFuncArgs { } pub trait FromPyObject: Sized { + fn typ(ctx: &PyContext) -> Option; + fn from_pyobject(obj: PyObjectRef) -> PyResult; } impl FromPyObject for PyObjectRef { + fn typ(_ctx: &PyContext) -> Option { + None + } + fn from_pyobject(obj: PyObjectRef) -> PyResult { Ok(obj) } @@ -951,22 +957,9 @@ impl IntoPyObject for PyResult { } pub trait FromPyFuncArgs: Sized { - fn from_py_func_args(args: &mut PyFuncArgs) -> PyResult; -} + fn required_params(ctx: &PyContext) -> Vec; -impl FromPyFuncArgs for Vec -where - T: FromPyFuncArgs, -{ - fn from_py_func_args(args: &mut PyFuncArgs) -> PyResult { - let mut v = Vec::with_capacity(args.args.len()); - // TODO: This will loop infinitely if T::from_py_func_args doesn't - // consume any positional args. Check for this and panic. - while !args.args.is_empty() { - v.push(T::from_py_func_args(args)?); - } - Ok(v) - } + fn from_py_func_args(args: &mut PyFuncArgs) -> PyResult; } macro_rules! tuple_from_py_func_args { @@ -975,6 +968,10 @@ macro_rules! tuple_from_py_func_args { where $($T: FromPyFuncArgs),+ { + fn required_params(ctx: &PyContext) -> Vec { + vec![$($T::required_params(ctx),)+].into_iter().flatten().collect() + } + fn from_py_func_args(args: &mut PyFuncArgs) -> PyResult { Ok(($($T::from_py_func_args(args)?,)+)) } @@ -992,6 +989,13 @@ impl FromPyFuncArgs for T where T: FromPyObject, { + fn required_params(ctx: &PyContext) -> Vec { + vec![Parameter { + kind: ParameterKind::PositionalOnly, + typ: T::typ(ctx), + }] + } + fn from_py_func_args(args: &mut PyFuncArgs) -> PyResult { Self::from_pyobject(args.shift()) } @@ -1027,7 +1031,21 @@ tuple_py_native_func_factory!(A, B, C); tuple_py_native_func_factory!(A, B, C, D); tuple_py_native_func_factory!(A, B, C, D, E); +pub struct Parameter { + typ: Option, + kind: ParameterKind, +} + +pub enum ParameterKind { + PositionalOnly, + KeywordOnly { name: String }, +} + impl FromPyFuncArgs for PyFuncArgs { + fn required_params(_ctx: &PyContext) -> Vec { + vec![] + } + fn from_py_func_args(args: &mut PyFuncArgs) -> PyResult { // HACK HACK HACK // TODO: get rid of this clone! From 2919d7f5208aee3a49e63c00d46224540572039f Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sat, 23 Feb 2019 10:04:29 -0800 Subject: [PATCH 013/380] Initial arg type checking --- vm/src/obj/objint.rs | 4 +- vm/src/obj/objrange.rs | 2 +- vm/src/obj/objstr.rs | 24 ++++++---- vm/src/pyobject.rs | 104 +++++++++++++++++++++++++++++++++-------- 4 files changed, 102 insertions(+), 32 deletions(-) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 5217ab0121..eea0313971 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -3,8 +3,8 @@ use super::objstr; use super::objtype; use crate::format::FormatSpec; use crate::pyobject::{ - FromPyObject, FromPyObjectRef, IntoPyObject, PyContext, PyFuncArgs, PyObject, PyObjectPayload, - PyObjectRef, PyResult, TypeProtocol, + FromPyObjectRef, IntoPyObject, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, + PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use num_bigint::{BigInt, ToBigInt}; diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 1da01c890d..e2ac5de45c 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -1,4 +1,4 @@ -use super::objint::{self, PyInt}; +use super::objint; use super::objtype; use crate::pyobject::{ FromPyObject, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 9c2051e6f7..04394f7c5d 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -3,7 +3,8 @@ use super::objsequence::PySliceableSequence; use super::objtype; use crate::format::{FormatParseError, FormatPart, FormatString}; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + FromPyObject, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, + TypeProtocol, }; use crate::vm::VirtualMachine; use num_traits::ToPrimitive; @@ -17,6 +18,16 @@ extern crate unicode_segmentation; use self::unicode_segmentation::UnicodeSegmentation; +impl FromPyObject for String { + fn typ(ctx: &PyContext) -> Option { + Some(ctx.str_type()) + } + + fn from_pyobject(obj: PyObjectRef) -> PyResult { + Ok(get_value(&obj)) + } +} + pub fn init(context: &PyContext) { let str_type = &context.str_type; context.set_attr(&str_type, "__add__", context.new_rustfunc(str_add)); @@ -474,15 +485,8 @@ fn str_rstrip(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_str(value)) } -fn str_endswith(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(s, Some(vm.ctx.str_type())), (pat, Some(vm.ctx.str_type()))] - ); - let value = get_value(&s); - let pat = get_value(&pat); - Ok(vm.ctx.new_bool(value.ends_with(pat.as_str()))) +fn str_endswith(_vm: &mut VirtualMachine, zelf: String, suffix: String) -> bool { + zelf.ends_with(&suffix) } fn str_isidentifier(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index d08cb24be9..d733238e3c 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -556,12 +556,10 @@ impl PyContext { pub fn new_rustfunc(&self, factory: F) -> PyObjectRef where F: PyNativeFuncFactory, - T: FromPyFuncArgs, - R: IntoPyObject, { PyObject::new( PyObjectPayload::RustFunction { - function: factory.create(), + function: factory.create(self), }, self.builtin_function_or_method_type(), ) @@ -945,13 +943,13 @@ pub trait IntoPyObject { } impl IntoPyObject for PyObjectRef { - fn into_pyobject(self, ctx: &PyContext) -> PyResult { + fn into_pyobject(self, _ctx: &PyContext) -> PyResult { Ok(self) } } impl IntoPyObject for PyResult { - fn into_pyobject(self, ctx: &PyContext) -> PyResult { + fn into_pyobject(self, _ctx: &PyContext) -> PyResult { self } } @@ -991,7 +989,7 @@ where { fn required_params(ctx: &PyContext) -> Vec { vec![Parameter { - kind: ParameterKind::PositionalOnly, + kind: PositionalOnly, typ: T::typ(ctx), }] } @@ -1004,7 +1002,16 @@ where pub type PyNativeFunc = Box PyResult>; pub trait PyNativeFuncFactory { - fn create(self) -> PyNativeFunc; + fn create(self, ctx: &PyContext) -> PyNativeFunc; +} + +impl PyNativeFuncFactory for F +where + F: Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult + 'static, +{ + fn create(self, _ctx: &PyContext) -> PyNativeFunc { + Box::new(self) + } } macro_rules! tuple_py_native_func_factory { @@ -1015,8 +1022,16 @@ macro_rules! tuple_py_native_func_factory { $($T: FromPyFuncArgs,)+ R: IntoPyObject, { - fn create(self) -> PyNativeFunc { + fn create(self, ctx: &PyContext) -> PyNativeFunc { + let parameters = vec![$($T::required_params(ctx)),+] + .into_iter() + .flatten() + .collect(); + let signature = Signature::new(parameters); + Box::new(move |vm, mut args| { + signature.check(vm, &mut args)?; + (self)(vm, $($T::from_py_func_args(&mut args)?,)+) .into_pyobject(&vm.ctx) }) @@ -1031,27 +1046,78 @@ tuple_py_native_func_factory!(A, B, C); tuple_py_native_func_factory!(A, B, C, D); tuple_py_native_func_factory!(A, B, C, D, E); +#[derive(Debug)] +pub struct Signature { + positional_params: Vec, + keyword_params: HashMap, +} + +impl Signature { + fn new(params: Vec) -> Self { + let mut positional_params = Vec::new(); + let mut keyword_params = HashMap::new(); + for param in params { + match param.kind { + PositionalOnly => { + positional_params.push(param); + } + KeywordOnly { ref name } => { + keyword_params.insert(name.clone(), param); + } + } + } + + Self { + positional_params, + keyword_params, + } + } + + fn arg_type(&self, pos: usize) -> Option<&PyObjectRef> { + self.positional_params[pos].typ.as_ref() + } + + #[allow(unused)] + fn kwarg_type(&self, name: &str) -> Option<&PyObjectRef> { + self.keyword_params[name].typ.as_ref() + } + + fn check(&self, vm: &mut VirtualMachine, args: &PyFuncArgs) -> PyResult<()> { + // TODO: check arity + + for (pos, arg) in args.args.iter().enumerate() { + if let Some(expected_type) = self.arg_type(pos) { + 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, + pos + 1, + actual_type + ))); + } + } + } + + Ok(()) + } +} + +#[derive(Debug)] pub struct Parameter { typ: Option, kind: ParameterKind, } +#[derive(Debug)] pub enum ParameterKind { PositionalOnly, KeywordOnly { name: String }, } -impl FromPyFuncArgs for PyFuncArgs { - fn required_params(_ctx: &PyContext) -> Vec { - vec![] - } - - fn from_py_func_args(args: &mut PyFuncArgs) -> PyResult { - // HACK HACK HACK - // TODO: get rid of this clone! - Ok(args.clone()) - } -} +use self::ParameterKind::*; /// Rather than determining the type of a python object, this enum is more /// a holder for the rust payload of a python object. It is more a carrier From 8186c77eb2c62c01589b03fad5183a2bd9c1b5f4 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sat, 23 Feb 2019 12:38:29 +0200 Subject: [PATCH 014/380] Initial socket module --- vm/src/pyobject.rs | 5 + vm/src/stdlib/mod.rs | 2 + vm/src/stdlib/socket.rs | 238 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 245 insertions(+) create mode 100644 vm/src/stdlib/socket.rs diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index dd1f839ff7..9e7a31f0f6 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -29,6 +29,7 @@ use crate::obj::objsuper; use crate::obj::objtuple; use crate::obj::objtype; use crate::obj::objzip; +use crate::stdlib::socket::Socket; use crate::vm::VirtualMachine; use num_bigint::BigInt; use num_bigint::ToBigInt; @@ -1045,6 +1046,9 @@ pub enum PyObjectPayload { RustFunction { function: Box PyResult>, }, + Socket { + socket: Socket, + }, } impl fmt::Debug for PyObjectPayload { @@ -1082,6 +1086,7 @@ impl fmt::Debug for PyObjectPayload { PyObjectPayload::Instance { .. } => write!(f, "instance"), PyObjectPayload::RustFunction { .. } => write!(f, "rust function"), PyObjectPayload::Frame { .. } => write!(f, "frame"), + PyObjectPayload::Socket { .. } => write!(f, "socket"), } } } diff --git a/vm/src/stdlib/mod.rs b/vm/src/stdlib/mod.rs index a0dcd9bff6..0a0d378be4 100644 --- a/vm/src/stdlib/mod.rs +++ b/vm/src/stdlib/mod.rs @@ -6,6 +6,7 @@ mod math; mod pystruct; mod random; mod re; +pub mod socket; mod string; mod time_module; mod tokenize; @@ -46,6 +47,7 @@ pub fn get_module_inits() -> HashMap { { modules.insert("io".to_string(), io::mk_module as StdlibInitFunc); modules.insert("os".to_string(), os::mk_module as StdlibInitFunc); + modules.insert("socket".to_string(), socket::mk_module as StdlibInitFunc); } modules diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs new file mode 100644 index 0000000000..b94b124344 --- /dev/null +++ b/vm/src/stdlib/socket.rs @@ -0,0 +1,238 @@ +use crate::obj::objbytes; +use crate::obj::objint; +use crate::obj::objstr; + +use crate::pyobject::{ + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, +}; +use crate::vm::VirtualMachine; + +use num_traits::ToPrimitive; +use std::fmt; +use std::io::Read; +use std::io::Write; +use std::net::{TcpListener, TcpStream, UdpSocket}; + +#[derive(Debug)] +enum AddressFamily { + AF_UNIX = 1, + AF_INET = 2, + AF_INET6 = 3, +} + +impl AddressFamily { + fn from_i32(value: i32) -> AddressFamily { + match value { + 1 => AddressFamily::AF_UNIX, + 2 => AddressFamily::AF_INET, + 3 => AddressFamily::AF_INET6, + _ => panic!("Unknown value: {}", value), + } + } +} + +#[derive(Debug)] +enum SocketKind { + SOCK_STREAM = 1, + SOCK_DGRAM = 2, +} + +impl SocketKind { + fn from_i32(value: i32) -> SocketKind { + match value { + 1 => SocketKind::SOCK_STREAM, + 2 => SocketKind::SOCK_DGRAM, + _ => panic!("Unknown value: {}", value), + } + } +} + +impl fmt::Display for AddressFamily { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +impl fmt::Display for SocketKind { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +pub struct Socket { + af: AddressFamily, + sk: SocketKind, + tcp_listener: Option, + tcp_stream: Option, + udp_socket: Option, +} + +impl Socket { + fn new(af: AddressFamily, sk: SocketKind) -> Socket { + Socket { + af: af, + sk: sk, + tcp_listener: None, + tcp_stream: None, + udp_socket: None, + } + } + + fn get_tcp_stream(&self) -> Option<&TcpStream> { + match &self.tcp_stream { + Some(v) => Some(v), + None => None, + } + } +} + +fn socket_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [ + (cls, None), + (family_int, Some(vm.ctx.int_type())), + (kind_int, Some(vm.ctx.int_type())) + ] + ); + + let family = AddressFamily::from_i32(objint::get_value(family_int).to_i32().unwrap()); + let kind = SocketKind::from_i32(objint::get_value(kind_int).to_i32().unwrap()); + + let socket = Socket::new(family, kind); + + Ok(PyObject::new( + PyObjectPayload::Socket { socket }, + cls.clone(), + )) +} + +fn socket_connect(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, None), (address, Some(vm.ctx.str_type()))] + ); + + let mut mut_obj = zelf.borrow_mut(); + + match mut_obj.payload { + PyObjectPayload::Socket { ref mut socket } => { + if let Ok(stream) = TcpStream::connect(objstr::get_value(&address)) { + socket.tcp_stream = Some(stream); + Ok(vm.get_none()) + } else { + // TODO: Socket error + Err(vm.new_type_error("socket failed".to_string())) + } + } + _ => Err(vm.new_type_error("".to_string())), + } +} + +fn socket_recv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, None), (bufsize, Some(vm.ctx.int_type()))] + ); + let mut mut_obj = zelf.borrow_mut(); + + match mut_obj.payload { + PyObjectPayload::Socket { ref mut socket } => match socket.af { + AddressFamily::AF_INET => match socket.sk { + SocketKind::SOCK_STREAM => { + let mut buffer = Vec::new(); + socket + .get_tcp_stream() + .unwrap() + .read_to_end(&mut buffer) + .unwrap(); + Ok(PyObject::new( + PyObjectPayload::Bytes { value: buffer }, + vm.ctx.bytes_type(), + )) + } + _ => Err(vm.new_type_error("".to_string())), + }, + _ => Err(vm.new_type_error("".to_string())), + }, + _ => Err(vm.new_type_error("".to_string())), + } +} + +fn socket_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, None), (bytes, Some(vm.ctx.bytes_type()))] + ); + let mut mut_obj = zelf.borrow_mut(); + + match mut_obj.payload { + PyObjectPayload::Socket { ref mut socket } => match socket.af { + AddressFamily::AF_INET => match socket.sk { + SocketKind::SOCK_STREAM => { + socket + .get_tcp_stream() + .unwrap() + .write(&objbytes::get_value(&bytes)) + .unwrap(); + Ok(vm.get_none()) + } + _ => Err(vm.new_type_error("".to_string())), + }, + _ => Err(vm.new_type_error("".to_string())), + }, + _ => Err(vm.new_type_error("".to_string())), + } +} + +fn socket_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, None)]); + let mut mut_obj = zelf.borrow_mut(); + + match mut_obj.payload { + PyObjectPayload::Socket { ref mut socket } => match socket.af { + AddressFamily::AF_INET => match socket.sk { + SocketKind::SOCK_STREAM => { + socket.tcp_stream = None; + Ok(vm.get_none()) + } + _ => Err(vm.new_type_error("".to_string())), + }, + _ => Err(vm.new_type_error("".to_string())), + }, + _ => Err(vm.new_type_error("".to_string())), + } +} + +pub fn mk_module(ctx: &PyContext) -> PyObjectRef { + let py_mod = ctx.new_module(&"socket".to_string(), ctx.new_scope(None)); + + ctx.set_attr( + &py_mod, + &AddressFamily::AF_INET.to_string(), + ctx.new_int(AddressFamily::AF_INET as i32), + ); + + ctx.set_attr( + &py_mod, + &SocketKind::SOCK_STREAM.to_string(), + ctx.new_int(SocketKind::SOCK_STREAM as i32), + ); + + let socket = { + let socket = ctx.new_class("socket", ctx.object()); + ctx.set_attr(&socket, "__new__", ctx.new_rustfunc(socket_new)); + ctx.set_attr(&socket, "connect", ctx.new_rustfunc(socket_connect)); + ctx.set_attr(&socket, "recv", ctx.new_rustfunc(socket_recv)); + ctx.set_attr(&socket, "send", ctx.new_rustfunc(socket_send)); + ctx.set_attr(&socket, "close", ctx.new_rustfunc(socket_close)); + socket + }; + ctx.set_attr(&py_mod, "socket", socket.clone()); + + py_mod +} From 49837f57a713522dfc2726d9eb72b44e79a20cab Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sun, 24 Feb 2019 20:38:26 +0200 Subject: [PATCH 015/380] Use Connection enum --- vm/src/stdlib/socket.rs | 142 +++++++++++++++++++--------------------- 1 file changed, 66 insertions(+), 76 deletions(-) diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index b94b124344..7b2b76ddbb 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -8,24 +8,24 @@ use crate::pyobject::{ use crate::vm::VirtualMachine; use num_traits::ToPrimitive; -use std::fmt; +use std::io; use std::io::Read; use std::io::Write; use std::net::{TcpListener, TcpStream, UdpSocket}; #[derive(Debug)] enum AddressFamily { - AF_UNIX = 1, - AF_INET = 2, - AF_INET6 = 3, + AfUnix = 1, + AfInet = 2, + AfInet6 = 3, } impl AddressFamily { fn from_i32(value: i32) -> AddressFamily { match value { - 1 => AddressFamily::AF_UNIX, - 2 => AddressFamily::AF_INET, - 3 => AddressFamily::AF_INET6, + 1 => AddressFamily::AfUnix, + 2 => AddressFamily::AfInet, + 3 => AddressFamily::AfInet6, _ => panic!("Unknown value: {}", value), } } @@ -33,55 +33,59 @@ impl AddressFamily { #[derive(Debug)] enum SocketKind { - SOCK_STREAM = 1, - SOCK_DGRAM = 2, + SockStream = 1, + SockDgram = 2, } impl SocketKind { fn from_i32(value: i32) -> SocketKind { match value { - 1 => SocketKind::SOCK_STREAM, - 2 => SocketKind::SOCK_DGRAM, + 1 => SocketKind::SockStream, + 2 => SocketKind::SockDgram, _ => panic!("Unknown value: {}", value), } } } -impl fmt::Display for AddressFamily { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) +enum Connection { + TcpListener(TcpListener), + TcpStream(TcpStream), + UdpSocket(UdpSocket), +} + +impl Read for Connection { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match self { + Connection::TcpStream(con) => con.read(buf), + _ => Err(io::Error::new(io::ErrorKind::Other, "oh no!")), + } } } -impl fmt::Display for SocketKind { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) +impl Write for Connection { + fn write(&mut self, buf: &[u8]) -> io::Result { + match self { + Connection::TcpStream(con) => con.write(buf), + _ => Err(io::Error::new(io::ErrorKind::Other, "oh no!")), + } + } + fn flush(&mut self) -> io::Result<()> { + Ok(()) } } pub struct Socket { - af: AddressFamily, + address_family: AddressFamily, sk: SocketKind, - tcp_listener: Option, - tcp_stream: Option, - udp_socket: Option, + con: Option, } impl Socket { - fn new(af: AddressFamily, sk: SocketKind) -> Socket { + fn new(address_family: AddressFamily, sk: SocketKind) -> Socket { Socket { - af: af, + address_family, sk: sk, - tcp_listener: None, - tcp_stream: None, - udp_socket: None, - } - } - - fn get_tcp_stream(&self) -> Option<&TcpStream> { - match &self.tcp_stream { - Some(v) => Some(v), - None => None, + con: None, } } } @@ -97,10 +101,10 @@ fn socket_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ] ); - let family = AddressFamily::from_i32(objint::get_value(family_int).to_i32().unwrap()); + let address_family = AddressFamily::from_i32(objint::get_value(family_int).to_i32().unwrap()); let kind = SocketKind::from_i32(objint::get_value(kind_int).to_i32().unwrap()); - let socket = Socket::new(family, kind); + let socket = Socket::new(address_family, kind); Ok(PyObject::new( PyObjectPayload::Socket { socket }, @@ -120,7 +124,7 @@ fn socket_connect(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match mut_obj.payload { PyObjectPayload::Socket { ref mut socket } => { if let Ok(stream) = TcpStream::connect(objstr::get_value(&address)) { - socket.tcp_stream = Some(stream); + socket.con = Some(Connection::TcpStream(stream)); Ok(vm.get_none()) } else { // TODO: Socket error @@ -140,24 +144,17 @@ fn socket_recv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut mut_obj = zelf.borrow_mut(); match mut_obj.payload { - PyObjectPayload::Socket { ref mut socket } => match socket.af { - AddressFamily::AF_INET => match socket.sk { - SocketKind::SOCK_STREAM => { - let mut buffer = Vec::new(); - socket - .get_tcp_stream() - .unwrap() - .read_to_end(&mut buffer) - .unwrap(); - Ok(PyObject::new( - PyObjectPayload::Bytes { value: buffer }, - vm.ctx.bytes_type(), - )) - } - _ => Err(vm.new_type_error("".to_string())), - }, - _ => Err(vm.new_type_error("".to_string())), - }, + PyObjectPayload::Socket { ref mut socket } => { + let mut buffer = Vec::new(); + let _temp = match socket.con { + Some(ref mut v) => v.read_to_end(&mut buffer).unwrap(), + None => 0, + }; + Ok(PyObject::new( + PyObjectPayload::Bytes { value: buffer }, + vm.ctx.bytes_type(), + )) + } _ => Err(vm.new_type_error("".to_string())), } } @@ -171,20 +168,13 @@ fn socket_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut mut_obj = zelf.borrow_mut(); match mut_obj.payload { - PyObjectPayload::Socket { ref mut socket } => match socket.af { - AddressFamily::AF_INET => match socket.sk { - SocketKind::SOCK_STREAM => { - socket - .get_tcp_stream() - .unwrap() - .write(&objbytes::get_value(&bytes)) - .unwrap(); - Ok(vm.get_none()) - } - _ => Err(vm.new_type_error("".to_string())), - }, - _ => Err(vm.new_type_error("".to_string())), - }, + PyObjectPayload::Socket { ref mut socket } => { + match socket.con { + Some(ref mut v) => v.write(&objbytes::get_value(&bytes)).unwrap(), + None => return Err(vm.new_type_error("".to_string())), + }; + Ok(vm.get_none()) + } _ => Err(vm.new_type_error("".to_string())), } } @@ -194,10 +184,10 @@ fn socket_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut mut_obj = zelf.borrow_mut(); match mut_obj.payload { - PyObjectPayload::Socket { ref mut socket } => match socket.af { - AddressFamily::AF_INET => match socket.sk { - SocketKind::SOCK_STREAM => { - socket.tcp_stream = None; + PyObjectPayload::Socket { ref mut socket } => match socket.address_family { + AddressFamily::AfInet => match socket.sk { + SocketKind::SockStream => { + socket.con = None; Ok(vm.get_none()) } _ => Err(vm.new_type_error("".to_string())), @@ -213,14 +203,14 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr( &py_mod, - &AddressFamily::AF_INET.to_string(), - ctx.new_int(AddressFamily::AF_INET as i32), + "AF_INET", + ctx.new_int(AddressFamily::AfInet as i32), ); ctx.set_attr( &py_mod, - &SocketKind::SOCK_STREAM.to_string(), - ctx.new_int(SocketKind::SOCK_STREAM as i32), + "SOCK_STREAM", + ctx.new_int(SocketKind::SockStream as i32), ); let socket = { From c84d35126e4494532bf03b762a1de7986438350a Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sun, 24 Feb 2019 21:35:47 +0200 Subject: [PATCH 016/380] Add socket.{bind,listen,accept} --- vm/src/stdlib/socket.rs | 77 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 3 deletions(-) diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index 7b2b76ddbb..d592cf15e6 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -11,9 +11,9 @@ use num_traits::ToPrimitive; use std::io; use std::io::Read; use std::io::Write; -use std::net::{TcpListener, TcpStream, UdpSocket}; +use std::net::{SocketAddr, TcpListener, TcpStream, UdpSocket}; -#[derive(Debug)] +#[derive(Copy, Clone)] enum AddressFamily { AfUnix = 1, AfInet = 2, @@ -31,7 +31,7 @@ impl AddressFamily { } } -#[derive(Debug)] +#[derive(Copy, Clone)] enum SocketKind { SockStream = 1, SockDgram = 2, @@ -53,6 +53,15 @@ enum Connection { UdpSocket(UdpSocket), } +impl Connection { + fn accept(&mut self) -> io::Result<(TcpStream, SocketAddr)> { + match self { + Connection::TcpListener(con) => con.accept(), + _ => Err(io::Error::new(io::ErrorKind::Other, "oh no!")), + } + } +} + impl Read for Connection { fn read(&mut self, buf: &mut [u8]) -> io::Result { match self { @@ -135,6 +144,65 @@ fn socket_connect(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } +fn socket_bind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, None), (address, Some(vm.ctx.str_type()))] + ); + + let mut mut_obj = zelf.borrow_mut(); + + match mut_obj.payload { + PyObjectPayload::Socket { ref mut socket } => { + if let Ok(stream) = TcpListener::bind(objstr::get_value(&address)) { + socket.con = Some(Connection::TcpListener(stream)); + Ok(vm.get_none()) + } else { + // TODO: Socket error + Err(vm.new_type_error("socket failed".to_string())) + } + } + _ => Err(vm.new_type_error("".to_string())), + } +} + +fn socket_listen(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + Ok(vm.get_none()) +} + +fn socket_accept(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, None)]); + + let mut mut_obj = zelf.borrow_mut(); + + match mut_obj.payload { + PyObjectPayload::Socket { ref mut socket } => { + let ret = match socket.con { + Some(ref mut v) => v.accept(), + None => return Err(vm.new_type_error("".to_string())), + }; + + let tcp_stream = match ret { + Ok((socket, _addr)) => socket, + _ => return Err(vm.new_type_error("".to_string())), + }; + + let socket = Socket { + address_family: socket.address_family.clone(), + sk: socket.sk.clone(), + con: Some(Connection::TcpStream(tcp_stream)), + }; + + Ok(PyObject::new( + PyObjectPayload::Socket { socket }, + mut_obj.typ(), + )) + } + _ => Err(vm.new_type_error("".to_string())), + } +} + fn socket_recv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -219,6 +287,9 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&socket, "connect", ctx.new_rustfunc(socket_connect)); ctx.set_attr(&socket, "recv", ctx.new_rustfunc(socket_recv)); ctx.set_attr(&socket, "send", ctx.new_rustfunc(socket_send)); + ctx.set_attr(&socket, "bind", ctx.new_rustfunc(socket_bind)); + ctx.set_attr(&socket, "accept", ctx.new_rustfunc(socket_accept)); + ctx.set_attr(&socket, "listen", ctx.new_rustfunc(socket_listen)); ctx.set_attr(&socket, "close", ctx.new_rustfunc(socket_close)); socket }; From 1322c8d6db34a8ea25b29971e9bf150bd92e67ca Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sun, 24 Feb 2019 22:20:07 +0200 Subject: [PATCH 017/380] Change socket parameters to match CPython --- vm/src/stdlib/socket.rs | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index d592cf15e6..f1634d6457 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -1,7 +1,7 @@ use crate::obj::objbytes; use crate::obj::objint; +use crate::obj::objsequence::get_elements; use crate::obj::objstr; - use crate::pyobject::{ PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; @@ -125,14 +125,20 @@ fn socket_connect(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [(zelf, None), (address, Some(vm.ctx.str_type()))] + required = [(zelf, None), (address, Some(vm.ctx.tuple_type()))] ); + let elements = get_elements(address); + let host = objstr::get_value(&elements[0]); + let port = objint::get_value(&elements[1]); + + let address_string = format!("{}:{}", host, port.to_string()); + let mut mut_obj = zelf.borrow_mut(); match mut_obj.payload { PyObjectPayload::Socket { ref mut socket } => { - if let Ok(stream) = TcpStream::connect(objstr::get_value(&address)) { + if let Ok(stream) = TcpStream::connect(address_string) { socket.con = Some(Connection::TcpStream(stream)); Ok(vm.get_none()) } else { @@ -148,14 +154,20 @@ fn socket_bind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [(zelf, None), (address, Some(vm.ctx.str_type()))] + required = [(zelf, None), (address, Some(vm.ctx.tuple_type()))] ); + let elements = get_elements(address); + let host = objstr::get_value(&elements[0]); + let port = objint::get_value(&elements[1]); + + let address_string = format!("{}:{}", host, port.to_string()); + let mut mut_obj = zelf.borrow_mut(); match mut_obj.payload { PyObjectPayload::Socket { ref mut socket } => { - if let Ok(stream) = TcpListener::bind(objstr::get_value(&address)) { + if let Ok(stream) = TcpListener::bind(address_string) { socket.con = Some(Connection::TcpListener(stream)); Ok(vm.get_none()) } else { @@ -194,9 +206,13 @@ fn socket_accept(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { con: Some(Connection::TcpStream(tcp_stream)), }; + let sock_obj = PyObject::new(PyObjectPayload::Socket { socket }, mut_obj.typ()); + + let elements = vec![sock_obj, vm.get_none()]; + Ok(PyObject::new( - PyObjectPayload::Socket { socket }, - mut_obj.typ(), + PyObjectPayload::Sequence { elements }, + vm.ctx.tuple_type(), )) } _ => Err(vm.new_type_error("".to_string())), From c87cf9004390fc598646eff3be86daa0aa5735ad Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sun, 24 Feb 2019 22:20:20 +0200 Subject: [PATCH 018/380] Add socket test --- tests/snippets/stdlib_socket.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/snippets/stdlib_socket.py diff --git a/tests/snippets/stdlib_socket.py b/tests/snippets/stdlib_socket.py new file mode 100644 index 0000000000..5419d802f3 --- /dev/null +++ b/tests/snippets/stdlib_socket.py @@ -0,0 +1,20 @@ +import socket + +listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +listener.bind(("127.0.0.1", 8080)) +listener.listen(1) + +connector = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +connector.connect(("127.0.0.1", 8080)) +connection = listener.accept()[0] + +message_a = b'aaaa' +message_b = b'bbbbb' + +connector.send(message_a) +connector.close() +recv_a = connection.recv(10) + +connection.close() +listener.close() + From f2079807bd36e89ae1cb4e98998c2f283572c0be Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Sun, 24 Feb 2019 14:22:18 -0600 Subject: [PATCH 019/380] Fix snippet selector's default selection --- wasm/demo/src/index.ejs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wasm/demo/src/index.ejs b/wasm/demo/src/index.ejs index 2d06c3200a..0cb2def1d5 100644 --- a/wasm/demo/src/index.ejs +++ b/wasm/demo/src/index.ejs @@ -20,7 +20,7 @@ From d6e317b185ba7b2f39e5a37d4f3b13854df3e478 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sun, 24 Feb 2019 10:38:19 -0800 Subject: [PATCH 020/380] Move f-string parser into own module and clean up a bit --- parser/src/fstring.rs | 200 ++++++++++++++++++++++++++++++++++ parser/src/lib.rs | 1 + parser/src/parser.rs | 219 -------------------------------------- parser/src/python.lalrpop | 10 +- 4 files changed, 207 insertions(+), 223 deletions(-) create mode 100644 parser/src/fstring.rs diff --git a/parser/src/fstring.rs b/parser/src/fstring.rs new file mode 100644 index 0000000000..d5881cdfa2 --- /dev/null +++ b/parser/src/fstring.rs @@ -0,0 +1,200 @@ +use std::iter; +use std::mem; +use std::str; + +use lalrpop_util::ParseError as LalrpopError; + +use crate::ast::StringGroup; +use crate::lexer::{LexicalError, Location, Tok}; +use crate::parser::parse_expression; + +use self::StringGroup::*; + +// TODO: consolidate these with ParseError +#[derive(Debug, PartialEq)] +pub enum FStringError { + UnclosedLbrace, + UnopenedRbrace, + InvalidExpression, +} + +impl From for LalrpopError { + fn from(_err: FStringError) -> Self { + lalrpop_util::ParseError::User { + error: LexicalError::StringError, + } + } +} + +struct FStringParser<'a> { + chars: iter::Peekable>, +} + +impl<'a> FStringParser<'a> { + fn new(source: &'a str) -> Self { + Self { + chars: source.chars().peekable(), + } + } + + fn parse_formatted_value(&mut self) -> Result { + let mut expression = String::new(); + let mut spec = String::new(); + let mut depth = 0; + + while let Some(ch) = self.chars.next() { + match ch { + ':' if depth == 0 => { + while let Some(&next) = self.chars.peek() { + if next != '}' { + spec.push(next); + self.chars.next(); + } else { + break; + } + } + } + '{' => { + if let Some('{') = self.chars.peek() { + expression.push_str("{{"); + self.chars.next(); + } else { + expression.push('{'); + depth += 1; + } + } + '}' => { + if let Some('}') = self.chars.peek() { + expression.push_str("}}"); + self.chars.next(); + } else if depth > 0 { + expression.push('}'); + depth -= 1; + } else { + return Ok(FormattedValue { + value: Box::new( + parse_expression(expression.trim()) + .map_err(|_| FStringError::InvalidExpression)?, + ), + spec, + }); + } + } + _ => { + expression.push(ch); + } + } + } + + return Err(FStringError::UnclosedLbrace); + } + + fn parse(mut self) -> Result { + let mut content = String::new(); + let mut values = vec![]; + + while let Some(ch) = self.chars.next() { + match ch { + '{' => { + if let Some('{') = self.chars.peek() { + self.chars.next(); + content.push('{'); + } else { + if !content.is_empty() { + values.push(Constant { + value: mem::replace(&mut content, String::new()), + }); + } + + values.push(self.parse_formatted_value()?); + } + } + '}' => { + if let Some('}') = self.chars.peek() { + self.chars.next(); + content.push('}'); + } else { + return Err(FStringError::UnopenedRbrace); + } + } + _ => { + content.push(ch); + } + } + } + + if !content.is_empty() { + values.push(Constant { value: content }) + } + + Ok(match values.len() { + 0 => Constant { + value: String::new(), + }, + 1 => values.into_iter().next().unwrap(), + _ => Joined { values }, + }) + } +} + +pub fn parse_fstring(source: &str) -> Result { + FStringParser::new(source).parse() +} + +#[cfg(test)] +mod tests { + use crate::ast; + + use super::*; + + fn mk_ident(name: &str) -> ast::Expression { + ast::Expression::Identifier { + name: name.to_owned(), + } + } + + #[test] + fn test_parse_fstring() { + let source = String::from("{a}{ b }{{foo}}"); + let parse_ast = parse_fstring(&source).unwrap(); + + assert_eq!( + parse_ast, + Joined { + values: vec![ + FormattedValue { + value: Box::new(mk_ident("a")), + spec: String::new(), + }, + FormattedValue { + value: Box::new(mk_ident("b")), + spec: String::new(), + }, + Constant { + value: "{foo}".to_owned() + } + ] + } + ); + } + + #[test] + fn test_parse_empty_fstring() { + assert_eq!( + parse_fstring(""), + Ok(Constant { + value: String::new(), + }), + ); + } + + #[test] + fn test_parse_invalid_fstring() { + assert_eq!(parse_fstring("{"), Err(FStringError::UnclosedLbrace)); + assert_eq!(parse_fstring("}"), Err(FStringError::UnopenedRbrace)); + assert_eq!( + parse_fstring("{class}"), + Err(FStringError::InvalidExpression) + ); + } +} diff --git a/parser/src/lib.rs b/parser/src/lib.rs index f7f369968b..b10c3d5514 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -3,6 +3,7 @@ extern crate log; pub mod ast; pub mod error; +mod fstring; pub mod lexer; pub mod parser; #[cfg_attr(rustfmt, rustfmt_skip)] diff --git a/parser/src/parser.rs b/parser/src/parser.rs index fb8f2f0e5a..2a39a41f85 100644 --- a/parser/src/parser.rs +++ b/parser/src/parser.rs @@ -65,180 +65,12 @@ pub fn parse_expression(source: &str) -> Result { do_lalr_parsing!(source, Expression, StartExpression) } -// TODO: consolidate these with ParseError -#[derive(Debug, PartialEq)] -pub enum FStringError { - UnclosedLbrace, - UnopenedRbrace, - InvalidExpression, -} - -impl From - for lalrpop_util::ParseError -{ - fn from(_err: FStringError) -> Self { - lalrpop_util::ParseError::User { - error: lexer::LexicalError::StringError, - } - } -} - -enum ParseState { - Text { - content: String, - }, - FormattedValue { - expression: String, - spec: Option, - depth: usize, - }, -} - -pub fn parse_fstring(source: &str) -> Result { - use self::ParseState::*; - - let mut values = vec![]; - let mut state = ParseState::Text { - content: String::new(), - }; - - let mut chars = source.chars().peekable(); - while let Some(ch) = chars.next() { - state = match state { - Text { mut content } => match ch { - '{' => { - if let Some('{') = chars.peek() { - chars.next(); - content.push('{'); - Text { content } - } else { - if !content.is_empty() { - values.push(ast::StringGroup::Constant { value: content }); - } - - FormattedValue { - expression: String::new(), - spec: None, - depth: 0, - } - } - } - '}' => { - if let Some('}') = chars.peek() { - chars.next(); - content.push('}'); - Text { content } - } else { - return Err(FStringError::UnopenedRbrace); - } - } - _ => { - content.push(ch); - Text { content } - } - }, - - FormattedValue { - mut expression, - mut spec, - depth, - } => match ch { - ':' if depth == 0 => FormattedValue { - expression, - spec: Some(String::new()), - depth, - }, - '{' => { - if let Some('{') = chars.peek() { - expression.push_str("{{"); - chars.next(); - FormattedValue { - expression, - spec, - depth, - } - } else { - expression.push('{'); - FormattedValue { - expression, - spec, - depth: depth + 1, - } - } - } - '}' => { - if let Some('}') = chars.peek() { - expression.push_str("}}"); - chars.next(); - FormattedValue { - expression, - spec, - depth, - } - } else if depth > 0 { - expression.push('}'); - FormattedValue { - expression, - spec, - depth: depth - 1, - } - } else { - values.push(ast::StringGroup::FormattedValue { - value: Box::new(match parse_expression(expression.trim()) { - Ok(expr) => expr, - Err(_) => return Err(FStringError::InvalidExpression), - }), - spec: spec.unwrap_or_default(), - }); - Text { - content: String::new(), - } - } - } - _ => { - if let Some(spec) = spec.as_mut() { - spec.push(ch) - } else { - expression.push(ch); - } - FormattedValue { - expression, - spec, - depth, - } - } - }, - }; - } - - match state { - Text { content } => { - if !content.is_empty() { - values.push(ast::StringGroup::Constant { value: content }) - } - } - FormattedValue { .. } => { - return Err(FStringError::UnclosedLbrace); - } - } - - Ok(match values.len() { - 0 => ast::StringGroup::Constant { - value: String::new(), - }, - 1 => values.into_iter().next().unwrap(), - _ => ast::StringGroup::Joined { values }, - }) -} - #[cfg(test)] mod tests { use super::ast; use super::parse_expression; - use super::parse_fstring; use super::parse_program; use super::parse_statement; - use super::FStringError; use num_bigint::BigInt; #[test] @@ -630,55 +462,4 @@ mod tests { } ); } - - fn mk_ident(name: &str) -> ast::Expression { - ast::Expression::Identifier { - name: name.to_owned(), - } - } - - #[test] - fn test_parse_fstring() { - let source = String::from("{a}{ b }{{foo}}"); - let parse_ast = parse_fstring(&source).unwrap(); - - assert_eq!( - parse_ast, - ast::StringGroup::Joined { - values: vec![ - ast::StringGroup::FormattedValue { - value: Box::new(mk_ident("a")), - spec: String::new(), - }, - ast::StringGroup::FormattedValue { - value: Box::new(mk_ident("b")), - spec: String::new(), - }, - ast::StringGroup::Constant { - value: "{foo}".to_owned() - } - ] - } - ); - } - - #[test] - fn test_parse_empty_fstring() { - assert_eq!( - parse_fstring(""), - Ok(ast::StringGroup::Constant { - value: String::new(), - }), - ); - } - - #[test] - fn test_parse_invalid_fstring() { - assert_eq!(parse_fstring("{"), Err(FStringError::UnclosedLbrace)); - assert_eq!(parse_fstring("}"), Err(FStringError::UnopenedRbrace)); - assert_eq!( - parse_fstring("{class}"), - Err(FStringError::InvalidExpression) - ); - } } diff --git a/parser/src/python.lalrpop b/parser/src/python.lalrpop index 8ee407f9ae..2748877ddd 100644 --- a/parser/src/python.lalrpop +++ b/parser/src/python.lalrpop @@ -4,10 +4,12 @@ // See also: https://greentreesnakes.readthedocs.io/en/latest/nodes.html#keyword #![allow(unknown_lints,clippy)] -use super::ast; -use super::lexer; -use super::parser; use std::iter::FromIterator; + +use crate::ast; +use crate::fstring::parse_fstring; +use crate::lexer; + use num_bigint::BigInt; grammar; @@ -1008,7 +1010,7 @@ StringGroup: ast::StringGroup = { let mut values = vec![]; for (value, is_fstring) in s { values.push(if is_fstring { - parser::parse_fstring(&value)? + parse_fstring(&value)? } else { ast::StringGroup::Constant { value } }) From ddc154a1dda4f1f7ba04b3a5a50e151c714d9e98 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sun, 24 Feb 2019 13:46:50 -0800 Subject: [PATCH 021/380] f-strings: support conversion flags --- parser/src/ast.rs | 12 ++++++++++++ parser/src/fstring.rs | 36 ++++++++++++++++++++++++++---------- tests/snippets/fstrings.py | 19 +++++++++++++++++++ vm/src/bytecode.rs | 6 +++++- vm/src/compile.rs | 11 +++++++++-- vm/src/frame.rs | 11 +++++++++-- 6 files changed, 80 insertions(+), 15 deletions(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index e607c4e4bf..749003d9a9 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -357,6 +357,17 @@ pub enum Number { Complex { real: f64, imag: f64 }, } +/// Transforms a value prior to formatting it. +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum ConversionFlag { + /// Converts by calling `str()`. + Str, + /// Converts by calling `ascii()`. + Ascii, + /// Converts by calling `repr()`. + Repr, +} + #[derive(Debug, PartialEq)] pub enum StringGroup { Constant { @@ -364,6 +375,7 @@ pub enum StringGroup { }, FormattedValue { value: Box, + conversion: Option, spec: String, }, Joined { diff --git a/parser/src/fstring.rs b/parser/src/fstring.rs index d5881cdfa2..34ac22d2db 100644 --- a/parser/src/fstring.rs +++ b/parser/src/fstring.rs @@ -4,10 +4,11 @@ use std::str; use lalrpop_util::ParseError as LalrpopError; -use crate::ast::StringGroup; +use crate::ast::{ConversionFlag, StringGroup}; use crate::lexer::{LexicalError, Location, Tok}; use crate::parser::parse_expression; +use self::FStringError::*; use self::StringGroup::*; // TODO: consolidate these with ParseError @@ -16,6 +17,7 @@ pub enum FStringError { UnclosedLbrace, UnopenedRbrace, InvalidExpression, + InvalidConversionFlag, } impl From for LalrpopError { @@ -41,9 +43,23 @@ impl<'a> FStringParser<'a> { let mut expression = String::new(); let mut spec = String::new(); let mut depth = 0; + let mut conversion = None; while let Some(ch) = self.chars.next() { match ch { + '!' if depth == 0 => { + conversion = Some(match self.chars.next() { + Some('s') => ConversionFlag::Str, + Some('a') => ConversionFlag::Ascii, + Some('r') => ConversionFlag::Repr, + Some(_) => { + return Err(InvalidConversionFlag); + } + None => { + break; + } + }) + } ':' if depth == 0 => { while let Some(&next) = self.chars.peek() { if next != '}' { @@ -74,8 +90,9 @@ impl<'a> FStringParser<'a> { return Ok(FormattedValue { value: Box::new( parse_expression(expression.trim()) - .map_err(|_| FStringError::InvalidExpression)?, + .map_err(|_| InvalidExpression)?, ), + conversion, spec, }); } @@ -86,7 +103,7 @@ impl<'a> FStringParser<'a> { } } - return Err(FStringError::UnclosedLbrace); + return Err(UnclosedLbrace); } fn parse(mut self) -> Result { @@ -114,7 +131,7 @@ impl<'a> FStringParser<'a> { self.chars.next(); content.push('}'); } else { - return Err(FStringError::UnopenedRbrace); + return Err(UnopenedRbrace); } } _ => { @@ -164,10 +181,12 @@ mod tests { values: vec![ FormattedValue { value: Box::new(mk_ident("a")), + conversion: None, spec: String::new(), }, FormattedValue { value: Box::new(mk_ident("b")), + conversion: None, spec: String::new(), }, Constant { @@ -190,11 +209,8 @@ mod tests { #[test] fn test_parse_invalid_fstring() { - assert_eq!(parse_fstring("{"), Err(FStringError::UnclosedLbrace)); - assert_eq!(parse_fstring("}"), Err(FStringError::UnopenedRbrace)); - assert_eq!( - parse_fstring("{class}"), - Err(FStringError::InvalidExpression) - ); + assert_eq!(parse_fstring("{"), Err(UnclosedLbrace)); + assert_eq!(parse_fstring("}"), Err(UnopenedRbrace)); + assert_eq!(parse_fstring("{class}"), Err(InvalidExpression)); } } diff --git a/tests/snippets/fstrings.py b/tests/snippets/fstrings.py index 2ee45742f8..eb00c1cdec 100644 --- a/tests/snippets/fstrings.py +++ b/tests/snippets/fstrings.py @@ -15,3 +15,22 @@ #assert f"{1 != 2}" == 'True' assert fr'x={4*10}\n' == 'x=40\\n' assert f'{16:0>+#10x}' == '00000+0x10' + + +# conversion flags + +class Value: + def __format__(self, spec): + return "foo" + + def __repr__(self): + return "bar" + + def __str__(self): + return "baz" + +v = Value() + +assert f'{v}' == 'foo' +assert f'{v!r}' == 'bar' +assert f'{v!s}' == 'baz' diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index 1e0b85f133..b7cefe0b3e 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -169,6 +169,7 @@ pub enum Instruction { }, Unpack, FormatValue { + conversion: Option, spec: String, }, } @@ -361,7 +362,10 @@ impl Instruction { UnpackSequence { size } => w!(UnpackSequence, size), UnpackEx { before, after } => w!(UnpackEx, before, after), Unpack => w!(Unpack), - FormatValue { spec } => w!(FormatValue, spec), + FormatValue { + conversion: _, + spec, + } => w!(FormatValue, spec), // TODO: write conversion } } } diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 866b0dad74..a562c1d3de 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -1352,9 +1352,16 @@ impl Compiler { }, }); } - ast::StringGroup::FormattedValue { value, spec } => { + ast::StringGroup::FormattedValue { + value, + conversion, + spec, + } => { self.compile_expression(value)?; - self.emit(Instruction::FormatValue { spec: spec.clone() }); + self.emit(Instruction::FormatValue { + conversion: *conversion, + spec: spec.clone(), + }); } } Ok(()) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 5de7f68099..f97be68942 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -654,8 +654,15 @@ impl Frame { } Ok(None) } - bytecode::Instruction::FormatValue { spec } => { - let value = self.pop_value(); + bytecode::Instruction::FormatValue { conversion, spec } => { + use ast::ConversionFlag::*; + let value = match conversion { + Some(Str) => vm.to_str(&self.pop_value())?, + Some(Repr) => vm.to_repr(&self.pop_value())?, + Some(Ascii) => self.pop_value(), // TODO + None => self.pop_value(), + }; + let spec = vm.new_str(spec.clone()); let formatted = vm.call_method(&value, "__format__", vec![spec])?; self.push_value(formatted); From 5dd2de0b1ee5f611603243be025efc483d130c1b Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Sun, 24 Feb 2019 21:13:51 -0600 Subject: [PATCH 022/380] Add requestAnimationFrame to browser module --- wasm/demo/snippets/mandelbrot.py | 20 +++++++---- wasm/lib/src/browser_module.rs | 61 ++++++++++++++++++++++++++++++-- 2 files changed, 72 insertions(+), 9 deletions(-) diff --git a/wasm/demo/snippets/mandelbrot.py b/wasm/demo/snippets/mandelbrot.py index 8d99aed4f9..a2de7e8ae1 100644 --- a/wasm/demo/snippets/mandelbrot.py +++ b/wasm/demo/snippets/mandelbrot.py @@ -1,11 +1,14 @@ -# NOTE: will take a while, up to around a minute, to run. -# Expect this page to freeze. +from browser import request_animation_frame w = 50.0 h = 50.0 -y = 0.0 -while y < h: +_y = {'y': 0.0} + +def mandel(_time_elapsed=None): + y = _y['y'] + if y >= h: + return x = 0.0 while x < w: Zr, Zi, Tr, Ti = 0.0, 0.0, 0.0, 0.0 @@ -18,14 +21,17 @@ Zr = Tr - Ti + Cr Tr = Zr * Zr Ti = Zi * Zi - i = i + 1 + i += 1 if Tr + Ti <= 4: print('*', end='') else: print('·', end='') - x = x + 1 + x += 1 print() - y = y + 1 + _y['y'] += 1 + request_animation_frame(mandel) + +mandel() \ No newline at end of file diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index 93b6ec5cde..3387eee037 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -1,7 +1,7 @@ use crate::{convert, vm_class::AccessibleVM, wasm_builtins::window}; use futures::{future, Future}; use js_sys::Promise; -use rustpython_vm::obj::objstr; +use rustpython_vm::obj::{objint, objstr}; use rustpython_vm::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use rustpython_vm::VirtualMachine; use wasm_bindgen::{prelude::*, JsCast}; @@ -125,11 +125,68 @@ fn browser_fetch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.get_none()) } +fn browser_request_animation_frame(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(func, Some(vm.ctx.function_type()))]); + + use std::{cell::RefCell, rc::Rc}; + + // this basic setup for request_animation_frame taken from: + // https://rustwasm.github.io/wasm-bindgen/examples/request-animation-frame.html + + let f = Rc::new(RefCell::new(None)); + let g = f.clone(); + + let func = func.clone(); + + let acc_vm = AccessibleVM::from_vm(vm); + + *g.borrow_mut() = Some(Closure::wrap(Box::new(move |time: f64| { + let vm = &mut acc_vm + .upgrade() + .expect("that the vm is valid from inside of request_animation_frame"); + let func = func.clone(); + let args = PyFuncArgs { + args: vec![vm.ctx.new_float(time)], + kwargs: vec![], + }; + let _ = vm.invoke(func, args); + + let closure = f.borrow_mut().take(); + drop(closure); + }) as Box)); + + let id = window() + .request_animation_frame(&js_sys::Function::from( + g.borrow().as_ref().unwrap().as_ref().clone(), + )) + .map_err(|err| convert::js_py_typeerror(vm, err))?; + + Ok(vm.ctx.new_int(id)) +} + +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"); + + window() + .cancel_animation_frame(id) + .map_err(|err| convert::js_py_typeerror(vm, err))?; + + Ok(vm.get_none()) +} + const BROWSER_NAME: &str = "browser"; pub fn mk_module(ctx: &PyContext) -> PyObjectRef { py_module!(ctx, BROWSER_NAME, { - "fetch" => ctx.new_rustfunc(browser_fetch) + "fetch" => ctx.new_rustfunc(browser_fetch), + "request_animation_frame" => ctx.new_rustfunc(browser_request_animation_frame), + "cancel_animation_frame" => ctx.new_rustfunc(browser_cancel_animation_frame), }) } From 1646b53c72b989be01c842dddae5d152973fbcf0 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Sun, 24 Feb 2019 21:28:25 -0600 Subject: [PATCH 023/380] Add a comment explaining the weird dict --- wasm/demo/snippets/mandelbrot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/wasm/demo/snippets/mandelbrot.py b/wasm/demo/snippets/mandelbrot.py index a2de7e8ae1..f8549188ee 100644 --- a/wasm/demo/snippets/mandelbrot.py +++ b/wasm/demo/snippets/mandelbrot.py @@ -3,6 +3,7 @@ w = 50.0 h = 50.0 +# to make up for the lack of `global` _y = {'y': 0.0} def mandel(_time_elapsed=None): From f050acba736af99190b8dec8b6908aa44a050940 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sun, 24 Feb 2019 17:24:17 -0800 Subject: [PATCH 024/380] f-strings: allow ':' and '!' to be used in the expression --- parser/src/fstring.rs | 68 ++++++++++++++++++++++++-------------- tests/snippets/fstrings.py | 9 +++-- 2 files changed, 50 insertions(+), 27 deletions(-) diff --git a/parser/src/fstring.rs b/parser/src/fstring.rs index 34ac22d2db..e6e5586b06 100644 --- a/parser/src/fstring.rs +++ b/parser/src/fstring.rs @@ -18,6 +18,8 @@ pub enum FStringError { UnopenedRbrace, InvalidExpression, InvalidConversionFlag, + EmptyExpression, + MismatchedDelimiter, } impl From for LalrpopError { @@ -42,12 +44,12 @@ impl<'a> FStringParser<'a> { fn parse_formatted_value(&mut self) -> Result { let mut expression = String::new(); let mut spec = String::new(); - let mut depth = 0; + let mut delims = Vec::new(); let mut conversion = None; while let Some(ch) = self.chars.next() { match ch { - '!' if depth == 0 => { + '!' if delims.is_empty() => { conversion = Some(match self.chars.next() { Some('s') => ConversionFlag::Str, Some('a') => ConversionFlag::Ascii, @@ -60,7 +62,7 @@ impl<'a> FStringParser<'a> { } }) } - ':' if depth == 0 => { + ':' if delims.is_empty() => { while let Some(&next) = self.chars.peek() { if next != '}' { spec.push(next); @@ -70,31 +72,47 @@ impl<'a> FStringParser<'a> { } } } - '{' => { - if let Some('{') = self.chars.peek() { - expression.push_str("{{"); - self.chars.next(); - } else { - expression.push('{'); - depth += 1; + '(' | '{' | '[' => { + expression.push(ch); + delims.push(ch); + } + ')' => { + if delims.pop() != Some('(') { + return Err(MismatchedDelimiter); } + expression.push(ch); + } + ']' => { + if delims.pop() != Some('[') { + return Err(MismatchedDelimiter); + } + expression.push(ch); + } + '}' if !delims.is_empty() => { + if delims.pop() != Some('{') { + return Err(MismatchedDelimiter); + } + expression.push(ch); } '}' => { - if let Some('}') = self.chars.peek() { - expression.push_str("}}"); - self.chars.next(); - } else if depth > 0 { - expression.push('}'); - depth -= 1; - } else { - return Ok(FormattedValue { - value: Box::new( - parse_expression(expression.trim()) - .map_err(|_| InvalidExpression)?, - ), - conversion, - spec, - }); + if expression.is_empty() { + return Err(EmptyExpression); + } + return Ok(FormattedValue { + value: Box::new( + parse_expression(expression.trim()).map_err(|_| InvalidExpression)?, + ), + conversion, + spec, + }); + } + '"' | '\'' => { + expression.push(ch); + while let Some(next) = self.chars.next() { + expression.push(next); + if next == ch { + break; + } } } _ => { diff --git a/tests/snippets/fstrings.py b/tests/snippets/fstrings.py index eb00c1cdec..c76967acb8 100644 --- a/tests/snippets/fstrings.py +++ b/tests/snippets/fstrings.py @@ -11,10 +11,15 @@ assert f'{f"{{"}' == '{' assert f'{f"}}"}' == '}' assert f'{foo}' f"{foo}" 'foo' == 'barbarfoo' -#assert f'{"!:"}' == '!:' -#assert f"{1 != 2}" == 'True' +assert f'{"!:"}' == '!:' assert fr'x={4*10}\n' == 'x=40\\n' assert f'{16:0>+#10x}' == '00000+0x10' +assert f"{{{(lambda x: f'hello, {x}')('world}')}" == '{hello, world}' + +# Normally `!` cannot appear outside of delimiters in the expression but +# cpython makes an exception for `!=`, so we should too. + +# assert f'{1 != 2}' == 'True' # conversion flags From e9b0454f1cc0263b5cf557b32d6d1cf86a08e4e8 Mon Sep 17 00:00:00 2001 From: Aaron Date: Mon, 25 Feb 2019 00:12:15 -0800 Subject: [PATCH 025/380] fix invalid break continue return yield --- vm/src/bytecode.rs | 3 +++ vm/src/compile.rs | 44 +++++++++++++++++++++++++++++--------------- vm/src/error.rs | 8 ++++++-- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index 1e0b85f133..8e3a26b6f4 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -26,6 +26,7 @@ pub struct CodeObject { pub first_line_number: usize, pub obj_name: String, // Name of the object that created this code object pub is_generator: bool, + pub is_function_obj: bool } bitflags! { @@ -252,6 +253,7 @@ impl CodeObject { source_path: String, first_line_number: usize, obj_name: String, + is_function_obj: bool ) -> CodeObject { CodeObject { instructions: Vec::new(), @@ -265,6 +267,7 @@ impl CodeObject { first_line_number, obj_name, is_generator: false, + is_function_obj } } diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 866b0dad74..0c6705ceba 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -16,7 +16,7 @@ struct Compiler { nxt_label: usize, source_path: Option, current_source_location: ast::Location, - in_loop: bool, + loop_depth: usize, } /// Compile a given sourcecode into a bytecode object. @@ -74,7 +74,7 @@ impl Compiler { nxt_label: 0, source_path: None, current_source_location: ast::Location::default(), - in_loop: false, + loop_depth: 0, } } @@ -88,6 +88,7 @@ impl Compiler { self.source_path.clone().unwrap(), line_number, obj_name, + false )); } @@ -232,9 +233,9 @@ impl Compiler { self.compile_test(test, None, Some(else_label), EvalContext::Statement)?; - self.in_loop = true; + self.loop_depth += 1; self.compile_statements(body)?; - self.in_loop = false; + self.loop_depth -= 1; self.emit(Instruction::Jump { target: start_label, }); @@ -295,10 +296,9 @@ impl Compiler { // Start of loop iteration, set targets: self.compile_store(target)?; - // Body of loop: - self.in_loop = true; + self.loop_depth += 1; self.compile_statements(body)?; - self.in_loop = false; + self.loop_depth -= 1; self.emit(Instruction::Jump { target: start_label, @@ -431,6 +431,9 @@ impl Compiler { decorator_list, } => { // Create bytecode for this function: + // remember to restore self.loop_depth to the original after the function is compiled + let original_loop_depth = self.loop_depth; + self.loop_depth = 0; let flags = self.enter_function(name, args)?; self.compile_statements(body)?; @@ -458,6 +461,7 @@ impl Compiler { self.emit(Instruction::StoreName { name: name.to_string(), }); + self.loop_depth = original_loop_depth; } ast::Statement::ClassDef { name, @@ -477,6 +481,7 @@ impl Compiler { self.source_path.clone().unwrap(), line_number, name.clone(), + false )); self.emit(Instruction::LoadName { name: String::from("__locals__"), @@ -572,18 +577,21 @@ impl Compiler { self.set_label(end_label); } ast::Statement::Break => { - if !self.in_loop { + if self.loop_depth == 0 { return Err(CompileError::InvalidBreak); } self.emit(Instruction::Break); } ast::Statement::Continue => { - if !self.in_loop { + if self.loop_depth == 0 { return Err(CompileError::InvalidContinue); } self.emit(Instruction::Continue); } ast::Statement::Return { value } => { + if !self.current_code_object().is_function_obj { + return Err(CompileError::InvalidReturn); + } match value { Some(e) => { let size = e.len(); @@ -663,7 +671,7 @@ impl Compiler { name: &str, args: &ast::Parameters, ) -> Result { - self.in_loop = false; + let have_kwargs = !args.defaults.is_empty(); if have_kwargs { // Construct a tuple: @@ -686,6 +694,7 @@ impl Compiler { self.source_path.clone().unwrap(), line_number, name.to_string(), + true )); let mut flags = bytecode::FunctionOpArg::empty(); @@ -971,6 +980,9 @@ impl Compiler { self.emit(Instruction::BuildSlice { size }); } ast::Expression::Yield { value } => { + if !self.current_code_object().is_function_obj { + return Err(CompileError::InvalidYield); + } self.mark_generator(); match value { Some(expression) => self.compile_expression(expression)?, @@ -1021,6 +1033,7 @@ impl Compiler { } ast::Expression::Lambda { args, body } => { let name = "".to_string(); + // no need to worry about the self.loop_depth because there are no loops in lambda expressions let flags = self.enter_function(&name, args)?; self.compile_expression(body)?; self.emit(Instruction::ReturnValue); @@ -1199,6 +1212,7 @@ impl Compiler { self.source_path.clone().unwrap(), line_number, name.clone(), + false )); // Create empty object of proper type: @@ -1362,10 +1376,11 @@ impl Compiler { // Low level helper functions: fn emit(&mut self, instruction: Instruction) { - self.current_code_object().instructions.push(instruction); - // TODO: insert source filename let location = self.current_source_location.clone(); - self.current_code_object().locations.push(location); + let mut cur_code_obj = self.current_code_object(); + cur_code_obj.instructions.push(instruction); + cur_code_obj.locations.push(location); + // TODO: insert source filename } fn current_code_object(&mut self) -> &mut CodeObject { @@ -1374,9 +1389,8 @@ impl Compiler { // Generate a new label fn new_label(&mut self) -> Label { - let l = self.nxt_label; self.nxt_label += 1; - l + self.nxt_label - 1 } // Assign current position the given label diff --git a/vm/src/error.rs b/vm/src/error.rs index 70c8ff71ec..8f152a6ca3 100644 --- a/vm/src/error.rs +++ b/vm/src/error.rs @@ -19,6 +19,8 @@ pub enum CompileError { InvalidBreak, /// Continue statement outside of loop. InvalidContinue, + InvalidReturn, + InvalidYield } impl fmt::Display for CompileError { @@ -29,8 +31,10 @@ impl fmt::Display for CompileError { CompileError::ExpectExpr => write!(f, "Expecting expression, got statement"), CompileError::Parse(err) => write!(f, "{}", err), CompileError::StarArgs => write!(f, "Two starred expressions in assignment"), - CompileError::InvalidBreak => write!(f, "break outside loop"), - CompileError::InvalidContinue => write!(f, "continue outside loop"), + CompileError::InvalidBreak => write!(f, "'break' outside loop"), + CompileError::InvalidContinue => write!(f, "'continue' outside loop"), + CompileError::InvalidReturn => write!(f, "'return' outside function"), + CompileError::InvalidYield => write!(f, "'yield' outside function") } } } From 7610d448653ab6036c11bc9995fbda6d9c5c4d66 Mon Sep 17 00:00:00 2001 From: Aaron Date: Mon, 25 Feb 2019 00:26:06 -0800 Subject: [PATCH 026/380] cargo fmt --- vm/src/bytecode.rs | 6 +++--- vm/src/compile.rs | 9 ++++----- vm/src/error.rs | 4 ++-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index 8e3a26b6f4..dac290453d 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -26,7 +26,7 @@ pub struct CodeObject { pub first_line_number: usize, pub obj_name: String, // Name of the object that created this code object pub is_generator: bool, - pub is_function_obj: bool + pub is_function_obj: bool, } bitflags! { @@ -253,7 +253,7 @@ impl CodeObject { source_path: String, first_line_number: usize, obj_name: String, - is_function_obj: bool + is_function_obj: bool, ) -> CodeObject { CodeObject { instructions: Vec::new(), @@ -267,7 +267,7 @@ impl CodeObject { first_line_number, obj_name, is_generator: false, - is_function_obj + is_function_obj, } } diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 0c6705ceba..bf9b21b515 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -88,7 +88,7 @@ impl Compiler { self.source_path.clone().unwrap(), line_number, obj_name, - false + false, )); } @@ -481,7 +481,7 @@ impl Compiler { self.source_path.clone().unwrap(), line_number, name.clone(), - false + false, )); self.emit(Instruction::LoadName { name: String::from("__locals__"), @@ -671,7 +671,6 @@ impl Compiler { name: &str, args: &ast::Parameters, ) -> Result { - let have_kwargs = !args.defaults.is_empty(); if have_kwargs { // Construct a tuple: @@ -694,7 +693,7 @@ impl Compiler { self.source_path.clone().unwrap(), line_number, name.to_string(), - true + true, )); let mut flags = bytecode::FunctionOpArg::empty(); @@ -1212,7 +1211,7 @@ impl Compiler { self.source_path.clone().unwrap(), line_number, name.clone(), - false + false, )); // Create empty object of proper type: diff --git a/vm/src/error.rs b/vm/src/error.rs index 8f152a6ca3..06eb387e2e 100644 --- a/vm/src/error.rs +++ b/vm/src/error.rs @@ -20,7 +20,7 @@ pub enum CompileError { /// Continue statement outside of loop. InvalidContinue, InvalidReturn, - InvalidYield + InvalidYield, } impl fmt::Display for CompileError { @@ -34,7 +34,7 @@ impl fmt::Display for CompileError { CompileError::InvalidBreak => write!(f, "'break' outside loop"), CompileError::InvalidContinue => write!(f, "'continue' outside loop"), CompileError::InvalidReturn => write!(f, "'return' outside function"), - CompileError::InvalidYield => write!(f, "'yield' outside function") + CompileError::InvalidYield => write!(f, "'yield' outside function"), } } } From 2f0f1b091312d033c5ef233eb030fd32cdbb4998 Mon Sep 17 00:00:00 2001 From: Aaron Date: Mon, 25 Feb 2019 09:08:36 -0800 Subject: [PATCH 027/380] use boolean flag --- vm/src/compile.rs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/vm/src/compile.rs b/vm/src/compile.rs index bf9b21b515..c22e00dcfe 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -16,7 +16,7 @@ struct Compiler { nxt_label: usize, source_path: Option, current_source_location: ast::Location, - loop_depth: usize, + in_loop: bool, } /// Compile a given sourcecode into a bytecode object. @@ -74,7 +74,7 @@ impl Compiler { nxt_label: 0, source_path: None, current_source_location: ast::Location::default(), - loop_depth: 0, + in_loop: false, } } @@ -233,9 +233,10 @@ impl Compiler { self.compile_test(test, None, Some(else_label), EvalContext::Statement)?; - self.loop_depth += 1; + let was_in_loop = self.in_loop; + self.in_loop = true; self.compile_statements(body)?; - self.loop_depth -= 1; + self.in_loop = was_in_loop; self.emit(Instruction::Jump { target: start_label, }); @@ -296,9 +297,10 @@ impl Compiler { // Start of loop iteration, set targets: self.compile_store(target)?; - self.loop_depth += 1; + let was_in_loop = self.in_loop; + self.in_loop = true; self.compile_statements(body)?; - self.loop_depth -= 1; + self.in_loop = was_in_loop; self.emit(Instruction::Jump { target: start_label, @@ -431,9 +433,9 @@ impl Compiler { decorator_list, } => { // Create bytecode for this function: - // remember to restore self.loop_depth to the original after the function is compiled - let original_loop_depth = self.loop_depth; - self.loop_depth = 0; + // remember to restore self.in_loop to the original after the function is compiled + let was_in_loop = self.in_loop; + self.in_loop = false; let flags = self.enter_function(name, args)?; self.compile_statements(body)?; @@ -461,7 +463,7 @@ impl Compiler { self.emit(Instruction::StoreName { name: name.to_string(), }); - self.loop_depth = original_loop_depth; + self.in_loop = was_in_loop; } ast::Statement::ClassDef { name, @@ -577,13 +579,13 @@ impl Compiler { self.set_label(end_label); } ast::Statement::Break => { - if self.loop_depth == 0 { + if !self.in_loop { return Err(CompileError::InvalidBreak); } self.emit(Instruction::Break); } ast::Statement::Continue => { - if self.loop_depth == 0 { + if !self.in_loop { return Err(CompileError::InvalidContinue); } self.emit(Instruction::Continue); @@ -1419,6 +1421,7 @@ mod tests { use crate::bytecode::Constant::*; use crate::bytecode::Instruction::*; use rustpython_parser::parser; + fn compile_exec(source: &str) -> CodeObject { let mut compiler = Compiler::new(); compiler.source_path = Some("source_path".to_string()); From 4a7d2cb01072ff377e06f88971786fcf4705e06f Mon Sep 17 00:00:00 2001 From: Aaron Date: Mon, 25 Feb 2019 09:42:02 -0800 Subject: [PATCH 028/380] reset in_loop in class definition block --- vm/src/compile.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vm/src/compile.rs b/vm/src/compile.rs index c22e00dcfe..94f85a990d 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -472,6 +472,8 @@ impl Compiler { keywords, decorator_list, } => { + let was_in_loop = self.in_loop; + self.in_loop = false; self.prepare_decorators(decorator_list)?; self.emit(Instruction::LoadBuildClass); let line_number = self.get_source_line_number(); @@ -553,6 +555,7 @@ impl Compiler { self.emit(Instruction::StoreName { name: name.to_string(), }); + self.in_loop = was_in_loop; } ast::Statement::Assert { test, msg } => { // TODO: if some flag, ignore all assert statements! From 985e35bb163443a1541a2f8f2836f19e93301ada Mon Sep 17 00:00:00 2001 From: Ricky Han Date: Mon, 25 Feb 2019 14:49:20 -0500 Subject: [PATCH 029/380] use closure instead of function pointer --- vm/src/stdlib/mod.rs | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/vm/src/stdlib/mod.rs b/vm/src/stdlib/mod.rs index a0dcd9bff6..8c542df992 100644 --- a/vm/src/stdlib/mod.rs +++ b/vm/src/stdlib/mod.rs @@ -20,32 +20,29 @@ mod os; use crate::pyobject::{PyContext, PyObjectRef}; -pub type StdlibInitFunc = fn(&PyContext) -> PyObjectRef; +pub type StdlibInitFunc = Box PyObjectRef>; pub fn get_module_inits() -> HashMap { let mut modules = HashMap::new(); - modules.insert("ast".to_string(), ast::mk_module as StdlibInitFunc); - modules.insert("dis".to_string(), dis::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("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); - modules.insert("struct".to_string(), pystruct::mk_module as StdlibInitFunc); - modules.insert("time".to_string(), time_module::mk_module as StdlibInitFunc); - modules.insert( - "tokenize".to_string(), - tokenize::mk_module as StdlibInitFunc, - ); - modules.insert("types".to_string(), types::mk_module as StdlibInitFunc); - modules.insert("_weakref".to_string(), weakref::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) as StdlibInitFunc); + modules.insert("json".to_string(), Box::new(json::mk_module) as StdlibInitFunc); + modules.insert("keyword".to_string(), Box::new(keyword::mk_module) as StdlibInitFunc); + modules.insert("math".to_string(), Box::new(math::mk_module) as StdlibInitFunc); + modules.insert("re".to_string(), Box::new(re::mk_module) as StdlibInitFunc); + modules.insert("random".to_string(), Box::new(random::mk_module) as StdlibInitFunc); + modules.insert("string".to_string(), Box::new(string::mk_module) as StdlibInitFunc); + modules.insert("struct".to_string(), Box::new(pystruct::mk_module) as StdlibInitFunc); + modules.insert("time".to_string(), Box::new(time_module::mk_module) as StdlibInitFunc); + modules.insert( "tokenize".to_string(), Box::new(tokenize::mk_module) as StdlibInitFunc); + modules.insert("types".to_string(), Box::new(types::mk_module) as StdlibInitFunc); + modules.insert("_weakref".to_string(), Box::new(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.insert("io".to_string(), Box::new(io::mk_module) as StdlibInitFunc); + modules.insert("os".to_string(), Box::new(os::mk_module) as StdlibInitFunc); } modules From 943ded07bd85cb82d34c69ad9362a40ef40847a6 Mon Sep 17 00:00:00 2001 From: Ricky Han Date: Mon, 25 Feb 2019 15:23:40 -0500 Subject: [PATCH 030/380] fix wasm --- wasm/lib/src/browser_module.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index 93b6ec5cde..f1be3033ff 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -134,5 +134,5 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { } pub fn setup_browser_module(vm: &mut VirtualMachine) { - vm.stdlib_inits.insert(BROWSER_NAME.to_string(), mk_module); + vm.stdlib_inits.insert(BROWSER_NAME.to_string(), Box::new(mk_module)); } From cf400501a1ba2dd5a1d9dede3a58dd2be40ece13 Mon Sep 17 00:00:00 2001 From: Ricky Han Date: Mon, 25 Feb 2019 16:32:50 -0500 Subject: [PATCH 031/380] 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 b0b53156c8..454cf84422 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 62f31b8c21..361952237f 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 073a5d646b..0ce906b24e 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 53507e81d7..97d85b3df1 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 1e07b4d9b8..0a93f2c968 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 149a5313cf..2953066a6c 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 6752f67292cf0de8a4c5dd6025ca9f980bca22e5 Mon Sep 17 00:00:00 2001 From: Ricky Han Date: Mon, 25 Feb 2019 16:34:55 -0500 Subject: [PATCH 032/380] cargo fmt --- parser/src/lexer.rs | 4 +++- vm/src/stdlib/mod.rs | 5 ++++- wasm/lib/src/browser_module.rs | 3 ++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index 619b2dd1a0..bb2b3bcd3d 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/stdlib/mod.rs b/vm/src/stdlib/mod.rs index 0dabba9b1c..4ec43f9cd1 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/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index f1be3033ff..621afb214d 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 6f85a44f7c58350d047f046a834c4c511f5dc2a1 Mon Sep 17 00:00:00 2001 From: Ricky Han Date: Mon, 25 Feb 2019 16:35:19 -0500 Subject: [PATCH 033/380] 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 619b2dd1a0..bb2b3bcd3d 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 361952237f..007be8a3bf 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 0dabba9b1c..4ec43f9cd1 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 0a93f2c968..59b88de8db 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 f1be3033ff..621afb214d 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 9066c73814c6cdd0d6afc2dc3f65e7c355b3f2fa Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Mon, 25 Feb 2019 19:09:26 -0600 Subject: [PATCH 034/380] Try to trigger a successful Azure Pipelines build --- wasm/demo/snippets/mandelbrot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wasm/demo/snippets/mandelbrot.py b/wasm/demo/snippets/mandelbrot.py index f8549188ee..adeeefe13e 100644 --- a/wasm/demo/snippets/mandelbrot.py +++ b/wasm/demo/snippets/mandelbrot.py @@ -35,4 +35,4 @@ def mandel(_time_elapsed=None): _y['y'] += 1 request_animation_frame(mandel) -mandel() \ No newline at end of file +request_animation_frame(mandel) From dfa5e12b3c40137329bc382a1a693c836927029d Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Mon, 25 Feb 2019 19:22:16 -0600 Subject: [PATCH 035/380] Add AnyRustValue payload variant --- vm/src/pyobject.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 073a5d646b..de6235abc1 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1246,6 +1246,9 @@ pub enum PyObjectPayload { Socket { socket: Socket, }, + AnyRustValue { + value: Box, + }, } impl fmt::Debug for PyObjectPayload { From dbfd0d4ade6c759b7e10cdc62d7afa080101e749 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Mon, 25 Feb 2019 20:05:41 -0600 Subject: [PATCH 036/380] Fix "not all variants covered" error --- vm/src/pyobject.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index de6235abc1..9685d7ee3a 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1287,6 +1287,7 @@ impl fmt::Debug for PyObjectPayload { PyObjectPayload::RustFunction { .. } => write!(f, "rust function"), PyObjectPayload::Frame { .. } => write!(f, "frame"), PyObjectPayload::Socket { .. } => write!(f, "socket"), + PyObjectPayload::AnyRustValue { .. } => write!(f, "some rust value"), } } } From d399faa42537a6cee952672a92796662646ee419 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Mon, 25 Feb 2019 20:50:53 -0600 Subject: [PATCH 037/380] Remove the console_error_panic_hook crate from Cargo.toml --- Cargo.lock | 11 ----------- wasm/lib/Cargo.toml | 1 - 2 files changed, 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dfab5148a2..061dee67fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -174,15 +174,6 @@ 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" @@ -764,7 +755,6 @@ 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)", "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)", "rustpython_parser 0.0.1", @@ -1223,7 +1213,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "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 103d069d9d..0c7e06b53d 100644 --- a/wasm/lib/Cargo.toml +++ b/wasm/lib/Cargo.toml @@ -18,7 +18,6 @@ wasm-bindgen = "0.2" wasm-bindgen-futures = "0.3" js-sys = "0.3" futures = "0.1" -console_error_panic_hook = "0.1" [dependencies.web-sys] version = "0.3" From 88665e9ebdac94339128ed11cbb4ea0add0e35b5 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Mon, 25 Feb 2019 22:26:58 -0600 Subject: [PATCH 038/380] Keep a Weak to the py_obj being moved to a closure --- wasm/lib/src/convert.rs | 11 +++++++---- wasm/lib/src/vm_class.rs | 27 ++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index df63c959e6..4c440d344d 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -21,13 +21,15 @@ pub fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { let wasm_vm = WASMVirtualMachine { id: wasm_id.clone(), }; - let mut py_obj = Some(py_obj); + let weak_py_obj = wasm_vm.push_held_rc(py_obj).unwrap(); + let closure = move |args: Option, kwargs: Option| -> Result { let py_obj = match wasm_vm.assert_valid() { - Ok(_) => py_obj.clone().expect("py_obj to be valid if VM is valid"), + Ok(_) => weak_py_obj + .upgrade() + .expect("weak_py_obj to be valid if VM is valid"), Err(err) => { - py_obj = None; return Err(err); } }; @@ -56,7 +58,8 @@ pub fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { as Box, Option) -> Result>); let func = closure.as_ref().clone(); - // TODO: Come up with a way of managing closure handles + // stores pretty much nothing, it's fine to leak this because if it gets dropped + // the error message is worse closure.forget(); return func; diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index f803d2d776..11399316ea 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -12,9 +12,16 @@ use std::collections::HashMap; use std::rc::{Rc, Weak}; use wasm_bindgen::prelude::*; +pub trait HeldRcInner {} + +impl HeldRcInner for T {} + pub(crate) struct StoredVirtualMachine { pub vm: VirtualMachine, pub scope: PyObjectRef, + /// you can put a Rc in here, keep it as a Weak, and it'll be held only for + /// as long as the StoredVM is alive + held_rcs: Vec>, } impl StoredVirtualMachine { @@ -26,7 +33,11 @@ impl StoredVirtualMachine { setup_browser_module(&mut vm); } vm.wasm_id = Some(id); - StoredVirtualMachine { vm, scope } + StoredVirtualMachine { + vm, + scope, + held_rcs: vec![], + } } } @@ -210,6 +221,17 @@ impl WASMVirtualMachine { STORED_VMS.with(|cell| cell.borrow().contains_key(&self.id)) } + pub(crate) fn push_held_rc( + &self, + rc: Rc, + ) -> Result, JsValue> { + self.with(|stored_vm| { + let weak = Rc::downgrade(&rc); + stored_vm.held_rcs.push(rc); + weak + }) + } + pub fn assert_valid(&self) -> Result<(), JsValue> { if self.valid() { Ok(()) @@ -233,6 +255,7 @@ impl WASMVirtualMachine { move |StoredVirtualMachine { ref mut vm, ref mut scope, + .. }| { let value = convert::js_to_py(vm, value); vm.ctx.set_attr(scope, &name, value); @@ -246,6 +269,7 @@ impl WASMVirtualMachine { move |StoredVirtualMachine { ref mut vm, ref mut scope, + .. }| { let print_fn: Box PyResult> = if let Some(selector) = stdout.as_string() { @@ -287,6 +311,7 @@ impl WASMVirtualMachine { |StoredVirtualMachine { ref mut vm, ref mut scope, + .. }| { source.push('\n'); let code = From f965c49704a7d53999a66fb783bde476c08252a1 Mon Sep 17 00:00:00 2001 From: Aaron Date: Mon, 25 Feb 2019 20:31:03 -0800 Subject: [PATCH 039/380] compiler flag in_function_def --- vm/src/bytecode.rs | 3 --- vm/src/compile.rs | 57 +++++++++++++++++++++++----------------------- 2 files changed, 29 insertions(+), 31 deletions(-) diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index dac290453d..1e0b85f133 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -26,7 +26,6 @@ pub struct CodeObject { pub first_line_number: usize, pub obj_name: String, // Name of the object that created this code object pub is_generator: bool, - pub is_function_obj: bool, } bitflags! { @@ -253,7 +252,6 @@ impl CodeObject { source_path: String, first_line_number: usize, obj_name: String, - is_function_obj: bool, ) -> CodeObject { CodeObject { instructions: Vec::new(), @@ -267,7 +265,6 @@ impl CodeObject { first_line_number, obj_name, is_generator: false, - is_function_obj, } } diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 94f85a990d..a5dc050d57 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -17,6 +17,7 @@ struct Compiler { source_path: Option, current_source_location: ast::Location, in_loop: bool, + in_function_def: bool, } /// Compile a given sourcecode into a bytecode object. @@ -75,6 +76,7 @@ impl Compiler { source_path: None, current_source_location: ast::Location::default(), in_loop: false, + in_function_def: false, } } @@ -88,7 +90,6 @@ impl Compiler { self.source_path.clone().unwrap(), line_number, obj_name, - false, )); } @@ -160,30 +161,30 @@ impl Compiler { symbol, alias, } in import_parts - { - match symbol { - Some(name) if name == "*" => { - self.emit(Instruction::ImportStar { - name: module.clone(), - }); - } - _ => { - self.emit(Instruction::Import { - name: module.clone(), - symbol: symbol.clone(), - }); - self.emit(Instruction::StoreName { - name: match alias { - Some(alias) => alias.clone(), - None => match symbol { - Some(symbol) => symbol.clone(), - None => module.clone(), + { + match symbol { + Some(name) if name == "*" => { + self.emit(Instruction::ImportStar { + name: module.clone(), + }); + } + _ => { + self.emit(Instruction::Import { + name: module.clone(), + symbol: symbol.clone(), + }); + self.emit(Instruction::StoreName { + name: match alias { + Some(alias) => alias.clone(), + None => match symbol { + Some(symbol) => symbol.clone(), + None => module.clone(), + }, }, - }, - }); + }); + } } } - } } ast::Statement::Expression { expression } => { self.compile_expression(expression)?; @@ -435,7 +436,9 @@ impl Compiler { // Create bytecode for this function: // remember to restore self.in_loop to the original after the function is compiled let was_in_loop = self.in_loop; + let was_in_function_def = self.in_function_def; self.in_loop = false; + self.in_function_def = true; let flags = self.enter_function(name, args)?; self.compile_statements(body)?; @@ -464,6 +467,7 @@ impl Compiler { name: name.to_string(), }); self.in_loop = was_in_loop; + self.in_function_def = was_in_function_def; } ast::Statement::ClassDef { name, @@ -485,7 +489,6 @@ impl Compiler { self.source_path.clone().unwrap(), line_number, name.clone(), - false, )); self.emit(Instruction::LoadName { name: String::from("__locals__"), @@ -594,7 +597,7 @@ impl Compiler { self.emit(Instruction::Continue); } ast::Statement::Return { value } => { - if !self.current_code_object().is_function_obj { + if !self.in_function_def { return Err(CompileError::InvalidReturn); } match value { @@ -698,7 +701,6 @@ impl Compiler { self.source_path.clone().unwrap(), line_number, name.to_string(), - true, )); let mut flags = bytecode::FunctionOpArg::empty(); @@ -984,7 +986,7 @@ impl Compiler { self.emit(Instruction::BuildSlice { size }); } ast::Expression::Yield { value } => { - if !self.current_code_object().is_function_obj { + if !self.in_function_def { return Err(CompileError::InvalidYield); } self.mark_generator(); @@ -1204,7 +1206,7 @@ impl Compiler { ast::ComprehensionKind::Set { .. } => "", ast::ComprehensionKind::Dict { .. } => "", } - .to_string(); + .to_string(); let line_number = self.get_source_line_number(); // Create magnificent function : @@ -1216,7 +1218,6 @@ impl Compiler { self.source_path.clone().unwrap(), line_number, name.clone(), - false, )); // Create empty object of proper type: From f037244641b80a840c731a0d3e1c11795ec9c352 Mon Sep 17 00:00:00 2001 From: Aaron Date: Mon, 25 Feb 2019 20:33:03 -0800 Subject: [PATCH 040/380] fmt --- vm/src/compile.rs | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/vm/src/compile.rs b/vm/src/compile.rs index a5dc050d57..eda9c6ec45 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -161,30 +161,30 @@ impl Compiler { symbol, alias, } in import_parts - { - match symbol { - Some(name) if name == "*" => { - self.emit(Instruction::ImportStar { - name: module.clone(), - }); - } - _ => { - self.emit(Instruction::Import { - name: module.clone(), - symbol: symbol.clone(), - }); - self.emit(Instruction::StoreName { - name: match alias { - Some(alias) => alias.clone(), - None => match symbol { - Some(symbol) => symbol.clone(), - None => module.clone(), - }, + { + match symbol { + Some(name) if name == "*" => { + self.emit(Instruction::ImportStar { + name: module.clone(), + }); + } + _ => { + self.emit(Instruction::Import { + name: module.clone(), + symbol: symbol.clone(), + }); + self.emit(Instruction::StoreName { + name: match alias { + Some(alias) => alias.clone(), + None => match symbol { + Some(symbol) => symbol.clone(), + None => module.clone(), }, - }); - } + }, + }); } } + } } ast::Statement::Expression { expression } => { self.compile_expression(expression)?; @@ -1206,7 +1206,7 @@ impl Compiler { ast::ComprehensionKind::Set { .. } => "", ast::ComprehensionKind::Dict { .. } => "", } - .to_string(); + .to_string(); let line_number = self.get_source_line_number(); // Create magnificent function : From fee6a47d88670a00e3e7f0ead287d7ea49d6f17d Mon Sep 17 00:00:00 2001 From: Aaron Date: Mon, 25 Feb 2019 20:35:43 -0800 Subject: [PATCH 041/380] rever new_label() --- vm/src/compile.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vm/src/compile.rs b/vm/src/compile.rs index eda9c6ec45..9c455581e4 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -1394,8 +1394,9 @@ impl Compiler { // Generate a new label fn new_label(&mut self) -> Label { + let l = self.nxt_label; self.nxt_label += 1; - self.nxt_label - 1 + l } // Assign current position the given label From b1c97ec8774eb74c8692b3e17590d3d75f14c819 Mon Sep 17 00:00:00 2001 From: Ricky Han Date: Mon, 25 Feb 2019 23:28:34 -0500 Subject: [PATCH 042/380] 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 a05972961a..64ce7d3d4b 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 88028dd8c599605a99653d2c20716f56ffefeb8d Mon Sep 17 00:00:00 2001 From: Ricky Han Date: Mon, 25 Feb 2019 23:41:17 -0500 Subject: [PATCH 043/380] cargo 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 bb2b3bcd3d..a970b84b49 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; From f10fa6db44c8df2876ee3aaed18f421cdbf50a2c Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Mon, 25 Feb 2019 19:01:01 -0800 Subject: [PATCH 044/380] Remove outer RefCell from PyObjectRef --- vm/src/frame.rs | 24 ++++----- vm/src/obj/objbool.rs | 10 ++-- vm/src/obj/objbytearray.rs | 26 ++++----- vm/src/obj/objbytes.rs | 65 +++++++++-------------- vm/src/obj/objcode.rs | 2 +- vm/src/obj/objcomplex.rs | 6 +-- vm/src/obj/objdict.rs | 44 ++++++++-------- vm/src/obj/objenumerate.rs | 19 ++++--- vm/src/obj/objfilter.rs | 6 +-- vm/src/obj/objfloat.rs | 6 +-- vm/src/obj/objframe.rs | 3 +- vm/src/obj/objfunction.rs | 2 +- vm/src/obj/objgenerator.rs | 21 ++++---- vm/src/obj/objint.rs | 2 +- vm/src/obj/objiter.rs | 23 ++++---- vm/src/obj/objlist.rs | 32 ++++------- vm/src/obj/objmap.rs | 6 +-- vm/src/obj/objobject.rs | 26 +++++---- vm/src/obj/objrange.rs | 4 +- vm/src/obj/objsequence.rs | 43 +++++++-------- vm/src/obj/objset.rs | 105 ++++++++++++++++++------------------- vm/src/obj/objslice.rs | 6 +-- vm/src/obj/objstr.rs | 42 ++++++--------- vm/src/obj/objtuple.rs | 40 +++++--------- vm/src/obj/objtype.rs | 26 +++++---- vm/src/obj/objzip.rs | 2 +- vm/src/pyobject.rs | 95 ++++++++++++++++++--------------- vm/src/stdlib/io.rs | 18 ++++--- vm/src/stdlib/json.rs | 4 +- vm/src/stdlib/socket.rs | 79 ++++++++++++---------------- vm/src/stdlib/weakref.rs | 2 +- vm/src/sysmodule.rs | 2 +- vm/src/vm.rs | 14 ++--- 33 files changed, 380 insertions(+), 425 deletions(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index f97be68942..6ef7e9863e 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -281,7 +281,7 @@ impl Frame { let mut out: Vec> = elements .into_iter() - .map(|x| match x.borrow().payload { + .map(|x| match x.payload { PyObjectPayload::Integer { ref value } => Some(value.clone()), PyObjectPayload::None => None, _ => panic!("Expect Int or None as BUILD_SLICE arguments, got {:?}", x), @@ -531,7 +531,7 @@ impl Frame { } else { let msg = format!( "Can only raise BaseException derived types, not {}", - exception.borrow() + exception ); let type_error_type = vm.ctx.exceptions.type_error.clone(); let type_error = vm.new_exception(type_error_type, msg); @@ -564,7 +564,7 @@ impl Frame { } bytecode::Instruction::PrintExpr => { let expr = self.pop_value(); - match expr.borrow().payload { + match expr.payload { PyObjectPayload::None => (), _ => { let repr = vm.to_repr(&expr)?; @@ -591,9 +591,9 @@ impl Frame { } bytecode::Instruction::StoreLocals => { let locals = self.pop_value(); - match self.locals.borrow_mut().payload { - PyObjectPayload::Scope { ref mut scope } => { - scope.locals = locals; + match self.locals.payload { + PyObjectPayload::Scope { ref scope } => { + //scope.locals = locals; // FIXME } _ => panic!("We really expect our scope to be a scope!"), } @@ -854,7 +854,7 @@ impl Frame { } fn delete_name(&mut self, vm: &mut VirtualMachine, name: &str) -> FrameResult { - let locals = match self.locals.borrow().payload { + let locals = match self.locals.payload { PyObjectPayload::Scope { ref scope } => scope.locals.clone(), _ => panic!("We really expect our scope to be a scope!"), }; @@ -1127,7 +1127,7 @@ impl fmt::Debug for Frame { let stack_str = self .stack .iter() - .map(|elem| format!("\n > {:?}", elem.borrow())) + .map(|elem| format!("\n > {:?}", elem)) .collect::>() .join(""); let block_str = self @@ -1136,12 +1136,12 @@ impl fmt::Debug for Frame { .map(|elem| format!("\n > {:?}", elem)) .collect::>() .join(""); - let local_str = match self.locals.borrow().payload { - PyObjectPayload::Scope { ref scope } => match scope.locals.borrow().payload { + let local_str = match self.locals.payload { + PyObjectPayload::Scope { ref scope } => match scope.locals.payload { PyObjectPayload::Dict { ref elements } => { - objdict::get_key_value_pairs_from_content(elements) + objdict::get_key_value_pairs_from_content(&elements.borrow()) .iter() - .map(|elem| format!("\n {:?} = {:?}", elem.0.borrow(), elem.1.borrow())) + .map(|elem| format!("\n {:?} = {:?}", elem.0, elem.1)) .collect::>() .join("") } diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index 41681a476f..cf16e1f5f4 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -12,17 +12,17 @@ impl IntoPyObject for bool { } pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result { - let result = match obj.borrow().payload { + let result = match obj.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::Sequence { ref elements } => !elements.borrow().is_empty(), + PyObjectPayload::Dict { ref elements } => !elements.borrow().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().payload { + let v = match bool_res.payload { PyObjectPayload::Integer { ref value } => !value.is_zero(), _ => return Err(vm.new_type_error(String::from("TypeError"))), }; @@ -60,7 +60,7 @@ pub fn not(vm: &mut VirtualMachine, obj: &PyObjectRef) -> PyResult { // Retrieve inner int value: pub fn get_value(obj: &PyObjectRef) -> bool { - if let PyObjectPayload::Integer { value } = &obj.borrow().payload { + if let PyObjectPayload::Integer { value } = &obj.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 d98b80e740..0cabf23fdc 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -1,5 +1,7 @@ //! Implementation of the python bytearray object. +use std::cell::RefCell; + use crate::pyobject::{PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyResult, TypeProtocol}; use super::objint; @@ -140,7 +142,12 @@ fn bytearray_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } else { vec![] }; - Ok(PyObject::new(PyObjectPayload::Bytes { value }, cls.clone())) + Ok(PyObject::new( + PyObjectPayload::Bytes { + value: RefCell::new(value), + }, + cls.clone(), + )) } fn bytesarray_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -283,10 +290,9 @@ fn bytearray_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn bytearray_clear(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); - let mut mut_obj = zelf.borrow_mut(); - match mut_obj.payload { - PyObjectPayload::Bytes { ref mut value } => { - value.clear(); + match zelf.payload { + PyObjectPayload::Bytes { ref value } => { + value.borrow_mut().clear(); Ok(vm.get_none()) } _ => panic!("Bytearray has incorrect payload."), @@ -307,17 +313,11 @@ fn bytearray_pop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn bytearray_lower(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, Some(vm.ctx.bytearray_type()))]); let value = get_value(obj).to_vec().to_ascii_lowercase(); - Ok(PyObject::new( - PyObjectPayload::Bytes { value }, - vm.ctx.bytearray_type(), - )) + Ok(vm.ctx.new_bytearray(value)) } fn bytearray_upper(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, Some(vm.ctx.bytearray_type()))]); let value = get_value(obj).to_vec().to_ascii_uppercase(); - Ok(PyObject::new( - PyObjectPayload::Bytes { value }, - vm.ctx.bytearray_type(), - )) + Ok(vm.ctx.new_bytearray(value)) } diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index f63a9c80de..b4b4898546 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -1,3 +1,8 @@ +use std::cell::RefCell; +use std::hash::{Hash, Hasher}; +use std::ops::Deref; +use std::ops::DerefMut; + use super::objint; use super::objtype; use crate::pyobject::{ @@ -5,11 +10,6 @@ use crate::pyobject::{ }; use crate::vm::VirtualMachine; use num_traits::ToPrimitive; -use std::cell::Ref; -use std::cell::RefMut; -use std::hash::{Hash, Hasher}; -use std::ops::Deref; -use std::ops::DerefMut; // Binary data support @@ -70,7 +70,12 @@ fn bytes_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vec![] }; - Ok(PyObject::new(PyObjectPayload::Bytes { value }, cls.clone())) + Ok(PyObject::new( + PyObjectPayload::Bytes { + value: RefCell::new(value), + }, + cls.clone(), + )) } fn bytes_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -98,11 +103,7 @@ 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 { - return Err(vm.new_type_error(format!( - "Cannot compare {} and {} using '>'", - a.borrow(), - b.borrow() - ))); + return Err(vm.new_type_error(format!("Cannot compare {} and {} using '>'", a, b))); }; Ok(vm.ctx.new_bool(result)) } @@ -117,11 +118,7 @@ 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 { - return Err(vm.new_type_error(format!( - "Cannot compare {} and {} using '>='", - a.borrow(), - b.borrow() - ))); + return Err(vm.new_type_error(format!("Cannot compare {} and {} using '>='", a, b))); }; Ok(vm.ctx.new_bool(result)) } @@ -136,11 +133,7 @@ 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 { - return Err(vm.new_type_error(format!( - "Cannot compare {} and {} using '<'", - a.borrow(), - b.borrow() - ))); + return Err(vm.new_type_error(format!("Cannot compare {} and {} using '<'", a, b))); }; Ok(vm.ctx.new_bool(result)) } @@ -155,11 +148,7 @@ 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 { - return Err(vm.new_type_error(format!( - "Cannot compare {} and {} using '<='", - a.borrow(), - b.borrow() - ))); + return Err(vm.new_type_error(format!("Cannot compare {} and {} using '<='", a, b))); }; Ok(vm.ctx.new_bool(result)) } @@ -181,23 +170,19 @@ 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 PyObjectPayload::Bytes { ref value } = py_obj.payload { - value - } else { - panic!("Inner error getting bytearray {:?}", obj); - } - }) + if let PyObjectPayload::Bytes { ref value } = obj.payload { + value.borrow() + } else { + panic!("Inner error getting bytearray {:?}", obj); + } } pub fn get_mut_value<'a>(obj: &'a PyObjectRef) -> impl DerefMut> + 'a { - RefMut::map(obj.borrow_mut(), |py_obj| { - if let PyObjectPayload::Bytes { ref mut value } = py_obj.payload { - value - } else { - panic!("Inner error getting bytearray {:?}", obj); - } - }) + if let PyObjectPayload::Bytes { ref value } = obj.payload { + value.borrow_mut() + } else { + panic!("Inner error getting bytearray {:?}", obj); + } } fn bytes_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index 05d1b6238d..178946de84 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -29,7 +29,7 @@ pub fn init(context: &PyContext) { } pub fn get_value(obj: &PyObjectRef) -> bytecode::CodeObject { - if let PyObjectPayload::Code { code } = &obj.borrow().payload { + if let PyObjectPayload::Code { code } = &obj.payload { code.clone() } else { panic!("Inner error getting code {:?}", obj) diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index 9182f7bc2e..0b0d5ba7a2 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -45,7 +45,7 @@ pub fn init(context: &PyContext) { } pub fn get_value(obj: &PyObjectRef) -> Complex64 { - if let PyObjectPayload::Complex { value } = &obj.borrow().payload { + if let PyObjectPayload::Complex { value } = &obj.payload { *value } else { panic!("Inner error getting complex"); @@ -117,7 +117,7 @@ fn complex_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { v1.im, ))) } else { - Err(vm.new_type_error(format!("Cannot add {} and {}", i.borrow(), i2.borrow()))) + Err(vm.new_type_error(format!("Cannot add {} and {}", i, i2))) } } @@ -136,7 +136,7 @@ fn complex_radd(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { v1.im, ))) } else { - Err(vm.new_type_error(format!("Cannot add {} and {}", i.borrow(), i2.borrow()))) + Err(vm.new_type_error(format!("Cannot add {} and {}", i, i2))) } } diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 42e188b467..9a7c365aac 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -6,7 +6,7 @@ use crate::pyobject::{ TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; -use std::cell::{Ref, RefCell, RefMut}; +use std::cell::RefCell; use std::collections::HashMap; use std::ops::{Deref, DerefMut}; @@ -19,30 +19,26 @@ pub type DictContentType = HashMap; pub fn new(dict_type: PyObjectRef) -> PyObjectRef { PyObject::new( PyObjectPayload::Dict { - elements: HashMap::new(), + elements: RefCell::new(HashMap::new()), }, dict_type.clone(), ) } pub fn get_elements<'a>(obj: &'a PyObjectRef) -> impl Deref + 'a { - Ref::map(obj.borrow(), |py_obj| { - if let PyObjectPayload::Dict { ref elements } = py_obj.payload { - elements - } else { - panic!("Cannot extract dict elements"); - } - }) + if let PyObjectPayload::Dict { ref elements } = obj.payload { + elements.borrow() + } else { + panic!("Cannot extract dict elements"); + } } fn get_mut_elements<'a>(obj: &'a PyObjectRef) -> impl DerefMut + 'a { - RefMut::map(obj.borrow_mut(), |py_obj| { - if let PyObjectPayload::Dict { ref mut elements } = py_obj.payload { - elements - } else { - panic!("Cannot extract dict elements"); - } - }) + if let PyObjectPayload::Dict { ref elements } = obj.payload { + elements.borrow_mut() + } else { + panic!("Cannot extract dict elements"); + } } pub fn set_item( @@ -309,12 +305,16 @@ 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: RefCell::new(HashMap::new()), - mro: vec![object_type], - }; - (*dict_type.borrow_mut()).typ = Some(type_type.clone()); + // this is not ideal + let ptr = PyObjectRef::into_raw(dict_type.clone()) as *mut PyObject; + unsafe { + (*ptr).payload = PyObjectPayload::Class { + name: String::from("dict"), + dict: RefCell::new(HashMap::new()), + mro: vec![object_type], + }; + (*ptr).typ = Some(type_type.clone()); + } } pub fn init(context: &PyContext) { diff --git a/vm/src/obj/objenumerate.rs b/vm/src/obj/objenumerate.rs index 9e5f369f1e..88097bfe5e 100644 --- a/vm/src/obj/objenumerate.rs +++ b/vm/src/obj/objenumerate.rs @@ -1,10 +1,12 @@ +use std::cell::RefCell; +use std::ops::AddAssign; + use super::objint; use super::objiter; use crate::pyobject::{PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; use num_bigint::BigInt; use num_traits::Zero; -use std::ops::AddAssign; fn enumerate_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( @@ -20,7 +22,10 @@ fn enumerate_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; let iterator = objiter::get_iter(vm, iterable)?; Ok(PyObject::new( - PyObjectPayload::EnumerateIterator { counter, iterator }, + PyObjectPayload::EnumerateIterator { + counter: RefCell::new(counter), + iterator, + }, cls.clone(), )) } @@ -33,16 +38,16 @@ fn enumerate_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); if let PyObjectPayload::EnumerateIterator { - ref mut counter, - ref mut iterator, - } = enumerate.borrow_mut().payload + ref counter, + ref iterator, + } = enumerate.payload { let next_obj = objiter::call_next(vm, iterator)?; let result = vm .ctx - .new_tuple(vec![vm.ctx.new_int(counter.clone()), next_obj]); + .new_tuple(vec![vm.ctx.new_int(counter.borrow().clone()), next_obj]); - AddAssign::add_assign(counter, 1); + AddAssign::add_assign(&mut counter.borrow_mut() as &mut BigInt, 1); Ok(result) } else { diff --git a/vm/src/obj/objfilter.rs b/vm/src/obj/objfilter.rs index 1fe163374a..998d51c9b8 100644 --- a/vm/src/obj/objfilter.rs +++ b/vm/src/obj/objfilter.rs @@ -25,9 +25,9 @@ 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 + ref predicate, + ref iterator, + } = filter.payload { loop { let next_obj = objiter::call_next(vm, iterator)?; diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index c7b46299d3..25c95f80f9 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -61,7 +61,7 @@ fn float_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // Retrieve inner float value: pub fn get_value(obj: &PyObjectRef) -> f64 { - if let PyObjectPayload::Float { value } = &obj.borrow().payload { + if let PyObjectPayload::Float { value } = &obj.payload { *value } else { panic!("Inner error getting float"); @@ -81,12 +81,12 @@ pub fn make_float(vm: &mut VirtualMachine, obj: &PyObjectRef) -> Result PyResult { diff --git a/vm/src/obj/objframe.rs b/vm/src/obj/objframe.rs index 3f9dd07d78..efd3792bf3 100644 --- a/vm/src/obj/objframe.rs +++ b/vm/src/obj/objframe.rs @@ -31,7 +31,6 @@ fn frame_flocals(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(frame, Some(vm.ctx.frame_type()))]); let frame = get_value(frame); let py_scope = frame.locals.clone(); - let py_scope = py_scope.borrow(); if let PyObjectPayload::Scope { scope } = &py_scope.payload { Ok(scope.locals.clone()) @@ -46,7 +45,7 @@ fn frame_fcode(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn get_value(obj: &PyObjectRef) -> Frame { - if let PyObjectPayload::Frame { frame } = &obj.borrow().payload { + if let PyObjectPayload::Frame { frame } = &obj.payload { frame.clone() } else { panic!("Inner error getting int {:?}", obj); diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index d59c7bb284..a2092c308f 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -67,7 +67,7 @@ fn bind_method(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn function_code(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - match args.args[0].borrow().payload { + match args.args[0].payload { PyObjectPayload::Function { ref code, .. } => Ok(code.clone()), _ => Err(vm.new_type_error("no code".to_string())), } diff --git a/vm/src/obj/objgenerator.rs b/vm/src/obj/objgenerator.rs index 0f204e5baa..fdde5a6c1a 100644 --- a/vm/src/obj/objgenerator.rs +++ b/vm/src/obj/objgenerator.rs @@ -56,16 +56,17 @@ fn generator_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn send(vm: &mut VirtualMachine, gen: &PyObjectRef, value: &PyObjectRef) -> PyResult { - if let PyObjectPayload::Generator { ref mut frame } = gen.borrow_mut().payload { - frame.push_value(value.clone()); - match frame.run_frame(vm)? { - ExecutionResult::Yield(value) => Ok(value), - ExecutionResult::Return(_value) => { - // Stop iteration! - let stop_iteration = vm.ctx.exceptions.stop_iteration.clone(); - Err(vm.new_exception(stop_iteration, "End of generator".to_string())) - } - } + if let PyObjectPayload::Generator { ref frame } = gen.payload { + //frame.push_value(value.clone()); + //match frame.run_frame(vm)? { + // ExecutionResult::Yield(value) => Ok(value), + // ExecutionResult::Return(_value) => { + // // Stop iteration! + // let stop_iteration = vm.ctx.exceptions.stop_iteration.clone(); + // Err(vm.new_exception(stop_iteration, "End of generator".to_string())) + // } + //} + unimplemented!("FIXME") } else { panic!("Cannot extract frame from non-generator"); } diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index c6bb93b9a1..9f5e48893b 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -94,7 +94,7 @@ pub fn to_int( // Retrieve inner int value: pub fn get_value(obj: &PyObjectRef) -> IntType { - if let PyObjectPayload::Integer { value } = &obj.borrow().payload { + if let PyObjectPayload::Integer { value } = &obj.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 a3e30a1522..70ae95e495 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -128,16 +128,15 @@ 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 + ref position, + iterated_obj: ref iterated_obj_ref, + } = iter.payload { - let iterated_obj = iterated_obj_ref.borrow_mut(); - match iterated_obj.payload { + match iterated_obj_ref.payload { PyObjectPayload::Sequence { ref elements } => { - if *position < elements.len() { - let obj_ref = elements[*position].clone(); - *position += 1; + if *position < elements.borrow().len() { + let obj_ref = elements.borrow()[*position].clone(); + //*position += 1; // FIXME Ok(obj_ref) } else { Err(new_stop_iteration(vm)) @@ -146,7 +145,7 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { PyObjectPayload::Range { ref range } => { if let Some(int) = range.get(*position) { - *position += 1; + //*position += 1; // FIXME Ok(vm.ctx.new_int(int)) } else { Err(new_stop_iteration(vm)) @@ -154,9 +153,9 @@ 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]); - *position += 1; + if *position < value.borrow().len() { + let obj_ref = vm.ctx.new_int(value.borrow()[*position]); + // *position += 1; // FIXME Ok(obj_ref) } else { Err(new_stop_iteration(vm)) diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 53f0d74981..8f32892c33 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -1,3 +1,5 @@ +use std::cell::RefCell; + use super::objbool; use super::objint; use super::objsequence::{ @@ -55,7 +57,9 @@ fn list_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; Ok(PyObject::new( - PyObjectPayload::Sequence { elements }, + PyObjectPayload::Sequence { + elements: RefCell::new(elements), + }, cls.clone(), )) } @@ -93,11 +97,7 @@ fn list_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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() - ))); + return Err(vm.new_type_error(format!("Cannot compare {} and {} using '<'", zelf, other))); }; Ok(vm.ctx.new_bool(result)) @@ -115,11 +115,7 @@ fn list_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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() - ))); + return Err(vm.new_type_error(format!("Cannot compare {} and {} using '>'", zelf, other))); }; Ok(vm.ctx.new_bool(result)) @@ -137,11 +133,7 @@ fn list_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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() - ))); + return Err(vm.new_type_error(format!("Cannot compare {} and {} using '>='", zelf, other))); }; Ok(vm.ctx.new_bool(result)) @@ -159,11 +151,7 @@ fn list_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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() - ))); + return Err(vm.new_type_error(format!("Cannot compare {} and {} using '<='", zelf, other))); }; Ok(vm.ctx.new_bool(result)) @@ -182,7 +170,7 @@ fn list_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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()))) + Err(vm.new_type_error(format!("Cannot add {} and {}", o, o2))) } } diff --git a/vm/src/obj/objmap.rs b/vm/src/obj/objmap.rs index 9ba88955b4..78cbdcf74d 100644 --- a/vm/src/obj/objmap.rs +++ b/vm/src/obj/objmap.rs @@ -28,9 +28,9 @@ 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 + ref mapper, + ref iterators, + } = map.payload { let next_objs = iterators .iter() diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 62f31b8c21..67414373a6 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, + AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, + PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use std::cell::RefCell; @@ -16,12 +16,16 @@ pub fn new_instance(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { } 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: RefCell::new(HashMap::new()), - mro: vec![], - }; - (*object_type.borrow_mut()).typ = Some(type_type.clone()); + // this is not ideal + let ptr = PyObjectRef::into_raw(object_type.clone()) as *mut PyObject; + unsafe { + (*ptr).payload = PyObjectPayload::Class { + name: String::from("object"), + dict: RefCell::new(HashMap::new()), + mro: vec![], + }; + (*ptr).typ = Some(type_type.clone()); + } } fn object_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -101,7 +105,7 @@ fn object_delattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ] ); - match zelf.borrow().payload { + match zelf.payload { PyObjectPayload::Class { ref dict, .. } | PyObjectPayload::Instance { ref dict, .. } => { let attr_name = objstr::get_value(attr); dict.borrow_mut().remove(&attr_name); @@ -174,7 +178,7 @@ fn object_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { } fn object_dict(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - match args.args[0].borrow().payload { + match args.args[0].payload { PyObjectPayload::Class { ref dict, .. } | PyObjectPayload::Instance { ref dict, .. } => { let new_dict = vm.new_dict(); for (attr, value) in dict.borrow().iter() { @@ -230,7 +234,7 @@ fn object_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let attribute_error = vm.context().exceptions.attribute_error.clone(); Err(vm.new_exception( attribute_error, - format!("{} has no attribute '{}'", obj.borrow(), name), + format!("{} has no attribute '{}'", obj, name), )) } } diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index e2ac5de45c..5ced88d918 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -151,7 +151,7 @@ impl RangeType { } pub fn get_value(obj: &PyObjectRef) -> RangeType { - if let PyObjectPayload::Range { range } = &obj.borrow().payload { + if let PyObjectPayload::Range { range } = &obj.payload { range.clone() } else { panic!("Inner error getting range {:?}", obj); @@ -287,7 +287,7 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let range = get_value(zelf); - match subscript.borrow().payload { + match subscript.payload { PyObjectPayload::Integer { ref value } => { if let Some(int) = range.get(value) { Ok(vm.ctx.new_int(int)) diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index b52c8e04d1..58437b98e8 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -1,12 +1,13 @@ +use std::cell::RefCell; +use std::marker::Sized; +use std::ops::{Deref, DerefMut, Range}; + use super::objbool; use super::objint; use crate::pyobject::{IdProtocol, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; use num_bigint::BigInt; use num_traits::{One, Signed, ToPrimitive, Zero}; -use std::cell::{Ref, RefMut}; -use std::marker::Sized; -use std::ops::{Deref, DerefMut, Range}; pub trait PySliceableSequence { fn do_slice(&self, range: Range) -> Self; @@ -64,7 +65,7 @@ pub trait PySliceableSequence { Self: Sized, { // TODO: we could potentially avoid this copy and use slice - match &(slice.borrow()).payload { + match &slice.payload { PyObjectPayload::Slice { start, stop, step } => { let step = step.clone().unwrap_or_else(BigInt::one); if step.is_zero() { @@ -140,7 +141,7 @@ pub fn get_item( elements: &[PyObjectRef], subscript: PyObjectRef, ) -> PyResult { - match &(subscript.borrow()).payload { + match &subscript.payload { PyObjectPayload::Integer { value } => match value.to_i32() { Some(value) => { if let Some(pos_index) = elements.to_vec().get_pos(value) { @@ -156,9 +157,9 @@ pub fn get_item( }, PyObjectPayload::Slice { .. } => Ok(PyObject::new( - match &(sequence.borrow()).payload { + match &sequence.payload { PyObjectPayload::Sequence { .. } => PyObjectPayload::Sequence { - elements: elements.to_vec().get_slice_items(vm, &subscript)?, + elements: RefCell::new(elements.to_vec().get_slice_items(vm, &subscript)?), }, ref payload => panic!("sequence get_item called for non-sequence: {:?}", payload), }, @@ -302,23 +303,19 @@ pub fn seq_mul(elements: &[PyObjectRef], product: &PyObjectRef) -> Vec(obj: &'a PyObjectRef) -> impl Deref> + 'a { - Ref::map(obj.borrow(), |x| { - if let PyObjectPayload::Sequence { ref elements } = x.payload { - elements - } else { - panic!("Cannot extract elements from non-sequence"); - } - }) + if let PyObjectPayload::Sequence { ref elements } = obj.payload { + elements.borrow() + } else { + panic!("Cannot extract elements from non-sequence"); + } } pub fn get_mut_elements<'a>(obj: &'a PyObjectRef) -> impl DerefMut> + 'a { - RefMut::map(obj.borrow_mut(), |x| { - if let PyObjectPayload::Sequence { ref mut elements } = x.payload { - elements - } else { - panic!("Cannot extract list elements from non-sequence"); - // TODO: raise proper error? - // Err(vm.new_type_error("list.append is called with no list".to_string())) - } - }) + if let PyObjectPayload::Sequence { ref elements } = obj.payload { + elements.borrow_mut() + } else { + panic!("Cannot extract list elements from non-sequence"); + // TODO: raise proper error? + // Err(vm.new_type_error("list.append is called with no list".to_string())) + } } diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index ebae2b44cd..dcb47bc8c4 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -2,6 +2,11 @@ * Builtin set type with a sequence of unique items. */ +use std::cell::RefCell; +use std::collections::hash_map::DefaultHasher; +use std::collections::HashMap; +use std::hash::{Hash, Hasher}; + use super::objbool; use super::objint; use super::objiter; @@ -11,13 +16,10 @@ use crate::pyobject::{ PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; -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 { - elements.clone() + if let PyObjectPayload::Set { elements } = &obj.payload { + elements.borrow().clone() } else { panic!("Cannot extract set elements from non-set"); } @@ -62,10 +64,10 @@ fn set_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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 } => insert_into_set(vm, elements, item), + match s.payload { + PyObjectPayload::Set { ref elements } => { + insert_into_set(vm, &mut elements.borrow_mut(), item) + } _ => Err(vm.new_type_error("set.add is called with no item".to_string())), } } @@ -77,10 +79,8 @@ fn set_remove(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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 } => { + match s.payload { + PyObjectPayload::Set { ref elements } => { fn remove( vm: &mut VirtualMachine, elements: &mut HashMap, @@ -89,13 +89,13 @@ fn set_remove(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ) -> PyResult { match elements.remove(&key) { None => { - let item_str = format!("{:?}", value.borrow()); + let item_str = format!("{:?}", value); Err(vm.new_key_error(item_str)) } Some(_) => Ok(vm.get_none()), } } - perform_action_with_hash(vm, elements, item, &remove) + perform_action_with_hash(vm, &mut elements.borrow_mut(), item, &remove) } _ => Err(vm.new_type_error("set.remove is called with no item".to_string())), } @@ -108,10 +108,8 @@ fn set_discard(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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 } => { + match s.payload { + PyObjectPayload::Set { ref elements } => { fn discard( vm: &mut VirtualMachine, elements: &mut HashMap, @@ -121,7 +119,7 @@ fn set_discard(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { elements.remove(&key); Ok(vm.get_none()) } - perform_action_with_hash(vm, elements, item, &discard) + perform_action_with_hash(vm, &mut elements.borrow_mut(), item, &discard) } _ => Err(vm.new_type_error("set.discard is called with no item".to_string())), } @@ -130,10 +128,9 @@ fn set_discard(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(); + match s.payload { + PyObjectPayload::Set { ref elements } => { + elements.borrow_mut().clear(); Ok(vm.get_none()) } _ => Err(vm.new_type_error("".to_string())), @@ -150,7 +147,7 @@ fn set_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); if !objtype::issubclass(cls, &vm.ctx.set_type()) { - return Err(vm.new_type_error(format!("{} is not a subtype of set", cls.borrow()))); + return Err(vm.new_type_error(format!("{} is not a subtype of set", cls))); } let elements: HashMap = match iterable { @@ -166,7 +163,9 @@ fn set_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; Ok(PyObject::new( - PyObjectPayload::Set { elements }, + PyObjectPayload::Set { + elements: RefCell::new(elements), + }, cls.clone(), )) } @@ -183,7 +182,9 @@ fn set_copy(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.set_type()))]); let elements = get_elements(s); Ok(PyObject::new( - PyObjectPayload::Set { elements }, + PyObjectPayload::Set { + elements: RefCell::new(elements), + }, vm.ctx.set_type(), )) } @@ -335,7 +336,9 @@ fn set_union(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { elements.extend(get_elements(other).clone()); Ok(PyObject::new( - PyObjectPayload::Set { elements }, + PyObjectPayload::Set { + elements: RefCell::new(elements), + }, vm.ctx.set_type(), )) } @@ -375,7 +378,9 @@ fn set_symmetric_difference(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResu } Ok(PyObject::new( - PyObjectPayload::Set { elements }, + PyObjectPayload::Set { + elements: RefCell::new(elements), + }, vm.ctx.set_type(), )) } @@ -413,7 +418,9 @@ fn set_combine_inner( } Ok(PyObject::new( - PyObjectPayload::Set { elements }, + PyObjectPayload::Set { + elements: RefCell::new(elements), + }, vm.ctx.set_type(), )) } @@ -421,11 +428,9 @@ fn set_combine_inner( fn set_pop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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 } => match elements.clone().keys().next() { - Some(key) => Ok(elements.remove(key).unwrap()), + match s.payload { + PyObjectPayload::Set { ref elements } => match elements.borrow().clone().keys().next() { + Some(key) => Ok(elements.borrow_mut().remove(key).unwrap()), None => Err(vm.new_key_error("pop from an empty set".to_string())), }, _ => Err(vm.new_type_error("".to_string())), @@ -444,13 +449,11 @@ fn set_ior(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.set_type())), (iterable, None)] ); - let mut mut_obj = zelf.borrow_mut(); - - match mut_obj.payload { - PyObjectPayload::Set { ref mut elements } => { + match zelf.payload { + PyObjectPayload::Set { ref elements } => { let iterator = objiter::get_iter(vm, iterable)?; while let Ok(v) = vm.call_method(&iterator, "__next__", vec![]) { - insert_into_set(vm, elements, &v)?; + insert_into_set(vm, &mut elements.borrow_mut(), &v)?; } } _ => return Err(vm.new_type_error("set.update is called with no other".to_string())), @@ -487,18 +490,16 @@ fn set_combine_update_inner( required = [(zelf, Some(vm.ctx.set_type())), (iterable, None)] ); - let mut mut_obj = zelf.borrow_mut(); - - match mut_obj.payload { - PyObjectPayload::Set { ref mut elements } => { - for element in elements.clone().iter() { + match zelf.payload { + PyObjectPayload::Set { ref elements } => { + for element in elements.borrow().clone().iter() { let value = vm.call_method(iterable, "__contains__", vec![element.1.clone()])?; let should_remove = match op { SetCombineOperation::Intersection => !objbool::get_value(&value), SetCombineOperation::Difference => objbool::get_value(&value), }; if should_remove { - elements.remove(&element.0.clone()); + elements.borrow_mut().remove(&element.0.clone()); } } } @@ -519,19 +520,17 @@ fn set_ixor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.set_type())), (iterable, None)] ); - let mut mut_obj = zelf.borrow_mut(); - - match mut_obj.payload { - PyObjectPayload::Set { ref mut elements } => { - let elements_original = elements.clone(); + match zelf.payload { + PyObjectPayload::Set { ref elements } => { + let elements_original = elements.borrow().clone(); let iterator = objiter::get_iter(vm, iterable)?; while let Ok(v) = vm.call_method(&iterator, "__next__", vec![]) { - insert_into_set(vm, elements, &v)?; + insert_into_set(vm, &mut elements.borrow_mut(), &v)?; } for element in elements_original.iter() { let value = vm.call_method(iterable, "__contains__", vec![element.1.clone()])?; if objbool::get_value(&value) { - elements.remove(&element.0.clone()); + elements.borrow_mut().remove(&element.0.clone()); } } } diff --git a/vm/src/obj/objslice.rs b/vm/src/obj/objslice.rs index 19abc214fa..0a81847e2c 100644 --- a/vm/src/obj/objslice.rs +++ b/vm/src/obj/objslice.rs @@ -59,7 +59,7 @@ fn get_property_value(vm: &mut VirtualMachine, value: &Option) -> PyResu 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 { + if let PyObjectPayload::Slice { start, .. } = &slice.payload { get_property_value(vm, start) } else { panic!("Slice has incorrect payload."); @@ -68,7 +68,7 @@ fn slice_start(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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 { + if let PyObjectPayload::Slice { stop, .. } = &slice.payload { get_property_value(vm, stop) } else { panic!("Slice has incorrect payload."); @@ -77,7 +77,7 @@ fn slice_stop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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 { + if let PyObjectPayload::Slice { step, .. } = &slice.payload { get_property_value(vm, step) } else { panic!("Slice has incorrect payload."); diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 04394f7c5d..580ab2b801 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -3,12 +3,10 @@ use super::objsequence::PySliceableSequence; use super::objtype; use crate::format::{FormatParseError, FormatPart, FormatString}; use crate::pyobject::{ - FromPyObject, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, - TypeProtocol, + FromPyObject, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use num_traits::ToPrimitive; -use std::cell::Ref; use std::hash::{Hash, Hasher}; use std::ops::Range; use std::str::FromStr; @@ -115,21 +113,19 @@ pub fn init(context: &PyContext) { } pub fn get_value(obj: &PyObjectRef) -> String { - if let PyObjectPayload::String { value } = &obj.borrow().payload { + if let PyObjectPayload::String { value } = &obj.payload { value.to_string() } else { panic!("Inner error getting str"); } } -pub fn borrow_value(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"); - } - }) +pub fn borrow_value(obj: &PyObjectRef) -> &str { + if let PyObjectPayload::String { value } = &obj.payload { + value.as_str() + } else { + panic!("Inner error getting str"); + } } fn str_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -158,7 +154,7 @@ fn str_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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()))) + Err(vm.new_type_error(format!("Cannot compare {} and {}", i, i2))) } } @@ -173,7 +169,7 @@ fn str_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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()))) + Err(vm.new_type_error(format!("Cannot compare {} and {}", i, i2))) } } @@ -188,7 +184,7 @@ fn str_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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()))) + Err(vm.new_type_error(format!("Cannot compare {} and {}", i, i2))) } } @@ -203,7 +199,7 @@ fn str_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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()))) + Err(vm.new_type_error(format!("Cannot compare {} and {}", i, i2))) } } @@ -258,7 +254,7 @@ fn str_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .ctx .new_str(format!("{}{}", get_value(&s), get_value(&s2)))) } else { - Err(vm.new_type_error(format!("Cannot add {} and {}", s.borrow(), s2.borrow()))) + Err(vm.new_type_error(format!("Cannot add {} and {}", s, s2))) } } @@ -387,11 +383,7 @@ fn str_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } Ok(vm.ctx.new_str(result)) } else { - Err(vm.new_type_error(format!( - "Cannot multiply {} and {}", - s.borrow(), - s2.borrow() - ))) + Err(vm.new_type_error(format!("Cannot multiply {} and {}", s, s2))) } } @@ -1115,7 +1107,7 @@ pub fn subscript(vm: &mut VirtualMachine, value: &str, b: PyObjectRef) -> PyResu } } } else { - match (*b.borrow()).payload { + match b.payload { PyObjectPayload::Slice { .. } => { let string = value.to_string().get_slice_items(vm, &b)?; Ok(vm.new_str(string)) @@ -1130,8 +1122,8 @@ pub fn subscript(vm: &mut VirtualMachine, value: &str, b: PyObjectRef) -> PyResu // help get optional string indices fn get_slice( - start: Option<&std::rc::Rc>>, - end: Option<&std::rc::Rc>>, + start: Option<&PyObjectRef>, + end: Option<&PyObjectRef>, len: usize, ) -> Result<(usize, usize), String> { let start_idx = match start { diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 81e3b99598..8ddc3640a7 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -1,3 +1,6 @@ +use std::cell::RefCell; +use std::hash::{Hash, Hasher}; + use super::objbool; use super::objint; use super::objsequence::{ @@ -7,7 +10,6 @@ use super::objstr; use super::objtype; use crate::pyobject::{PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyResult, TypeProtocol}; use crate::vm::{ReprGuard, VirtualMachine}; -use std::hash::{Hash, Hasher}; fn tuple_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( @@ -21,11 +23,7 @@ fn tuple_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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() - ))); + return Err(vm.new_type_error(format!("Cannot compare {} and {} using '<'", zelf, other))); }; Ok(vm.ctx.new_bool(result)) @@ -43,11 +41,7 @@ fn tuple_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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() - ))); + return Err(vm.new_type_error(format!("Cannot compare {} and {} using '>'", zelf, other))); }; Ok(vm.ctx.new_bool(result)) @@ -65,11 +59,7 @@ fn tuple_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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() - ))); + return Err(vm.new_type_error(format!("Cannot compare {} and {} using '>='", zelf, other))); }; Ok(vm.ctx.new_bool(result)) @@ -87,11 +77,7 @@ fn tuple_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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() - ))); + return Err(vm.new_type_error(format!("Cannot compare {} and {} using '<='", zelf, other))); }; Ok(vm.ctx.new_bool(result)) @@ -110,11 +96,7 @@ fn tuple_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let elements = e1.iter().chain(e2.iter()).cloned().collect(); Ok(vm.ctx.new_tuple(elements)) } else { - Err(vm.new_type_error(format!( - "Cannot add {} and {}", - zelf.borrow(), - other.borrow() - ))) + Err(vm.new_type_error(format!("Cannot add {} and {}", zelf, other))) } } @@ -193,7 +175,7 @@ fn tuple_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); if !objtype::issubclass(cls, &vm.ctx.tuple_type()) { - return Err(vm.new_type_error(format!("{} is not a subtype of tuple", cls.borrow()))); + return Err(vm.new_type_error(format!("{} is not a subtype of tuple", cls))); } let elements = if let Some(iterable) = iterable { @@ -203,7 +185,9 @@ fn tuple_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; Ok(PyObject::new( - PyObjectPayload::Sequence { elements }, + PyObjectPayload::Sequence { + elements: RefCell::new(elements), + }, cls.clone(), )) } diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index aa7618e38b..6935996752 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -14,12 +14,16 @@ use std::collections::HashMap; */ 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: RefCell::new(PyAttributes::new()), - mro: vec![object_type], - }; - (*type_type.borrow_mut()).typ = Some(type_type.clone()); + // this is not ideal + let ptr = PyObjectRef::into_raw(type_type.clone()) as *mut PyObject; + unsafe { + (*ptr).payload = PyObjectPayload::Class { + name: String::from("type"), + dict: RefCell::new(PyAttributes::new()), + mro: vec![object_type], + }; + (*ptr).typ = Some(type_type); + } } pub fn init(context: &PyContext) { @@ -71,7 +75,7 @@ fn type_mro(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn _mro(cls: PyObjectRef) -> Option> { - match cls.borrow().payload { + match cls.payload { PyObjectPayload::Class { ref mro, .. } => { let mut mro = mro.clone(); mro.insert(0, cls.clone()); @@ -96,7 +100,7 @@ pub fn issubclass(typ: &PyObjectRef, cls: &PyObjectRef) -> bool { } pub fn get_type_name(typ: &PyObjectRef) -> String { - if let PyObjectPayload::Class { name, .. } = &typ.borrow().payload { + if let PyObjectPayload::Class { name, .. } = &typ.payload { name.clone() } else { panic!("Cannot get type_name of non-type type {:?}", typ); @@ -211,7 +215,7 @@ pub fn type_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult let attribute_error = vm.context().exceptions.attribute_error.clone(); Err(vm.new_exception( attribute_error, - format!("{} has no attribute '{}'", cls.borrow(), name), + format!("{} has no attribute '{}'", cls, name), )) } } @@ -224,7 +228,7 @@ pub fn get_attributes(obj: &PyObjectRef) -> PyAttributes { let mut base_classes = objtype::base_classes(obj); base_classes.reverse(); for bc in base_classes { - if let PyObjectPayload::Class { dict, .. } = &bc.borrow().payload { + if let PyObjectPayload::Class { dict, .. } = &bc.payload { for (name, value) in dict.borrow().iter() { attributes.insert(name.to_string(), value.clone()); } @@ -232,7 +236,7 @@ pub fn get_attributes(obj: &PyObjectRef) -> PyAttributes { } // Get instance attributes: - if let PyObjectPayload::Instance { dict } = &obj.borrow().payload { + if let PyObjectPayload::Instance { dict } = &obj.payload { for (name, value) in dict.borrow().iter() { attributes.insert(name.to_string(), value.clone()); } diff --git a/vm/src/obj/objzip.rs b/vm/src/obj/objzip.rs index 43005f4168..d1cb833db4 100644 --- a/vm/src/obj/objzip.rs +++ b/vm/src/obj/objzip.rs @@ -19,7 +19,7 @@ fn zip_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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 let PyObjectPayload::ZipIterator { ref iterators } = zip.payload { if iterators.is_empty() { Err(objiter::new_stop_iteration(vm)) } else { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 073a5d646b..c854efa0e9 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -54,21 +54,15 @@ Basically reference counting, but then done by rust. * Good reference: https://github.com/ProgVal/pythonvm-rust/blob/master/src/objects/mod.rs */ -/* -The PyRef type implements -https://doc.rust-lang.org/std/cell/index.html#introducing-mutability-inside-of-something-immutable -*/ -pub type PyRef = Rc>; - /// The `PyObjectRef` is one of the most used types. It is a reference to a /// python object. A single python object can have multiple references, and /// this reference counting is accounted for by this type. Use the `.clone()` /// method to create a new reference and increment the amount of references /// to the python object by 1. -pub type PyObjectRef = PyRef; +pub type PyObjectRef = Rc; /// Same as PyObjectRef, except for being a weak reference. -pub type PyObjectWeakRef = Weak>; +pub type PyObjectWeakRef = Weak; /// Use this type for function which return a python object or and exception. /// Both the python object and the python exception are `PyObjectRef` types @@ -488,12 +482,19 @@ impl PyContext { } pub fn new_bytes(&self, data: Vec) -> PyObjectRef { - PyObject::new(PyObjectPayload::Bytes { value: data }, self.bytes_type()) + PyObject::new( + PyObjectPayload::Bytes { + value: RefCell::new(data), + }, + self.bytes_type(), + ) } pub fn new_bytearray(&self, data: Vec) -> PyObjectRef { PyObject::new( - PyObjectPayload::Bytes { value: data }, + PyObjectPayload::Bytes { + value: RefCell::new(data), + }, self.bytearray_type(), ) } @@ -507,24 +508,38 @@ impl PyContext { } pub fn new_tuple(&self, elements: Vec) -> PyObjectRef { - PyObject::new(PyObjectPayload::Sequence { elements }, self.tuple_type()) + PyObject::new( + PyObjectPayload::Sequence { + elements: RefCell::new(elements), + }, + self.tuple_type(), + ) } pub fn new_list(&self, elements: Vec) -> PyObjectRef { - PyObject::new(PyObjectPayload::Sequence { elements }, self.list_type()) + PyObject::new( + PyObjectPayload::Sequence { + elements: RefCell::new(elements), + }, + self.list_type(), + ) } 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 }, self.set_type()) + PyObject::new( + PyObjectPayload::Set { + elements: RefCell::new(HashMap::new()), + }, + self.set_type(), + ) } pub fn new_dict(&self) -> PyObjectRef { PyObject::new( PyObjectPayload::Dict { - elements: HashMap::new(), + elements: RefCell::new(HashMap::new()), }, self.dict_type(), ) @@ -642,10 +657,10 @@ 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 } => { + match obj.payload { + PyObjectPayload::Dict { ref elements } => { let key = self.new_str(key.to_string()); - objdict::set_item_in_content(elements, &key, &v); + objdict::set_item_in_content(&mut elements.borrow_mut(), &key, &v); } ref k => panic!("TODO {:?}", k), }; @@ -659,7 +674,7 @@ impl PyContext { } pub fn set_attr(&self, obj: &PyObjectRef, attr_name: &str, value: PyObjectRef) { - match obj.borrow().payload { + match obj.payload { PyObjectPayload::Module { ref dict, .. } => self.set_attr(dict, attr_name, value), PyObjectPayload::Instance { ref dict } | PyObjectPayload::Class { ref dict, .. } => { dict.borrow_mut().insert(attr_name.to_string(), value); @@ -708,7 +723,7 @@ pub trait IdProtocol { impl IdProtocol for PyObjectRef { fn get_id(&self) -> usize { - self.as_ptr() as usize + &*self as &PyObject as *const PyObject as usize } fn is(&self, other: &PyObjectRef) -> bool { @@ -726,7 +741,7 @@ pub trait TypeProtocol { impl TypeProtocol for PyObjectRef { fn typ(&self) -> PyObjectRef { - self.borrow().typ() + (**self).typ() } } @@ -746,14 +761,14 @@ pub trait ParentProtocol { impl ParentProtocol for PyObjectRef { fn has_parent(&self) -> bool { - match self.borrow().payload { + match self.payload { PyObjectPayload::Scope { ref scope } => scope.parent.is_some(), _ => panic!("Only scopes have parent (not {:?}", self), } } fn get_parent(&self) -> PyObjectRef { - match self.borrow().payload { + match self.payload { PyObjectPayload::Scope { ref scope } => match scope.parent { Some(ref value) => value.clone(), None => panic!("OMG"), @@ -769,7 +784,6 @@ 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).cloned(), _ => panic!("Only classes should be in MRO!"), @@ -777,7 +791,6 @@ 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.borrow().contains_key(attr_name), _ => panic!("Only classes should be in MRO!"), @@ -786,8 +799,7 @@ 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.payload { + match self.payload { PyObjectPayload::Module { ref dict, .. } => dict.get_item(attr_name), PyObjectPayload::Class { ref mro, .. } => { if let Some(item) = class_get_item(self, attr_name) { @@ -806,8 +818,7 @@ impl AttributeProtocol for PyObjectRef { } fn has_attr(&self, attr_name: &str) -> bool { - let obj = self.borrow(); - match obj.payload { + match self.payload { PyObjectPayload::Module { ref dict, .. } => dict.contains_key(attr_name), PyObjectPayload::Class { ref mro, .. } => { class_has_item(self, attr_name) || mro.iter().any(|d| class_has_item(d, attr_name)) @@ -826,9 +837,9 @@ pub trait DictProtocol { impl DictProtocol for PyObjectRef { fn contains_key(&self, k: &str) -> bool { - match self.borrow().payload { + match self.payload { PyObjectPayload::Dict { ref elements } => { - objdict::content_contains_key_str(elements, k) + objdict::content_contains_key_str(&elements.borrow(), k) } PyObjectPayload::Scope { ref scope } => scope.locals.contains_key(k), ref payload => unimplemented!("TODO {:?}", payload), @@ -836,15 +847,17 @@ 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), + match self.payload { + PyObjectPayload::Dict { ref elements } => { + objdict::content_get_key_str(&elements.borrow(), k) + } PyObjectPayload::Scope { ref scope } => scope.locals.get_item(k), _ => panic!("TODO"), } } fn get_key_value_pairs(&self) -> Vec<(PyObjectRef, PyObjectRef)> { - match self.borrow().payload { + match self.payload { 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(), @@ -1161,23 +1174,23 @@ pub enum PyObjectPayload { value: Complex64, }, Bytes { - value: Vec, + value: RefCell>, }, Sequence { - elements: Vec, + elements: RefCell>, }, Dict { - elements: objdict::DictContentType, + elements: RefCell, }, Set { - elements: HashMap, + elements: RefCell>, }, Iterator { position: usize, iterated_obj: PyObjectRef, }, EnumerateIterator { - counter: BigInt, + counter: RefCell, iterator: PyObjectRef, }, FilterIterator { @@ -1244,7 +1257,7 @@ pub enum PyObjectPayload { function: Box PyResult>, }, Socket { - socket: Socket, + socket: RefCell, }, } @@ -1303,7 +1316,7 @@ impl PyObject { // Move this object into a reference object, transferring ownership. pub fn into_ref(self) -> PyObjectRef { - Rc::new(RefCell::new(self)) + Rc::new(self) } } diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 0d7a46ee00..f1b9c24f9f 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -84,8 +84,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 - if let PyObjectPayload::Bytes { ref mut value } = buffer.borrow_mut().payload { - result.extend(value.iter().cloned()); + if let PyObjectPayload::Bytes { ref value } = buffer.payload { + result.extend(value.borrow().iter().cloned()); }; let len = vm.get_method(buffer.clone(), &"__len__".to_string()); @@ -167,11 +167,12 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let handle = os::rust_file(raw_fd); let mut f = handle.take(length); - if let PyObjectPayload::Bytes { ref mut value } = obj.borrow_mut().payload { + if let PyObjectPayload::Bytes { ref value } = obj.payload { //TODO: Implement for MemoryView - value.clear(); - match f.read_to_end(&mut *value) { + let mut value_mut = value.borrow_mut(); + value_mut.clear(); + match f.read_to_end(&mut value_mut) { Ok(_) => {} Err(_) => return Err(vm.new_value_error("Error reading from Take".to_string())), } @@ -197,9 +198,10 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //to support windows - i.e. raw file_handles let mut handle = os::rust_file(raw_fd); - match obj.borrow_mut().payload { - PyObjectPayload::Bytes { ref mut value } => { - match handle.write(&value[..]) { + match obj.payload { + PyObjectPayload::Bytes { ref value } => { + let value_mut = value.borrow(); + match handle.write(&value_mut[..]) { Ok(len) => { //reset raw fd on the FileIO object let updated = os::raw_file_number(handle); diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index 53507e81d7..e0288bdf6f 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -65,7 +65,7 @@ impl<'s> serde::Serialize for PyObjectSerializer<'s> { map.serialize_entry(&key, &self.clone_with_object(&e.1))?; } map.end() - } else if let PyObjectPayload::None = self.pyobject.borrow().payload { + } else if let PyObjectPayload::None = self.pyobject.payload { serializer.serialize_none() } else { Err(serde::ser::Error::custom(format!( @@ -167,7 +167,7 @@ impl<'de> Visitor<'de> for PyObjectDeserializer<'de> { // 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 { + let key = match key_obj.payload { PyObjectPayload::String { ref value } => value.clone(), _ => unimplemented!("map keys must be strings"), }; diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index f1634d6457..533f4fdcd0 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -1,3 +1,9 @@ +use std::cell::RefCell; +use std::io; +use std::io::Read; +use std::io::Write; +use std::net::{SocketAddr, TcpListener, TcpStream, UdpSocket}; + use crate::obj::objbytes; use crate::obj::objint; use crate::obj::objsequence::get_elements; @@ -8,10 +14,6 @@ use crate::pyobject::{ use crate::vm::VirtualMachine; use num_traits::ToPrimitive; -use std::io; -use std::io::Read; -use std::io::Write; -use std::net::{SocketAddr, TcpListener, TcpStream, UdpSocket}; #[derive(Copy, Clone)] enum AddressFamily { @@ -113,7 +115,7 @@ fn socket_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let address_family = AddressFamily::from_i32(objint::get_value(family_int).to_i32().unwrap()); let kind = SocketKind::from_i32(objint::get_value(kind_int).to_i32().unwrap()); - let socket = Socket::new(address_family, kind); + let socket = RefCell::new(Socket::new(address_family, kind)); Ok(PyObject::new( PyObjectPayload::Socket { socket }, @@ -134,12 +136,10 @@ fn socket_connect(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let address_string = format!("{}:{}", host, port.to_string()); - let mut mut_obj = zelf.borrow_mut(); - - match mut_obj.payload { - PyObjectPayload::Socket { ref mut socket } => { + match zelf.payload { + PyObjectPayload::Socket { ref socket } => { if let Ok(stream) = TcpStream::connect(address_string) { - socket.con = Some(Connection::TcpStream(stream)); + socket.borrow_mut().con = Some(Connection::TcpStream(stream)); Ok(vm.get_none()) } else { // TODO: Socket error @@ -163,12 +163,10 @@ fn socket_bind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let address_string = format!("{}:{}", host, port.to_string()); - let mut mut_obj = zelf.borrow_mut(); - - match mut_obj.payload { - PyObjectPayload::Socket { ref mut socket } => { + match zelf.payload { + PyObjectPayload::Socket { ref socket } => { if let Ok(stream) = TcpListener::bind(address_string) { - socket.con = Some(Connection::TcpListener(stream)); + socket.borrow_mut().con = Some(Connection::TcpListener(stream)); Ok(vm.get_none()) } else { // TODO: Socket error @@ -186,11 +184,9 @@ fn socket_listen(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn socket_accept(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, None)]); - let mut mut_obj = zelf.borrow_mut(); - - match mut_obj.payload { - PyObjectPayload::Socket { ref mut socket } => { - let ret = match socket.con { + match zelf.payload { + PyObjectPayload::Socket { ref socket } => { + let ret = match socket.borrow_mut().con { Some(ref mut v) => v.accept(), None => return Err(vm.new_type_error("".to_string())), }; @@ -200,15 +196,15 @@ fn socket_accept(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { _ => return Err(vm.new_type_error("".to_string())), }; - let socket = Socket { - address_family: socket.address_family.clone(), - sk: socket.sk.clone(), + let socket = RefCell::new(Socket { + address_family: socket.borrow().address_family.clone(), + sk: socket.borrow().sk.clone(), con: Some(Connection::TcpStream(tcp_stream)), - }; + }); - let sock_obj = PyObject::new(PyObjectPayload::Socket { socket }, mut_obj.typ()); + let sock_obj = PyObject::new(PyObjectPayload::Socket { socket }, zelf.typ()); - let elements = vec![sock_obj, vm.get_none()]; + let elements = RefCell::new(vec![sock_obj, vm.get_none()]); Ok(PyObject::new( PyObjectPayload::Sequence { elements }, @@ -225,19 +221,14 @@ fn socket_recv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(zelf, None), (bufsize, Some(vm.ctx.int_type()))] ); - let mut mut_obj = zelf.borrow_mut(); - - match mut_obj.payload { - PyObjectPayload::Socket { ref mut socket } => { + match zelf.payload { + PyObjectPayload::Socket { ref socket } => { let mut buffer = Vec::new(); - let _temp = match socket.con { + let _temp = match socket.borrow_mut().con { Some(ref mut v) => v.read_to_end(&mut buffer).unwrap(), None => 0, }; - Ok(PyObject::new( - PyObjectPayload::Bytes { value: buffer }, - vm.ctx.bytes_type(), - )) + Ok(vm.ctx.new_bytes(buffer)) } _ => Err(vm.new_type_error("".to_string())), } @@ -249,11 +240,9 @@ fn socket_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(zelf, None), (bytes, Some(vm.ctx.bytes_type()))] ); - let mut mut_obj = zelf.borrow_mut(); - - match mut_obj.payload { - PyObjectPayload::Socket { ref mut socket } => { - match socket.con { + match zelf.payload { + PyObjectPayload::Socket { ref socket } => { + match socket.borrow_mut().con { Some(ref mut v) => v.write(&objbytes::get_value(&bytes)).unwrap(), None => return Err(vm.new_type_error("".to_string())), }; @@ -265,13 +254,11 @@ fn socket_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn socket_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, None)]); - let mut mut_obj = zelf.borrow_mut(); - - match mut_obj.payload { - PyObjectPayload::Socket { ref mut socket } => match socket.address_family { - AddressFamily::AfInet => match socket.sk { + match zelf.payload { + PyObjectPayload::Socket { ref socket } => match socket.borrow().address_family { + AddressFamily::AfInet => match socket.borrow().sk { SocketKind::SockStream => { - socket.con = None; + socket.borrow_mut().con = None; Ok(vm.get_none()) } _ => Err(vm.new_type_error("".to_string())), diff --git a/vm/src/stdlib/weakref.rs b/vm/src/stdlib/weakref.rs index 0fc0878679..5b67f1664e 100644 --- a/vm/src/stdlib/weakref.rs +++ b/vm/src/stdlib/weakref.rs @@ -46,7 +46,7 @@ fn ref_call(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn get_value(obj: &PyObjectRef) -> PyObjectWeakRef { - if let PyObjectPayload::WeakRef { referent } = &obj.borrow().payload { + if let PyObjectPayload::WeakRef { referent } = &obj.payload { referent.clone() } else { panic!("Inner error getting weak ref {:?}", obj); diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index 1e07b4d9b8..00453f10eb 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -30,7 +30,7 @@ fn sys_getrefcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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()); + let size = mem::size_of_val(&object); Ok(vm.ctx.new_int(size)) } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 149a5313cf..0a0f97ce5e 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -190,7 +190,7 @@ impl VirtualMachine { } pub fn get_builtin_scope(&mut self) -> PyObjectRef { - let a2 = &*self.builtins.borrow(); + let a2 = &*self.builtins; match a2.payload { PyObjectPayload::Module { ref dict, .. } => dict.clone(), _ => { @@ -271,7 +271,7 @@ impl VirtualMachine { pub fn invoke(&mut self, func_ref: PyObjectRef, args: PyFuncArgs) -> PyResult { trace!("Invoke: {:?} {:?}", func_ref, args); - match func_ref.borrow().payload { + match func_ref.payload { PyObjectPayload::RustFunction { ref function } => function(self, args), PyObjectPayload::Function { ref code, @@ -406,8 +406,8 @@ 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().payload { - PyObjectPayload::Sequence { ref elements } => elements.clone(), + let available_defaults = match defaults.payload { + PyObjectPayload::Sequence { ref elements } => elements.borrow().clone(), PyObjectPayload::None => vec![], _ => panic!("function defaults not tuple or None"), }; @@ -493,11 +493,7 @@ impl VirtualMachine { let cls = obj.typ(); match cls.get_attr(method_name) { Some(method) => self.call_get_descriptor(method, obj.clone()), - None => Err(self.new_type_error(format!( - "{} has no method {:?}", - obj.borrow(), - method_name - ))), + None => Err(self.new_type_error(format!("{} has no method {:?}", obj, method_name))), } } From 3fc583813ab13f5abbef65db347eee2382e308c5 Mon Sep 17 00:00:00 2001 From: Ricky Han Date: Tue, 26 Feb 2019 00:05:41 -0500 Subject: [PATCH 045/380] 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 64ce7d3d4b..6b725519e2 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 046/380] 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 619b2dd1a0..de15771f44 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 e959908a4982d439a6a0bdf50e301e27328d45ab Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Mon, 25 Feb 2019 21:26:15 -0800 Subject: [PATCH 047/380] Fix iterator --- vm/src/obj/objbytes.rs | 4 ++-- vm/src/obj/objdict.rs | 9 +++++---- vm/src/obj/objiter.rs | 16 ++++++++-------- vm/src/obj/objlist.rs | 4 ++-- vm/src/obj/objrange.rs | 8 +++++--- vm/src/obj/objset.rs | 4 ++-- vm/src/obj/objtuple.rs | 4 ++-- vm/src/pyobject.rs | 4 ++-- 8 files changed, 28 insertions(+), 25 deletions(-) diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index b4b4898546..6ab2f72af2 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -1,4 +1,4 @@ -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::hash::{Hash, Hasher}; use std::ops::Deref; use std::ops::DerefMut; @@ -197,7 +197,7 @@ fn bytes_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let iter_obj = PyObject::new( PyObjectPayload::Iterator { - position: 0, + position: Cell::new(0), iterated_obj: obj.clone(), }, vm.ctx.iter_type(), diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 9a7c365aac..03a307b4c3 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -1,3 +1,7 @@ +use std::cell::{Cell, RefCell}; +use std::collections::HashMap; +use std::ops::{Deref, DerefMut}; + use super::objiter; use super::objstr; use super::objtype; @@ -6,9 +10,6 @@ use crate::pyobject::{ TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; -use std::cell::RefCell; -use std::collections::HashMap; -use std::ops::{Deref, DerefMut}; // This typedef abstracts the actual dict type used. // pub type DictContentType = HashMap>; @@ -258,7 +259,7 @@ fn dict_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let iter_obj = PyObject::new( PyObjectPayload::Iterator { - position: 0, + position: Cell::new(0), iterated_obj: key_list, }, vm.ctx.iter_type(), diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 70ae95e495..a72339fbb0 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -134,9 +134,9 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { { match iterated_obj_ref.payload { PyObjectPayload::Sequence { ref elements } => { - if *position < elements.borrow().len() { - let obj_ref = elements.borrow()[*position].clone(); - //*position += 1; // FIXME + if position.get() < elements.borrow().len() { + let obj_ref = elements.borrow()[position.get()].clone(); + position.set(position.get() + 1); Ok(obj_ref) } else { Err(new_stop_iteration(vm)) @@ -144,8 +144,8 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } PyObjectPayload::Range { ref range } => { - if let Some(int) = range.get(*position) { - //*position += 1; // FIXME + if let Some(int) = range.get(position.get()) { + position.set(position.get() + 1); Ok(vm.ctx.new_int(int)) } else { Err(new_stop_iteration(vm)) @@ -153,9 +153,9 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } PyObjectPayload::Bytes { ref value } => { - if *position < value.borrow().len() { - let obj_ref = vm.ctx.new_int(value.borrow()[*position]); - // *position += 1; // FIXME + if position.get() < value.borrow().len() { + let obj_ref = vm.ctx.new_int(value.borrow()[position.get()]); + position.set(position.get() + 1); Ok(obj_ref) } else { Err(new_stop_iteration(vm)) diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 8f32892c33..8c72bc9a9a 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -1,4 +1,4 @@ -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use super::objbool; use super::objint; @@ -381,7 +381,7 @@ fn list_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let iter_obj = PyObject::new( PyObjectPayload::Iterator { - position: 0, + position: Cell::new(0), iterated_obj: list.clone(), }, vm.ctx.iter_type(), diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 5ced88d918..d80b6aa015 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -1,3 +1,6 @@ +use std::cell::Cell; +use std::ops::Mul; + use super::objint; use super::objtype; use crate::pyobject::{ @@ -8,7 +11,6 @@ use crate::vm::VirtualMachine; 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 { @@ -247,7 +249,7 @@ fn range_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(PyObject::new( PyObjectPayload::Iterator { - position: 0, + position: Cell::new(0), iterated_obj: range.clone(), }, vm.ctx.iter_type(), @@ -261,7 +263,7 @@ fn range_reversed(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(PyObject::new( PyObjectPayload::Iterator { - position: 0, + position: Cell::new(0), iterated_obj: PyObject::new(PyObjectPayload::Range { range }, vm.ctx.range_type()), }, vm.ctx.iter_type(), diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index dcb47bc8c4..8d6d648470 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -2,7 +2,7 @@ * Builtin set type with a sequence of unique items. */ -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::collections::hash_map::DefaultHasher; use std::collections::HashMap; use std::hash::{Hash, Hasher}; @@ -547,7 +547,7 @@ fn set_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let set_list = vm.ctx.new_list(items); let iter_obj = PyObject::new( PyObjectPayload::Iterator { - position: 0, + position: Cell::new(0), iterated_obj: set_list, }, vm.ctx.iter_type(), diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 8ddc3640a7..7747d2fc92 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -1,4 +1,4 @@ -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::hash::{Hash, Hasher}; use super::objbool; @@ -151,7 +151,7 @@ fn tuple_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let iter_obj = PyObject::new( PyObjectPayload::Iterator { - position: 0, + position: Cell::new(0), iterated_obj: tuple.clone(), }, vm.ctx.iter_type(), diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index c854efa0e9..4841ce181c 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -35,7 +35,7 @@ use num_bigint::BigInt; use num_bigint::ToBigInt; use num_complex::Complex64; use num_traits::{One, Zero}; -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::fmt; use std::rc::{Rc, Weak}; @@ -1186,7 +1186,7 @@ pub enum PyObjectPayload { elements: RefCell>, }, Iterator { - position: usize, + position: Cell, iterated_obj: PyObjectRef, }, EnumerateIterator { From 93a6b4fef9175fdc1527087019f6b3c4cb9a3864 Mon Sep 17 00:00:00 2001 From: Ricky Han Date: Tue, 26 Feb 2019 00:32:19 -0500 Subject: [PATCH 048/380] 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 6b725519e2..fa4ddfd21f 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 0ce906b24e..3efe4efd8b 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 432b9dffaf10631e90eed526b3b9bb5b260c14fc Mon Sep 17 00:00:00 2001 From: Ricky Han Date: Tue, 26 Feb 2019 00:34:40 -0500 Subject: [PATCH 049/380] cargo fmt --- parser/src/lexer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index a970b84b49..619b2dd1a0 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -687,7 +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; From b28b164d75c96f40aa8eb29c6eb2519410593faa Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Mon, 25 Feb 2019 21:35:59 -0800 Subject: [PATCH 050/380] Fix generators --- vm/src/obj/objgenerator.rs | 31 +++++++++++++++++-------------- vm/src/pyobject.rs | 2 +- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/vm/src/obj/objgenerator.rs b/vm/src/obj/objgenerator.rs index fdde5a6c1a..cb727499ac 100644 --- a/vm/src/obj/objgenerator.rs +++ b/vm/src/obj/objgenerator.rs @@ -2,6 +2,8 @@ * The mythical generator. */ +use std::cell::RefCell; + use crate::frame::{ExecutionResult, Frame}; use crate::pyobject::{ PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, @@ -28,11 +30,12 @@ pub fn init(context: &PyContext) { } pub fn new_generator(vm: &mut VirtualMachine, frame: Frame) -> PyResult { - let g = PyObject::new( - PyObjectPayload::Generator { frame }, + Ok(PyObject::new( + PyObjectPayload::Generator { + frame: RefCell::new(frame), + }, vm.ctx.generator_type.clone(), - ); - Ok(g) + )) } fn generator_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -57,16 +60,16 @@ fn generator_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn send(vm: &mut VirtualMachine, gen: &PyObjectRef, value: &PyObjectRef) -> PyResult { if let PyObjectPayload::Generator { ref frame } = gen.payload { - //frame.push_value(value.clone()); - //match frame.run_frame(vm)? { - // ExecutionResult::Yield(value) => Ok(value), - // ExecutionResult::Return(_value) => { - // // Stop iteration! - // let stop_iteration = vm.ctx.exceptions.stop_iteration.clone(); - // Err(vm.new_exception(stop_iteration, "End of generator".to_string())) - // } - //} - unimplemented!("FIXME") + let mut frame_mut = frame.borrow_mut(); + frame_mut.push_value(value.clone()); + match frame_mut.run_frame(vm)? { + ExecutionResult::Yield(value) => Ok(value), + ExecutionResult::Return(_value) => { + // Stop iteration! + let stop_iteration = vm.ctx.exceptions.stop_iteration.clone(); + Err(vm.new_exception(stop_iteration, "End of generator".to_string())) + } + } } else { panic!("Cannot extract frame from non-generator"); } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 4841ce181c..962a2d8b9f 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1227,7 +1227,7 @@ pub enum PyObjectPayload { defaults: PyObjectRef, }, Generator { - frame: Frame, + frame: RefCell, }, BoundMethod { function: PyObjectRef, From d3646925eac79d2aabb04e387a763a36234159bb Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Mon, 25 Feb 2019 21:57:37 -0800 Subject: [PATCH 051/380] Fix float() --- vm/src/obj/objfloat.rs | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 25c95f80f9..4617364ad5 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -3,7 +3,7 @@ use super::objint; use super::objstr; use super::objtype; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use num_bigint::ToBigInt; @@ -15,14 +15,9 @@ fn float_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_str(v.to_string())) } -// __init__() -fn float_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.float_type())), (arg, None)] - ); - let val = if objtype::isinstance(arg, &vm.ctx.float_type()) { +fn float_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(cls, None), (arg, None)]); + let value = if objtype::isinstance(arg, &vm.ctx.float_type()) { get_value(arg) } else if objtype::isinstance(arg, &vm.ctx.int_type()) { match objint::get_value(arg).to_f64() { @@ -55,8 +50,7 @@ fn float_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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()) + Ok(PyObject::new(PyObjectPayload::Float { value }, cls.clone())) } // Retrieve inner float value: @@ -64,7 +58,7 @@ pub fn get_value(obj: &PyObjectRef) -> f64 { if let PyObjectPayload::Float { value } = &obj.payload { *value } else { - panic!("Inner error getting float"); + panic!("Inner error getting float: {}", obj); } } @@ -85,10 +79,6 @@ pub fn make_float(vm: &mut VirtualMachine, obj: &PyObjectRef) -> Result PyResult { arg_check!( vm, @@ -454,7 +444,7 @@ pub fn init(context: &PyContext) { "__floordiv__", context.new_rustfunc(float_floordiv), ); - context.set_attr(&float_type, "__init__", context.new_rustfunc(float_init)); + context.set_attr(&float_type, "__new__", context.new_rustfunc(float_new)); context.set_attr(&float_type, "__mod__", context.new_rustfunc(float_mod)); context.set_attr(&float_type, "__neg__", context.new_rustfunc(float_neg)); context.set_attr(&float_type, "__pow__", context.new_rustfunc(float_pow)); From 92fd12c9bd12ba622b414f49066ca842723bd260 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Mon, 25 Feb 2019 22:07:08 -0800 Subject: [PATCH 052/380] Fix scope --- vm/src/builtins.rs | 5 ++++- vm/src/frame.rs | 6 +++--- vm/src/obj/objframe.rs | 2 +- vm/src/pyobject.rs | 16 ++++++++-------- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index a14925add0..57f77c81da 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -3,6 +3,7 @@ //! Implements functions listed here: https://docs.python.org/3/library/builtins.html // use std::ops::Deref; +use std::cell::RefCell; use std::char; use std::io::{self, Write}; @@ -273,7 +274,9 @@ fn make_scope(vm: &mut VirtualMachine, locals: Option<&PyObjectRef>) -> PyObject }; PyObject { - payload: PyObjectPayload::Scope { scope: scope_inner }, + payload: PyObjectPayload::Scope { + scope: RefCell::new(scope_inner), + }, typ: None, } .into_ref() diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 6ef7e9863e..81697e1f01 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -593,7 +593,7 @@ impl Frame { let locals = self.pop_value(); match self.locals.payload { PyObjectPayload::Scope { ref scope } => { - //scope.locals = locals; // FIXME + (*scope.borrow_mut()).locals = locals; } _ => panic!("We really expect our scope to be a scope!"), } @@ -855,7 +855,7 @@ impl Frame { fn delete_name(&mut self, vm: &mut VirtualMachine, name: &str) -> FrameResult { let locals = match self.locals.payload { - PyObjectPayload::Scope { ref scope } => scope.locals.clone(), + PyObjectPayload::Scope { ref scope } => scope.borrow().locals.clone(), _ => panic!("We really expect our scope to be a scope!"), }; @@ -1137,7 +1137,7 @@ impl fmt::Debug for Frame { .collect::>() .join(""); let local_str = match self.locals.payload { - PyObjectPayload::Scope { ref scope } => match scope.locals.payload { + PyObjectPayload::Scope { ref scope } => match scope.borrow().locals.payload { PyObjectPayload::Dict { ref elements } => { objdict::get_key_value_pairs_from_content(&elements.borrow()) .iter() diff --git a/vm/src/obj/objframe.rs b/vm/src/obj/objframe.rs index efd3792bf3..ef9f509798 100644 --- a/vm/src/obj/objframe.rs +++ b/vm/src/obj/objframe.rs @@ -33,7 +33,7 @@ fn frame_flocals(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let py_scope = frame.locals.clone(); if let PyObjectPayload::Scope { scope } = &py_scope.payload { - Ok(scope.locals.clone()) + Ok(scope.borrow().locals.clone()) } else { panic!("The scope isn't a scope!"); } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 962a2d8b9f..77a7bed541 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -551,7 +551,7 @@ impl PyContext { pub fn new_scope(&self, parent: Option) -> PyObjectRef { let locals = self.new_dict(); - let scope = Scope { locals, parent }; + let scope = RefCell::new(Scope { locals, parent }); PyObject { payload: PyObjectPayload::Scope { scope }, typ: None, @@ -680,7 +680,7 @@ impl PyContext { dict.borrow_mut().insert(attr_name.to_string(), value); } PyObjectPayload::Scope { ref scope } => { - self.set_item(&scope.locals, attr_name, value); + self.set_item(&scope.borrow().locals, attr_name, value); } ref payload => unimplemented!("set_attr unimplemented for: {:?}", payload), }; @@ -762,14 +762,14 @@ pub trait ParentProtocol { impl ParentProtocol for PyObjectRef { fn has_parent(&self) -> bool { match self.payload { - PyObjectPayload::Scope { ref scope } => scope.parent.is_some(), + PyObjectPayload::Scope { ref scope } => scope.borrow().parent.is_some(), _ => panic!("Only scopes have parent (not {:?}", self), } } fn get_parent(&self) -> PyObjectRef { match self.payload { - PyObjectPayload::Scope { ref scope } => match scope.parent { + PyObjectPayload::Scope { ref scope } => match scope.borrow().parent { Some(ref value) => value.clone(), None => panic!("OMG"), }, @@ -841,7 +841,7 @@ impl DictProtocol for PyObjectRef { PyObjectPayload::Dict { ref elements } => { objdict::content_contains_key_str(&elements.borrow(), k) } - PyObjectPayload::Scope { ref scope } => scope.locals.contains_key(k), + PyObjectPayload::Scope { ref scope } => scope.borrow().locals.contains_key(k), ref payload => unimplemented!("TODO {:?}", payload), } } @@ -851,7 +851,7 @@ impl DictProtocol for PyObjectRef { PyObjectPayload::Dict { ref elements } => { objdict::content_get_key_str(&elements.borrow(), k) } - PyObjectPayload::Scope { ref scope } => scope.locals.get_item(k), + PyObjectPayload::Scope { ref scope } => scope.borrow().locals.get_item(k), _ => panic!("TODO"), } } @@ -860,7 +860,7 @@ impl DictProtocol for PyObjectRef { match self.payload { 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(), + PyObjectPayload::Scope { ref scope } => scope.borrow().locals.get_key_value_pairs(), _ => panic!("TODO"), } } @@ -1234,7 +1234,7 @@ pub enum PyObjectPayload { object: PyObjectRef, }, Scope { - scope: Scope, + scope: RefCell, }, Module { name: String, From baf3fe4becb4ec99a730618f40797e961edeb592 Mon Sep 17 00:00:00 2001 From: Ricky Han Date: Tue, 26 Feb 2019 01:15:09 -0500 Subject: [PATCH 053/380] 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 df63c959e6..187e33c1f2 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 054/380] 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 187e33c1f2..a3d976d9f1 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 3dcbc4d2aac5c26b76841f5986844f96d98a7685 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Mon, 25 Feb 2019 22:20:15 -0800 Subject: [PATCH 055/380] Fix set --- vm/src/obj/objset.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 8d6d648470..c041ce5673 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -429,10 +429,13 @@ fn set_pop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.set_type()))]); match s.payload { - PyObjectPayload::Set { ref elements } => match elements.borrow().clone().keys().next() { - Some(key) => Ok(elements.borrow_mut().remove(key).unwrap()), - None => Err(vm.new_key_error("pop from an empty set".to_string())), - }, + PyObjectPayload::Set { ref elements } => { + let mut elements = elements.borrow_mut(); + match elements.clone().keys().next() { + Some(key) => Ok(elements.remove(key).unwrap()), + None => Err(vm.new_key_error("pop from an empty set".to_string())), + } + } _ => Err(vm.new_type_error("".to_string())), } } @@ -492,14 +495,15 @@ fn set_combine_update_inner( match zelf.payload { PyObjectPayload::Set { ref elements } => { - for element in elements.borrow().clone().iter() { + let mut elements = elements.borrow_mut(); + for element in elements.clone().iter() { let value = vm.call_method(iterable, "__contains__", vec![element.1.clone()])?; let should_remove = match op { SetCombineOperation::Intersection => !objbool::get_value(&value), SetCombineOperation::Difference => objbool::get_value(&value), }; if should_remove { - elements.borrow_mut().remove(&element.0.clone()); + elements.remove(&element.0.clone()); } } } From bd3630260a7209de063e689a35b26a590459965a Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Mon, 25 Feb 2019 22:22:45 -0800 Subject: [PATCH 056/380] fix socket --- vm/src/stdlib/socket.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index 533f4fdcd0..4eb930dcbd 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -255,16 +255,19 @@ fn socket_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn socket_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, None)]); match zelf.payload { - PyObjectPayload::Socket { ref socket } => match socket.borrow().address_family { - AddressFamily::AfInet => match socket.borrow().sk { - SocketKind::SockStream => { - socket.borrow_mut().con = None; - Ok(vm.get_none()) - } + PyObjectPayload::Socket { ref socket } => { + let mut socket = socket.borrow_mut(); + match socket.address_family { + AddressFamily::AfInet => match socket.sk { + SocketKind::SockStream => { + socket.con = None; + Ok(vm.get_none()) + } + _ => Err(vm.new_type_error("".to_string())), + }, _ => Err(vm.new_type_error("".to_string())), - }, - _ => Err(vm.new_type_error("".to_string())), - }, + } + } _ => Err(vm.new_type_error("".to_string())), } } From b8eb4c38e8a404d353a55c6b2bc4c479e8714f7d Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Mon, 25 Feb 2019 22:48:45 -0800 Subject: [PATCH 057/380] Fix wasm --- wasm/lib/src/vm_class.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index f803d2d776..a61b36b921 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -4,7 +4,7 @@ use crate::wasm_builtins; use js_sys::{SyntaxError, TypeError}; use rustpython_vm::{ compile, - pyobject::{PyFuncArgs, PyObjectRef, PyRef, PyResult}, + pyobject::{PyFuncArgs, PyObjectRef, PyResult}, VirtualMachine, }; use std::cell::RefCell; @@ -34,8 +34,8 @@ impl StoredVirtualMachine { // gets compiled down to a normal-ish static varible, like Atomic* types: // https://rustwasm.github.io/2018/10/24/multithreading-rust-and-wasm.html#atomic-instructions thread_local! { - static STORED_VMS: PyRef>> = Rc::default(); - static ACTIVE_VMS: PyRef> = Rc::default(); + static STORED_VMS: Rc>>>> = Rc::default(); + static ACTIVE_VMS: Rc>> = Rc::default(); } #[wasm_bindgen(js_name = vmStore)] From b1088c629c52cf90fc19070d299b0cd6d4a796f6 Mon Sep 17 00:00:00 2001 From: Aaron Date: Tue, 26 Feb 2019 00:55:16 -0800 Subject: [PATCH 058/380] 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 de15771f44..7061279dcc 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 2c0016a171714bc5b43caeaf44db0bc6550d4c93 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Fri, 22 Feb 2019 09:55:10 +0000 Subject: [PATCH 059/380] Minimal definition of dis.dis. --- tests/snippets/dismod.py | 8 ++++---- vm/src/stdlib/dis.rs | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/snippets/dismod.py b/tests/snippets/dismod.py index cc9c1f63b8..5c3811cfa1 100644 --- a/tests/snippets/dismod.py +++ b/tests/snippets/dismod.py @@ -1,10 +1,10 @@ import dis -dis.disassemble(compile("5 + x + 5 or 2", "", "eval")) +dis.dis(compile("5 + x + 5 or 2", "", "eval")) print("\n") -dis.disassemble(compile("def f(x):\n return 1", "", "exec")) +dis.dis(compile("def f(x):\n return 1", "", "exec")) print("\n") -dis.disassemble(compile("if a:\n 1 or 2\nelif x == 'hello':\n 3\nelse:\n 4", "", "exec")) +dis.dis(compile("if a:\n 1 or 2\nelif x == 'hello':\n 3\nelse:\n 4", "", "exec")) print("\n") -dis.disassemble(compile("f(x=1, y=2)", "", "eval")) +dis.dis(compile("f(x=1, y=2)", "", "eval")) print("\n") diff --git a/vm/src/stdlib/dis.rs b/vm/src/stdlib/dis.rs index 5e1a30b033..f2a18a3f2c 100644 --- a/vm/src/stdlib/dis.rs +++ b/vm/src/stdlib/dis.rs @@ -2,6 +2,10 @@ use crate::obj::objcode; use crate::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; +fn dis_dis(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + dis_disassemble(vm, args) +} + fn dis_disassemble(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(co, Some(vm.ctx.code_type()))]); @@ -12,6 +16,7 @@ fn dis_disassemble(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn mk_module(ctx: &PyContext) -> PyObjectRef { py_module!(ctx, "dis", { + "dis" => ctx.new_rustfunc(dis_dis), "disassemble" => ctx.new_rustfunc(dis_disassemble) }) } From 79f43ad76252eb17bca7cfc317be861453e3981c Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Fri, 22 Feb 2019 10:08:04 +0000 Subject: [PATCH 060/380] Add support for pulling __code__ out of a function (and a test that hits lots of instructions). --- tests/snippets/dismod.py | 10 ++++++++++ vm/src/stdlib/dis.rs | 9 ++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/snippets/dismod.py b/tests/snippets/dismod.py index 5c3811cfa1..2dfcd2ec3f 100644 --- a/tests/snippets/dismod.py +++ b/tests/snippets/dismod.py @@ -8,3 +8,13 @@ print("\n") dis.dis(compile("f(x=1, y=2)", "", "eval")) print("\n") + +def f(): + with g(): + try: + for a in {1: 4, 2: 5}: + yield [True and False or True, []] + except Exception: + raise not ValueError({1 for i in [1,2,3]}) + +dis.dis(f) diff --git a/vm/src/stdlib/dis.rs b/vm/src/stdlib/dis.rs index f2a18a3f2c..df08daf7c4 100644 --- a/vm/src/stdlib/dis.rs +++ b/vm/src/stdlib/dis.rs @@ -3,7 +3,14 @@ use crate::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol use crate::vm::VirtualMachine; fn dis_dis(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - dis_disassemble(vm, args) + arg_check!(vm, args, required = [(obj, None)]); + let code_name = vm.new_str("__code__".to_string()); + let code = match vm.get_attribute(obj.clone(), code_name) { + Ok(co) => co, + Err(..) => obj.clone(), + }; + + dis_disassemble(vm, PyFuncArgs::new(vec![code], vec![])) } fn dis_disassemble(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From 73a65a1c779a3c8f8ac30aaefb002852f6188530 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Tue, 26 Feb 2019 09:40:48 +0000 Subject: [PATCH 061/380] dis.rs - small refactor dis_dis. --- tests/snippets/dismod.py | 14 ++++++++++++++ vm/src/stdlib/dis.rs | 11 ++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/tests/snippets/dismod.py b/tests/snippets/dismod.py index 2dfcd2ec3f..eac1d3f726 100644 --- a/tests/snippets/dismod.py +++ b/tests/snippets/dismod.py @@ -18,3 +18,17 @@ def f(): raise not ValueError({1 for i in [1,2,3]}) dis.dis(f) + +class A(object): + def f(): + x += 1 + pass + def g(): + for i in range(5): + if i: + continue + else: + break + +print("A.f\n") +dis.dis(A.f) diff --git a/vm/src/stdlib/dis.rs b/vm/src/stdlib/dis.rs index df08daf7c4..d726b71106 100644 --- a/vm/src/stdlib/dis.rs +++ b/vm/src/stdlib/dis.rs @@ -4,13 +4,14 @@ use crate::vm::VirtualMachine; fn dis_dis(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, None)]); + + // Method or function: let code_name = vm.new_str("__code__".to_string()); - let code = match vm.get_attribute(obj.clone(), code_name) { - Ok(co) => co, - Err(..) => obj.clone(), - }; + if let Ok(co) = vm.get_attribute(obj.clone(), code_name) { + return dis_disassemble(vm, PyFuncArgs::new(vec![co], vec![])); + } - dis_disassemble(vm, PyFuncArgs::new(vec![code], vec![])) + dis_disassemble(vm, PyFuncArgs::new(vec![obj.clone()], vec![])) } fn dis_disassemble(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From 53eff6c9d7763128712eac4eb5728f69ce54a244 Mon Sep 17 00:00:00 2001 From: Ricky Han Date: Tue, 26 Feb 2019 05:11:38 -0500 Subject: [PATCH 062/380] 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 fa4ddfd21f..cd6546e5d8 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 3efe4efd8b..0ce906b24e 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 c33abe91e9e335e4c1ee3f12725d498141212c9e Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Tue, 26 Feb 2019 19:53:58 +0100 Subject: [PATCH 063/380] Use py_module macro at more places. --- vm/src/stdlib/json.rs | 12 +++++------- vm/src/stdlib/keyword.rs | 10 ++++------ vm/src/stdlib/pystruct.rs | 10 ++++------ vm/src/stdlib/string.rs | 24 +++++++++++------------- vm/src/stdlib/time_module.rs | 10 ++++------ vm/src/stdlib/tokenize.rs | 9 +++------ vm/src/stdlib/types.rs | 17 +++++++---------- 7 files changed, 38 insertions(+), 54 deletions(-) diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index e0288bdf6f..78fc05dc9b 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -225,11 +225,6 @@ fn json_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(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( "JSONDecodeError", @@ -237,7 +232,10 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { &ctx.exceptions.exception_type, &ctx.dict_type, ); - ctx.set_attr(&json_mod, "JSONDecodeError", json_decode_error); - json_mod + py_module!(ctx, "json", { + "dumps" => ctx.new_rustfunc(json_dumps), + "loads" => ctx.new_rustfunc(json_loads), + "JSONDecodeError" => json_decode_error + }) } diff --git a/vm/src/stdlib/keyword.rs b/vm/src/stdlib/keyword.rs index 5d353fffad..85d6fa49e6 100644 --- a/vm/src/stdlib/keyword.rs +++ b/vm/src/stdlib/keyword.rs @@ -18,17 +18,15 @@ 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() .map(|k| ctx.new_str(k.to_string())) .collect(), ); - ctx.set_attr(&py_mod, "kwlist", keyword_kwlist); - py_mod + py_module!(ctx, "keyword", { + "iskeyword" => ctx.new_rustfunc(keyword_iskeyword), + "kwlist" => keyword_kwlist + }) } diff --git a/vm/src/stdlib/pystruct.rs b/vm/src/stdlib/pystruct.rs index 8c12674cf2..c686b00063 100644 --- a/vm/src/stdlib/pystruct.rs +++ b/vm/src/stdlib/pystruct.rs @@ -341,10 +341,8 @@ 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 + py_module!(ctx, "struct", { + "pack" => ctx.new_rustfunc(struct_pack), + "unpack" => ctx.new_rustfunc(struct_unpack) + }) } diff --git a/vm/src/stdlib/string.rs b/vm/src/stdlib/string.rs index 07f62b4970..5b3f2a7282 100644 --- a/vm/src/stdlib/string.rs +++ b/vm/src/stdlib/string.rs @@ -6,8 +6,6 @@ use crate::pyobject::{PyContext, PyObjectRef}; pub fn mk_module(ctx: &PyContext) -> PyObjectRef { - let py_mod = ctx.new_module(&"string".to_string(), ctx.new_scope(None)); - let ascii_lowercase = "abcdefghijklmnopqrstuvwxyz".to_string(); let ascii_uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".to_string(); let ascii_letters = format!("{}{}", ascii_lowercase, ascii_uppercase); @@ -21,15 +19,15 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { */ // Constants: - 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 + py_module!(ctx, "string", { + "ascii_letters" => ctx.new_str(ascii_letters), + "ascii_lowercase" => ctx.new_str(ascii_lowercase), + "ascii_uppercase" => ctx.new_str(ascii_uppercase), + "digits" => ctx.new_str(digits), + "hexdigits" => ctx.new_str(hexdigits), + "octdigits" => ctx.new_str(octdigits), + // "printable", ctx.new_str(printable) + "punctuation" => ctx.new_str(punctuation) + // "whitespace", ctx.new_str(whitespace) + }) } diff --git a/vm/src/stdlib/time_module.rs b/vm/src/stdlib/time_module.rs index 1f96a4ba43..b9a360ce81 100644 --- a/vm/src/stdlib/time_module.rs +++ b/vm/src/stdlib/time_module.rs @@ -31,10 +31,8 @@ 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 + py_module!(ctx, "time", { + "sleep" => ctx.new_rustfunc(time_sleep), + "time" => ctx.new_rustfunc(time_time) + }) } diff --git a/vm/src/stdlib/tokenize.rs b/vm/src/stdlib/tokenize.rs index cbd4a78704..8207e9efc5 100644 --- a/vm/src/stdlib/tokenize.rs +++ b/vm/src/stdlib/tokenize.rs @@ -26,10 +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", ctx.new_scope(None)); - - // Number theory functions: - ctx.set_attr(&py_mod, "tokenize", ctx.new_rustfunc(tokenize_tokenize)); - - py_mod + py_module!(ctx, "tokenize", { + "tokenize" => ctx.new_rustfunc(tokenize_tokenize) + }) } diff --git a/vm/src/stdlib/types.rs b/vm/src/stdlib/types.rs index b5a0997c3f..959b141f55 100644 --- a/vm/src/stdlib/types.rs +++ b/vm/src/stdlib/types.rs @@ -31,14 +31,11 @@ fn types_new_class(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn mk_module(ctx: &PyContext) -> PyObjectRef { - 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)); - ctx.set_attr(&py_mod, "FunctionType", ctx.function_type()); - ctx.set_attr(&py_mod, "LambdaType", ctx.function_type()); - ctx.set_attr(&py_mod, "CodeType", ctx.code_type()); - ctx.set_attr(&py_mod, "FrameType", ctx.frame_type()); - - py_mod + py_module!(ctx, "types", { + "new_class" => ctx.new_rustfunc(types_new_class), + "FunctionType" => ctx.function_type(), + "LambdaType" => ctx.function_type(), + "CodeType" => ctx.code_type(), + "FrameType" => ctx.frame_type() + }) } From 5949a9bd13d798241b190c54017a8654ae28454a Mon Sep 17 00:00:00 2001 From: silmeth Date: Tue, 26 Feb 2019 19:31:35 +0100 Subject: [PATCH 064/380] fix io module being imported multiple times --- vm/src/stdlib/io.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index f1b9c24f9f..077f907d89 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -7,6 +7,7 @@ use std::collections::HashSet; use std::fs::File; use std::io::prelude::*; use std::io::BufReader; +use std::path::PathBuf; //3rd party imports use num_bigint::ToBigInt; @@ -23,6 +24,7 @@ use crate::pyobject::{ PyResult, TypeProtocol, }; +use crate::import; use crate::vm::VirtualMachine; fn compute_c_flag(mode: &str) -> u16 { @@ -267,7 +269,7 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { optional = [(mode, Some(vm.ctx.str_type()))] ); - let module = mk_module(&vm.ctx); + let module = import::import_module(vm, PathBuf::default(), "io").unwrap(); //mode is optional: 'rt' is the default mode (open from reading text) let rust_mode = mode.map_or("rt".to_string(), |m| objstr::get_value(m)); From f2e60b24e18ec8c61e2199bc9ed165e1606ce688 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Tue, 26 Feb 2019 21:42:38 +0100 Subject: [PATCH 065/380] Add initial platform module. --- Cargo.lock | 35 +++++++++++++++++++++++++++++++++++ vm/Cargo.toml | 1 + vm/src/stdlib/mod.rs | 2 ++ vm/src/stdlib/platform.rs | 29 +++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+) create mode 100644 vm/src/stdlib/platform.rs diff --git a/Cargo.lock b/Cargo.lock index 061dee67fd..fd613d7f7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -701,6 +701,23 @@ name = "rustc-demangle" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc_version_runtime" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rustpython" version = "0.0.1" @@ -742,6 +759,7 @@ dependencies = [ "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)", + "rustc_version_runtime 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustpython_parser 0.0.1", "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)", @@ -790,6 +808,19 @@ name = "scoped_threadpool" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "serde" version = "1.0.66" @@ -1277,9 +1308,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" "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 rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum rustc_version_runtime 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6de8ecd7fad7731f306f69b6e10ec5a3178c61e464dcc06979427aa4cc891145" "checksum rustyline 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6010155119d53aac4f5b987cb8f6ea913d0d64d9b237da36f8f96a90cb3f5385" "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 semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "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" "checksum serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "44dd2cfde475037451fa99b7e5df77aa3cfd1536575fa8e7a538ab36dcde49ae" diff --git a/vm/Cargo.toml b/vm/Cargo.toml index f0ab63aabf..85e0bf936e 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -18,6 +18,7 @@ serde_derive = "1.0.66" serde_json = "1.0.26" byteorder = "1.2.6" regex = "1" +rustc_version_runtime = "0.1.*" statrs = "0.10.0" caseless = "0.2.1" unicode-segmentation = "1.2.1" diff --git a/vm/src/stdlib/mod.rs b/vm/src/stdlib/mod.rs index 4ec43f9cd1..2a22093751 100644 --- a/vm/src/stdlib/mod.rs +++ b/vm/src/stdlib/mod.rs @@ -3,6 +3,7 @@ mod dis; mod json; mod keyword; mod math; +mod platform; mod pystruct; mod random; mod re; @@ -33,6 +34,7 @@ pub fn get_module_inits() -> HashMap { modules.insert("json".to_string(), Box::new(json::mk_module)); modules.insert("keyword".to_string(), Box::new(keyword::mk_module)); modules.insert("math".to_string(), Box::new(math::mk_module)); + modules.insert("platform".to_string(), Box::new(platform::mk_module)); modules.insert("re".to_string(), Box::new(re::mk_module)); modules.insert("random".to_string(), Box::new(random::mk_module)); modules.insert("string".to_string(), Box::new(string::mk_module)); diff --git a/vm/src/stdlib/platform.rs b/vm/src/stdlib/platform.rs new file mode 100644 index 0000000000..e1681a4dd5 --- /dev/null +++ b/vm/src/stdlib/platform.rs @@ -0,0 +1,29 @@ +extern crate rustc_version_runtime; + +use crate::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult}; +use crate::VirtualMachine; + +pub fn mk_module(ctx: &PyContext) -> PyObjectRef { + py_module!(ctx, "platform", { + "python_compiler" => ctx.new_rustfunc(platform_python_compiler), + "python_implementation" => ctx.new_rustfunc(platform_python_implementation), + "python_version" => ctx.new_rustfunc(platform_python_version), + }) +} + +fn platform_python_implementation(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args); + Ok(vm.new_str("RustPython".to_string())) +} + +fn platform_python_version(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args); + // TODO: fetch version from somewhere. + Ok(vm.new_str("4.0.0".to_string())) +} + +fn platform_python_compiler(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args); + let version = rustc_version_runtime::version_meta(); + Ok(vm.new_str(format!("rustc {}", version.semver))) +} From 267934bb49a2c43c5f731692ced1460f41858b8f Mon Sep 17 00:00:00 2001 From: Aaron Date: Tue, 26 Feb 2019 12:50:18 -0800 Subject: [PATCH 066/380] 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 7061279dcc..0c3d887806 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 067/380] 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 881279f075..9e385abdce 100644 --- a/tests/snippets/list.py +++ b/tests/snippets/list.py @@ -117,3 +117,42 @@ def __eq__(self, x): 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 57f77c81da..870b3573e4 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 8c72bc9a9a..e09f15a8b1 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 58437b98e8..46912c1b8f 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 d6242ac6e3e89474a3c75645ac3380d8430b3db9 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Tue, 26 Feb 2019 22:27:59 +0100 Subject: [PATCH 068/380] Use py_module macro on ast module. --- vm/src/stdlib/ast.rs | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index 682b297c6a..e4eba415a9 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -602,23 +602,10 @@ 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()), - ); - - 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 + py_module!(ctx, "ast", { + "parse" => ctx.new_rustfunc(ast_parse), + "Module" => ctx.new_class("_ast.Module", ctx.object()), + "FunctionDef" =>ctx.new_class("_ast.FunctionDef", ctx.object()), + "Call" => ctx.new_class("_ast.Call", ctx.object()) + }) } From 830cc7e9900cf3149aba69972ef33566de14fc66 Mon Sep 17 00:00:00 2001 From: ricky han Date: Tue, 26 Feb 2019 17:46:06 -0500 Subject: [PATCH 069/380] 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 cd6546e5d8..a05972961a 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 070/380] 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 a9c44f165d..73a4df608f 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 2cf8e9d935..d29650b26f 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 a3d976d9f1..adcbb31735 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 071/380] 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 619b2dd1a0..bb2b3bcd3d 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 0000000000..ae9df84594 --- /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 072/380] 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 bb2b3bcd3d..24fa7c969c 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 2a39a41f85..4587e457ec 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 ae9df84594..32a9786fa1 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 073/380] 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 bb2b3bcd3d..619b2dd1a0 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 ae9df84594..32a9786fa1 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1 +1 @@ -version = "One" +edition = "2018" From ba1c5d78aa6fb0903ddb0029546e2d23fa7a86f3 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 26 Feb 2019 20:28:25 -0600 Subject: [PATCH 074/380] Add a Promise class to the browser module --- wasm/demo/snippets/fetch.py | 8 +- wasm/lib/src/browser_module.rs | 176 +++++++++++++++++++++++++-------- 2 files changed, 139 insertions(+), 45 deletions(-) diff --git a/wasm/demo/snippets/fetch.py b/wasm/demo/snippets/fetch.py index 63b21b69d8..f507057b22 100644 --- a/wasm/demo/snippets/fetch.py +++ b/wasm/demo/snippets/fetch.py @@ -5,8 +5,8 @@ def fetch_handler(res): fetch( "https://httpbin.org/get", - fetch_handler, - lambda err: print(f"error: {err}"), response_format="json", - headers={"X-Header-Thing": "rustpython is neat!"}, -) + headers={ + "X-Header-Thing": "rustpython is neat!" + }, +).then(fetch_handler, lambda err: print(f"error: {err}")) diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index e3f6978385..f2ed9406ae 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -1,9 +1,12 @@ use crate::{convert, vm_class::AccessibleVM, wasm_builtins::window}; -use futures::{future, Future}; +use futures::Future; use js_sys::Promise; use rustpython_vm::obj::{objint, objstr}; -use rustpython_vm::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; -use rustpython_vm::VirtualMachine; +use rustpython_vm::pyobject::{ + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, +}; +use rustpython_vm::{import::import, VirtualMachine}; +use std::path::PathBuf; use wasm_bindgen::{prelude::*, JsCast}; use wasm_bindgen_futures::{future_to_promise, JsFuture}; @@ -32,15 +35,10 @@ impl FetchResponseFormat { } fn browser_fetch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (url, Some(vm.ctx.str_type())), - (handler, Some(vm.ctx.function_type())) - ], - optional = [(reject_handler, Some(vm.ctx.function_type()))] - ); + arg_check!(vm, args, required = [(url, Some(vm.ctx.str_type()))]); + + let promise_type = import_promise_type(vm)?; + let response_format = args.get_optional_kwarg_with_type("response_format", vm.ctx.str_type(), vm)?; let method = args.get_optional_kwarg_with_type("method", vm.ctx.str_type(), vm)?; @@ -87,11 +85,6 @@ fn browser_fetch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let window = window(); let request_prom = window.fetch_with_request(&request); - let handler = handler.clone(); - let reject_handler = reject_handler.cloned(); - - let acc_vm = AccessibleVM::from_vm(vm); - let future = JsFuture::from(request_prom) .and_then(move |val| { let response = val @@ -99,30 +92,9 @@ fn browser_fetch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .expect("val to be of type Response"); response_format.get_response(&response) }) - .and_then(JsFuture::from) - .then(move |val| { - let vm = &mut acc_vm - .upgrade() - .expect("that the VM *not* be destroyed while promise is being resolved"); - match val { - Ok(val) => { - let val = convert::js_to_py(vm, val); - let args = PyFuncArgs::new(vec![val], vec![]); - let _ = vm.invoke(handler, args); - } - Err(val) => { - if let Some(reject_handler) = reject_handler { - let val = convert::js_to_py(vm, val); - let args = PyFuncArgs::new(vec![val], vec![]); - let _ = vm.invoke(reject_handler, args); - } - } - } - future::ok(JsValue::UNDEFINED) - }); - future_to_promise(future); + .and_then(JsFuture::from); - Ok(vm.get_none()) + Ok(PyPromise::new(promise_type, future_to_promise(future))) } fn browser_request_animation_frame(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -167,7 +139,7 @@ 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 + // questionable, but it's probably fine let id = objint::get_value(id) .to_string() .parse() @@ -180,13 +152,135 @@ fn browser_cancel_animation_frame(vm: &mut VirtualMachine, args: PyFuncArgs) -> Ok(vm.get_none()) } +pub struct PyPromise { + value: Promise, +} + +impl PyPromise { + pub fn new(promise_type: PyObjectRef, value: Promise) -> PyObjectRef { + PyObject::new( + PyObjectPayload::AnyRustValue { + value: Box::new(PyPromise { value }), + }, + promise_type, + ) + } +} + +fn get_value(obj: &PyObjectRef) -> Promise { + if let PyObjectPayload::AnyRustValue { value } = &obj.payload { + if let Some(promise) = value.downcast_ref::() { + return promise.value.clone(); + } + } + panic!("Inner error getting promise") +} + +fn import_promise_type(vm: &mut VirtualMachine) -> PyResult { + import( + vm, + PathBuf::default(), + BROWSER_NAME, + &Some("Promise".into()), + ) +} + +fn promise_then(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + let promise_type = import_promise_type(vm)?; + arg_check!( + vm, + args, + required = [ + (zelf, Some(promise_type.clone())), + (on_fulfill, Some(vm.ctx.function_type())) + ], + optional = [(on_reject, Some(vm.ctx.function_type()))] + ); + + let on_fulfill = on_fulfill.clone(); + let on_reject = on_reject.cloned(); + + let acc_vm = AccessibleVM::from_vm(vm); + + let promise = get_value(zelf); + + let ret_future = JsFuture::from(promise).then(move |res| { + let vm = &mut acc_vm + .upgrade() + .expect("that the vm is valid when the promise resolves"); + let ret = match res { + Ok(val) => { + let val = convert::js_to_py(vm, val); + vm.invoke(on_fulfill, PyFuncArgs::new(vec![val], vec![])) + } + Err(err) => { + if let Some(on_reject) = on_reject { + let err = convert::js_to_py(vm, err); + vm.invoke(on_reject, PyFuncArgs::new(vec![err], vec![])) + } else { + return Err(err); + } + } + }; + ret.map(|val| convert::py_to_js(vm, val)) + .map_err(|err| convert::py_to_js(vm, err)) + }); + + let ret_promise = future_to_promise(ret_future); + + Ok(PyPromise::new(promise_type, ret_promise)) +} + +fn promise_catch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + let promise_type = import_promise_type(vm)?; + arg_check!( + vm, + args, + required = [ + (zelf, Some(promise_type.clone())), + (on_reject, Some(vm.ctx.function_type())) + ] + ); + + let on_reject = on_reject.clone(); + + let acc_vm = AccessibleVM::from_vm(vm); + + let promise = get_value(zelf); + + let ret_future = JsFuture::from(promise).then(move |res| match res { + Ok(val) => Ok(val), + Err(err) => { + let vm = &mut acc_vm + .upgrade() + .expect("that the vm is valid when the promise resolves"); + let err = convert::js_to_py(vm, err); + vm.invoke(on_reject, PyFuncArgs::new(vec![err], vec![])) + .map(|val| convert::py_to_js(vm, val)) + .map_err(|err| convert::py_to_js(vm, err)) + } + }); + + let ret_promise = future_to_promise(ret_future); + + Ok(PyPromise::new(promise_type, ret_promise)) +} + const BROWSER_NAME: &str = "browser"; pub fn mk_module(ctx: &PyContext) -> PyObjectRef { + let promise = { + let promise = ctx.new_class("Promise", ctx.object()); + ctx.set_attr(&promise, "then", ctx.new_rustfunc(promise_then)); + ctx.set_attr(&promise, "catch", ctx.new_rustfunc(promise_catch)); + promise + }; + py_module!(ctx, BROWSER_NAME, { "fetch" => ctx.new_rustfunc(browser_fetch), "request_animation_frame" => ctx.new_rustfunc(browser_request_animation_frame), "cancel_animation_frame" => ctx.new_rustfunc(browser_cancel_animation_frame), + "Promise" => promise, }) } From 1d193e7fc797a134bc7d19bbcbd02e14fe7c2cf2 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 26 Feb 2019 20:37:59 -0600 Subject: [PATCH 075/380] Convert between JS Promises and PyPromises --- wasm/lib/src/browser_module.rs | 8 ++++---- wasm/lib/src/convert.rs | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index f2ed9406ae..bfc39d87c2 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -167,7 +167,7 @@ impl PyPromise { } } -fn get_value(obj: &PyObjectRef) -> Promise { +pub fn get_promise_value(obj: &PyObjectRef) -> Promise { if let PyObjectPayload::AnyRustValue { value } = &obj.payload { if let Some(promise) = value.downcast_ref::() { return promise.value.clone(); @@ -176,7 +176,7 @@ fn get_value(obj: &PyObjectRef) -> Promise { panic!("Inner error getting promise") } -fn import_promise_type(vm: &mut VirtualMachine) -> PyResult { +pub fn import_promise_type(vm: &mut VirtualMachine) -> PyResult { import( vm, PathBuf::default(), @@ -202,7 +202,7 @@ fn promise_then(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let acc_vm = AccessibleVM::from_vm(vm); - let promise = get_value(zelf); + let promise = get_promise_value(zelf); let ret_future = JsFuture::from(promise).then(move |res| { let vm = &mut acc_vm @@ -246,7 +246,7 @@ fn promise_catch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let acc_vm = AccessibleVM::from_vm(vm); - let promise = get_value(zelf); + let promise = get_promise_value(zelf); let ret_future = JsFuture::from(promise).then(move |res| match res { Ok(val) => Ok(val), diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index df63c959e6..4672f7f2d9 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -1,5 +1,6 @@ +use crate::browser_module; use crate::vm_class::{AccessibleVM, WASMVirtualMachine}; -use js_sys::{Array, ArrayBuffer, Object, Reflect, Uint8Array}; +use js_sys::{Array, ArrayBuffer, Object, Promise, Reflect, Uint8Array}; use rustpython_vm::obj::{objbytes, objtype}; use rustpython_vm::pyobject::{self, PyFuncArgs, PyObjectRef, PyResult}; use rustpython_vm::VirtualMachine; @@ -61,6 +62,12 @@ pub fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { return func; } + // the browser module might not be injected + if let Ok(promise_type) = browser_module::import_promise_type(vm) { + if objtype::isinstance(&py_obj, &promise_type) { + return browser_module::get_promise_value(&py_obj).into(); + } + } } if objtype::isinstance(&py_obj, &vm.ctx.bytes_type()) || objtype::isinstance(&py_obj, &vm.ctx.bytearray_type()) @@ -108,6 +115,12 @@ pub fn pyresult_to_jsresult(vm: &mut VirtualMachine, result: PyResult) -> Result pub fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef { if js_val.is_object() { + if let Some(promise) = js_val.dyn_ref::() { + // the browser module might not be injected + if let Ok(promise_type) = browser_module::import_promise_type(vm) { + return browser_module::PyPromise::new(promise_type, promise.clone()); + } + } if Array::is_array(&js_val) { let js_arr: Array = js_val.into(); let elems = js_arr 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 076/380] 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 f18dac7d06..642507ceca 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(); -}); +}; + +snippets.addEventListener('change', updateSnippet); -// Run once for demo -runCodeFromTextarea(); +// 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 077/380] 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 642507ceca..0735f8d227 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 b8f4e121ff..c91d8c31eb 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 078/380] 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 c91d8c31eb..7f2b7a6225 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 079/380] 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 fd613d7f7e..2fa56aaa77 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 0c7e06b53d..5c4e6c86d9 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 e3f6978385..2824040a6f 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 179304bc93cc1a114c2db89cdbff0baab4a79427 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 26 Feb 2019 23:46:53 -0600 Subject: [PATCH 080/380] Update WASM example crate/app --- wasm/example/src/main.js | 13 ++++--------- wasm/example/src/main.py | 15 ++++++++++----- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/wasm/example/src/main.js b/wasm/example/src/main.js index fcd32165e8..0ba7fa42c0 100644 --- a/wasm/example/src/main.js +++ b/wasm/example/src/main.js @@ -1,11 +1,6 @@ -import * as py from 'rustpython_wasm'; +import * as rp from 'rustpython_wasm'; import pyCode from 'raw-loader!./main.py'; -fetch('https://github-trending-api.now.sh/repositories') - .then(r => r.json()) - .then(repos => { - const result = py.pyEval(pyCode, { - vars: { repos } - }); - alert(result); - }); +const vm = rp.vmStore.get('main'); + +vm.exec(pyCode); diff --git a/wasm/example/src/main.py b/wasm/example/src/main.py index e2d590a3f3..8ba2ec00b7 100644 --- a/wasm/example/src/main.py +++ b/wasm/example/src/main.py @@ -1,8 +1,13 @@ -repos = js_vars['repos'] +from browser import fetch -star_sum = 0 +def fetch_handler(repos): + star_sum = 0 + for repo in repos: + star_sum += repo['stars'] + print(f'Average github trending star count: {star_sum / len(repos)}') -for repo in repos: - star_sum += repo['stars'] -return 'Average github trending star count: ' + str(star_sum / len(repos)) +fetch( + 'https://github-trending-api.now.sh/repositories', + response_format='json', +).then(fetch_handler) \ No newline at end of file From 15e6187583533dbf2893c2dc7390b4e753ae1f51 Mon Sep 17 00:00:00 2001 From: alexpantyukhin Date: Wed, 27 Feb 2019 11:04:16 +0400 Subject: [PATCH 081/380] 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 9aa83eba41..3bf91491d2 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 (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 9f5e48893b..018e995fe9 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 082/380] 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 018e995fe9..8c240fba37 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)), } From 4dd6592aad4e7db7310f1381fdd191809da47ef9 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Wed, 27 Feb 2019 09:45:27 +0000 Subject: [PATCH 083/380] Keep a stack of frames in the VM. --- tests/snippets/getframe.py | 2 ++ vm/src/frame.rs | 43 +++++++++++++++----------------------- vm/src/obj/objgenerator.rs | 20 +++++++++--------- vm/src/pyobject.rs | 11 +++++++--- vm/src/sysmodule.rs | 30 +++++++++++++++++++++----- vm/src/vm.rs | 30 +++++++++++++++++++------- 6 files changed, 85 insertions(+), 51 deletions(-) diff --git a/tests/snippets/getframe.py b/tests/snippets/getframe.py index 6b02e027da..cdf5c175bb 100644 --- a/tests/snippets/getframe.py +++ b/tests/snippets/getframe.py @@ -10,6 +10,8 @@ def test_function(): x = 17 assert sys._getframe().f_locals is not locals_dict assert sys._getframe().f_locals['x'] == 17 + assert sys._getframe(1).f_locals['foo'] == 'bar' + print(sys._getframe(1).f_locals) test_function() diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 81697e1f01..4cc5301d92 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -1,8 +1,8 @@ extern crate rustpython_parser; use self::rustpython_parser::ast; +use std::cell::RefCell; use std::fmt; -use std::mem; use std::path::PathBuf; use crate::builtins; @@ -49,10 +49,10 @@ enum BlockType { pub struct Frame { pub code: bytecode::CodeObject, // We need 1 stack per frame - stack: Vec, // The main data frame of the stack machine - blocks: Vec, // Block frames, for controlling loops and exceptions - pub locals: PyObjectRef, // Variables - pub lasti: usize, // index of last instruction ran + stack: RefCell>, // The main data frame of the stack machine + blocks: Vec, // Block frames, for controlling loops and exceptions + pub locals: PyObjectRef, // Variables + pub lasti: usize, // index of last instruction ran } // Running a frame can result in one of the below: @@ -79,7 +79,7 @@ impl Frame { Frame { code: objcode::get_value(&code), - stack: vec![], + stack: RefCell::new(vec![]), blocks: vec![], // save the callargs as locals // globals: locals.clone(), @@ -88,18 +88,9 @@ impl Frame { } } - pub fn run_frame_full(&mut self, vm: &mut VirtualMachine) -> PyResult { - match self.run_frame(vm)? { - ExecutionResult::Return(value) => Ok(value), - _ => panic!("Got unexpected result from function"), - } - } - - pub fn run_frame(&mut self, vm: &mut VirtualMachine) -> Result { + pub fn run(&mut self, vm: &mut VirtualMachine) -> Result { let filename = &self.code.source_path.to_string(); - let prev_frame = mem::replace(&mut vm.current_frame, Some(vm.ctx.new_frame(self.clone()))); - // This is the name of the object being run: let run_obj_name = &self.code.obj_name.to_string(); @@ -148,7 +139,6 @@ impl Frame { } }; - vm.current_frame = prev_frame; value } @@ -1082,13 +1072,13 @@ impl Frame { fn push_block(&mut self, typ: BlockType) { self.blocks.push(Block { typ, - level: self.stack.len(), + level: self.stack.borrow().len(), }); } fn pop_block(&mut self) -> Option { let block = self.blocks.pop()?; - self.stack.truncate(block.level); + self.stack.borrow_mut().truncate(block.level); Some(block) } @@ -1096,29 +1086,29 @@ impl Frame { self.blocks.last() } - pub fn push_value(&mut self, obj: PyObjectRef) { - self.stack.push(obj); + pub fn push_value(&self, obj: PyObjectRef) { + self.stack.borrow_mut().push(obj); } - fn pop_value(&mut self) -> PyObjectRef { - self.stack.pop().unwrap() + fn pop_value(&self) -> PyObjectRef { + self.stack.borrow_mut().pop().unwrap() } fn pop_multiple(&mut self, count: usize) -> Vec { let mut objs: Vec = Vec::new(); for _x in 0..count { - objs.push(self.stack.pop().unwrap()); + objs.push(self.pop_value()); } objs.reverse(); objs } fn last_value(&self) -> PyObjectRef { - self.stack.last().unwrap().clone() + self.stack.borrow().last().unwrap().clone() } fn nth_value(&self, depth: usize) -> PyObjectRef { - self.stack[self.stack.len() - depth - 1].clone() + self.stack.borrow_mut()[self.stack.borrow().len() - depth - 1].clone() } } @@ -1126,6 +1116,7 @@ impl fmt::Debug for Frame { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let stack_str = self .stack + .borrow() .iter() .map(|elem| format!("\n > {:?}", elem)) .collect::>() diff --git a/vm/src/obj/objgenerator.rs b/vm/src/obj/objgenerator.rs index cb727499ac..73cdbab68a 100644 --- a/vm/src/obj/objgenerator.rs +++ b/vm/src/obj/objgenerator.rs @@ -2,9 +2,7 @@ * The mythical generator. */ -use std::cell::RefCell; - -use crate::frame::{ExecutionResult, Frame}; +use crate::frame::ExecutionResult; use crate::pyobject::{ PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; @@ -29,11 +27,9 @@ pub fn init(context: &PyContext) { ); } -pub fn new_generator(vm: &mut VirtualMachine, frame: Frame) -> PyResult { +pub fn new_generator(vm: &mut VirtualMachine, frame: PyObjectRef) -> PyResult { Ok(PyObject::new( - PyObjectPayload::Generator { - frame: RefCell::new(frame), - }, + PyObjectPayload::Generator { frame: frame }, vm.ctx.generator_type.clone(), )) } @@ -60,9 +56,13 @@ fn generator_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn send(vm: &mut VirtualMachine, gen: &PyObjectRef, value: &PyObjectRef) -> PyResult { if let PyObjectPayload::Generator { ref frame } = gen.payload { - let mut frame_mut = frame.borrow_mut(); - frame_mut.push_value(value.clone()); - match frame_mut.run_frame(vm)? { + if let PyObjectPayload::Frame { ref frame } = frame.payload { + frame.push_value(value.clone()); + } else { + panic!("Generator frame isn't a frame."); + } + + match vm.run_frame(frame.clone())? { ExecutionResult::Yield(value) => Ok(value), ExecutionResult::Return(_value) => { // Stop iteration! diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index d29650b26f..6be9bf4573 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -591,8 +591,13 @@ impl PyContext { ) } - pub fn new_frame(&self, frame: Frame) -> PyObjectRef { - PyObject::new(PyObjectPayload::Frame { frame }, self.frame_type()) + pub fn new_frame(&self, code: PyObjectRef, scope: PyObjectRef) -> PyObjectRef { + PyObject::new( + PyObjectPayload::Frame { + frame: Frame::new(code, scope), + }, + self.frame_type(), + ) } pub fn new_property PyResult>( @@ -1246,7 +1251,7 @@ pub enum PyObjectPayload { defaults: PyObjectRef, }, Generator { - frame: RefCell, + frame: PyObjectRef, }, BoundMethod { function: PyObjectRef, diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index fbc3fc9125..5f5ca94704 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -1,5 +1,7 @@ +use crate::obj::objint; use crate::pyobject::{DictProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; +use num_traits::ToPrimitive; use std::rc::Rc; use std::{env, mem}; @@ -13,12 +15,30 @@ fn argv(ctx: &PyContext) -> PyObjectRef { ctx.new_list(argv) } -fn getframe(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { - if let Some(frame) = &vm.current_frame { - Ok(frame.clone()) - } else { - panic!("Current frame is undefined!") +fn frame_idx(vm: &mut VirtualMachine, offset: Option<&PyObjectRef>) -> Result { + if let Some(int) = offset { + if let Some(offset) = objint::get_value(&int).to_usize() { + if offset > vm.frames.len() - 1 { + return Err(vm.new_value_error("call stack is not deep enough".to_string())); + } + return Ok(offset); + } } + return Ok(0); +} + +fn getframe(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [], + optional = [(offset, Some(vm.ctx.int_type()))] + ); + + let idx = frame_idx(vm, offset)?; + let idx = vm.frames.len() - idx - 1; + let frame = &vm.frames[idx]; + Ok(frame.clone()) } fn sys_getrefcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/vm.rs b/vm/src/vm.rs index d14d92668a..46871c730a 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -12,9 +12,10 @@ use std::sync::{Mutex, MutexGuard}; use crate::builtins; use crate::bytecode; -use crate::frame::Frame; +use crate::frame::ExecutionResult; use crate::obj::objbool; use crate::obj::objcode; +use crate::obj::objframe; use crate::obj::objgenerator; use crate::obj::objiter; use crate::obj::objsequence; @@ -39,7 +40,7 @@ pub struct VirtualMachine { pub sys_module: PyObjectRef, pub stdlib_inits: HashMap, pub ctx: PyContext, - pub current_frame: Option, + pub frames: Vec, pub wasm_id: Option, } @@ -62,14 +63,29 @@ impl VirtualMachine { sys_module: sysmod, stdlib_inits, ctx, - current_frame: None, + frames: vec![], wasm_id: None, } } pub fn run_code_obj(&mut self, code: PyObjectRef, scope: PyObjectRef) -> PyResult { - let mut frame = Frame::new(code, scope); - frame.run_frame_full(self) + let frame = self.ctx.new_frame(code, scope); + self.run_frame_full(frame) + } + + pub fn run_frame_full(&mut self, frame: PyObjectRef) -> PyResult { + match self.run_frame(frame)? { + ExecutionResult::Return(value) => Ok(value), + _ => panic!("Got unexpected result from function"), + } + } + + pub fn run_frame(&mut self, frame: PyObjectRef) -> Result { + self.frames.push(frame.clone()); + let mut frame = objframe::get_value(&frame); + let result = frame.run(self); + self.frames.pop(); + result } /// Create a new python string object. @@ -312,13 +328,13 @@ impl VirtualMachine { self.fill_scope_from_args(&code_object, &scope, args, defaults)?; // Construct frame: - let mut frame = Frame::new(code.clone(), scope); + let frame = self.ctx.new_frame(code.clone(), scope); // If we have a generator, create a new generator if code_object.is_generator { objgenerator::new_generator(self, frame) } else { - frame.run_frame_full(self) + self.run_frame_full(frame) } } From 676130fc49a3510f62adc3a7a5344f1a34604d97 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Wed, 27 Feb 2019 10:44:23 +0000 Subject: [PATCH 084/380] Use refcells in frames to solve frame mutability problem. --- tests/snippets/getframe.py | 1 - vm/src/frame.rs | 99 +++++++++++++++++--------------------- vm/src/obj/objframe.rs | 6 +-- vm/src/vm.rs | 2 +- 4 files changed, 47 insertions(+), 61 deletions(-) diff --git a/tests/snippets/getframe.py b/tests/snippets/getframe.py index cdf5c175bb..d4328286aa 100644 --- a/tests/snippets/getframe.py +++ b/tests/snippets/getframe.py @@ -11,7 +11,6 @@ def test_function(): assert sys._getframe().f_locals is not locals_dict assert sys._getframe().f_locals['x'] == 17 assert sys._getframe(1).f_locals['foo'] == 'bar' - print(sys._getframe(1).f_locals) test_function() diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 4cc5301d92..ba0466e379 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -45,14 +45,13 @@ enum BlockType { }, } -#[derive(Clone)] pub struct Frame { pub code: bytecode::CodeObject, // We need 1 stack per frame stack: RefCell>, // The main data frame of the stack machine - blocks: Vec, // Block frames, for controlling loops and exceptions + blocks: RefCell>, // Block frames, for controlling loops and exceptions pub locals: PyObjectRef, // Variables - pub lasti: usize, // index of last instruction ran + pub lasti: RefCell, // index of last instruction ran } // Running a frame can result in one of the below: @@ -80,15 +79,15 @@ impl Frame { Frame { code: objcode::get_value(&code), stack: RefCell::new(vec![]), - blocks: vec![], + blocks: RefCell::new(vec![]), // save the callargs as locals // globals: locals.clone(), locals, - lasti: 0, + lasti: RefCell::new(0), } } - pub fn run(&mut self, vm: &mut VirtualMachine) -> Result { + pub fn run(&self, vm: &mut VirtualMachine) -> Result { let filename = &self.code.source_path.to_string(); // This is the name of the object being run: @@ -142,16 +141,16 @@ impl Frame { value } - pub fn fetch_instruction(&mut self) -> bytecode::Instruction { + pub fn fetch_instruction(&self) -> bytecode::Instruction { // TODO: an immutable reference is enough, we should not // clone the instruction. - let ins2 = self.code.instructions[self.lasti].clone(); - self.lasti += 1; + let ins2 = self.code.instructions[*self.lasti.borrow()].clone(); + *self.lasti.borrow_mut() += 1; ins2 } // Execute a single instruction: - fn execute_instruction(&mut self, vm: &mut VirtualMachine) -> FrameResult { + fn execute_instruction(&self, vm: &mut VirtualMachine) -> FrameResult { let instruction = self.fetch_instruction(); { trace!("======="); @@ -344,7 +343,7 @@ impl Frame { match next_obj { Some(value) => { // Set back program counter: - self.lasti -= 1; + *self.lasti.borrow_mut() -= 1; Ok(Some(ExecutionResult::Yield(value))) } None => { @@ -662,7 +661,7 @@ impl Frame { } fn get_elements( - &mut self, + &self, vm: &mut VirtualMachine, size: usize, unpack: bool, @@ -683,7 +682,7 @@ impl Frame { } fn import( - &mut self, + &self, vm: &mut VirtualMachine, module: &str, symbol: &Option, @@ -701,7 +700,7 @@ impl Frame { Ok(None) } - fn import_star(&mut self, vm: &mut VirtualMachine, module: &str) -> FrameResult { + fn import_star(&self, vm: &mut VirtualMachine, module: &str) -> FrameResult { let current_path = { let mut source_pathbuf = PathBuf::from(&self.code.source_path); source_pathbuf.pop(); @@ -719,7 +718,7 @@ impl Frame { } // Unwind all blocks: - fn unwind_blocks(&mut self, vm: &mut VirtualMachine) -> Option { + fn unwind_blocks(&self, vm: &mut VirtualMachine) -> Option { while let Some(block) = self.pop_block() { match block.typ { BlockType::Loop { .. } => {} @@ -743,9 +742,9 @@ impl Frame { None } - fn unwind_loop(&mut self, vm: &mut VirtualMachine) -> Block { + fn unwind_loop(&self, vm: &mut VirtualMachine) -> Block { loop { - let block = self.current_block().cloned().expect("not in a loop"); + let block = self.current_block().expect("not in a loop"); match block.typ { BlockType::Loop { .. } => break block, BlockType::TryExcept { .. } => { @@ -765,11 +764,7 @@ impl Frame { } } - fn unwind_exception( - &mut self, - vm: &mut VirtualMachine, - exc: PyObjectRef, - ) -> Option { + fn unwind_exception(&self, vm: &mut VirtualMachine, exc: PyObjectRef) -> Option { // unwind block stack on exception and find any handlers: while let Some(block) = self.pop_block() { match block.typ { @@ -837,13 +832,13 @@ impl Frame { vm.call_method(context_manager, "__exit__", args) } - fn store_name(&mut self, vm: &mut VirtualMachine, name: &str) -> FrameResult { + fn store_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult { let obj = self.pop_value(); vm.ctx.set_attr(&self.locals, name, obj); Ok(None) } - fn delete_name(&mut self, vm: &mut VirtualMachine, name: &str) -> FrameResult { + fn delete_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult { let locals = match self.locals.payload { PyObjectPayload::Scope { ref scope } => scope.borrow().locals.clone(), _ => panic!("We really expect our scope to be a scope!"), @@ -855,7 +850,7 @@ impl Frame { Ok(None) } - fn load_name(&mut self, vm: &mut VirtualMachine, name: &str) -> FrameResult { + fn load_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult { // Lookup name in scope and put it onto the stack! let mut scope = self.locals.clone(); loop { @@ -874,11 +869,11 @@ impl Frame { } } - fn subscript(&mut self, vm: &mut VirtualMachine, a: PyObjectRef, b: PyObjectRef) -> PyResult { + fn subscript(&self, vm: &mut VirtualMachine, a: PyObjectRef, b: PyObjectRef) -> PyResult { vm.call_method(&a, "__getitem__", vec![b]) } - fn execute_store_subscript(&mut self, vm: &mut VirtualMachine) -> FrameResult { + fn execute_store_subscript(&self, vm: &mut VirtualMachine) -> FrameResult { let idx = self.pop_value(); let obj = self.pop_value(); let value = self.pop_value(); @@ -886,21 +881,21 @@ impl Frame { Ok(None) } - fn execute_delete_subscript(&mut self, vm: &mut VirtualMachine) -> FrameResult { + fn execute_delete_subscript(&self, vm: &mut VirtualMachine) -> FrameResult { let idx = self.pop_value(); let obj = self.pop_value(); vm.call_method(&obj, "__delitem__", vec![idx])?; Ok(None) } - fn jump(&mut self, label: bytecode::Label) { + fn jump(&self, label: bytecode::Label) { let target_pc = self.code.label_map[&label]; trace!("program counter from {:?} to {:?}", self.lasti, target_pc); - self.lasti = target_pc; + *self.lasti.borrow_mut() = target_pc; } fn execute_binop( - &mut self, + &self, vm: &mut VirtualMachine, op: &bytecode::BinaryOperator, inplace: bool, @@ -943,11 +938,7 @@ impl Frame { Ok(None) } - fn execute_unop( - &mut self, - vm: &mut VirtualMachine, - op: &bytecode::UnaryOperator, - ) -> FrameResult { + fn execute_unop(&self, vm: &mut VirtualMachine, op: &bytecode::UnaryOperator) -> FrameResult { let a = self.pop_value(); let value = match *op { bytecode::UnaryOperator::Minus => vm.call_method(&a, "__neg__", vec![])?, @@ -968,7 +959,7 @@ impl Frame { // https://docs.python.org/3/reference/expressions.html#membership-test-operations fn _membership( - &mut self, + &self, vm: &mut VirtualMachine, needle: PyObjectRef, haystack: &PyObjectRef, @@ -978,12 +969,7 @@ impl Frame { // not implemented. } - fn _in( - &mut self, - vm: &mut VirtualMachine, - needle: PyObjectRef, - haystack: PyObjectRef, - ) -> PyResult { + fn _in(&self, vm: &mut VirtualMachine, needle: PyObjectRef, haystack: PyObjectRef) -> PyResult { match self._membership(vm, needle, &haystack) { Ok(found) => Ok(found), Err(_) => Err(vm.new_type_error(format!( @@ -994,7 +980,7 @@ impl Frame { } fn _not_in( - &mut self, + &self, vm: &mut VirtualMachine, needle: PyObjectRef, haystack: PyObjectRef, @@ -1020,7 +1006,7 @@ impl Frame { } fn execute_compare( - &mut self, + &self, vm: &mut VirtualMachine, op: &bytecode::ComparisonOperator, ) -> FrameResult { @@ -1043,7 +1029,7 @@ impl Frame { Ok(None) } - fn load_attr(&mut self, vm: &mut VirtualMachine, attr_name: &str) -> FrameResult { + fn load_attr(&self, vm: &mut VirtualMachine, attr_name: &str) -> FrameResult { let parent = self.pop_value(); let attr_name = vm.new_str(attr_name.to_string()); let obj = vm.get_attribute(parent, attr_name)?; @@ -1051,14 +1037,14 @@ impl Frame { Ok(None) } - fn store_attr(&mut self, vm: &mut VirtualMachine, attr_name: &str) -> FrameResult { + fn store_attr(&self, vm: &mut VirtualMachine, attr_name: &str) -> FrameResult { let parent = self.pop_value(); let value = self.pop_value(); vm.ctx.set_attr(&parent, attr_name, value); Ok(None) } - fn delete_attr(&mut self, vm: &mut VirtualMachine, attr_name: &str) -> FrameResult { + fn delete_attr(&self, vm: &mut VirtualMachine, attr_name: &str) -> FrameResult { let parent = self.pop_value(); let name = vm.ctx.new_str(attr_name.to_string()); vm.del_attr(&parent, name)?; @@ -1066,24 +1052,24 @@ impl Frame { } pub fn get_lineno(&self) -> ast::Location { - self.code.locations[self.lasti].clone() + self.code.locations[*self.lasti.borrow()].clone() } - fn push_block(&mut self, typ: BlockType) { - self.blocks.push(Block { + fn push_block(&self, typ: BlockType) { + self.blocks.borrow_mut().push(Block { typ, level: self.stack.borrow().len(), }); } - fn pop_block(&mut self) -> Option { - let block = self.blocks.pop()?; + fn pop_block(&self) -> Option { + let block = self.blocks.borrow_mut().pop()?; self.stack.borrow_mut().truncate(block.level); Some(block) } - fn current_block(&self) -> Option<&Block> { - self.blocks.last() + fn current_block(&self) -> Option { + self.blocks.borrow().last().cloned() } pub fn push_value(&self, obj: PyObjectRef) { @@ -1094,7 +1080,7 @@ impl Frame { self.stack.borrow_mut().pop().unwrap() } - fn pop_multiple(&mut self, count: usize) -> Vec { + fn pop_multiple(&self, count: usize) -> Vec { let mut objs: Vec = Vec::new(); for _x in 0..count { objs.push(self.pop_value()); @@ -1123,6 +1109,7 @@ impl fmt::Debug for Frame { .join(""); let block_str = self .blocks + .borrow() .iter() .map(|elem| format!("\n > {:?}", elem)) .collect::>() diff --git a/vm/src/obj/objframe.rs b/vm/src/obj/objframe.rs index ef9f509798..d5cc8e0c53 100644 --- a/vm/src/obj/objframe.rs +++ b/vm/src/obj/objframe.rs @@ -41,12 +41,12 @@ fn frame_flocals(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn frame_fcode(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(frame, Some(vm.ctx.frame_type()))]); - Ok(vm.ctx.new_code_object(get_value(frame).code)) + Ok(vm.ctx.new_code_object(get_value(frame).code.clone())) } -pub fn get_value(obj: &PyObjectRef) -> Frame { +pub fn get_value(obj: &PyObjectRef) -> &Frame { if let PyObjectPayload::Frame { frame } = &obj.payload { - frame.clone() + frame } else { panic!("Inner error getting int {:?}", obj); } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 46871c730a..be15f73f9a 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -82,7 +82,7 @@ impl VirtualMachine { pub fn run_frame(&mut self, frame: PyObjectRef) -> Result { self.frames.push(frame.clone()); - let mut frame = objframe::get_value(&frame); + let frame = objframe::get_value(&frame); let result = frame.run(self); self.frames.pop(); result From 2bdbc40a459990e29adbe1c9fad3041600cbb89f Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Mon, 25 Feb 2019 20:47:10 +0200 Subject: [PATCH 085/380] socket.recv read amount of bytes asked --- tests/snippets/stdlib_socket.py | 8 ++++++-- vm/src/stdlib/socket.rs | 8 ++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/snippets/stdlib_socket.py b/tests/snippets/stdlib_socket.py index 5419d802f3..0faeb8389f 100644 --- a/tests/snippets/stdlib_socket.py +++ b/tests/snippets/stdlib_socket.py @@ -12,9 +12,13 @@ message_b = b'bbbbb' connector.send(message_a) -connector.close() -recv_a = connection.recv(10) +connection.send(message_b) +recv_a = connection.recv(len(message_a)) +recv_b = connector.recv(len(message_b)) +assert recv_a == message_a +assert recv_b == message_b connection.close() +connector.close() listener.close() diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index 4eb930dcbd..d1750c2340 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -223,10 +223,10 @@ fn socket_recv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); match zelf.payload { PyObjectPayload::Socket { ref socket } => { - let mut buffer = Vec::new(); - let _temp = match socket.borrow_mut().con { - Some(ref mut v) => v.read_to_end(&mut buffer).unwrap(), - None => 0, + let mut buffer = vec![0u8; objint::get_value(bufsize).to_usize().unwrap()]; + match socket.borrow_mut().con { + Some(ref mut v) => v.read_exact(&mut buffer).unwrap(), + None => return Err(vm.new_type_error("".to_string())), }; Ok(vm.ctx.new_bytes(buffer)) } From dff6b0be362912afb90bada000b1008860f1c3d9 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Mon, 25 Feb 2019 20:56:09 +0200 Subject: [PATCH 086/380] Rename sk to socket_kind --- vm/src/stdlib/socket.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index d1750c2340..471ddc4fa4 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -87,15 +87,15 @@ impl Write for Connection { pub struct Socket { address_family: AddressFamily, - sk: SocketKind, + socket_kind: SocketKind, con: Option, } impl Socket { - fn new(address_family: AddressFamily, sk: SocketKind) -> Socket { + fn new(address_family: AddressFamily, socket_kind: SocketKind) -> Socket { Socket { address_family, - sk: sk, + socket_kind: socket_kind, con: None, } } @@ -198,7 +198,7 @@ fn socket_accept(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let socket = RefCell::new(Socket { address_family: socket.borrow().address_family.clone(), - sk: socket.borrow().sk.clone(), + socket_kind: socket.borrow().socket_kind.clone(), con: Some(Connection::TcpStream(tcp_stream)), }); @@ -258,7 +258,7 @@ fn socket_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { PyObjectPayload::Socket { ref socket } => { let mut socket = socket.borrow_mut(); match socket.address_family { - AddressFamily::AfInet => match socket.sk { + AddressFamily::AfInet => match socket.socket_kind { SocketKind::SockStream => { socket.con = None; Ok(vm.get_none()) From 716eb4afca129879ee2b245dde96b0c176973f7e Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Mon, 25 Feb 2019 21:39:01 +0200 Subject: [PATCH 087/380] Add validation to connect and bind address --- tests/snippets/stdlib_socket.py | 12 +++++++++++ vm/src/stdlib/socket.rs | 36 ++++++++++++++++++++++++--------- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/tests/snippets/stdlib_socket.py b/tests/snippets/stdlib_socket.py index 0faeb8389f..eec408eb07 100644 --- a/tests/snippets/stdlib_socket.py +++ b/tests/snippets/stdlib_socket.py @@ -1,4 +1,5 @@ import socket +from testutils import assertRaises listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM) listener.bind(("127.0.0.1", 8080)) @@ -22,3 +23,14 @@ connector.close() listener.close() +s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +with assertRaises(TypeError): + s.connect(("127.0.0.1", 8888, 8888)) + +with assertRaises(TypeError): + s.bind(("127.0.0.1", 8888, 8888)) + +with assertRaises(TypeError): + s.bind((888, 8888)) + +s.close() diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index 471ddc4fa4..2da8762681 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -130,11 +130,7 @@ fn socket_connect(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, None), (address, Some(vm.ctx.tuple_type()))] ); - let elements = get_elements(address); - let host = objstr::get_value(&elements[0]); - let port = objint::get_value(&elements[1]); - - let address_string = format!("{}:{}", host, port.to_string()); + let address_string = get_address_string(vm, address)?; match zelf.payload { PyObjectPayload::Socket { ref socket } => { @@ -157,11 +153,7 @@ fn socket_bind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, None), (address, Some(vm.ctx.tuple_type()))] ); - let elements = get_elements(address); - let host = objstr::get_value(&elements[0]); - let port = objint::get_value(&elements[1]); - - let address_string = format!("{}:{}", host, port.to_string()); + let address_string = get_address_string(vm, address)?; match zelf.payload { PyObjectPayload::Socket { ref socket } => { @@ -177,6 +169,30 @@ fn socket_bind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } +fn get_address_string( + vm: &mut VirtualMachine, + address: &PyObjectRef, +) -> Result { + let args = PyFuncArgs { + args: get_elements(address).to_vec(), + kwargs: vec![], + }; + arg_check!( + vm, + args, + required = [ + (host, Some(vm.ctx.str_type())), + (port, Some(vm.ctx.int_type())) + ] + ); + + Ok(format!( + "{}:{}", + objstr::get_value(host), + objint::get_value(port).to_string() + )) +} + fn socket_listen(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.get_none()) } From 4191ba67b3be05a3b876e28133c7fddfc9a14267 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Wed, 27 Feb 2019 20:19:10 +0200 Subject: [PATCH 088/380] Copy instead of clone --- vm/src/stdlib/socket.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index 2da8762681..8c82368c73 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -213,8 +213,8 @@ fn socket_accept(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; let socket = RefCell::new(Socket { - address_family: socket.borrow().address_family.clone(), - socket_kind: socket.borrow().socket_kind.clone(), + address_family: socket.borrow().address_family, + socket_kind: socket.borrow().socket_kind, con: Some(Connection::TcpStream(tcp_stream)), }); From dc0bd73d128d56cde1f591fe6b4ff2cee0873e89 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Wed, 27 Feb 2019 20:22:53 +0200 Subject: [PATCH 089/380] Fix compiler warnings --- vm/src/stdlib/socket.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index 8c82368c73..cd4bbdf706 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -2,7 +2,7 @@ use std::cell::RefCell; use std::io; use std::io::Read; use std::io::Write; -use std::net::{SocketAddr, TcpListener, TcpStream, UdpSocket}; +use std::net::{SocketAddr, TcpListener, TcpStream}; use crate::obj::objbytes; use crate::obj::objint; @@ -52,7 +52,7 @@ impl SocketKind { enum Connection { TcpListener(TcpListener), TcpStream(TcpStream), - UdpSocket(UdpSocket), + // UdpSocket(UdpSocket), } impl Connection { @@ -194,6 +194,11 @@ fn get_address_string( } fn socket_listen(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(_zelf, None), (_num, Some(vm.ctx.int_type()))] + ); Ok(vm.get_none()) } From 2cc9a606da02c2327f9c074ac644aee2d54d4c10 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Wed, 27 Feb 2019 18:23:39 +0000 Subject: [PATCH 090/380] Avoid double borrow in Frame.nth_value. --- vm/src/frame.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index ba0466e379..b6253b6c6a 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -1094,7 +1094,8 @@ impl Frame { } fn nth_value(&self, depth: usize) -> PyObjectRef { - self.stack.borrow_mut()[self.stack.borrow().len() - depth - 1].clone() + let stack = self.stack.borrow_mut(); + stack[stack.len() - depth - 1].clone() } } From 8087ccf274dd972f686952904d09783f0fd143bf Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Wed, 27 Feb 2019 20:24:58 +0200 Subject: [PATCH 091/380] Add socket.getsockname --- tests/snippets/stdlib_socket.py | 5 +++-- vm/src/stdlib/socket.rs | 35 +++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/tests/snippets/stdlib_socket.py b/tests/snippets/stdlib_socket.py index eec408eb07..e0ea6d1682 100644 --- a/tests/snippets/stdlib_socket.py +++ b/tests/snippets/stdlib_socket.py @@ -1,12 +1,13 @@ import socket from testutils import assertRaises + listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM) -listener.bind(("127.0.0.1", 8080)) +listener.bind(("127.0.0.1", 0)) listener.listen(1) connector = socket.socket(socket.AF_INET, socket.SOCK_STREAM) -connector.connect(("127.0.0.1", 8080)) +connector.connect(("127.0.0.1", listener.getsockname()[1])) connection = listener.accept()[0] message_a = b'aaaa' diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index cd4bbdf706..d3ee6a1316 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -62,6 +62,13 @@ impl Connection { _ => Err(io::Error::new(io::ErrorKind::Other, "oh no!")), } } + + fn local_addr(&self) -> io::Result { + match self { + Connection::TcpListener(con) => con.local_addr(), + _ => Err(io::Error::new(io::ErrorKind::Other, "oh no!")), + } + } } impl Read for Connection { @@ -293,6 +300,33 @@ fn socket_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } +fn socket_getsockname(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, None)]); + match zelf.payload { + PyObjectPayload::Socket { ref socket } => { + let addr = match socket.borrow_mut().con { + Some(ref mut v) => v.local_addr(), + None => return Err(vm.new_type_error("".to_string())), + }; + + match addr { + Ok(addr) => { + let port = vm.ctx.new_int(addr.port()); + let ip = vm.ctx.new_str(addr.ip().to_string()); + let elements = RefCell::new(vec![ip, port]); + + Ok(PyObject::new( + PyObjectPayload::Sequence { elements }, + vm.ctx.tuple_type(), + )) + } + _ => return Err(vm.new_type_error("".to_string())), + } + } + _ => Err(vm.new_type_error("".to_string())), + } +} + pub fn mk_module(ctx: &PyContext) -> PyObjectRef { let py_mod = ctx.new_module(&"socket".to_string(), ctx.new_scope(None)); @@ -318,6 +352,7 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&socket, "accept", ctx.new_rustfunc(socket_accept)); ctx.set_attr(&socket, "listen", ctx.new_rustfunc(socket_listen)); ctx.set_attr(&socket, "close", ctx.new_rustfunc(socket_close)); + ctx.set_attr(&socket, "getsockname", ctx.new_rustfunc(socket_getsockname)); socket }; ctx.set_attr(&py_mod, "socket", socket.clone()); From d806a19c23c2447bed111d211f6e1e993ed00c05 Mon Sep 17 00:00:00 2001 From: alexpantyukhin Date: Wed, 27 Feb 2019 11:55:33 +0400 Subject: [PATCH 092/380] add kwarg handling for int --- tests/snippets/ints.py | 4 ++++ vm/src/obj/objint.rs | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/snippets/ints.py b/tests/snippets/ints.py index 9aa83eba41..5b5c8c89cb 100644 --- a/tests/snippets/ints.py +++ b/tests/snippets/ints.py @@ -15,6 +15,10 @@ assert 1 >= 1.0 assert 1 <= 1.0 +# check for argument handling + +assert int("101", base=2) == 5 + # magic methods should only be implemented for other ints assert (1).__eq__(1) == True diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 9f5e48893b..8a88cf893b 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -48,8 +48,10 @@ fn int_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { return Err(vm.new_type_error(format!("{:?} is not a subtype of int", cls))); } - // TODO: extract kwargs: - let base = 10; + let base = match args.get_optional_kwarg("base") { + Some(argument) => get_value(&argument).to_u32().unwrap(), + None => 10 + }; let val = match val_option { Some(val) => to_int(vm, val, base)?, None => Zero::zero(), From 7e2c702c5ea3f2465b1b6c56827b8777e7688b52 Mon Sep 17 00:00:00 2001 From: apantykhin Date: Wed, 27 Feb 2019 21:22:24 +0000 Subject: [PATCH 093/380] fix fmt --- vm/src/obj/objint.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 8a88cf893b..19919ff011 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -50,7 +50,7 @@ fn int_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let base = match args.get_optional_kwarg("base") { Some(argument) => get_value(&argument).to_u32().unwrap(), - None => 10 + None => 10, }; let val = match val_option { Some(val) => to_int(vm, val, base)?, From ea93fb871c2c0c10207373abbde610176d742ddb Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Wed, 27 Feb 2019 17:38:24 -0600 Subject: [PATCH 094/380] Add alert, confirm, and prompt to the browser module --- wasm/lib/src/browser_module.rs | 48 ++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index e3f6978385..64c9cba3b6 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -180,6 +180,51 @@ fn browser_cancel_animation_frame(vm: &mut VirtualMachine, args: PyFuncArgs) -> Ok(vm.get_none()) } +fn browser_alert(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(message, Some(vm.ctx.str_type()))]); + + window() + .alert_with_message(&objstr::get_value(message)) + .expect("alert() not to fail"); + + Ok(vm.get_none()) +} + +fn browser_confirm(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(message, Some(vm.ctx.str_type()))]); + + let result = window() + .confirm_with_message(&objstr::get_value(message)) + .expect("confirm() not to fail"); + + Ok(vm.new_bool(result)) +} + +fn browser_prompt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(message, Some(vm.ctx.str_type()))], + optional = [(default, Some(vm.ctx.str_type()))] + ); + + let result = if let Some(default) = default { + window().prompt_with_message_and_default( + &objstr::get_value(message), + &objstr::get_value(default), + ) + } else { + window().prompt_with_message(&objstr::get_value(message)) + }; + + let result = match result.expect("prompt() not to fail") { + Some(result) => vm.new_str(result), + None => vm.get_none(), + }; + + Ok(result) +} + const BROWSER_NAME: &str = "browser"; pub fn mk_module(ctx: &PyContext) -> PyObjectRef { @@ -187,6 +232,9 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { "fetch" => ctx.new_rustfunc(browser_fetch), "request_animation_frame" => ctx.new_rustfunc(browser_request_animation_frame), "cancel_animation_frame" => ctx.new_rustfunc(browser_cancel_animation_frame), + "alert" => ctx.new_rustfunc(browser_alert), + "confirm" => ctx.new_rustfunc(browser_confirm), + "prompt" => ctx.new_rustfunc(browser_prompt), }) } From f6196126e4421859bf06297bc1259213fe108122 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Wed, 27 Feb 2019 19:25:06 -0600 Subject: [PATCH 095/380] Add an `injectModule` method to WASMVM --- wasm/lib/src/vm_class.rs | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index a61b36b921..d4f2c51974 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -1,10 +1,10 @@ use crate::browser_module::setup_browser_module; use crate::convert; use crate::wasm_builtins; -use js_sys::{SyntaxError, TypeError}; +use js_sys::{Object, SyntaxError, TypeError}; use rustpython_vm::{ compile, - pyobject::{PyFuncArgs, PyObjectRef, PyResult}, + pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult}, VirtualMachine, }; use std::cell::RefCell; @@ -281,6 +281,32 @@ impl WASMVirtualMachine { )? } + #[wasm_bindgen(js_name = injectModule)] + pub fn inject_module(&self, name: String, module: Object) -> Result<(), JsValue> { + self.with(|StoredVirtualMachine { ref mut vm, .. }| { + let mut module_items: HashMap = HashMap::new(); + for entry in convert::object_entries(&module) { + let (key, value) = entry?; + let key = Object::from(key).to_string(); + module_items.insert(key.into(), convert::js_to_py(vm, value)); + } + + let mod_name = name.clone(); + + let stdlib_init_fn = move |ctx: &PyContext| { + let py_mod = ctx.new_module(&name, ctx.new_scope(None)); + for (key, value) in module_items.clone() { + ctx.set_attr(&py_mod, &key, value); + } + py_mod + }; + + vm.stdlib_inits.insert(mod_name, Box::new(stdlib_init_fn)); + + Ok(()) + })? + } + fn run(&self, mut source: String, mode: compile::Mode) -> Result { self.assert_valid()?; self.with_unchecked( From 7f75e3ee8a51ab4955c7cd875be5b412f73de243 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Wed, 27 Feb 2019 19:53:20 -0800 Subject: [PATCH 096/380] Significant improvements to new function definition style - PyRef type for accepting references to payloads. - Args type for consuming remaining positional args (mirrors python `*args`). - KwArgs type for consuming remaining keyword args (mirrors python `*kwargs`). - OptArg type for consuming remaining keyword args (no python code equivalent, only possible in native functions like in cpython). - PyIterable for accepting an iterator over a sequence of Ts. - Arity checking (but TypeError messages need work) --- vm/src/function.rs | 41 +++++ vm/src/lib.rs | 1 + vm/src/obj/objint.rs | 22 ++- vm/src/obj/objrange.rs | 33 ++-- vm/src/obj/objstr.rs | 23 ++- vm/src/pyobject.rs | 384 ++++++++++++++++++++++++++--------------- 6 files changed, 332 insertions(+), 172 deletions(-) create mode 100644 vm/src/function.rs diff --git a/vm/src/function.rs b/vm/src/function.rs new file mode 100644 index 0000000000..434f995bb5 --- /dev/null +++ b/vm/src/function.rs @@ -0,0 +1,41 @@ +use std::marker::PhantomData; +use std::ops::Deref; + +use crate::obj::objtype; +use crate::pyobject::{PyObjectPayload2, PyObjectRef, PyResult, TryFromObject}; +use crate::vm::VirtualMachine; + +// TODO: Move PyFuncArgs, FromArgs, etc. here + +pub struct PyRef { + // invariant: this obj must always have payload of type T + obj: PyObjectRef, + _payload: PhantomData, +} + +impl Deref for PyRef +where + T: PyObjectPayload2, +{ + type Target = T; + + fn deref(&self) -> &T { + self.obj.payload().expect("unexpected payload for type") + } +} + +impl TryFromObject for PyRef +where + T: PyObjectPayload2, +{ + fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { + if objtype::isinstance(&obj, &T::required_type(&vm.ctx)) { + Ok(PyRef { + obj, + _payload: PhantomData, + }) + } else { + Err(vm.new_type_error("wrong type".to_string())) // TODO: better message + } + } +} diff --git a/vm/src/lib.rs b/vm/src/lib.rs index f785e294d7..02fec6d3fa 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -38,6 +38,7 @@ pub mod eval; mod exceptions; pub mod format; mod frame; +pub mod function; pub mod import; pub mod obj; pub mod pyobject; diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 9f5e48893b..5f644750aa 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -4,7 +4,7 @@ use super::objtype; use crate::format::FormatSpec; use crate::pyobject::{ FromPyObjectRef, IntoPyObject, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, - PyResult, TypeProtocol, + PyResult, TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; use num_bigint::{BigInt, ToBigInt}; @@ -31,6 +31,26 @@ impl IntoPyObject for usize { } } +impl TryFromObject for usize { + fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { + // FIXME: don't use get_value + match get_value(&obj).to_usize() { + Some(value) => Ok(value), + None => Err(vm.new_overflow_error("Int value cannot fit into Rust usize".to_string())), + } + } +} + +impl TryFromObject for isize { + fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { + // FIXME: don't use get_value + match get_value(&obj).to_isize() { + Some(value) => Ok(value), + None => Err(vm.new_overflow_error("Int value cannot fit into Rust isize".to_string())), + } + } +} + fn int_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(int, Some(vm.ctx.int_type()))]); let v = get_value(int); diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index d80b6aa015..daa5aab52f 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -4,8 +4,7 @@ use std::ops::Mul; use super::objint; use super::objtype; use crate::pyobject::{ - FromPyObject, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, - TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use num_bigint::{BigInt, Sign}; @@ -21,18 +20,6 @@ pub struct RangeType { pub step: BigInt, } -type PyRange = RangeType; - -impl FromPyObject for PyRange { - fn typ(ctx: &PyContext) -> Option { - Some(ctx.range_type()) - } - - fn from_pyobject(obj: PyObjectRef) -> PyResult { - Ok(get_value(&obj)) - } -} - impl RangeType { #[inline] pub fn try_len(&self) -> Option { @@ -360,12 +347,22 @@ fn range_bool(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bool(len > 0)) } -fn range_contains(vm: &mut VirtualMachine, zelf: PyRange, needle: PyObjectRef) -> bool { - if objtype::isinstance(&needle, &vm.ctx.int_type()) { - zelf.contains(&objint::get_value(&needle)) +fn range_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.range_type())), (needle, None)] + ); + + let range = get_value(zelf); + + let result = if objtype::isinstance(needle, &vm.ctx.int_type()) { + range.contains(&objint::get_value(needle)) } else { false - } + }; + + Ok(vm.ctx.new_bool(result)) } fn range_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 580ab2b801..0ddb455f04 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -3,7 +3,7 @@ use super::objsequence::PySliceableSequence; use super::objtype; use crate::format::{FormatParseError, FormatPart, FormatString}; use crate::pyobject::{ - FromPyObject, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use num_traits::ToPrimitive; @@ -16,16 +16,6 @@ extern crate unicode_segmentation; use self::unicode_segmentation::UnicodeSegmentation; -impl FromPyObject for String { - fn typ(ctx: &PyContext) -> Option { - Some(ctx.str_type()) - } - - fn from_pyobject(obj: PyObjectRef) -> PyResult { - Ok(get_value(&obj)) - } -} - pub fn init(context: &PyContext) { let str_type = &context.str_type; context.set_attr(&str_type, "__add__", context.new_rustfunc(str_add)); @@ -477,8 +467,15 @@ fn str_rstrip(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_str(value)) } -fn str_endswith(_vm: &mut VirtualMachine, zelf: String, suffix: String) -> bool { - zelf.ends_with(&suffix) +fn str_endswith(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(s, Some(vm.ctx.str_type())), (pat, Some(vm.ctx.str_type()))] + ); + let value = get_value(&s); + let pat = get_value(&pat); + Ok(vm.ctx.new_bool(value.ends_with(pat.as_str()))) } fn str_isidentifier(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 54201d643c..0b878c7150 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1,3 +1,9 @@ +use std::cell::{Cell, RefCell}; +use std::collections::HashMap; +use std::fmt; +use std::iter; +use std::rc::{Rc, Weak}; + use crate::bytecode; use crate::exceptions; use crate::frame::Frame; @@ -35,10 +41,6 @@ use num_bigint::BigInt; use num_bigint::ToBigInt; use num_complex::Complex64; use num_traits::{One, Zero}; -use std::cell::{Cell, RefCell}; -use std::collections::HashMap; -use std::fmt; -use std::rc::{Rc, Weak}; /* Python objects and references. @@ -575,7 +577,7 @@ impl PyContext { { PyObject::new( PyObjectPayload::RustFunction { - function: factory.create(self), + function: factory.create(), }, self.builtin_function_or_method_type(), ) @@ -957,24 +959,191 @@ impl PyFuncArgs { None => Ok(None), } } + + fn into_iter(self) -> impl Iterator { + self.args.into_iter().map(PyArg::Positional).chain( + self.kwargs + .into_iter() + .map(|(name, value)| PyArg::Keyword(name, value)), + ) + } + + fn bind(self, vm: &mut VirtualMachine) -> PyResult { + let mut args = self.into_iter().peekable(); + let bound = T::from_args(vm, &mut args)?; + + if args.next().is_none() { + Ok(bound) + } else { + Err(vm.new_type_error("too many args".to_string())) // TODO: improve error message + } + } } -pub trait FromPyObject: Sized { - fn typ(ctx: &PyContext) -> Option; +pub trait FromArgs: Sized { + fn from_args(vm: &mut VirtualMachine, args: &mut iter::Peekable) -> PyResult + where + I: Iterator; +} - fn from_pyobject(obj: PyObjectRef) -> PyResult; +pub struct PyIterable { + obj: PyObjectRef, + method: PyObjectRef, + _item: std::marker::PhantomData, } -impl FromPyObject for PyObjectRef { - fn typ(_ctx: &PyContext) -> Option { - None +impl PyIterable { + pub fn iter<'a>(&self, vm: &'a mut VirtualMachine) -> PyResult> { + let iter_obj = vm.invoke( + self.method.clone(), + PyFuncArgs { + args: vec![], + kwargs: vec![], + }, + )?; + + Ok(PyIterator { + vm, + obj: iter_obj, + _item: std::marker::PhantomData, + }) + } +} + +pub struct PyIterator<'a, T> { + vm: &'a mut VirtualMachine, + obj: PyObjectRef, + _item: std::marker::PhantomData, +} + +impl<'a, T> Iterator for PyIterator<'a, T> +where + T: TryFromObject, +{ + type Item = PyResult; + + fn next(&mut self) -> Option { + match self.vm.call_method(&self.obj, "__next__", vec![]) { + Ok(value) => Some(T::try_from_object(self.vm, value)), + Err(ref err) if objtype::isinstance(err, &self.vm.ctx.exceptions.stop_iteration) => { + None + } + Err(err) => Some(Err(err)), + } + } +} + +impl TryFromObject for PyIterable +where + T: TryFromObject, +{ + fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { + Ok(PyIterable { + obj: obj.clone(), + method: vm.get_method(obj, "__iter__")?, + _item: std::marker::PhantomData, + }) } +} - fn from_pyobject(obj: PyObjectRef) -> PyResult { +impl TryFromObject for PyObjectRef { + fn try_from_object(_vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { Ok(obj) } } +pub struct KwArgs(HashMap); + +impl FromArgs for KwArgs +where + T: TryFromObject, +{ + fn from_args(vm: &mut VirtualMachine, args: &mut iter::Peekable) -> PyResult + where + I: Iterator, + { + let mut kwargs = HashMap::new(); + while let Some(PyArg::Keyword(name, value)) = args.next() { + kwargs.insert(name, T::try_from_object(vm, value)?); + } + Ok(KwArgs(kwargs)) + } +} + +pub struct Args(Vec); + +impl FromArgs for Args +where + T: TryFromObject, +{ + fn from_args(vm: &mut VirtualMachine, args: &mut iter::Peekable) -> PyResult + where + I: Iterator, + { + let mut varargs = Vec::new(); + while let Some(PyArg::Positional(value)) = args.next() { + varargs.push(T::try_from_object(vm, value)?); + } + Ok(Args(varargs)) + } +} + +impl FromArgs for T +where + T: TryFromObject, +{ + fn from_args(vm: &mut VirtualMachine, args: &mut iter::Peekable) -> PyResult + where + I: Iterator, + { + if let Some(PyArg::Positional(value)) = args.next() { + Ok(T::try_from_object(vm, value)?) + } else { + Err(vm.new_type_error("not enough args".to_string())) // TODO: improve error message + } + } +} + +pub struct OptArg(Option); + +impl std::ops::Deref for OptArg { + type Target = Option; + + fn deref(&self) -> &Option { + &self.0 + } +} + +impl FromArgs for OptArg +where + T: TryFromObject, +{ + fn from_args(vm: &mut VirtualMachine, args: &mut iter::Peekable) -> PyResult + where + I: Iterator, + { + Ok(OptArg(if let Some(PyArg::Positional(_)) = args.peek() { + let value = if let Some(PyArg::Positional(value)) = args.next() { + value + } else { + unreachable!() + }; + Some(T::try_from_object(vm, value)?) + } else { + None + })) + } +} + +pub enum PyArg { + Positional(PyObjectRef), + Keyword(String, PyObjectRef), +} + +pub trait TryFromObject: Sized { + fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult; +} + pub trait IntoPyObject { fn into_pyobject(self, ctx: &PyContext) -> PyResult; } @@ -985,30 +1154,46 @@ impl IntoPyObject for PyObjectRef { } } -impl IntoPyObject for PyResult { - fn into_pyobject(self, _ctx: &PyContext) -> PyResult { - self +impl IntoPyObject for PyResult +where + T: IntoPyObject, +{ + fn into_pyobject(self, ctx: &PyContext) -> PyResult { + self.and_then(|res| T::into_pyobject(res, ctx)) } } -pub trait FromPyFuncArgs: Sized { - fn required_params(ctx: &PyContext) -> Vec; +impl IntoPyObject for () { + fn into_pyobject(self, ctx: &PyContext) -> PyResult { + Ok(ctx.none()) + } +} - fn from_py_func_args(args: &mut PyFuncArgs) -> PyResult; +impl IntoPyObject for T +where + T: PyObjectPayload2 + Sized, +{ + fn into_pyobject(self, ctx: &PyContext) -> PyResult { + Ok(PyObject::new( + PyObjectPayload::AnyRustValue { + value: Box::new(self), + }, + T::required_type(ctx), + )) + } } macro_rules! tuple_from_py_func_args { ($($T:ident),+) => { - impl<$($T),+> FromPyFuncArgs for ($($T,)+) + impl<$($T),+> FromArgs for ($($T,)+) where - $($T: FromPyFuncArgs),+ + $($T: FromArgs),+ { - fn required_params(ctx: &PyContext) -> Vec { - vec![$($T::required_params(ctx),)+].into_iter().flatten().collect() - } - - fn from_py_func_args(args: &mut PyFuncArgs) -> PyResult { - Ok(($($T::from_py_func_args(args)?,)+)) + fn from_args(vm: &mut VirtualMachine, args: &mut iter::Peekable) -> PyResult + where + I: Iterator + { + Ok(($($T::from_args(vm, args)?,)+)) } } }; @@ -1020,141 +1205,46 @@ tuple_from_py_func_args!(A, B, C); tuple_from_py_func_args!(A, B, C, D); tuple_from_py_func_args!(A, B, C, D, E); -impl FromPyFuncArgs for T -where - T: FromPyObject, -{ - fn required_params(ctx: &PyContext) -> Vec { - vec![Parameter { - kind: PositionalOnly, - typ: T::typ(ctx), - }] - } - - fn from_py_func_args(args: &mut PyFuncArgs) -> PyResult { - Self::from_pyobject(args.shift()) - } -} - pub type PyNativeFunc = Box PyResult>; pub trait PyNativeFuncFactory { - fn create(self, ctx: &PyContext) -> PyNativeFunc; + fn create(self) -> PyNativeFunc; } impl PyNativeFuncFactory for F where F: Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult + 'static, { - fn create(self, _ctx: &PyContext) -> PyNativeFunc { + fn create(self) -> PyNativeFunc { Box::new(self) } } -macro_rules! tuple_py_native_func_factory { - ($($T:ident),+) => { +macro_rules! py_native_func_factory_tuple { + ($(($n:tt, $T:ident)),+) => { impl PyNativeFuncFactory<($($T,)+), R> for F where - F: Fn(&mut VirtualMachine, $($T),+) -> R + 'static, - $($T: FromPyFuncArgs,)+ + F: Fn($($T,)+ &mut VirtualMachine) -> R + 'static, + $($T: FromArgs,)+ + ($($T,)+): FromArgs, R: IntoPyObject, { - fn create(self, ctx: &PyContext) -> PyNativeFunc { - let parameters = vec![$($T::required_params(ctx)),+] - .into_iter() - .flatten() - .collect(); - let signature = Signature::new(parameters); - - Box::new(move |vm, mut args| { - signature.check(vm, &mut args)?; + fn create(self) -> PyNativeFunc { + Box::new(move |vm, args| { + let ($($n,)+) = args.bind::<($($T,)+)>(vm)?; - (self)(vm, $($T::from_py_func_args(&mut args)?,)+) - .into_pyobject(&vm.ctx) + (self)($($n,)+ vm).into_pyobject(&vm.ctx) }) } } }; } -tuple_py_native_func_factory!(A); -tuple_py_native_func_factory!(A, B); -tuple_py_native_func_factory!(A, B, C); -tuple_py_native_func_factory!(A, B, C, D); -tuple_py_native_func_factory!(A, B, C, D, E); - -#[derive(Debug)] -pub struct Signature { - positional_params: Vec, - keyword_params: HashMap, -} - -impl Signature { - fn new(params: Vec) -> Self { - let mut positional_params = Vec::new(); - let mut keyword_params = HashMap::new(); - for param in params { - match param.kind { - PositionalOnly => { - positional_params.push(param); - } - KeywordOnly { ref name } => { - keyword_params.insert(name.clone(), param); - } - } - } - - Self { - positional_params, - keyword_params, - } - } - - fn arg_type(&self, pos: usize) -> Option<&PyObjectRef> { - self.positional_params[pos].typ.as_ref() - } - - #[allow(unused)] - fn kwarg_type(&self, name: &str) -> Option<&PyObjectRef> { - self.keyword_params[name].typ.as_ref() - } - - fn check(&self, vm: &mut VirtualMachine, args: &PyFuncArgs) -> PyResult<()> { - // TODO: check arity - - for (pos, arg) in args.args.iter().enumerate() { - if let Some(expected_type) = self.arg_type(pos) { - 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, - pos + 1, - actual_type - ))); - } - } - } - - Ok(()) - } -} - -#[derive(Debug)] -pub struct Parameter { - typ: Option, - kind: ParameterKind, -} - -#[derive(Debug)] -pub enum ParameterKind { - PositionalOnly, - KeywordOnly { name: String }, -} - -use self::ParameterKind::*; +py_native_func_factory_tuple!((a, A)); +py_native_func_factory_tuple!((a, A), (b, B)); +py_native_func_factory_tuple!((a, A), (b, B), (c, C)); +py_native_func_factory_tuple!((a, A), (b, B), (c, C), (d, D)); +py_native_func_factory_tuple!((a, A), (b, B), (c, C), (d, D), (e, E)); /// Rather than determining the type of a python object, this enum is more /// a holder for the rust payload of a python object. It is more a carrier @@ -1182,9 +1272,6 @@ pub enum PyObjectPayload { Dict { elements: RefCell, }, - Set { - elements: RefCell>, - }, Iterator { position: Cell, iterated_obj: PyObjectRef, @@ -1247,6 +1334,9 @@ pub enum PyObjectPayload { dict: RefCell, mro: Vec, }, + Set { + elements: RefCell>, + }, WeakRef { referent: PyObjectWeakRef, }, @@ -1322,6 +1412,20 @@ impl PyObject { pub fn into_ref(self) -> PyObjectRef { Rc::new(self) } + + pub fn payload(&self) -> Option<&T> { + if let PyObjectPayload::AnyRustValue { ref value } = self.payload { + value.downcast_ref() + } else { + None + } + } +} + +// The intention is for this to replace `PyObjectPayload` once everything is +// converted to use `PyObjectPayload::AnyRustvalue`. +pub trait PyObjectPayload2: std::any::Any + fmt::Debug { + fn required_type(ctx: &PyContext) -> PyObjectRef; } #[cfg(test)] From 713edc57ee799bc335167d78e19e45e560315703 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Wed, 27 Feb 2019 19:53:31 -0800 Subject: [PATCH 097/380] Convert some objstring methods to new style. --- vm/src/obj/objbool.rs | 3 +- vm/src/obj/objstr.rs | 148 +++++++++++++++++++++++------------------- vm/src/pyobject.rs | 11 ++-- vm/src/stdlib/json.rs | 5 +- 4 files changed, 93 insertions(+), 74 deletions(-) diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index cf16e1f5f4..02ae3cc2f2 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -17,7 +17,8 @@ pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result value != 0.0, PyObjectPayload::Sequence { ref elements } => !elements.borrow().is_empty(), PyObjectPayload::Dict { ref elements } => !elements.borrow().is_empty(), - PyObjectPayload::String { ref value } => !value.is_empty(), + // FIXME + //PyObjectPayload::String { ref value } => !value.is_empty(), PyObjectPayload::None { .. } => false, _ => { if let Ok(f) = vm.get_method(obj.clone(), "__bool__") { diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 0ddb455f04..b24f389b36 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -2,8 +2,10 @@ use super::objint; use super::objsequence::PySliceableSequence; use super::objtype; use crate::format::{FormatParseError, FormatPart, FormatString}; +use crate::function::PyRef; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + OptArg, PyContext, PyFuncArgs, PyIterable, PyObjectPayload, PyObjectPayload2, PyObjectRef, + PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use num_traits::ToPrimitive; @@ -16,6 +18,74 @@ extern crate unicode_segmentation; use self::unicode_segmentation::UnicodeSegmentation; +#[derive(Clone, Debug)] +pub struct PyString { + // TODO: shouldn't be public + pub value: String, +} + +impl PyString { + pub fn endswith( + zelf: PyRef, + suffix: PyRef, + start: OptArg, + end: OptArg, + _vm: &mut VirtualMachine, + ) -> bool { + let start = start.unwrap_or(0); + let end = end.unwrap_or(zelf.value.len()); + zelf.value[start..end].ends_with(&suffix.value) + } + + pub fn startswith( + zelf: PyRef, + prefix: PyRef, + start: OptArg, + end: OptArg, + _vm: &mut VirtualMachine, + ) -> bool { + let start = start.unwrap_or(0); + let end = end.unwrap_or(zelf.value.len()); + zelf.value[start..end].starts_with(&prefix.value) + } + + fn upper(zelf: PyRef, _vm: &mut VirtualMachine) -> PyString { + PyString { + value: zelf.value.to_uppercase(), + } + } + + fn lower(zelf: PyRef, _vm: &mut VirtualMachine) -> PyString { + PyString { + value: zelf.value.to_lowercase(), + } + } + + fn join( + zelf: PyRef, + iterable: PyIterable>, + vm: &mut VirtualMachine, + ) -> PyResult { + let mut joined = String::new(); + + for (idx, elem) in iterable.iter(vm)?.enumerate() { + let elem = elem?; + if idx != 0 { + joined.push_str(&zelf.value); + } + joined.push_str(&elem.value) + } + + Ok(PyString { value: joined }) + } +} + +impl PyObjectPayload2 for PyString { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.str_type() + } +} + pub fn init(context: &PyContext) { let str_type = &context.str_type; context.set_attr(&str_type, "__add__", context.new_rustfunc(str_add)); @@ -37,9 +107,9 @@ pub fn init(context: &PyContext) { context.set_attr(&str_type, "__str__", context.new_rustfunc(str_str)); context.set_attr(&str_type, "__repr__", context.new_rustfunc(str_repr)); context.set_attr(&str_type, "format", context.new_rustfunc(str_format)); - context.set_attr(&str_type, "lower", context.new_rustfunc(str_lower)); + context.set_attr(&str_type, "lower", context.new_rustfunc(PyString::lower)); context.set_attr(&str_type, "casefold", context.new_rustfunc(str_casefold)); - context.set_attr(&str_type, "upper", context.new_rustfunc(str_upper)); + context.set_attr(&str_type, "upper", context.new_rustfunc(PyString::upper)); context.set_attr( &str_type, "capitalize", @@ -50,11 +120,15 @@ pub fn init(context: &PyContext) { context.set_attr(&str_type, "strip", context.new_rustfunc(str_strip)); context.set_attr(&str_type, "lstrip", context.new_rustfunc(str_lstrip)); context.set_attr(&str_type, "rstrip", context.new_rustfunc(str_rstrip)); - context.set_attr(&str_type, "endswith", context.new_rustfunc(str_endswith)); + context.set_attr( + &str_type, + "endswith", + context.new_rustfunc(PyString::endswith), + ); context.set_attr( &str_type, "startswith", - context.new_rustfunc(str_startswith), + context.new_rustfunc(PyString::startswith), ); context.set_attr(&str_type, "isalnum", context.new_rustfunc(str_isalnum)); context.set_attr(&str_type, "isnumeric", context.new_rustfunc(str_isnumeric)); @@ -74,7 +148,7 @@ pub fn init(context: &PyContext) { "splitlines", context.new_rustfunc(str_splitlines), ); - context.set_attr(&str_type, "join", context.new_rustfunc(str_join)); + context.set_attr(&str_type, "join", context.new_rustfunc(PyString::join)); context.set_attr(&str_type, "find", context.new_rustfunc(str_find)); context.set_attr(&str_type, "rfind", context.new_rustfunc(str_rfind)); context.set_attr(&str_type, "index", context.new_rustfunc(str_index)); @@ -103,19 +177,11 @@ pub fn init(context: &PyContext) { } pub fn get_value(obj: &PyObjectRef) -> String { - if let PyObjectPayload::String { value } = &obj.payload { - value.to_string() - } else { - panic!("Inner error getting str"); - } + obj.payload::().unwrap().value.clone() } pub fn borrow_value(obj: &PyObjectRef) -> &str { - if let PyObjectPayload::String { value } = &obj.payload { - value.as_str() - } else { - panic!("Inner error getting str"); - } + &obj.payload::().unwrap().value } fn str_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -377,18 +443,6 @@ fn str_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn str_upper(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let value = get_value(&s).to_uppercase(); - Ok(vm.ctx.new_str(value)) -} - -fn str_lower(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let value = get_value(&s).to_lowercase(); - Ok(vm.ctx.new_str(value)) -} - 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); @@ -467,17 +521,6 @@ fn str_rstrip(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_str(value)) } -fn str_endswith(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(s, Some(vm.ctx.str_type())), (pat, Some(vm.ctx.str_type()))] - ); - let value = get_value(&s); - let pat = get_value(&pat); - Ok(vm.ctx.new_bool(value.ends_with(pat.as_str()))) -} - fn str_isidentifier(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); let value = get_value(&s); @@ -557,22 +600,6 @@ fn str_zfill(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_str(new_str)) } -fn str_join(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(s, Some(vm.ctx.str_type())), (iterable, None)] - ); - let value = get_value(&s); - let elements: Vec = vm - .extract_elements(iterable)? - .iter() - .map(|w| get_value(&w)) - .collect(); - let joined = elements.join(&value); - Ok(vm.ctx.new_str(joined)) -} - fn str_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -862,17 +889,6 @@ fn str_center(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_str(new_str)) } -fn str_startswith(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(s, Some(vm.ctx.str_type())), (pat, Some(vm.ctx.str_type()))] - ); - let value = get_value(&s); - let pat = get_value(&pat); - Ok(vm.ctx.new_bool(value.starts_with(pat.as_str()))) -} - fn str_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 0b878c7150..77b3ca7aa7 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -480,7 +480,12 @@ impl PyContext { } pub fn new_str(&self, s: String) -> PyObjectRef { - PyObject::new(PyObjectPayload::String { value: s }, self.str_type()) + PyObject::new( + PyObjectPayload::AnyRustValue { + value: Box::new(objstr::PyString { value: s }), + }, + self.str_type(), + ) } pub fn new_bytes(&self, data: Vec) -> PyObjectRef { @@ -1251,9 +1256,6 @@ py_native_func_factory_tuple!((a, A), (b, B), (c, C), (d, D), (e, E)); /// of rust data for a particular python object. Determine the python type /// by using for example the `.typ()` method on a python object. pub enum PyObjectPayload { - String { - value: String, - }, Integer { value: BigInt, }, @@ -1357,7 +1359,6 @@ 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), diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index 78fc05dc9b..10ebf3859b 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -167,8 +167,9 @@ impl<'de> Visitor<'de> for PyObjectDeserializer<'de> { // 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.payload { - PyObjectPayload::String { ref value } => value.clone(), + let key: String = match key_obj.payload { + // FIXME + // PyObjectPayload::String { ref value } => value.clone(), _ => unimplemented!("map keys must be strings"), }; self.vm.ctx.set_item(&dict, &key, value); From 67f8c020875e7cfecc934cfb574420a4524253db Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Wed, 27 Feb 2019 20:05:04 -0800 Subject: [PATCH 098/380] cleanup --- vm/src/obj/objbool.rs | 6 ++++-- vm/src/stdlib/json.rs | 11 +++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index 02ae3cc2f2..8550099f60 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -1,3 +1,4 @@ +use super::objstr::PyString; use super::objtype; use crate::pyobject::{ IntoPyObject, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, @@ -12,13 +13,14 @@ impl IntoPyObject for bool { } pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result { + if let Some(s) = obj.payload::() { + return Ok(!s.value.is_empty()); + } let result = match obj.payload { PyObjectPayload::Integer { ref value } => !value.is_zero(), PyObjectPayload::Float { value } => value != 0.0, PyObjectPayload::Sequence { ref elements } => !elements.borrow().is_empty(), PyObjectPayload::Dict { ref elements } => !elements.borrow().is_empty(), - // FIXME - //PyObjectPayload::String { ref value } => !value.is_empty(), PyObjectPayload::None { .. } => false, _ => { if let Ok(f) = vm.get_method(obj.clone(), "__bool__") { diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index 10ebf3859b..c8ea3c9bb7 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -5,7 +5,11 @@ use serde::de::{DeserializeSeed, Visitor}; use serde::ser::{SerializeMap, SerializeSeq}; use serde_json; -use crate::obj::{objbool, objdict, objfloat, objint, objsequence, objstr, objtype}; +use crate::obj::{ + objbool, objdict, objfloat, objint, objsequence, + objstr::{self, PyString}, + objtype, +}; use crate::pyobject::{ create_type, DictProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, @@ -167,9 +171,8 @@ impl<'de> Visitor<'de> for PyObjectDeserializer<'de> { // 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: String = match key_obj.payload { - // FIXME - // PyObjectPayload::String { ref value } => value.clone(), + let key: String = match key_obj.payload::() { + Some(PyString { ref value }) => value.clone(), _ => unimplemented!("map keys must be strings"), }; self.vm.ctx.set_item(&dict, &key, value); From 9081cb2b2c7eedf23b7ae7b5d22c3aee6ff62b17 Mon Sep 17 00:00:00 2001 From: apantykhin Date: Thu, 28 Feb 2019 06:35:08 +0000 Subject: [PATCH 099/380] remove redundant mut --- vm/src/compile.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 6ec3fd6cc4..1fcce93c93 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -1389,7 +1389,7 @@ impl Compiler { // Low level helper functions: fn emit(&mut self, instruction: Instruction) { let location = self.current_source_location.clone(); - let mut cur_code_obj = self.current_code_object(); + let cur_code_obj = self.current_code_object(); cur_code_obj.instructions.push(instruction); cur_code_obj.locations.push(location); // TODO: insert source filename From 52c32fab3a8761e832adac941c06b79184ef5e13 Mon Sep 17 00:00:00 2001 From: ben Date: Tue, 26 Feb 2019 21:34:41 +1300 Subject: [PATCH 100/380] Call __instancecheck__ and __subclasscheck__ as part of isinstance and issubclass --- tests/snippets/isinstance.py | 54 +++++++++++++++++++++++++++ tests/snippets/issubclass.py | 63 ++++++++++++++++++++++++++++++++ vm/src/builtins.rs | 42 +++++++++++---------- vm/src/exceptions.rs | 4 +- vm/src/frame.rs | 4 +- vm/src/macros.rs | 3 +- vm/src/obj/objbool.rs | 2 +- vm/src/obj/objbytearray.rs | 4 +- vm/src/obj/objbytes.rs | 12 +++--- vm/src/obj/objcomplex.rs | 14 +++---- vm/src/obj/objdict.rs | 2 +- vm/src/obj/objfloat.rs | 69 ++++++++++++++++++----------------- vm/src/obj/objint.rs | 54 +++++++++++++-------------- vm/src/obj/objiter.rs | 2 +- vm/src/obj/objlist.rs | 18 ++++----- vm/src/obj/objrange.rs | 6 +-- vm/src/obj/objset.rs | 2 +- vm/src/obj/objstr.rs | 20 +++++----- vm/src/obj/objsuper.rs | 5 ++- vm/src/obj/objtuple.rs | 14 +++---- vm/src/obj/objtype.rs | 61 +++++++++++++++++++++++++++++-- vm/src/pyobject.rs | 4 +- vm/src/stdlib/json.rs | 14 +++---- vm/src/stdlib/pystruct.rs | 8 ++-- vm/src/stdlib/types.rs | 2 +- vm/src/vm.rs | 4 +- wasm/lib/src/convert.rs | 6 +-- wasm/lib/src/wasm_builtins.rs | 4 +- 28 files changed, 338 insertions(+), 159 deletions(-) create mode 100644 tests/snippets/isinstance.py create mode 100644 tests/snippets/issubclass.py diff --git a/tests/snippets/isinstance.py b/tests/snippets/isinstance.py new file mode 100644 index 0000000000..52645e9b87 --- /dev/null +++ b/tests/snippets/isinstance.py @@ -0,0 +1,54 @@ + +class Regular: + pass + + +assert isinstance(Regular(), Regular) + + +class MCNotInstanceOf(type): + def __instancecheck__(self, instance): + return False + + +class NotInstanceOf(metaclass=MCNotInstanceOf): + pass + + +class InheritedNotInstanceOf(NotInstanceOf): + pass + + +assert not isinstance(Regular(), NotInstanceOf) +assert not isinstance(1, NotInstanceOf) + +# weird cpython behaviour if exact match then isinstance return true +assert isinstance(NotInstanceOf(), NotInstanceOf) +assert not NotInstanceOf.__instancecheck__(NotInstanceOf()) +assert not isinstance(InheritedNotInstanceOf(), NotInstanceOf) + + +class MCAlwaysInstanceOf(type): + def __instancecheck__(self, instance): + return True + + +class AlwaysInstanceOf(metaclass=MCAlwaysInstanceOf): + pass + + +assert isinstance(AlwaysInstanceOf(), AlwaysInstanceOf) +assert isinstance(Regular(), AlwaysInstanceOf) +assert isinstance(1, AlwaysInstanceOf) + + +class MCReturnInt(type): + def __instancecheck__(self, instance): + return 3 + + +class ReturnInt(metaclass=MCReturnInt): + pass + + +assert isinstance("a", ReturnInt) is True diff --git a/tests/snippets/issubclass.py b/tests/snippets/issubclass.py new file mode 100644 index 0000000000..62577f2133 --- /dev/null +++ b/tests/snippets/issubclass.py @@ -0,0 +1,63 @@ + +class A: + pass + + +class B(A): + pass + + +assert issubclass(A, A) +assert issubclass(B, A) +assert not issubclass(A, B) + + +class MCNotSubClass(type): + def __subclasscheck__(self, subclass): + return False + + +class NotSubClass(metaclass=MCNotSubClass): + pass + + +class InheritedNotSubClass(NotSubClass): + pass + + +assert not issubclass(A, NotSubClass) +assert not issubclass(NotSubClass, NotSubClass) +assert not issubclass(InheritedNotSubClass, NotSubClass) +assert not issubclass(NotSubClass, InheritedNotSubClass) + + +class MCAlwaysSubClass(type): + def __subclasscheck__(self, subclass): + return True + + +class AlwaysSubClass(metaclass=MCAlwaysSubClass): + pass + + +class InheritedAlwaysSubClass(AlwaysSubClass): + pass + + +assert issubclass(A, AlwaysSubClass) +assert issubclass(AlwaysSubClass, AlwaysSubClass) +assert issubclass(InheritedAlwaysSubClass, AlwaysSubClass) +assert issubclass(AlwaysSubClass, InheritedAlwaysSubClass) + + +class MCAVirtualSubClass(type): + def __subclasscheck__(self, subclass): + return subclass is A + + +class AVirtualSubClass(metaclass=MCAVirtualSubClass): + pass + + +assert issubclass(A, AVirtualSubClass) +assert not isinstance(B, AVirtualSubClass) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 870b3573e4..1467edf5f7 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -198,9 +198,9 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); // Determine code object: - let code_obj = if objtype::isinstance(source, &vm.ctx.code_type()) { + let code_obj = if objtype::real_isinstance(source, &vm.ctx.code_type()) { source.clone() - } else if objtype::isinstance(source, &vm.ctx.str_type()) { + } else if objtype::real_isinstance(source, &vm.ctx.str_type()) { let mode = compile::Mode::Eval; let source = objstr::get_value(source); // TODO: fix this newline bug: @@ -235,7 +235,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); // Determine code object: - let code_obj = if objtype::isinstance(source, &vm.ctx.str_type()) { + let code_obj = if objtype::real_isinstance(source, &vm.ctx.str_type()) { let mode = compile::Mode::Exec; let source = objstr::get_value(source); // TODO: fix this newline bug: @@ -246,7 +246,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm.new_exception(syntax_error, err.to_string()) }, )? - } else if objtype::isinstance(source, &vm.ctx.code_type()) { + } else if objtype::real_isinstance(source, &vm.ctx.code_type()) { source.clone() } else { return Err(vm.new_type_error("source argument must be str or code object".to_string())); @@ -349,21 +349,25 @@ fn builtin_id(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // builtin_input fn builtin_isinstance(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(obj, None), (typ, None)]); + arg_check!( + vm, + args, + required = [(obj, None), (typ, Some(vm.get_type()))] + ); - let isinstance = objtype::isinstance(obj, typ); - Ok(vm.context().new_bool(isinstance)) + let isinstance = objtype::isinstance(vm, obj, typ)?; + Ok(vm.new_bool(isinstance)) } fn builtin_issubclass(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - if args.args.len() != 2 { - panic!("issubclass expects exactly two parameters"); - } - - let cls1 = &args.args[0]; - let cls2 = &args.args[1]; + arg_check!( + vm, + args, + required = [(subclass, Some(vm.get_type())), (cls, Some(vm.get_type()))] + ); - Ok(vm.context().new_bool(objtype::issubclass(cls1, cls2))) + let issubclass = objtype::issubclass(vm, subclass, cls)?; + Ok(vm.context().new_bool(issubclass)) } fn builtin_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -501,7 +505,7 @@ fn builtin_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match vm.call_method(iterator, "__next__", vec![]) { Ok(value) => Ok(value), Err(value) => { - if objtype::isinstance(&value, &vm.ctx.exceptions.stop_iteration) { + if objtype::real_isinstance(&value, &vm.ctx.exceptions.stop_iteration) { match default_value { None => Err(value), Some(value) => Ok(value.clone()), @@ -581,7 +585,7 @@ pub fn builtin_print(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .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()) { + if !objtype::real_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()) @@ -595,7 +599,7 @@ pub fn builtin_print(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .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()) { + if !objtype::real_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()) @@ -818,9 +822,9 @@ pub fn builtin_build_class_(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> Py let mut metaclass = args.get_kwarg("metaclass", vm.get_type()); for base in bases.clone() { - if objtype::issubclass(&base.typ(), &metaclass) { + if objtype::issubclass(vm, &base.typ(), &metaclass)? { metaclass = base.typ(); - } else if !objtype::issubclass(&metaclass, &base.typ()) { + } else if !objtype::real_issubclass(&metaclass, &base.typ()) { return Err(vm.new_type_error("metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases".to_string())); } } diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index cf3018426d..cade411986 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -23,11 +23,11 @@ fn exception_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn print_exception(vm: &mut VirtualMachine, exc: &PyObjectRef) { if let Some(tb) = exc.get_attr("__traceback__") { println!("Traceback (most recent call last):"); - if objtype::isinstance(&tb, &vm.ctx.list_type()) { + if objtype::real_isinstance(&tb, &vm.ctx.list_type()) { let mut elements = objsequence::get_elements(&tb).to_vec(); elements.reverse(); for element in elements.iter() { - if objtype::isinstance(&element, &vm.ctx.tuple_type()) { + if objtype::real_isinstance(&element, &vm.ctx.tuple_type()) { let element = objsequence::get_elements(&element); let filename = if let Ok(x) = vm.to_str(&element[0]) { objstr::get_value(&x) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index b6253b6c6a..5e98a42bd9 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -105,7 +105,7 @@ impl Frame { Err(exception) => { // unwind block stack on exception and find any handlers. // Add an entry in the traceback: - assert!(objtype::isinstance( + assert!(objtype::real_isinstance( &exception, &vm.ctx.exceptions.base_exception_type )); @@ -514,7 +514,7 @@ impl Frame { 0 | 2 | 3 => panic!("Not implemented!"), _ => panic!("Invalid parameter for RAISE_VARARGS, must be between 0 to 3"), }; - if objtype::isinstance(&exception, &vm.ctx.exceptions.base_exception_type) { + if objtype::real_isinstance(&exception, &vm.ctx.exceptions.base_exception_type) { info!("Exception raised: {:?}", exception); Err(exception) } else { diff --git a/vm/src/macros.rs b/vm/src/macros.rs index a05972961a..f50dd26de6 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -18,7 +18,8 @@ macro_rules! type_check { // 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) { + + if !$crate::obj::objtype::isinstance($vm, 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)?; diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index cf16e1f5f4..2405910e54 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -50,7 +50,7 @@ The class bool is a subclass of the class int, and cannot be subclassed."; } pub fn not(vm: &mut VirtualMachine, obj: &PyObjectRef) -> PyResult { - if objtype::isinstance(obj, &vm.ctx.bool_type()) { + if objtype::real_isinstance(obj, &vm.ctx.bool_type()) { let value = get_value(obj); Ok(vm.ctx.new_bool(!value)) } else { diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index 0cabf23fdc..57fc9d6c2c 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -121,7 +121,7 @@ fn bytearray_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(cls, None)], optional = [(val_option, None)] ); - if !objtype::issubclass(cls, &vm.ctx.bytearray_type()) { + if !objtype::real_issubclass(cls, &vm.ctx.bytearray_type()) { return Err(vm.new_type_error(format!("{:?} is not a subtype of bytearray", cls))); } @@ -164,7 +164,7 @@ fn bytearray_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(a, Some(vm.ctx.bytearray_type())), (b, None)] ); - let result = if objtype::isinstance(b, &vm.ctx.bytearray_type()) { + let result = if objtype::real_isinstance(b, &vm.ctx.bytearray_type()) { get_value(a).to_vec() == get_value(b).to_vec() } else { false diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 6ab2f72af2..71b8282484 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -52,7 +52,7 @@ fn bytes_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(cls, None)], optional = [(val_option, None)] ); - if !objtype::issubclass(cls, &vm.ctx.bytes_type()) { + if !objtype::real_issubclass(cls, &vm.ctx.bytes_type()) { return Err(vm.new_type_error(format!("{:?} is not a subtype of bytes", cls))); } @@ -85,7 +85,7 @@ fn bytes_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(a, Some(vm.ctx.bytes_type())), (b, None)] ); - let result = if objtype::isinstance(b, &vm.ctx.bytes_type()) { + let result = if objtype::real_isinstance(b, &vm.ctx.bytes_type()) { get_value(a).to_vec() == get_value(b).to_vec() } else { false @@ -100,7 +100,7 @@ fn bytes_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(a, Some(vm.ctx.bytes_type())), (b, None)] ); - let result = if objtype::isinstance(b, &vm.ctx.bytes_type()) { + let result = if objtype::real_isinstance(b, &vm.ctx.bytes_type()) { get_value(a).to_vec() >= get_value(b).to_vec() } else { return Err(vm.new_type_error(format!("Cannot compare {} and {} using '>'", a, b))); @@ -115,7 +115,7 @@ fn bytes_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(a, Some(vm.ctx.bytes_type())), (b, None)] ); - let result = if objtype::isinstance(b, &vm.ctx.bytes_type()) { + let result = if objtype::real_isinstance(b, &vm.ctx.bytes_type()) { get_value(a).to_vec() > get_value(b).to_vec() } else { return Err(vm.new_type_error(format!("Cannot compare {} and {} using '>='", a, b))); @@ -130,7 +130,7 @@ fn bytes_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(a, Some(vm.ctx.bytes_type())), (b, None)] ); - let result = if objtype::isinstance(b, &vm.ctx.bytes_type()) { + let result = if objtype::real_isinstance(b, &vm.ctx.bytes_type()) { get_value(a).to_vec() <= get_value(b).to_vec() } else { return Err(vm.new_type_error(format!("Cannot compare {} and {} using '<'", a, b))); @@ -145,7 +145,7 @@ fn bytes_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(a, Some(vm.ctx.bytes_type())), (b, None)] ); - let result = if objtype::isinstance(b, &vm.ctx.bytes_type()) { + let result = if objtype::real_isinstance(b, &vm.ctx.bytes_type()) { get_value(a).to_vec() < get_value(b).to_vec() } else { return Err(vm.new_type_error(format!("Cannot compare {} and {} using '<='", a, b))); diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index 0b0d5ba7a2..eba0de66e6 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -60,7 +60,7 @@ fn complex_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { optional = [(real, None), (imag, None)] ); - if !objtype::issubclass(cls, &vm.ctx.complex_type()) { + if !objtype::real_issubclass(cls, &vm.ctx.complex_type()) { return Err(vm.new_type_error(format!("{:?} is not a subtype of complex", cls))); } @@ -109,9 +109,9 @@ fn complex_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.complex_type()) { + if objtype::real_isinstance(i2, &vm.ctx.complex_type()) { Ok(vm.ctx.new_complex(v1 + get_value(i2))) - } else if objtype::isinstance(i2, &vm.ctx.int_type()) { + } else if objtype::real_isinstance(i2, &vm.ctx.int_type()) { Ok(vm.ctx.new_complex(Complex64::new( v1.re + objint::get_value(i2).to_f64().unwrap(), v1.im, @@ -130,7 +130,7 @@ fn complex_radd(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.int_type()) { + if objtype::real_isinstance(i2, &vm.ctx.int_type()) { Ok(vm.ctx.new_complex(Complex64::new( v1.re + objint::get_value(i2).to_f64().unwrap(), v1.im, @@ -156,14 +156,14 @@ fn complex_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let z = get_value(zelf); - let result = if objtype::isinstance(other, &vm.ctx.complex_type()) { + let result = if objtype::real_isinstance(other, &vm.ctx.complex_type()) { z == get_value(other) - } else if objtype::isinstance(other, &vm.ctx.int_type()) { + } else if objtype::real_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()) { + } else if objtype::real_isinstance(other, &vm.ctx.float_type()) { z.im == 0.0 && z.re == objfloat::get_value(other) } else { return Ok(vm.ctx.not_implemented()); diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 03a307b4c3..151c8a57fd 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -142,7 +142,7 @@ fn dict_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let dict = vm.ctx.new_dict(); if let Some(dict_obj) = dict_obj { - if objtype::isinstance(&dict_obj, &vm.ctx.dict_type()) { + if objtype::real_isinstance(&dict_obj, &vm.ctx.dict_type()) { for (needle, value) in get_key_value_pairs(&dict_obj) { set_item(&dict, vm, &needle, &value); } diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 4617364ad5..4de98b03ff 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -17,16 +17,16 @@ fn float_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn float_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(cls, None), (arg, None)]); - let value = if objtype::isinstance(arg, &vm.ctx.float_type()) { + let value = if objtype::real_isinstance(arg, &vm.ctx.float_type()) { get_value(arg) - } else if objtype::isinstance(arg, &vm.ctx.int_type()) { + } else if objtype::real_isinstance(arg, &vm.ctx.int_type()) { 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()) { + } else if objtype::real_isinstance(arg, &vm.ctx.str_type()) { match lexical::try_parse(objstr::get_value(arg)) { Ok(f) => f, Err(_) => { @@ -36,7 +36,7 @@ fn float_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); } } - } else if objtype::isinstance(arg, &vm.ctx.bytes_type()) { + } else if objtype::real_isinstance(arg, &vm.ctx.bytes_type()) { match lexical::try_parse(objbytes::get_value(arg).as_slice()) { Ok(f) => f, Err(_) => { @@ -63,7 +63,7 @@ pub fn get_value(obj: &PyObjectRef) -> f64 { } pub fn make_float(vm: &mut VirtualMachine, obj: &PyObjectRef) -> Result { - if objtype::isinstance(obj, &vm.ctx.float_type()) { + if objtype::real_isinstance(obj, &vm.ctx.float_type()) { Ok(get_value(obj)) } else if let Ok(method) = vm.get_method(obj.clone(), "__float__") { let res = vm.invoke( @@ -86,10 +86,10 @@ fn float_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.float_type())), (other, None)] ); let zelf = get_value(zelf); - let result = if objtype::isinstance(other, &vm.ctx.float_type()) { + let result = if objtype::real_isinstance(other, &vm.ctx.float_type()) { let other = get_value(other); zelf == other - } else if objtype::isinstance(other, &vm.ctx.int_type()) { + } else if objtype::real_isinstance(other, &vm.ctx.int_type()) { let other_int = objint::get_value(other); if let (Some(zelf_int), Some(other_float)) = (zelf.to_bigint(), other_int.to_f64()) { @@ -111,9 +111,9 @@ fn float_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.float_type()) { + if objtype::real_isinstance(i2, &vm.ctx.float_type()) { Ok(vm.ctx.new_bool(v1 < get_value(i2))) - } else if objtype::isinstance(i2, &vm.ctx.int_type()) { + } else if objtype::real_isinstance(i2, &vm.ctx.int_type()) { Ok(vm .ctx .new_bool(v1 < objint::get_value(i2).to_f64().unwrap())) @@ -130,9 +130,9 @@ fn float_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.float_type()) { + if objtype::real_isinstance(i2, &vm.ctx.float_type()) { Ok(vm.ctx.new_bool(v1 <= get_value(i2))) - } else if objtype::isinstance(i2, &vm.ctx.int_type()) { + } else if objtype::real_isinstance(i2, &vm.ctx.int_type()) { Ok(vm .ctx .new_bool(v1 <= objint::get_value(i2).to_f64().unwrap())) @@ -149,9 +149,9 @@ fn float_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.float_type()) { + if objtype::real_isinstance(i2, &vm.ctx.float_type()) { Ok(vm.ctx.new_bool(v1 > get_value(i2))) - } else if objtype::isinstance(i2, &vm.ctx.int_type()) { + } else if objtype::real_isinstance(i2, &vm.ctx.int_type()) { Ok(vm .ctx .new_bool(v1 > objint::get_value(i2).to_f64().unwrap())) @@ -168,9 +168,9 @@ fn float_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.float_type()) { + if objtype::real_isinstance(i2, &vm.ctx.float_type()) { Ok(vm.ctx.new_bool(v1 >= get_value(i2))) - } else if objtype::isinstance(i2, &vm.ctx.int_type()) { + } else if objtype::real_isinstance(i2, &vm.ctx.int_type()) { Ok(vm .ctx .new_bool(v1 >= objint::get_value(i2).to_f64().unwrap())) @@ -192,9 +192,9 @@ fn float_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(zelf); - if objtype::isinstance(other, &vm.ctx.float_type()) { + if objtype::real_isinstance(other, &vm.ctx.float_type()) { Ok(vm.ctx.new_float(v1 + get_value(other))) - } else if objtype::isinstance(other, &vm.ctx.int_type()) { + } else if objtype::real_isinstance(other, &vm.ctx.int_type()) { Ok(vm .ctx .new_float(v1 + objint::get_value(other).to_f64().unwrap())) @@ -214,7 +214,8 @@ fn float_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.float_type())), (i2, None)] ); 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()) + if objtype::real_isinstance(i2, &vm.ctx.float_type()) + || objtype::real_isinstance(i2, &vm.ctx.int_type()) { let r1 = float_floordiv(vm, args.clone())?; let r2 = float_mod(vm, args.clone())?; @@ -232,9 +233,9 @@ fn float_floordiv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); - let v2 = if objtype::isinstance(i2, &vm.ctx.float_type) { + let v2 = if objtype::real_isinstance(i2, &vm.ctx.float_type) { get_value(i2) - } else if objtype::isinstance(i2, &vm.ctx.int_type) { + } else if objtype::real_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()))? @@ -256,9 +257,9 @@ fn float_sub(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.float_type())), (other, None)] ); let v1 = get_value(zelf); - if objtype::isinstance(other, &vm.ctx.float_type()) { + if objtype::real_isinstance(other, &vm.ctx.float_type()) { Ok(vm.ctx.new_float(v1 - get_value(other))) - } else if objtype::isinstance(other, &vm.ctx.int_type()) { + } else if objtype::real_isinstance(other, &vm.ctx.int_type()) { Ok(vm .ctx .new_float(v1 - objint::get_value(other).to_f64().unwrap())) @@ -274,9 +275,9 @@ fn float_rsub(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.float_type())), (other, None)] ); let v1 = get_value(zelf); - if objtype::isinstance(other, &vm.ctx.float_type()) { + if objtype::real_isinstance(other, &vm.ctx.float_type()) { Ok(vm.ctx.new_float(get_value(other) - v1)) - } else if objtype::isinstance(other, &vm.ctx.int_type()) { + } else if objtype::real_isinstance(other, &vm.ctx.int_type()) { Ok(vm .ctx .new_float(objint::get_value(other).to_f64().unwrap() - v1)) @@ -293,9 +294,9 @@ fn float_mod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); - let v2 = if objtype::isinstance(i2, &vm.ctx.float_type) { + let v2 = if objtype::real_isinstance(i2, &vm.ctx.float_type) { get_value(i2) - } else if objtype::isinstance(i2, &vm.ctx.int_type) { + } else if objtype::real_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()))? @@ -325,10 +326,10 @@ fn float_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.float_type()) { + if objtype::real_isinstance(i2, &vm.ctx.float_type()) { let result = v1.powf(get_value(i2)); Ok(vm.ctx.new_float(result)) - } else if objtype::isinstance(i2, &vm.ctx.int_type()) { + } else if objtype::real_isinstance(i2, &vm.ctx.int_type()) { let result = v1.powf(objint::get_value(i2).to_f64().unwrap()); Ok(vm.ctx.new_float(result)) } else { @@ -344,9 +345,9 @@ fn float_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(zelf); - let v2 = if objtype::isinstance(other, &vm.ctx.float_type) { + let v2 = if objtype::real_isinstance(other, &vm.ctx.float_type) { get_value(other) - } else if objtype::isinstance(other, &vm.ctx.int_type) { + } else if objtype::real_isinstance(other, &vm.ctx.int_type) { objint::get_value(other) .to_f64() .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))? @@ -369,9 +370,9 @@ fn float_rtruediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(zelf); - let v2 = if objtype::isinstance(other, &vm.ctx.float_type) { + let v2 = if objtype::real_isinstance(other, &vm.ctx.float_type) { get_value(other) - } else if objtype::isinstance(other, &vm.ctx.int_type) { + } else if objtype::real_isinstance(other, &vm.ctx.int_type) { objint::get_value(other) .to_f64() .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))? @@ -393,9 +394,9 @@ fn float_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.float_type())), (other, None)] ); let v1 = get_value(zelf); - if objtype::isinstance(other, &vm.ctx.float_type) { + if objtype::real_isinstance(other, &vm.ctx.float_type) { Ok(vm.ctx.new_float(v1 * get_value(other))) - } else if objtype::isinstance(other, &vm.ctx.int_type) { + } else if objtype::real_isinstance(other, &vm.ctx.int_type) { Ok(vm .ctx .new_float(v1 * objint::get_value(other).to_f64().unwrap())) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 7e22f19be2..611880ecc1 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -44,7 +44,7 @@ fn int_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(cls, None)], optional = [(val_option, None)] ); - if !objtype::issubclass(cls, &vm.ctx.int_type()) { + if !objtype::real_issubclass(cls, &vm.ctx.int_type()) { return Err(vm.new_type_error(format!("{:?} is not a subtype of int", cls))); } @@ -68,11 +68,11 @@ pub fn to_int( obj: &PyObjectRef, base: u32, ) -> Result { - let val = if objtype::isinstance(obj, &vm.ctx.int_type()) { + let val = if objtype::real_isinstance(obj, &vm.ctx.int_type()) { get_value(obj) - } else if objtype::isinstance(obj, &vm.ctx.float_type()) { + } else if objtype::real_isinstance(obj, &vm.ctx.float_type()) { objfloat::get_value(obj).to_bigint().unwrap() - } else if objtype::isinstance(obj, &vm.ctx.str_type()) { + } else if objtype::real_isinstance(obj, &vm.ctx.str_type()) { let s = objstr::get_value(obj); match i32::from_str_radix(&s, base) { Ok(v) => v.to_bigint().unwrap(), @@ -131,7 +131,7 @@ fn int_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let zelf = BigInt::from_pyobj(zelf); - let result = if objtype::isinstance(other, &vm.ctx.int_type()) { + let result = if objtype::real_isinstance(other, &vm.ctx.int_type()) { let other = BigInt::from_pyobj(other); zelf == other } else { @@ -148,7 +148,7 @@ fn int_ne(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let zelf = BigInt::from_pyobj(zelf); - let result = if objtype::isinstance(other, &vm.ctx.int_type()) { + let result = if objtype::real_isinstance(other, &vm.ctx.int_type()) { let other = BigInt::from_pyobj(other); zelf != other } else { @@ -164,7 +164,7 @@ fn int_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); - if !objtype::isinstance(other, &vm.ctx.int_type()) { + if !objtype::real_isinstance(other, &vm.ctx.int_type()) { return Ok(vm.ctx.not_implemented()); } @@ -181,7 +181,7 @@ fn int_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); - if !objtype::isinstance(other, &vm.ctx.int_type()) { + if !objtype::real_isinstance(other, &vm.ctx.int_type()) { return Ok(vm.ctx.not_implemented()); } @@ -198,7 +198,7 @@ fn int_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); - if !objtype::isinstance(other, &vm.ctx.int_type()) { + if !objtype::real_isinstance(other, &vm.ctx.int_type()) { return Ok(vm.ctx.not_implemented()); } @@ -215,7 +215,7 @@ fn int_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); - if !objtype::isinstance(other, &vm.ctx.int_type()) { + if !objtype::real_isinstance(other, &vm.ctx.int_type()) { return Ok(vm.ctx.not_implemented()); } @@ -232,7 +232,7 @@ fn int_lshift(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); - if !objtype::isinstance(i2, &vm.ctx.int_type()) { + if !objtype::real_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()), @@ -261,7 +261,7 @@ fn int_rshift(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); - if !objtype::isinstance(i2, &vm.ctx.int_type()) { + if !objtype::real_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()), @@ -303,7 +303,7 @@ fn int_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); - if objtype::isinstance(other, &vm.ctx.int_type()) { + if objtype::real_isinstance(other, &vm.ctx.int_type()) { Ok(vm.ctx.new_int(get_value(zelf) + get_value(other))) } else { Ok(vm.ctx.not_implemented()) @@ -326,7 +326,7 @@ fn int_floordiv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); - if objtype::isinstance(i2, &vm.ctx.int_type()) { + if objtype::real_isinstance(i2, &vm.ctx.int_type()) { let (v1, v2) = (get_value(i), get_value(i2)); if v2 != BigInt::zero() { @@ -378,7 +378,7 @@ fn int_sub(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); - if objtype::isinstance(other, &vm.ctx.int_type()) { + if objtype::real_isinstance(other, &vm.ctx.int_type()) { Ok(vm.ctx.new_int(get_value(zelf) - get_value(other))) } else { Ok(vm.ctx.not_implemented()) @@ -391,7 +391,7 @@ fn int_rsub(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); - if objtype::isinstance(other, &vm.ctx.int_type()) { + if objtype::real_isinstance(other, &vm.ctx.int_type()) { Ok(vm.ctx.new_int(get_value(other) - get_value(zelf))) } else { Ok(vm.ctx.not_implemented()) @@ -404,7 +404,7 @@ fn int_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); - if objtype::isinstance(other, &vm.ctx.int_type()) { + if objtype::real_isinstance(other, &vm.ctx.int_type()) { Ok(vm.ctx.new_int(get_value(zelf) * get_value(other))) } else { Ok(vm.ctx.not_implemented()) @@ -422,7 +422,7 @@ fn int_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); - if objtype::isinstance(other, &vm.ctx.int_type()) { + if objtype::real_isinstance(other, &vm.ctx.int_type()) { div_ints(vm, &get_value(zelf), &get_value(other)) } else { Ok(vm.ctx.not_implemented()) @@ -436,7 +436,7 @@ fn int_rtruediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); - if objtype::isinstance(other, &vm.ctx.int_type()) { + if objtype::real_isinstance(other, &vm.ctx.int_type()) { div_ints(vm, &get_value(other), &get_value(zelf)) } else { Ok(vm.ctx.not_implemented()) @@ -482,7 +482,7 @@ fn int_mod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.int_type()) { + if objtype::real_isinstance(i2, &vm.ctx.int_type()) { let v2 = get_value(i2); if v2 != BigInt::zero() { @@ -513,10 +513,10 @@ fn int_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.int_type()) { + if objtype::real_isinstance(i2, &vm.ctx.int_type()) { let v2 = get_value(i2).to_u32().unwrap(); Ok(vm.ctx.new_int(v1.pow(v2))) - } else if objtype::isinstance(i2, &vm.ctx.float_type()) { + } else if objtype::real_isinstance(i2, &vm.ctx.float_type()) { let v2 = objfloat::get_value(i2); Ok(vm.ctx.new_float((v1.to_f64().unwrap()).powf(v2))) } else { @@ -531,7 +531,7 @@ fn int_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); - if objtype::isinstance(i2, &vm.ctx.int_type()) { + if objtype::real_isinstance(i2, &vm.ctx.int_type()) { let v1 = get_value(i); let v2 = get_value(i2); @@ -556,7 +556,7 @@ fn int_xor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.int_type()) { + if objtype::real_isinstance(i2, &vm.ctx.int_type()) { let v2 = get_value(i2); Ok(vm.ctx.new_int(v1 ^ v2)) } else { @@ -571,7 +571,7 @@ fn int_rxor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); - if objtype::isinstance(i2, &vm.ctx.int_type()) { + if objtype::real_isinstance(i2, &vm.ctx.int_type()) { let right_val = get_value(i); let left_val = get_value(i2); @@ -588,7 +588,7 @@ fn int_or(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.int_type()) { + if objtype::real_isinstance(i2, &vm.ctx.int_type()) { let v2 = get_value(i2); Ok(vm.ctx.new_int(v1 | v2)) } else { @@ -603,7 +603,7 @@ fn int_and(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.int_type()) { + if objtype::real_isinstance(i2, &vm.ctx.int_type()) { let v2 = get_value(i2); Ok(vm.ctx.new_int(v1 & v2)) } else { diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index a72339fbb0..53f55dff14 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -39,7 +39,7 @@ pub fn get_next_object( Ok(value) => Ok(Some(value)), Err(next_error) => { // Check if we have stopiteration, or something else: - if objtype::isinstance(&next_error, &vm.ctx.exceptions.stop_iteration) { + if objtype::real_isinstance(&next_error, &vm.ctx.exceptions.stop_iteration) { Ok(None) } else { Err(next_error) diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index e09f15a8b1..b9e8637145 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -22,7 +22,7 @@ fn set_item( idx: PyObjectRef, obj: PyObjectRef, ) -> PyResult { - if objtype::isinstance(&idx, &vm.ctx.int_type()) { + if objtype::real_isinstance(&idx, &vm.ctx.int_type()) { let value = objint::get_value(&idx).to_i32().unwrap(); if let Some(pos_index) = l.get_pos(value) { l[pos_index] = obj; @@ -46,7 +46,7 @@ fn list_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { optional = [(iterable, None)] ); - if !objtype::issubclass(cls, &vm.ctx.list_type()) { + if !objtype::real_issubclass(cls, &vm.ctx.list_type()) { return Err(vm.new_type_error(format!("{:?} is not a subtype of list", cls))); } @@ -75,7 +75,7 @@ fn list_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { return Ok(vm.ctx.new_bool(true)); } - let result = if objtype::isinstance(other, &vm.ctx.list_type()) { + let result = if objtype::real_isinstance(other, &vm.ctx.list_type()) { let zelf = get_elements(zelf); let other = get_elements(other); seq_equal(vm, &zelf, &other)? @@ -92,7 +92,7 @@ fn list_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.list_type())), (other, None)] ); - let result = if objtype::isinstance(other, &vm.ctx.list_type()) { + let result = if objtype::real_isinstance(other, &vm.ctx.list_type()) { let zelf = get_elements(zelf); let other = get_elements(other); seq_lt(vm, &zelf, &other)? @@ -110,7 +110,7 @@ fn list_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.list_type())), (other, None)] ); - let result = if objtype::isinstance(other, &vm.ctx.list_type()) { + let result = if objtype::real_isinstance(other, &vm.ctx.list_type()) { let zelf = get_elements(zelf); let other = get_elements(other); seq_gt(vm, &zelf, &other)? @@ -128,7 +128,7 @@ fn list_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.list_type())), (other, None)] ); - let result = if objtype::isinstance(other, &vm.ctx.list_type()) { + let result = if objtype::real_isinstance(other, &vm.ctx.list_type()) { let zelf = get_elements(zelf); let other = get_elements(other); seq_ge(vm, &zelf, &other)? @@ -146,7 +146,7 @@ fn list_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.list_type())), (other, None)] ); - let result = if objtype::isinstance(other, &vm.ctx.list_type()) { + let result = if objtype::real_isinstance(other, &vm.ctx.list_type()) { let zelf = get_elements(zelf); let other = get_elements(other); seq_le(vm, &zelf, &other)? @@ -164,7 +164,7 @@ fn list_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(o, Some(vm.ctx.list_type())), (o2, None)] ); - if objtype::isinstance(o2, &vm.ctx.list_type()) { + if objtype::real_isinstance(o2, &vm.ctx.list_type()) { let e1 = get_elements(o); let e2 = get_elements(o2); let elements = e1.iter().chain(e2.iter()).cloned().collect(); @@ -181,7 +181,7 @@ fn list_iadd(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.list_type())), (other, None)] ); - if objtype::isinstance(other, &vm.ctx.list_type()) { + if objtype::real_isinstance(other, &vm.ctx.list_type()) { get_mut_elements(zelf).extend_from_slice(&get_elements(other)); Ok(zelf.clone()) } else { diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index d80b6aa015..89c1facecb 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -361,7 +361,7 @@ fn range_bool(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn range_contains(vm: &mut VirtualMachine, zelf: PyRange, needle: PyObjectRef) -> bool { - if objtype::isinstance(&needle, &vm.ctx.int_type()) { + if objtype::real_isinstance(&needle, &vm.ctx.int_type()) { zelf.contains(&objint::get_value(&needle)) } else { false @@ -377,7 +377,7 @@ fn range_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let range = get_value(zelf); - if objtype::isinstance(needle, &vm.ctx.int_type()) { + if objtype::real_isinstance(needle, &vm.ctx.int_type()) { let needle = objint::get_value(needle); match range.index_of(&needle) { @@ -398,7 +398,7 @@ fn range_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let range = get_value(zelf); - if objtype::isinstance(item, &vm.ctx.int_type()) { + if objtype::real_isinstance(item, &vm.ctx.int_type()) { Ok(vm.ctx.new_int(range.count(&objint::get_value(item)))) } else { Ok(vm.ctx.new_int(0)) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index c041ce5673..3a1f3b6c73 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -146,7 +146,7 @@ fn set_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { optional = [(iterable, None)] ); - if !objtype::issubclass(cls, &vm.ctx.set_type()) { + if !objtype::real_issubclass(cls, &vm.ctx.set_type()) { return Err(vm.new_type_error(format!("{} is not a subtype of set", cls))); } diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 580ab2b801..7a02ca24fb 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -135,7 +135,7 @@ fn str_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(a, Some(vm.ctx.str_type())), (b, None)] ); - let result = if objtype::isinstance(b, &vm.ctx.str_type()) { + let result = if objtype::real_isinstance(b, &vm.ctx.str_type()) { get_value(a) == get_value(b) } else { false @@ -151,7 +151,7 @@ fn str_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.str_type()) { + if objtype::real_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, i2))) @@ -166,7 +166,7 @@ fn str_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.str_type()) { + if objtype::real_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, i2))) @@ -181,7 +181,7 @@ fn str_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.str_type()) { + if objtype::real_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, i2))) @@ -196,7 +196,7 @@ fn str_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.str_type()) { + if objtype::real_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, i2))) @@ -249,7 +249,7 @@ fn str_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(s, Some(vm.ctx.str_type())), (s2, None)] ); - if objtype::isinstance(s2, &vm.ctx.str_type()) { + if objtype::real_isinstance(s2, &vm.ctx.str_type()) { Ok(vm .ctx .new_str(format!("{}{}", get_value(&s), get_value(&s2)))) @@ -266,7 +266,7 @@ fn str_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } let zelf = &args.args[0]; - if !objtype::isinstance(&zelf, &vm.ctx.str_type()) { + if !objtype::real_isinstance(&zelf, &vm.ctx.str_type()) { let zelf_typ = zelf.typ(); let actual_type = vm.to_pystr(&zelf_typ)?; return Err(vm.new_type_error(format!( @@ -293,7 +293,7 @@ fn call_object_format( ) -> PyResult { 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()) { + if !objtype::real_isinstance(&result, &vm.ctx.str_type()) { let result_type = result.typ(); let actual_type = vm.to_pystr(&result_type)?; return Err(vm.new_type_error(format!("__format__ must return a str, not {}", actual_type))); @@ -374,7 +374,7 @@ fn str_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(s, Some(vm.ctx.str_type())), (s2, None)] ); - if objtype::isinstance(s2, &vm.ctx.int_type()) { + if objtype::real_isinstance(s2, &vm.ctx.int_type()) { let value1 = get_value(&s); let value2 = objint::get_value(s2).to_i32().unwrap(); let mut result = String::new(); @@ -1092,7 +1092,7 @@ 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()) { + if objtype::real_isinstance(&b, &vm.ctx.int_type()) { match objint::get_value(&b).to_i32() { Some(pos) => { let graphemes = to_graphemes(value); diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index 79c516d4c0..0ff9270d37 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -56,7 +56,7 @@ fn super_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; // Check type argument: - if !objtype::isinstance(&py_type, &vm.get_type()) { + if !objtype::real_isinstance(&py_type, &vm.get_type()) { let type_name = objtype::get_type_name(&py_type.typ()); return Err(vm.new_type_error(format!( "super() argument 1 must be type, not {}", @@ -72,7 +72,8 @@ 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)) { + if !(objtype::isinstance(vm, &py_obj, &py_type)? || objtype::issubclass(vm, &py_obj, &py_type)?) + { return Err(vm.new_type_error( "super(type, obj): obj must be an instance or subtype of type".to_string(), )); diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 7747d2fc92..1c532de26a 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -18,7 +18,7 @@ fn tuple_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] ); - let result = if objtype::isinstance(other, &vm.ctx.tuple_type()) { + let result = if objtype::real_isinstance(other, &vm.ctx.tuple_type()) { let zelf = get_elements(zelf); let other = get_elements(other); seq_lt(vm, &zelf, &other)? @@ -36,7 +36,7 @@ fn tuple_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] ); - let result = if objtype::isinstance(other, &vm.ctx.tuple_type()) { + let result = if objtype::real_isinstance(other, &vm.ctx.tuple_type()) { let zelf = get_elements(zelf); let other = get_elements(other); seq_gt(vm, &zelf, &other)? @@ -54,7 +54,7 @@ fn tuple_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] ); - let result = if objtype::isinstance(other, &vm.ctx.tuple_type()) { + let result = if objtype::real_isinstance(other, &vm.ctx.tuple_type()) { let zelf = get_elements(zelf); let other = get_elements(other); seq_ge(vm, &zelf, &other)? @@ -72,7 +72,7 @@ fn tuple_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] ); - let result = if objtype::isinstance(other, &vm.ctx.tuple_type()) { + let result = if objtype::real_isinstance(other, &vm.ctx.tuple_type()) { let zelf = get_elements(zelf); let other = get_elements(other); seq_le(vm, &zelf, &other)? @@ -90,7 +90,7 @@ fn tuple_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] ); - if objtype::isinstance(other, &vm.ctx.tuple_type()) { + if objtype::real_isinstance(other, &vm.ctx.tuple_type()) { let e1 = get_elements(zelf); let e2 = get_elements(other); let elements = e1.iter().chain(e2.iter()).cloned().collect(); @@ -124,7 +124,7 @@ fn tuple_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] ); - let result = if objtype::isinstance(other, &vm.ctx.tuple_type()) { + let result = if objtype::real_isinstance(other, &vm.ctx.tuple_type()) { let zelf = get_elements(zelf); let other = get_elements(other); seq_equal(vm, &zelf, &other)? @@ -174,7 +174,7 @@ fn tuple_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { optional = [(iterable, None)] ); - if !objtype::issubclass(cls, &vm.ctx.tuple_type()) { + if !objtype::real_issubclass(cls, &vm.ctx.tuple_type()) { return Err(vm.new_type_error(format!("{} is not a subtype of tuple", cls))); } diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 6935996752..37c915b83c 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -1,3 +1,4 @@ +use super::objbool; use super::objdict; use super::objstr; use super::objtype; // Required for arg_check! to use isinstance @@ -8,6 +9,7 @@ use crate::pyobject::{ use crate::vm::VirtualMachine; use std::cell::RefCell; use std::collections::HashMap; +use std::rc::Rc; /* * The magical type type @@ -56,6 +58,16 @@ pub fn init(context: &PyContext) { "__getattribute__", context.new_rustfunc(type_getattribute), ); + context.set_attr( + &type_type, + "__instancecheck__", + context.new_rustfunc(type_instance_check), + ); + context.set_attr( + &type_type, + "__subclasscheck__", + context.new_rustfunc(type_subclass_check), + ); context.set_attr(&type_type, "__doc__", context.new_str(type_doc.to_string())); } @@ -89,16 +101,59 @@ pub fn base_classes(obj: &PyObjectRef) -> Vec { _mro(obj.typ()).unwrap() } -pub fn isinstance(obj: &PyObjectRef, cls: &PyObjectRef) -> bool { +/// Determines if obj actually an instance of cls, this doesn't call __instancecheck__, so only use +/// this if cls is known to have not overridden the base __instancecheck__ magic method. +pub fn real_isinstance(obj: &PyObjectRef, cls: &PyObjectRef) -> bool { let mro = _mro(obj.typ()).unwrap(); mro.into_iter().any(|c| c.is(&cls)) } -pub fn issubclass(typ: &PyObjectRef, cls: &PyObjectRef) -> bool { - let mro = _mro(typ.clone()).unwrap(); +pub fn isinstance(vm: &mut VirtualMachine, obj: &PyObjectRef, cls: &PyObjectRef) -> PyResult { + // cpython first does an exact check on the type, although documentation doesn't state that + // https://github.com/python/cpython/blob/a24107b04c1277e3c1105f98aff5bfa3a98b33a0/Objects/abstract.c#L2408 + if Rc::ptr_eq(&obj.typ(), cls) { + Ok(true) + } else { + let ret = vm.call_method(cls, "__instancecheck__", vec![obj.clone()])?; + objbool::boolval(vm, ret) + } +} + +fn type_instance_check(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(typ, Some(vm.ctx.type_type())), (obj, None)] + ); + Ok(vm.new_bool(real_isinstance(obj, typ))) +} + +pub fn real_issubclass(subclass: &PyObjectRef, cls: &PyObjectRef) -> bool { + let mro = _mro(subclass.clone()).unwrap(); mro.into_iter().any(|c| c.is(&cls)) } +pub fn issubclass( + vm: &mut VirtualMachine, + subclass: &PyObjectRef, + cls: &PyObjectRef, +) -> PyResult { + let ret = vm.call_method(cls, "__subclasscheck__", vec![subclass.clone()])?; + objbool::boolval(vm, ret) +} + +fn type_subclass_check(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [ + (cls, Some(vm.ctx.type_type())), + (subclass, Some(vm.ctx.type_type())) + ] + ); + Ok(vm.new_bool(real_issubclass(subclass, cls))) +} + pub fn get_type_name(typ: &PyObjectRef) -> String { if let PyObjectPayload::Class { name, .. } = &typ.payload { name.clone() diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 6be9bf4573..df5d646712 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -967,7 +967,7 @@ impl PyFuncArgs { ) -> Result, PyObjectRef> { match self.get_optional_kwarg(key) { Some(kwarg) => { - if objtype::isinstance(&kwarg, &ty) { + if objtype::isinstance(vm, &kwarg, &ty)? { Ok(Some(kwarg)) } else { let expected_ty_name = vm.to_pystr(&ty)?; @@ -1148,7 +1148,7 @@ impl Signature { for (pos, arg) in args.args.iter().enumerate() { if let Some(expected_type) = self.arg_type(pos) { - if !objtype::isinstance(arg, expected_type) { + if !objtype::isinstance(vm, 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)?; diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index fef2840009..b5569823a5 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -42,23 +42,23 @@ impl<'s> serde::Serialize for PyObjectSerializer<'s> { } seq.end() }; - if objtype::isinstance(self.pyobject, &self.vm.ctx.str_type()) { + if objtype::real_isinstance(self.pyobject, &self.vm.ctx.str_type()) { serializer.serialize_str(&objstr::get_value(&self.pyobject)) - } else if objtype::isinstance(self.pyobject, &self.vm.ctx.float_type()) { + } else if objtype::real_isinstance(self.pyobject, &self.vm.ctx.float_type()) { serializer.serialize_f64(objfloat::get_value(self.pyobject)) - } else if objtype::isinstance(self.pyobject, &self.vm.ctx.bool_type()) { + } else if objtype::real_isinstance(self.pyobject, &self.vm.ctx.bool_type()) { serializer.serialize_bool(objbool::get_value(self.pyobject)) - } else if objtype::isinstance(self.pyobject, &self.vm.ctx.int_type()) { + } else if objtype::real_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.vm.ctx.list_type()) - || objtype::isinstance(self.pyobject, &self.vm.ctx.tuple_type()) + } else if objtype::real_isinstance(self.pyobject, &self.vm.ctx.list_type()) + || objtype::real_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.vm.ctx.dict_type()) { + } else if objtype::real_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() { diff --git a/vm/src/stdlib/pystruct.rs b/vm/src/stdlib/pystruct.rs index c686b00063..ad0a8adeac 100644 --- a/vm/src/stdlib/pystruct.rs +++ b/vm/src/stdlib/pystruct.rs @@ -71,7 +71,7 @@ fn pack_bool( arg: &PyObjectRef, data: &mut Write, ) -> Result<(), PyObjectRef> { - if objtype::isinstance(&arg, &vm.ctx.bool_type()) { + if objtype::real_isinstance(&arg, &vm.ctx.bool_type()) { let v = if objbool::get_value(arg) { 1 } else { 0 }; data.write_u8(v).unwrap(); Ok(()) @@ -145,7 +145,7 @@ fn pack_f32( arg: &PyObjectRef, data: &mut Write, ) -> Result<(), PyObjectRef> { - if objtype::isinstance(&arg, &vm.ctx.float_type()) { + if objtype::real_isinstance(&arg, &vm.ctx.float_type()) { let v = objfloat::get_value(arg) as f32; data.write_f32::(v).unwrap(); Ok(()) @@ -159,7 +159,7 @@ fn pack_f64( arg: &PyObjectRef, data: &mut Write, ) -> Result<(), PyObjectRef> { - if objtype::isinstance(&arg, &vm.ctx.float_type()) { + if objtype::real_isinstance(&arg, &vm.ctx.float_type()) { let v = objfloat::get_value(arg) as f64; data.write_f64::(v).unwrap(); Ok(()) @@ -176,7 +176,7 @@ fn struct_pack(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ))) } else { let fmt_arg = args.args[0].clone(); - if objtype::isinstance(&fmt_arg, &vm.ctx.str_type()) { + if objtype::real_isinstance(&fmt_arg, &vm.ctx.str_type()) { let fmt_str = objstr::get_value(&fmt_arg); let codes = parse_format_string(fmt_str); diff --git a/vm/src/stdlib/types.rs b/vm/src/stdlib/types.rs index 959b141f55..9beb8efe5e 100644 --- a/vm/src/stdlib/types.rs +++ b/vm/src/stdlib/types.rs @@ -18,7 +18,7 @@ fn types_new_class(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let bases = match bases { Some(b) => { - if objtype::isinstance(b, &vm.ctx.tuple_type()) { + if objtype::real_isinstance(b, &vm.ctx.tuple_type()) { objsequence::get_elements(b).to_vec() } else { return Err(vm.new_type_error("Bases must be a tuple".to_string())); diff --git a/vm/src/vm.rs b/vm/src/vm.rs index be15f73f9a..3d10506f6a 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -488,8 +488,8 @@ impl VirtualMachine { value: &PyObjectRef, ) -> Result, PyObjectRef> { // Extract elements from item, if possible: - let elements = if objtype::isinstance(value, &self.ctx.tuple_type()) - || objtype::isinstance(value, &self.ctx.list_type()) + let elements = if objtype::real_isinstance(value, &self.ctx.tuple_type()) + || objtype::real_isinstance(value, &self.ctx.list_type()) { objsequence::get_elements(value).to_vec() } else { diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index adcbb31735..3d0a84bad0 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -17,7 +17,7 @@ pub fn js_py_typeerror(vm: &mut VirtualMachine, js_err: JsValue) -> PyObjectRef pub fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { if let Some(ref wasm_id) = vm.wasm_id { - if objtype::isinstance(&py_obj, &vm.ctx.function_type()) { + if objtype::real_isinstance(&py_obj, &vm.ctx.function_type()) { let wasm_vm = WASMVirtualMachine { id: wasm_id.clone(), }; @@ -62,8 +62,8 @@ pub fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { return func; } } - if objtype::isinstance(&py_obj, &vm.ctx.bytes_type()) - || objtype::isinstance(&py_obj, &vm.ctx.bytearray_type()) + if objtype::real_isinstance(&py_obj, &vm.ctx.bytes_type()) + || objtype::real_isinstance(&py_obj, &vm.ctx.bytearray_type()) { let bytes = objbytes::get_value(&py_obj); let arr = Uint8Array::new_with_length(bytes.len() as u32); diff --git a/wasm/lib/src/wasm_builtins.rs b/wasm/lib/src/wasm_builtins.rs index 7f2b7a6225..831e379994 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 Result Date: Thu, 28 Feb 2019 20:26:03 +1300 Subject: [PATCH 101/380] Swap naming of is{instance, subclass} <-> real_is{instance, subclass} --- vm/src/builtins.rs | 22 +++++------ vm/src/exceptions.rs | 4 +- vm/src/frame.rs | 4 +- vm/src/macros.rs | 2 +- vm/src/obj/objbool.rs | 2 +- vm/src/obj/objbytearray.rs | 4 +- vm/src/obj/objbytes.rs | 12 +++--- vm/src/obj/objcomplex.rs | 14 +++---- vm/src/obj/objdict.rs | 2 +- vm/src/obj/objfloat.rs | 70 +++++++++++++++++------------------ vm/src/obj/objint.rs | 54 +++++++++++++-------------- vm/src/obj/objiter.rs | 2 +- vm/src/obj/objlist.rs | 18 ++++----- vm/src/obj/objrange.rs | 6 +-- vm/src/obj/objset.rs | 2 +- vm/src/obj/objstr.rs | 20 +++++----- vm/src/obj/objsuper.rs | 4 +- vm/src/obj/objtuple.rs | 14 +++---- vm/src/obj/objtype.rs | 23 ++++++++---- vm/src/pyobject.rs | 4 +- vm/src/stdlib/json.rs | 14 +++---- vm/src/stdlib/pystruct.rs | 8 ++-- vm/src/stdlib/types.rs | 2 +- vm/src/vm.rs | 4 +- wasm/lib/src/convert.rs | 6 +-- wasm/lib/src/wasm_builtins.rs | 4 +- 26 files changed, 164 insertions(+), 157 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 1467edf5f7..2bae47a8d7 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -198,9 +198,9 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); // Determine code object: - let code_obj = if objtype::real_isinstance(source, &vm.ctx.code_type()) { + let code_obj = if objtype::isinstance(source, &vm.ctx.code_type()) { source.clone() - } else if objtype::real_isinstance(source, &vm.ctx.str_type()) { + } else if objtype::isinstance(source, &vm.ctx.str_type()) { let mode = compile::Mode::Eval; let source = objstr::get_value(source); // TODO: fix this newline bug: @@ -235,7 +235,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); // Determine code object: - let code_obj = if objtype::real_isinstance(source, &vm.ctx.str_type()) { + let code_obj = if objtype::isinstance(source, &vm.ctx.str_type()) { let mode = compile::Mode::Exec; let source = objstr::get_value(source); // TODO: fix this newline bug: @@ -246,7 +246,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm.new_exception(syntax_error, err.to_string()) }, )? - } else if objtype::real_isinstance(source, &vm.ctx.code_type()) { + } else if objtype::isinstance(source, &vm.ctx.code_type()) { source.clone() } else { return Err(vm.new_type_error("source argument must be str or code object".to_string())); @@ -355,7 +355,7 @@ fn builtin_isinstance(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(obj, None), (typ, Some(vm.get_type()))] ); - let isinstance = objtype::isinstance(vm, obj, typ)?; + let isinstance = objtype::real_isinstance(vm, obj, typ)?; Ok(vm.new_bool(isinstance)) } @@ -366,7 +366,7 @@ fn builtin_issubclass(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(subclass, Some(vm.get_type())), (cls, Some(vm.get_type()))] ); - let issubclass = objtype::issubclass(vm, subclass, cls)?; + let issubclass = objtype::real_issubclass(vm, subclass, cls)?; Ok(vm.context().new_bool(issubclass)) } @@ -505,7 +505,7 @@ fn builtin_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match vm.call_method(iterator, "__next__", vec![]) { Ok(value) => Ok(value), Err(value) => { - if objtype::real_isinstance(&value, &vm.ctx.exceptions.stop_iteration) { + if objtype::isinstance(&value, &vm.ctx.exceptions.stop_iteration) { match default_value { None => Err(value), Some(value) => Ok(value.clone()), @@ -585,7 +585,7 @@ pub fn builtin_print(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .get_optional_kwarg("sep") .filter(|obj| !obj.is(&vm.get_none())); if let Some(ref obj) = sep_arg { - if !objtype::real_isinstance(obj, &vm.ctx.str_type()) { + 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()) @@ -599,7 +599,7 @@ pub fn builtin_print(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .get_optional_kwarg("end") .filter(|obj| !obj.is(&vm.get_none())); if let Some(ref obj) = end_arg { - if !objtype::real_isinstance(obj, &vm.ctx.str_type()) { + 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()) @@ -822,9 +822,9 @@ pub fn builtin_build_class_(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> Py let mut metaclass = args.get_kwarg("metaclass", vm.get_type()); for base in bases.clone() { - if objtype::issubclass(vm, &base.typ(), &metaclass)? { + if objtype::real_issubclass(vm, &base.typ(), &metaclass)? { metaclass = base.typ(); - } else if !objtype::real_issubclass(&metaclass, &base.typ()) { + } else if !objtype::issubclass(&metaclass, &base.typ()) { return Err(vm.new_type_error("metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases".to_string())); } } diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index cade411986..cf3018426d 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -23,11 +23,11 @@ fn exception_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn print_exception(vm: &mut VirtualMachine, exc: &PyObjectRef) { if let Some(tb) = exc.get_attr("__traceback__") { println!("Traceback (most recent call last):"); - if objtype::real_isinstance(&tb, &vm.ctx.list_type()) { + if objtype::isinstance(&tb, &vm.ctx.list_type()) { let mut elements = objsequence::get_elements(&tb).to_vec(); elements.reverse(); for element in elements.iter() { - if objtype::real_isinstance(&element, &vm.ctx.tuple_type()) { + if objtype::isinstance(&element, &vm.ctx.tuple_type()) { let element = objsequence::get_elements(&element); let filename = if let Ok(x) = vm.to_str(&element[0]) { objstr::get_value(&x) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 5e98a42bd9..b6253b6c6a 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -105,7 +105,7 @@ impl Frame { Err(exception) => { // unwind block stack on exception and find any handlers. // Add an entry in the traceback: - assert!(objtype::real_isinstance( + assert!(objtype::isinstance( &exception, &vm.ctx.exceptions.base_exception_type )); @@ -514,7 +514,7 @@ impl Frame { 0 | 2 | 3 => panic!("Not implemented!"), _ => panic!("Invalid parameter for RAISE_VARARGS, must be between 0 to 3"), }; - if objtype::real_isinstance(&exception, &vm.ctx.exceptions.base_exception_type) { + if objtype::isinstance(&exception, &vm.ctx.exceptions.base_exception_type) { info!("Exception raised: {:?}", exception); Err(exception) } else { diff --git a/vm/src/macros.rs b/vm/src/macros.rs index f50dd26de6..2c48286d34 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -19,7 +19,7 @@ macro_rules! type_check { if let Some(expected_type) = $arg_type { let arg = &$args.args[$arg_count]; - if !$crate::obj::objtype::isinstance($vm, arg, &expected_type)? { + if !$crate::obj::objtype::real_isinstance($vm, 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)?; diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index 2405910e54..cf16e1f5f4 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -50,7 +50,7 @@ The class bool is a subclass of the class int, and cannot be subclassed."; } pub fn not(vm: &mut VirtualMachine, obj: &PyObjectRef) -> PyResult { - if objtype::real_isinstance(obj, &vm.ctx.bool_type()) { + if objtype::isinstance(obj, &vm.ctx.bool_type()) { let value = get_value(obj); Ok(vm.ctx.new_bool(!value)) } else { diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index 57fc9d6c2c..0cabf23fdc 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -121,7 +121,7 @@ fn bytearray_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(cls, None)], optional = [(val_option, None)] ); - if !objtype::real_issubclass(cls, &vm.ctx.bytearray_type()) { + if !objtype::issubclass(cls, &vm.ctx.bytearray_type()) { return Err(vm.new_type_error(format!("{:?} is not a subtype of bytearray", cls))); } @@ -164,7 +164,7 @@ fn bytearray_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(a, Some(vm.ctx.bytearray_type())), (b, None)] ); - let result = if objtype::real_isinstance(b, &vm.ctx.bytearray_type()) { + let result = if objtype::isinstance(b, &vm.ctx.bytearray_type()) { get_value(a).to_vec() == get_value(b).to_vec() } else { false diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 71b8282484..6ab2f72af2 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -52,7 +52,7 @@ fn bytes_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(cls, None)], optional = [(val_option, None)] ); - if !objtype::real_issubclass(cls, &vm.ctx.bytes_type()) { + if !objtype::issubclass(cls, &vm.ctx.bytes_type()) { return Err(vm.new_type_error(format!("{:?} is not a subtype of bytes", cls))); } @@ -85,7 +85,7 @@ fn bytes_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(a, Some(vm.ctx.bytes_type())), (b, None)] ); - let result = if objtype::real_isinstance(b, &vm.ctx.bytes_type()) { + let result = if objtype::isinstance(b, &vm.ctx.bytes_type()) { get_value(a).to_vec() == get_value(b).to_vec() } else { false @@ -100,7 +100,7 @@ fn bytes_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(a, Some(vm.ctx.bytes_type())), (b, None)] ); - let result = if objtype::real_isinstance(b, &vm.ctx.bytes_type()) { + let result = if objtype::isinstance(b, &vm.ctx.bytes_type()) { get_value(a).to_vec() >= get_value(b).to_vec() } else { return Err(vm.new_type_error(format!("Cannot compare {} and {} using '>'", a, b))); @@ -115,7 +115,7 @@ fn bytes_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(a, Some(vm.ctx.bytes_type())), (b, None)] ); - let result = if objtype::real_isinstance(b, &vm.ctx.bytes_type()) { + let result = if objtype::isinstance(b, &vm.ctx.bytes_type()) { get_value(a).to_vec() > get_value(b).to_vec() } else { return Err(vm.new_type_error(format!("Cannot compare {} and {} using '>='", a, b))); @@ -130,7 +130,7 @@ fn bytes_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(a, Some(vm.ctx.bytes_type())), (b, None)] ); - let result = if objtype::real_isinstance(b, &vm.ctx.bytes_type()) { + let result = if objtype::isinstance(b, &vm.ctx.bytes_type()) { get_value(a).to_vec() <= get_value(b).to_vec() } else { return Err(vm.new_type_error(format!("Cannot compare {} and {} using '<'", a, b))); @@ -145,7 +145,7 @@ fn bytes_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(a, Some(vm.ctx.bytes_type())), (b, None)] ); - let result = if objtype::real_isinstance(b, &vm.ctx.bytes_type()) { + let result = if objtype::isinstance(b, &vm.ctx.bytes_type()) { get_value(a).to_vec() < get_value(b).to_vec() } else { return Err(vm.new_type_error(format!("Cannot compare {} and {} using '<='", a, b))); diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index eba0de66e6..0b0d5ba7a2 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -60,7 +60,7 @@ fn complex_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { optional = [(real, None), (imag, None)] ); - if !objtype::real_issubclass(cls, &vm.ctx.complex_type()) { + if !objtype::issubclass(cls, &vm.ctx.complex_type()) { return Err(vm.new_type_error(format!("{:?} is not a subtype of complex", cls))); } @@ -109,9 +109,9 @@ fn complex_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); - if objtype::real_isinstance(i2, &vm.ctx.complex_type()) { + if objtype::isinstance(i2, &vm.ctx.complex_type()) { Ok(vm.ctx.new_complex(v1 + get_value(i2))) - } else if objtype::real_isinstance(i2, &vm.ctx.int_type()) { + } else if objtype::isinstance(i2, &vm.ctx.int_type()) { Ok(vm.ctx.new_complex(Complex64::new( v1.re + objint::get_value(i2).to_f64().unwrap(), v1.im, @@ -130,7 +130,7 @@ fn complex_radd(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let v1 = get_value(i); - if objtype::real_isinstance(i2, &vm.ctx.int_type()) { + if objtype::isinstance(i2, &vm.ctx.int_type()) { Ok(vm.ctx.new_complex(Complex64::new( v1.re + objint::get_value(i2).to_f64().unwrap(), v1.im, @@ -156,14 +156,14 @@ fn complex_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let z = get_value(zelf); - let result = if objtype::real_isinstance(other, &vm.ctx.complex_type()) { + let result = if objtype::isinstance(other, &vm.ctx.complex_type()) { z == get_value(other) - } else if objtype::real_isinstance(other, &vm.ctx.int_type()) { + } 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::real_isinstance(other, &vm.ctx.float_type()) { + } else if objtype::isinstance(other, &vm.ctx.float_type()) { z.im == 0.0 && z.re == objfloat::get_value(other) } else { return Ok(vm.ctx.not_implemented()); diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 151c8a57fd..03a307b4c3 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -142,7 +142,7 @@ fn dict_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let dict = vm.ctx.new_dict(); if let Some(dict_obj) = dict_obj { - if objtype::real_isinstance(&dict_obj, &vm.ctx.dict_type()) { + if objtype::isinstance(&dict_obj, &vm.ctx.dict_type()) { for (needle, value) in get_key_value_pairs(&dict_obj) { set_item(&dict, vm, &needle, &value); } diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 4de98b03ff..4f8bf15d92 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -17,16 +17,16 @@ fn float_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn float_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(cls, None), (arg, None)]); - let value = if objtype::real_isinstance(arg, &vm.ctx.float_type()) { + let value = if objtype::isinstance(arg, &vm.ctx.float_type()) { get_value(arg) - } else if objtype::real_isinstance(arg, &vm.ctx.int_type()) { + } else if objtype::isinstance(arg, &vm.ctx.int_type()) { 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::real_isinstance(arg, &vm.ctx.str_type()) { + } else if objtype::isinstance(arg, &vm.ctx.str_type()) { match lexical::try_parse(objstr::get_value(arg)) { Ok(f) => f, Err(_) => { @@ -36,7 +36,7 @@ fn float_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); } } - } else if objtype::real_isinstance(arg, &vm.ctx.bytes_type()) { + } else if objtype::isinstance(arg, &vm.ctx.bytes_type()) { match lexical::try_parse(objbytes::get_value(arg).as_slice()) { Ok(f) => f, Err(_) => { @@ -63,7 +63,7 @@ pub fn get_value(obj: &PyObjectRef) -> f64 { } pub fn make_float(vm: &mut VirtualMachine, obj: &PyObjectRef) -> Result { - if objtype::real_isinstance(obj, &vm.ctx.float_type()) { + if objtype::isinstance(obj, &vm.ctx.float_type()) { Ok(get_value(obj)) } else if let Ok(method) = vm.get_method(obj.clone(), "__float__") { let res = vm.invoke( @@ -86,10 +86,10 @@ fn float_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.float_type())), (other, None)] ); let zelf = get_value(zelf); - let result = if objtype::real_isinstance(other, &vm.ctx.float_type()) { + let result = if objtype::isinstance(other, &vm.ctx.float_type()) { let other = get_value(other); zelf == other - } else if objtype::real_isinstance(other, &vm.ctx.int_type()) { + } else if objtype::isinstance(other, &vm.ctx.int_type()) { let other_int = objint::get_value(other); if let (Some(zelf_int), Some(other_float)) = (zelf.to_bigint(), other_int.to_f64()) { @@ -111,9 +111,9 @@ fn float_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); - if objtype::real_isinstance(i2, &vm.ctx.float_type()) { + if objtype::isinstance(i2, &vm.ctx.float_type()) { Ok(vm.ctx.new_bool(v1 < get_value(i2))) - } else if objtype::real_isinstance(i2, &vm.ctx.int_type()) { + } else if objtype::isinstance(i2, &vm.ctx.int_type()) { Ok(vm .ctx .new_bool(v1 < objint::get_value(i2).to_f64().unwrap())) @@ -130,9 +130,9 @@ fn float_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); - if objtype::real_isinstance(i2, &vm.ctx.float_type()) { + if objtype::isinstance(i2, &vm.ctx.float_type()) { Ok(vm.ctx.new_bool(v1 <= get_value(i2))) - } else if objtype::real_isinstance(i2, &vm.ctx.int_type()) { + } else if objtype::isinstance(i2, &vm.ctx.int_type()) { Ok(vm .ctx .new_bool(v1 <= objint::get_value(i2).to_f64().unwrap())) @@ -149,9 +149,9 @@ fn float_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); - if objtype::real_isinstance(i2, &vm.ctx.float_type()) { + if objtype::isinstance(i2, &vm.ctx.float_type()) { Ok(vm.ctx.new_bool(v1 > get_value(i2))) - } else if objtype::real_isinstance(i2, &vm.ctx.int_type()) { + } else if objtype::isinstance(i2, &vm.ctx.int_type()) { Ok(vm .ctx .new_bool(v1 > objint::get_value(i2).to_f64().unwrap())) @@ -168,9 +168,9 @@ fn float_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); - if objtype::real_isinstance(i2, &vm.ctx.float_type()) { + if objtype::isinstance(i2, &vm.ctx.float_type()) { Ok(vm.ctx.new_bool(v1 >= get_value(i2))) - } else if objtype::real_isinstance(i2, &vm.ctx.int_type()) { + } else if objtype::isinstance(i2, &vm.ctx.int_type()) { Ok(vm .ctx .new_bool(v1 >= objint::get_value(i2).to_f64().unwrap())) @@ -192,9 +192,9 @@ fn float_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(zelf); - if objtype::real_isinstance(other, &vm.ctx.float_type()) { + if objtype::isinstance(other, &vm.ctx.float_type()) { Ok(vm.ctx.new_float(v1 + get_value(other))) - } else if objtype::real_isinstance(other, &vm.ctx.int_type()) { + } else if objtype::isinstance(other, &vm.ctx.int_type()) { Ok(vm .ctx .new_float(v1 + objint::get_value(other).to_f64().unwrap())) @@ -214,8 +214,8 @@ fn float_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.float_type())), (i2, None)] ); let args = PyFuncArgs::new(vec![i.clone(), i2.clone()], vec![]); - if objtype::real_isinstance(i2, &vm.ctx.float_type()) - || objtype::real_isinstance(i2, &vm.ctx.int_type()) + 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())?; @@ -233,9 +233,9 @@ fn float_floordiv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); - let v2 = if objtype::real_isinstance(i2, &vm.ctx.float_type) { + let v2 = if objtype::isinstance(i2, &vm.ctx.float_type) { get_value(i2) - } else if objtype::real_isinstance(i2, &vm.ctx.int_type) { + } 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()))? @@ -257,9 +257,9 @@ fn float_sub(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.float_type())), (other, None)] ); let v1 = get_value(zelf); - if objtype::real_isinstance(other, &vm.ctx.float_type()) { + if objtype::isinstance(other, &vm.ctx.float_type()) { Ok(vm.ctx.new_float(v1 - get_value(other))) - } else if objtype::real_isinstance(other, &vm.ctx.int_type()) { + } else if objtype::isinstance(other, &vm.ctx.int_type()) { Ok(vm .ctx .new_float(v1 - objint::get_value(other).to_f64().unwrap())) @@ -275,9 +275,9 @@ fn float_rsub(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.float_type())), (other, None)] ); let v1 = get_value(zelf); - if objtype::real_isinstance(other, &vm.ctx.float_type()) { + if objtype::isinstance(other, &vm.ctx.float_type()) { Ok(vm.ctx.new_float(get_value(other) - v1)) - } else if objtype::real_isinstance(other, &vm.ctx.int_type()) { + } else if objtype::isinstance(other, &vm.ctx.int_type()) { Ok(vm .ctx .new_float(objint::get_value(other).to_f64().unwrap() - v1)) @@ -294,9 +294,9 @@ fn float_mod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); - let v2 = if objtype::real_isinstance(i2, &vm.ctx.float_type) { + let v2 = if objtype::isinstance(i2, &vm.ctx.float_type) { get_value(i2) - } else if objtype::real_isinstance(i2, &vm.ctx.int_type) { + } 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()))? @@ -326,10 +326,10 @@ fn float_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); - if objtype::real_isinstance(i2, &vm.ctx.float_type()) { + if objtype::isinstance(i2, &vm.ctx.float_type()) { let result = v1.powf(get_value(i2)); Ok(vm.ctx.new_float(result)) - } else if objtype::real_isinstance(i2, &vm.ctx.int_type()) { + } else if objtype::isinstance(i2, &vm.ctx.int_type()) { let result = v1.powf(objint::get_value(i2).to_f64().unwrap()); Ok(vm.ctx.new_float(result)) } else { @@ -345,9 +345,9 @@ fn float_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(zelf); - let v2 = if objtype::real_isinstance(other, &vm.ctx.float_type) { + let v2 = if objtype::isinstance(other, &vm.ctx.float_type) { get_value(other) - } else if objtype::real_isinstance(other, &vm.ctx.int_type) { + } else if objtype::isinstance(other, &vm.ctx.int_type) { objint::get_value(other) .to_f64() .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))? @@ -370,9 +370,9 @@ fn float_rtruediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(zelf); - let v2 = if objtype::real_isinstance(other, &vm.ctx.float_type) { + let v2 = if objtype::isinstance(other, &vm.ctx.float_type) { get_value(other) - } else if objtype::real_isinstance(other, &vm.ctx.int_type) { + } else if objtype::isinstance(other, &vm.ctx.int_type) { objint::get_value(other) .to_f64() .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))? @@ -394,9 +394,9 @@ fn float_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.float_type())), (other, None)] ); let v1 = get_value(zelf); - if objtype::real_isinstance(other, &vm.ctx.float_type) { + if objtype::isinstance(other, &vm.ctx.float_type) { Ok(vm.ctx.new_float(v1 * get_value(other))) - } else if objtype::real_isinstance(other, &vm.ctx.int_type) { + } else if objtype::isinstance(other, &vm.ctx.int_type) { Ok(vm .ctx .new_float(v1 * objint::get_value(other).to_f64().unwrap())) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 611880ecc1..7e22f19be2 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -44,7 +44,7 @@ fn int_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(cls, None)], optional = [(val_option, None)] ); - if !objtype::real_issubclass(cls, &vm.ctx.int_type()) { + if !objtype::issubclass(cls, &vm.ctx.int_type()) { return Err(vm.new_type_error(format!("{:?} is not a subtype of int", cls))); } @@ -68,11 +68,11 @@ pub fn to_int( obj: &PyObjectRef, base: u32, ) -> Result { - let val = if objtype::real_isinstance(obj, &vm.ctx.int_type()) { + let val = if objtype::isinstance(obj, &vm.ctx.int_type()) { get_value(obj) - } else if objtype::real_isinstance(obj, &vm.ctx.float_type()) { + } else if objtype::isinstance(obj, &vm.ctx.float_type()) { objfloat::get_value(obj).to_bigint().unwrap() - } else if objtype::real_isinstance(obj, &vm.ctx.str_type()) { + } else if objtype::isinstance(obj, &vm.ctx.str_type()) { let s = objstr::get_value(obj); match i32::from_str_radix(&s, base) { Ok(v) => v.to_bigint().unwrap(), @@ -131,7 +131,7 @@ fn int_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let zelf = BigInt::from_pyobj(zelf); - let result = if objtype::real_isinstance(other, &vm.ctx.int_type()) { + let result = if objtype::isinstance(other, &vm.ctx.int_type()) { let other = BigInt::from_pyobj(other); zelf == other } else { @@ -148,7 +148,7 @@ fn int_ne(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let zelf = BigInt::from_pyobj(zelf); - let result = if objtype::real_isinstance(other, &vm.ctx.int_type()) { + let result = if objtype::isinstance(other, &vm.ctx.int_type()) { let other = BigInt::from_pyobj(other); zelf != other } else { @@ -164,7 +164,7 @@ fn int_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); - if !objtype::real_isinstance(other, &vm.ctx.int_type()) { + if !objtype::isinstance(other, &vm.ctx.int_type()) { return Ok(vm.ctx.not_implemented()); } @@ -181,7 +181,7 @@ fn int_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); - if !objtype::real_isinstance(other, &vm.ctx.int_type()) { + if !objtype::isinstance(other, &vm.ctx.int_type()) { return Ok(vm.ctx.not_implemented()); } @@ -198,7 +198,7 @@ fn int_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); - if !objtype::real_isinstance(other, &vm.ctx.int_type()) { + if !objtype::isinstance(other, &vm.ctx.int_type()) { return Ok(vm.ctx.not_implemented()); } @@ -215,7 +215,7 @@ fn int_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); - if !objtype::real_isinstance(other, &vm.ctx.int_type()) { + if !objtype::isinstance(other, &vm.ctx.int_type()) { return Ok(vm.ctx.not_implemented()); } @@ -232,7 +232,7 @@ fn int_lshift(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); - if !objtype::real_isinstance(i2, &vm.ctx.int_type()) { + 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()), @@ -261,7 +261,7 @@ fn int_rshift(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); - if !objtype::real_isinstance(i2, &vm.ctx.int_type()) { + 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()), @@ -303,7 +303,7 @@ fn int_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); - if objtype::real_isinstance(other, &vm.ctx.int_type()) { + if objtype::isinstance(other, &vm.ctx.int_type()) { Ok(vm.ctx.new_int(get_value(zelf) + get_value(other))) } else { Ok(vm.ctx.not_implemented()) @@ -326,7 +326,7 @@ fn int_floordiv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); - if objtype::real_isinstance(i2, &vm.ctx.int_type()) { + if objtype::isinstance(i2, &vm.ctx.int_type()) { let (v1, v2) = (get_value(i), get_value(i2)); if v2 != BigInt::zero() { @@ -378,7 +378,7 @@ fn int_sub(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); - if objtype::real_isinstance(other, &vm.ctx.int_type()) { + if objtype::isinstance(other, &vm.ctx.int_type()) { Ok(vm.ctx.new_int(get_value(zelf) - get_value(other))) } else { Ok(vm.ctx.not_implemented()) @@ -391,7 +391,7 @@ fn int_rsub(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); - if objtype::real_isinstance(other, &vm.ctx.int_type()) { + if objtype::isinstance(other, &vm.ctx.int_type()) { Ok(vm.ctx.new_int(get_value(other) - get_value(zelf))) } else { Ok(vm.ctx.not_implemented()) @@ -404,7 +404,7 @@ fn int_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); - if objtype::real_isinstance(other, &vm.ctx.int_type()) { + if objtype::isinstance(other, &vm.ctx.int_type()) { Ok(vm.ctx.new_int(get_value(zelf) * get_value(other))) } else { Ok(vm.ctx.not_implemented()) @@ -422,7 +422,7 @@ fn int_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); - if objtype::real_isinstance(other, &vm.ctx.int_type()) { + if objtype::isinstance(other, &vm.ctx.int_type()) { div_ints(vm, &get_value(zelf), &get_value(other)) } else { Ok(vm.ctx.not_implemented()) @@ -436,7 +436,7 @@ fn int_rtruediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.int_type())), (other, None)] ); - if objtype::real_isinstance(other, &vm.ctx.int_type()) { + if objtype::isinstance(other, &vm.ctx.int_type()) { div_ints(vm, &get_value(other), &get_value(zelf)) } else { Ok(vm.ctx.not_implemented()) @@ -482,7 +482,7 @@ fn int_mod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); let v1 = get_value(i); - if objtype::real_isinstance(i2, &vm.ctx.int_type()) { + if objtype::isinstance(i2, &vm.ctx.int_type()) { let v2 = get_value(i2); if v2 != BigInt::zero() { @@ -513,10 +513,10 @@ fn int_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); let v1 = get_value(i); - if objtype::real_isinstance(i2, &vm.ctx.int_type()) { + if objtype::isinstance(i2, &vm.ctx.int_type()) { let v2 = get_value(i2).to_u32().unwrap(); Ok(vm.ctx.new_int(v1.pow(v2))) - } else if objtype::real_isinstance(i2, &vm.ctx.float_type()) { + } 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))) } else { @@ -531,7 +531,7 @@ fn int_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); - if objtype::real_isinstance(i2, &vm.ctx.int_type()) { + if objtype::isinstance(i2, &vm.ctx.int_type()) { let v1 = get_value(i); let v2 = get_value(i2); @@ -556,7 +556,7 @@ fn int_xor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); let v1 = get_value(i); - if objtype::real_isinstance(i2, &vm.ctx.int_type()) { + if objtype::isinstance(i2, &vm.ctx.int_type()) { let v2 = get_value(i2); Ok(vm.ctx.new_int(v1 ^ v2)) } else { @@ -571,7 +571,7 @@ fn int_rxor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); - if objtype::real_isinstance(i2, &vm.ctx.int_type()) { + if objtype::isinstance(i2, &vm.ctx.int_type()) { let right_val = get_value(i); let left_val = get_value(i2); @@ -588,7 +588,7 @@ fn int_or(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); let v1 = get_value(i); - if objtype::real_isinstance(i2, &vm.ctx.int_type()) { + if objtype::isinstance(i2, &vm.ctx.int_type()) { let v2 = get_value(i2); Ok(vm.ctx.new_int(v1 | v2)) } else { @@ -603,7 +603,7 @@ fn int_and(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); let v1 = get_value(i); - if objtype::real_isinstance(i2, &vm.ctx.int_type()) { + if objtype::isinstance(i2, &vm.ctx.int_type()) { let v2 = get_value(i2); Ok(vm.ctx.new_int(v1 & v2)) } else { diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 53f55dff14..a72339fbb0 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -39,7 +39,7 @@ pub fn get_next_object( Ok(value) => Ok(Some(value)), Err(next_error) => { // Check if we have stopiteration, or something else: - if objtype::real_isinstance(&next_error, &vm.ctx.exceptions.stop_iteration) { + if objtype::isinstance(&next_error, &vm.ctx.exceptions.stop_iteration) { Ok(None) } else { Err(next_error) diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index b9e8637145..e09f15a8b1 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -22,7 +22,7 @@ fn set_item( idx: PyObjectRef, obj: PyObjectRef, ) -> PyResult { - if objtype::real_isinstance(&idx, &vm.ctx.int_type()) { + if objtype::isinstance(&idx, &vm.ctx.int_type()) { let value = objint::get_value(&idx).to_i32().unwrap(); if let Some(pos_index) = l.get_pos(value) { l[pos_index] = obj; @@ -46,7 +46,7 @@ fn list_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { optional = [(iterable, None)] ); - if !objtype::real_issubclass(cls, &vm.ctx.list_type()) { + if !objtype::issubclass(cls, &vm.ctx.list_type()) { return Err(vm.new_type_error(format!("{:?} is not a subtype of list", cls))); } @@ -75,7 +75,7 @@ fn list_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { return Ok(vm.ctx.new_bool(true)); } - let result = if objtype::real_isinstance(other, &vm.ctx.list_type()) { + let result = if objtype::isinstance(other, &vm.ctx.list_type()) { let zelf = get_elements(zelf); let other = get_elements(other); seq_equal(vm, &zelf, &other)? @@ -92,7 +92,7 @@ fn list_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.list_type())), (other, None)] ); - let result = if objtype::real_isinstance(other, &vm.ctx.list_type()) { + 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)? @@ -110,7 +110,7 @@ fn list_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.list_type())), (other, None)] ); - let result = if objtype::real_isinstance(other, &vm.ctx.list_type()) { + 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)? @@ -128,7 +128,7 @@ fn list_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.list_type())), (other, None)] ); - let result = if objtype::real_isinstance(other, &vm.ctx.list_type()) { + 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)? @@ -146,7 +146,7 @@ fn list_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.list_type())), (other, None)] ); - let result = if objtype::real_isinstance(other, &vm.ctx.list_type()) { + 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)? @@ -164,7 +164,7 @@ fn list_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(o, Some(vm.ctx.list_type())), (o2, None)] ); - if objtype::real_isinstance(o2, &vm.ctx.list_type()) { + 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()).cloned().collect(); @@ -181,7 +181,7 @@ fn list_iadd(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.list_type())), (other, None)] ); - if objtype::real_isinstance(other, &vm.ctx.list_type()) { + if objtype::isinstance(other, &vm.ctx.list_type()) { get_mut_elements(zelf).extend_from_slice(&get_elements(other)); Ok(zelf.clone()) } else { diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 89c1facecb..d80b6aa015 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -361,7 +361,7 @@ fn range_bool(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn range_contains(vm: &mut VirtualMachine, zelf: PyRange, needle: PyObjectRef) -> bool { - if objtype::real_isinstance(&needle, &vm.ctx.int_type()) { + if objtype::isinstance(&needle, &vm.ctx.int_type()) { zelf.contains(&objint::get_value(&needle)) } else { false @@ -377,7 +377,7 @@ fn range_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let range = get_value(zelf); - if objtype::real_isinstance(needle, &vm.ctx.int_type()) { + if objtype::isinstance(needle, &vm.ctx.int_type()) { let needle = objint::get_value(needle); match range.index_of(&needle) { @@ -398,7 +398,7 @@ fn range_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let range = get_value(zelf); - if objtype::real_isinstance(item, &vm.ctx.int_type()) { + if objtype::isinstance(item, &vm.ctx.int_type()) { Ok(vm.ctx.new_int(range.count(&objint::get_value(item)))) } else { Ok(vm.ctx.new_int(0)) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 3a1f3b6c73..c041ce5673 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -146,7 +146,7 @@ fn set_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { optional = [(iterable, None)] ); - if !objtype::real_issubclass(cls, &vm.ctx.set_type()) { + if !objtype::issubclass(cls, &vm.ctx.set_type()) { return Err(vm.new_type_error(format!("{} is not a subtype of set", cls))); } diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 7a02ca24fb..580ab2b801 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -135,7 +135,7 @@ fn str_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(a, Some(vm.ctx.str_type())), (b, None)] ); - let result = if objtype::real_isinstance(b, &vm.ctx.str_type()) { + let result = if objtype::isinstance(b, &vm.ctx.str_type()) { get_value(a) == get_value(b) } else { false @@ -151,7 +151,7 @@ fn str_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); - if objtype::real_isinstance(i2, &vm.ctx.str_type()) { + 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, i2))) @@ -166,7 +166,7 @@ fn str_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); - if objtype::real_isinstance(i2, &vm.ctx.str_type()) { + 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, i2))) @@ -181,7 +181,7 @@ fn str_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); - if objtype::real_isinstance(i2, &vm.ctx.str_type()) { + 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, i2))) @@ -196,7 +196,7 @@ fn str_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); - if objtype::real_isinstance(i2, &vm.ctx.str_type()) { + 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, i2))) @@ -249,7 +249,7 @@ fn str_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(s, Some(vm.ctx.str_type())), (s2, None)] ); - if objtype::real_isinstance(s2, &vm.ctx.str_type()) { + if objtype::isinstance(s2, &vm.ctx.str_type()) { Ok(vm .ctx .new_str(format!("{}{}", get_value(&s), get_value(&s2)))) @@ -266,7 +266,7 @@ fn str_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } let zelf = &args.args[0]; - if !objtype::real_isinstance(&zelf, &vm.ctx.str_type()) { + if !objtype::isinstance(&zelf, &vm.ctx.str_type()) { let zelf_typ = zelf.typ(); let actual_type = vm.to_pystr(&zelf_typ)?; return Err(vm.new_type_error(format!( @@ -293,7 +293,7 @@ fn call_object_format( ) -> PyResult { let returned_type = vm.ctx.new_str(format_spec.to_string()); let result = vm.call_method(&argument, "__format__", vec![returned_type])?; - if !objtype::real_isinstance(&result, &vm.ctx.str_type()) { + if !objtype::isinstance(&result, &vm.ctx.str_type()) { let result_type = result.typ(); let actual_type = vm.to_pystr(&result_type)?; return Err(vm.new_type_error(format!("__format__ must return a str, not {}", actual_type))); @@ -374,7 +374,7 @@ fn str_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(s, Some(vm.ctx.str_type())), (s2, None)] ); - if objtype::real_isinstance(s2, &vm.ctx.int_type()) { + if objtype::isinstance(s2, &vm.ctx.int_type()) { let value1 = get_value(&s); let value2 = objint::get_value(s2).to_i32().unwrap(); let mut result = String::new(); @@ -1092,7 +1092,7 @@ fn to_graphemes>(value: S) -> Vec { } pub fn subscript(vm: &mut VirtualMachine, value: &str, b: PyObjectRef) -> PyResult { - if objtype::real_isinstance(&b, &vm.ctx.int_type()) { + if objtype::isinstance(&b, &vm.ctx.int_type()) { match objint::get_value(&b).to_i32() { Some(pos) => { let graphemes = to_graphemes(value); diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index 0ff9270d37..62e4c61ef8 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -56,7 +56,7 @@ fn super_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; // Check type argument: - if !objtype::real_isinstance(&py_type, &vm.get_type()) { + if !objtype::isinstance(&py_type, &vm.get_type()) { let type_name = objtype::get_type_name(&py_type.typ()); return Err(vm.new_type_error(format!( "super() argument 1 must be type, not {}", @@ -72,7 +72,7 @@ fn super_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; // Check obj type: - if !(objtype::isinstance(vm, &py_obj, &py_type)? || objtype::issubclass(vm, &py_obj, &py_type)?) + if !(objtype::real_isinstance(vm, &py_obj, &py_type)? || objtype::real_issubclass(vm, &py_obj, &py_type)?) { return Err(vm.new_type_error( "super(type, obj): obj must be an instance or subtype of type".to_string(), diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 1c532de26a..7747d2fc92 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -18,7 +18,7 @@ fn tuple_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] ); - let result = if objtype::real_isinstance(other, &vm.ctx.tuple_type()) { + 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)? @@ -36,7 +36,7 @@ fn tuple_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] ); - let result = if objtype::real_isinstance(other, &vm.ctx.tuple_type()) { + 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)? @@ -54,7 +54,7 @@ fn tuple_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] ); - let result = if objtype::real_isinstance(other, &vm.ctx.tuple_type()) { + 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)? @@ -72,7 +72,7 @@ fn tuple_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] ); - let result = if objtype::real_isinstance(other, &vm.ctx.tuple_type()) { + 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)? @@ -90,7 +90,7 @@ fn tuple_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] ); - if objtype::real_isinstance(other, &vm.ctx.tuple_type()) { + 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()).cloned().collect(); @@ -124,7 +124,7 @@ fn tuple_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] ); - let result = if objtype::real_isinstance(other, &vm.ctx.tuple_type()) { + let result = if objtype::isinstance(other, &vm.ctx.tuple_type()) { let zelf = get_elements(zelf); let other = get_elements(other); seq_equal(vm, &zelf, &other)? @@ -174,7 +174,7 @@ fn tuple_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { optional = [(iterable, None)] ); - if !objtype::real_issubclass(cls, &vm.ctx.tuple_type()) { + if !objtype::issubclass(cls, &vm.ctx.tuple_type()) { return Err(vm.new_type_error(format!("{} is not a subtype of tuple", cls))); } diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 37c915b83c..dde9f11496 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -101,14 +101,16 @@ pub fn base_classes(obj: &PyObjectRef) -> Vec { _mro(obj.typ()).unwrap() } -/// Determines if obj actually an instance of cls, this doesn't call __instancecheck__, so only use -/// this if cls is known to have not overridden the base __instancecheck__ magic method. -pub fn real_isinstance(obj: &PyObjectRef, cls: &PyObjectRef) -> bool { +/// Determines if `obj` actually an instance of `cls`, this doesn't call __instancecheck__, so only +/// use this if `cls` is known to have not overridden the base __instancecheck__ magic method. +pub fn isinstance(obj: &PyObjectRef, cls: &PyObjectRef) -> bool { let mro = _mro(obj.typ()).unwrap(); mro.into_iter().any(|c| c.is(&cls)) } -pub fn isinstance(vm: &mut VirtualMachine, obj: &PyObjectRef, cls: &PyObjectRef) -> PyResult { +/// Determines if `obj` is an instance of `cls`, either directly, indirectly or virtually via the +/// __instancecheck__ magic method. +pub fn real_isinstance(vm: &mut VirtualMachine, obj: &PyObjectRef, cls: &PyObjectRef) -> PyResult { // cpython first does an exact check on the type, although documentation doesn't state that // https://github.com/python/cpython/blob/a24107b04c1277e3c1105f98aff5bfa3a98b33a0/Objects/abstract.c#L2408 if Rc::ptr_eq(&obj.typ(), cls) { @@ -125,15 +127,20 @@ fn type_instance_check(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(typ, Some(vm.ctx.type_type())), (obj, None)] ); - Ok(vm.new_bool(real_isinstance(obj, typ))) + Ok(vm.new_bool(isinstance(obj, typ))) } -pub fn real_issubclass(subclass: &PyObjectRef, cls: &PyObjectRef) -> bool { +/// Determines if `subclass` is actually a subclass of `cls`, this doesn't call __subclasscheck__, +/// so only use this if `cls` is known to have not overridden the base __subclasscheck__ magic +/// method. +pub fn issubclass(subclass: &PyObjectRef, cls: &PyObjectRef) -> bool { let mro = _mro(subclass.clone()).unwrap(); mro.into_iter().any(|c| c.is(&cls)) } -pub fn issubclass( +/// Determines if `subclass` is a subclass of `cls`, either directly, indirectly or virtually via +/// the __subclasscheck__ magic method. +pub fn real_issubclass( vm: &mut VirtualMachine, subclass: &PyObjectRef, cls: &PyObjectRef, @@ -151,7 +158,7 @@ fn type_subclass_check(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { (subclass, Some(vm.ctx.type_type())) ] ); - Ok(vm.new_bool(real_issubclass(subclass, cls))) + Ok(vm.new_bool(issubclass(subclass, cls))) } pub fn get_type_name(typ: &PyObjectRef) -> String { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index df5d646712..44c65d625a 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -967,7 +967,7 @@ impl PyFuncArgs { ) -> Result, PyObjectRef> { match self.get_optional_kwarg(key) { Some(kwarg) => { - if objtype::isinstance(vm, &kwarg, &ty)? { + if objtype::real_isinstance(vm, &kwarg, &ty)? { Ok(Some(kwarg)) } else { let expected_ty_name = vm.to_pystr(&ty)?; @@ -1148,7 +1148,7 @@ impl Signature { for (pos, arg) in args.args.iter().enumerate() { if let Some(expected_type) = self.arg_type(pos) { - if !objtype::isinstance(vm, arg, expected_type)? { + if !objtype::real_isinstance(vm, 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)?; diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index b5569823a5..fef2840009 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -42,23 +42,23 @@ impl<'s> serde::Serialize for PyObjectSerializer<'s> { } seq.end() }; - if objtype::real_isinstance(self.pyobject, &self.vm.ctx.str_type()) { + if objtype::isinstance(self.pyobject, &self.vm.ctx.str_type()) { serializer.serialize_str(&objstr::get_value(&self.pyobject)) - } else if objtype::real_isinstance(self.pyobject, &self.vm.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::real_isinstance(self.pyobject, &self.vm.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::real_isinstance(self.pyobject, &self.vm.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::real_isinstance(self.pyobject, &self.vm.ctx.list_type()) - || objtype::real_isinstance(self.pyobject, &self.vm.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::real_isinstance(self.pyobject, &self.vm.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() { diff --git a/vm/src/stdlib/pystruct.rs b/vm/src/stdlib/pystruct.rs index ad0a8adeac..c686b00063 100644 --- a/vm/src/stdlib/pystruct.rs +++ b/vm/src/stdlib/pystruct.rs @@ -71,7 +71,7 @@ fn pack_bool( arg: &PyObjectRef, data: &mut Write, ) -> Result<(), PyObjectRef> { - if objtype::real_isinstance(&arg, &vm.ctx.bool_type()) { + if objtype::isinstance(&arg, &vm.ctx.bool_type()) { let v = if objbool::get_value(arg) { 1 } else { 0 }; data.write_u8(v).unwrap(); Ok(()) @@ -145,7 +145,7 @@ fn pack_f32( arg: &PyObjectRef, data: &mut Write, ) -> Result<(), PyObjectRef> { - if objtype::real_isinstance(&arg, &vm.ctx.float_type()) { + if objtype::isinstance(&arg, &vm.ctx.float_type()) { let v = objfloat::get_value(arg) as f32; data.write_f32::(v).unwrap(); Ok(()) @@ -159,7 +159,7 @@ fn pack_f64( arg: &PyObjectRef, data: &mut Write, ) -> Result<(), PyObjectRef> { - if objtype::real_isinstance(&arg, &vm.ctx.float_type()) { + if objtype::isinstance(&arg, &vm.ctx.float_type()) { let v = objfloat::get_value(arg) as f64; data.write_f64::(v).unwrap(); Ok(()) @@ -176,7 +176,7 @@ fn struct_pack(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ))) } else { let fmt_arg = args.args[0].clone(); - if objtype::real_isinstance(&fmt_arg, &vm.ctx.str_type()) { + if objtype::isinstance(&fmt_arg, &vm.ctx.str_type()) { let fmt_str = objstr::get_value(&fmt_arg); let codes = parse_format_string(fmt_str); diff --git a/vm/src/stdlib/types.rs b/vm/src/stdlib/types.rs index 9beb8efe5e..959b141f55 100644 --- a/vm/src/stdlib/types.rs +++ b/vm/src/stdlib/types.rs @@ -18,7 +18,7 @@ fn types_new_class(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let bases = match bases { Some(b) => { - if objtype::real_isinstance(b, &vm.ctx.tuple_type()) { + if objtype::isinstance(b, &vm.ctx.tuple_type()) { objsequence::get_elements(b).to_vec() } else { return Err(vm.new_type_error("Bases must be a tuple".to_string())); diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 3d10506f6a..be15f73f9a 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -488,8 +488,8 @@ impl VirtualMachine { value: &PyObjectRef, ) -> Result, PyObjectRef> { // Extract elements from item, if possible: - let elements = if objtype::real_isinstance(value, &self.ctx.tuple_type()) - || objtype::real_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 { diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index 3d0a84bad0..adcbb31735 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -17,7 +17,7 @@ pub fn js_py_typeerror(vm: &mut VirtualMachine, js_err: JsValue) -> PyObjectRef pub fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { if let Some(ref wasm_id) = vm.wasm_id { - if objtype::real_isinstance(&py_obj, &vm.ctx.function_type()) { + if objtype::isinstance(&py_obj, &vm.ctx.function_type()) { let wasm_vm = WASMVirtualMachine { id: wasm_id.clone(), }; @@ -62,8 +62,8 @@ pub fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { return func; } } - if objtype::real_isinstance(&py_obj, &vm.ctx.bytes_type()) - || objtype::real_isinstance(&py_obj, &vm.ctx.bytearray_type()) + if objtype::isinstance(&py_obj, &vm.ctx.bytes_type()) + || objtype::isinstance(&py_obj, &vm.ctx.bytearray_type()) { let bytes = objbytes::get_value(&py_obj); let arr = Uint8Array::new_with_length(bytes.len() as u32); diff --git a/wasm/lib/src/wasm_builtins.rs b/wasm/lib/src/wasm_builtins.rs index 831e379994..7f2b7a6225 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 Result Date: Thu, 28 Feb 2019 20:40:32 +1300 Subject: [PATCH 102/380] rustfmt --- vm/src/obj/objfloat.rs | 3 +-- vm/src/obj/objsuper.rs | 3 ++- vm/src/obj/objtype.rs | 6 +++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 4f8bf15d92..4617364ad5 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -214,8 +214,7 @@ fn float_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.float_type())), (i2, None)] ); 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()) + 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())?; diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index 62e4c61ef8..b3d2184300 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -72,7 +72,8 @@ fn super_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; // Check obj type: - if !(objtype::real_isinstance(vm, &py_obj, &py_type)? || objtype::real_issubclass(vm, &py_obj, &py_type)?) + if !(objtype::real_isinstance(vm, &py_obj, &py_type)? + || objtype::real_issubclass(vm, &py_obj, &py_type)?) { return Err(vm.new_type_error( "super(type, obj): obj must be an instance or subtype of type".to_string(), diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index dde9f11496..e1d1ceaf5e 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -110,7 +110,11 @@ pub fn isinstance(obj: &PyObjectRef, cls: &PyObjectRef) -> bool { /// Determines if `obj` is an instance of `cls`, either directly, indirectly or virtually via the /// __instancecheck__ magic method. -pub fn real_isinstance(vm: &mut VirtualMachine, obj: &PyObjectRef, cls: &PyObjectRef) -> PyResult { +pub fn real_isinstance( + vm: &mut VirtualMachine, + obj: &PyObjectRef, + cls: &PyObjectRef, +) -> PyResult { // cpython first does an exact check on the type, although documentation doesn't state that // https://github.com/python/cpython/blob/a24107b04c1277e3c1105f98aff5bfa3a98b33a0/Objects/abstract.c#L2408 if Rc::ptr_eq(&obj.typ(), cls) { From 786eb2cbfbae0899e632ec9df9103a75ce0bf05f Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Thu, 28 Feb 2019 08:33:43 +0000 Subject: [PATCH 103/380] fetch_instruction can just return a reference. --- vm/src/frame.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index b6253b6c6a..9eeb584bd3 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -141,10 +141,8 @@ impl Frame { value } - pub fn fetch_instruction(&self) -> bytecode::Instruction { - // TODO: an immutable reference is enough, we should not - // clone the instruction. - let ins2 = self.code.instructions[*self.lasti.borrow()].clone(); + pub fn fetch_instruction(&self) -> &bytecode::Instruction { + let ins2 = &self.code.instructions[*self.lasti.borrow()]; *self.lasti.borrow_mut() += 1; ins2 } From 1519426cb4c50d7ef0e09598b389ad1969fbda48 Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Thu, 28 Feb 2019 21:52:22 +0100 Subject: [PATCH 104/380] Support raising exceptions without call --- tests/snippets/try_exceptions.py | 13 +++++++++++++ vm/src/frame.rs | 6 ++++++ vm/src/vm.rs | 10 ++++++++++ 3 files changed, 29 insertions(+) diff --git a/tests/snippets/try_exceptions.py b/tests/snippets/try_exceptions.py index a121241a87..4e3b7269aa 100644 --- a/tests/snippets/try_exceptions.py +++ b/tests/snippets/try_exceptions.py @@ -8,6 +8,19 @@ # print(ex.__traceback__) # print(type(ex.__traceback__)) +try: + raise ZeroDivisionError +except ZeroDivisionError as ex: + pass + +class E(Exception): + def __init__(self): + asdf + +try: + raise E +except NameError as ex: + pass l = [] try: diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 9eeb584bd3..7662f016d6 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -515,6 +515,12 @@ impl Frame { if objtype::isinstance(&exception, &vm.ctx.exceptions.base_exception_type) { info!("Exception raised: {:?}", exception); Err(exception) + } else if objtype::isinstance(&exception, &vm.ctx.type_type()) + && objtype::issubclass(&exception, &vm.ctx.exceptions.base_exception_type) + { + let exception = vm.new_empty_exception(exception)?; + info!("Exception raised: {:?}", exception); + Err(exception) } else { let msg = format!( "Can only raise BaseException derived types, not {}", diff --git a/vm/src/vm.rs b/vm/src/vm.rs index be15f73f9a..babeae24f0 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -107,7 +107,17 @@ impl VirtualMachine { self.ctx.new_dict() } + pub fn new_empty_exception(&mut self, exc_type: PyObjectRef) -> PyResult { + info!("New exception created: no msg"); + let args = PyFuncArgs { + args: vec![], + kwargs: vec![], + }; + self.invoke(exc_type, args) + } + pub fn new_exception(&mut self, exc_type: PyObjectRef, msg: String) -> PyObjectRef { + // TODO: exc_type may be user-defined exception, so we should return PyResult // TODO: maybe there is a clearer way to create an instance: info!("New exception created: {}", msg); let pymsg = self.new_str(msg); From e2e3353441dcb83a4297d6eefec3a0d6c50e4fe9 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Fri, 1 Mar 2019 11:39:52 -0800 Subject: [PATCH 105/380] Add factory impl for boxed fn and remove extra constructor --- vm/src/pyobject.rs | 20 ++++++++------------ wasm/lib/src/vm_class.rs | 2 +- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index aed924b1a4..bbde769b24 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -588,16 +588,6 @@ impl PyContext { ) } - pub fn new_rustfunc_from_box( - &self, - function: Box PyResult>, - ) -> PyObjectRef { - PyObject::new( - PyObjectPayload::RustFunction { function }, - self.builtin_function_or_method_type(), - ) - } - pub fn new_frame(&self, code: PyObjectRef, scope: PyObjectRef) -> PyObjectRef { PyObject::new( PyObjectPayload::Frame { @@ -1242,7 +1232,7 @@ tuple_from_py_func_args!(A, B, C); tuple_from_py_func_args!(A, B, C, D); tuple_from_py_func_args!(A, B, C, D, E); -pub type PyNativeFunc = Box PyResult>; +pub type PyNativeFunc = Box PyResult + 'static>; pub trait PyNativeFuncFactory { fn create(self) -> PyNativeFunc; @@ -1257,6 +1247,12 @@ where } } +impl PyNativeFuncFactory for PyNativeFunc { + fn create(self) -> PyNativeFunc { + self + } +} + macro_rules! py_native_func_factory_tuple { ($(($n:tt, $T:ident)),+) => { impl PyNativeFuncFactory<($($T,)+), R> for F @@ -1378,7 +1374,7 @@ pub enum PyObjectPayload { dict: RefCell, }, RustFunction { - function: Box PyResult>, + function: PyNativeFunc, }, Socket { socket: RefCell, diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index a61b36b921..31f9215dc4 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -275,7 +275,7 @@ impl WASMVirtualMachine { .into()); }; vm.ctx - .set_attr(scope, "print", vm.ctx.new_rustfunc_from_box(print_fn)); + .set_attr(scope, "print", vm.ctx.new_rustfunc(print_fn)); Ok(()) }, )? From c2e2441388feb41ad132563c41278f084708701e Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Fri, 1 Mar 2019 10:27:31 +0000 Subject: [PATCH 106/380] Remove Scope variant from PyObject. --- src/main.rs | 8 +--- vm/src/builtins.rs | 28 +++--------- vm/src/bytecode.rs | 2 - vm/src/compile.rs | 6 +-- vm/src/eval.rs | 9 +--- vm/src/frame.rs | 93 +++++++++++++++++----------------------- vm/src/import.rs | 5 ++- vm/src/obj/objframe.rs | 8 +--- vm/src/pyobject.rs | 77 ++++++++------------------------- vm/src/vm.rs | 65 ++++++++++++++++++---------- wasm/lib/src/vm_class.rs | 11 ++--- 11 files changed, 123 insertions(+), 189 deletions(-) diff --git a/src/main.rs b/src/main.rs index 662e433858..cc71ecf126 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,7 +15,7 @@ use rustpython_vm::{ import, obj::objstr, print_exception, - pyobject::{AttributeProtocol, PyObjectRef, PyResult}, + pyobject::{AttributeProtocol, PyResult, ScopeRef}, util, VirtualMachine, }; use rustyline::{error::ReadlineError, Editor}; @@ -120,11 +120,7 @@ fn run_script(vm: &mut VirtualMachine, script_file: &str) -> PyResult { } } -fn shell_exec( - vm: &mut VirtualMachine, - source: &str, - scope: PyObjectRef, -) -> Result<(), CompileError> { +fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: ScopeRef) -> Result<(), CompileError> { match compile::compile( source, &compile::Mode::Single, diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 2bae47a8d7..a846bf6732 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -3,7 +3,6 @@ //! Implements functions listed here: https://docs.python.org/3/library/builtins.html // use std::ops::Deref; -use std::cell::RefCell; use std::char; use std::io::{self, Write}; @@ -16,9 +15,10 @@ use crate::obj::objstr; use crate::obj::objtype; use crate::pyobject::{ - AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, - PyResult, Scope, TypeProtocol, + AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, Scope, ScopeRef, + TypeProtocol, }; +use std::rc::Rc; #[cfg(not(target_arch = "wasm32"))] use crate::stdlib::io::io_open; @@ -258,7 +258,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm.run_code_obj(code_obj, scope) } -fn make_scope(vm: &mut VirtualMachine, locals: Option<&PyObjectRef>) -> PyObjectRef { +fn make_scope(vm: &mut VirtualMachine, locals: Option<&PyObjectRef>) -> ScopeRef { // handle optional global and locals let locals = if let Some(locals) = locals { locals.clone() @@ -268,18 +268,10 @@ fn make_scope(vm: &mut VirtualMachine, locals: Option<&PyObjectRef>) -> PyObject // TODO: handle optional globals // Construct new scope: - let scope_inner = Scope { + Rc::new(Scope { locals, parent: None, - }; - - PyObject { - payload: PyObjectPayload::Scope { - scope: RefCell::new(scope_inner), - }, - typ: None, - } - .into_ref() + }) } fn builtin_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -842,13 +834,7 @@ pub fn builtin_build_class_(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> Py }, )?; - vm.invoke( - function, - PyFuncArgs { - args: vec![namespace.clone()], - kwargs: vec![], - }, - )?; + vm.invoke_with_locals(function, namespace.clone())?; vm.call_method(&metaclass, "__call__", vec![name_arg, bases, namespace]) } diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index b7cefe0b3e..7e31604dc1 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -159,7 +159,6 @@ pub enum Instruction { }, PrintExpr, LoadBuildClass, - StoreLocals, UnpackSequence { size: usize, }, @@ -358,7 +357,6 @@ impl Instruction { MapAdd { i } => w!(MapAdd, i), PrintExpr => w!(PrintExpr), LoadBuildClass => w!(LoadBuildClass), - StoreLocals => w!(StoreLocals), UnpackSequence { size } => w!(UnpackSequence, size), UnpackEx { before, after } => w!(UnpackEx, before, after), Unpack => w!(Unpack), diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 1fcce93c93..9e2a0984f9 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -482,7 +482,7 @@ impl Compiler { self.emit(Instruction::LoadBuildClass); let line_number = self.get_source_line_number(); self.code_object_stack.push(CodeObject::new( - vec![String::from("__locals__")], + vec![], None, vec![], None, @@ -490,10 +490,6 @@ impl Compiler { line_number, name.clone(), )); - self.emit(Instruction::LoadName { - name: String::from("__locals__"), - }); - self.emit(Instruction::StoreLocals); self.compile_statements(body)?; self.emit(Instruction::LoadConst { value: bytecode::Constant::None, diff --git a/vm/src/eval.rs b/vm/src/eval.rs index 30c6034f58..fe88623b2e 100644 --- a/vm/src/eval.rs +++ b/vm/src/eval.rs @@ -3,15 +3,10 @@ extern crate rustpython_parser; use std::error::Error; use crate::compile; -use crate::pyobject::{PyObjectRef, PyResult}; +use crate::pyobject::{PyResult, ScopeRef}; use crate::vm::VirtualMachine; -pub fn eval( - vm: &mut VirtualMachine, - source: &str, - scope: PyObjectRef, - source_path: &str, -) -> PyResult { +pub fn eval(vm: &mut VirtualMachine, source: &str, scope: ScopeRef, source_path: &str) -> PyResult { match compile::compile( source, &compile::Mode::Eval, diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 7662f016d6..7a7e937116 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -16,8 +16,8 @@ use crate::obj::objlist; use crate::obj::objstr; use crate::obj::objtype; use crate::pyobject::{ - DictProtocol, IdProtocol, ParentProtocol, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, - PyResult, TypeProtocol, + DictProtocol, IdProtocol, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, + ScopeRef, TypeProtocol, }; use crate::vm::VirtualMachine; use num_bigint::BigInt; @@ -50,7 +50,7 @@ pub struct Frame { // We need 1 stack per frame stack: RefCell>, // The main data frame of the stack machine blocks: RefCell>, // Block frames, for controlling loops and exceptions - pub locals: PyObjectRef, // Variables + pub scope: ScopeRef, // Variables pub lasti: RefCell, // index of last instruction ran } @@ -64,7 +64,7 @@ pub enum ExecutionResult { pub type FrameResult = Result, PyObjectRef>; impl Frame { - pub fn new(code: PyObjectRef, globals: PyObjectRef) -> Frame { + pub fn new(code: PyObjectRef, scope: ScopeRef) -> Frame { //populate the globals and locals //TODO: This is wrong, check https://github.com/nedbat/byterun/blob/31e6c4a8212c35b5157919abff43a7daa0f377c6/byterun/pyvm2.py#L95 /* @@ -73,7 +73,7 @@ impl Frame { None => HashMap::new(), }; */ - let locals = globals; + // let locals = globals; // locals.extend(callargs); Frame { @@ -82,7 +82,7 @@ impl Frame { blocks: RefCell::new(vec![]), // save the callargs as locals // globals: locals.clone(), - locals, + scope, lasti: RefCell::new(0), } } @@ -436,7 +436,7 @@ impl Frame { }; // pop argc arguments // argument: name, args, globals - let scope = self.locals.clone(); + let scope = self.scope.clone(); let obj = vm.ctx.new_function(code_obj, scope, defaults); self.push_value(obj); Ok(None) @@ -582,16 +582,6 @@ impl Frame { self.push_value(rustfunc); Ok(None) } - bytecode::Instruction::StoreLocals => { - let locals = self.pop_value(); - match self.locals.payload { - PyObjectPayload::Scope { ref scope } => { - (*scope.borrow_mut()).locals = locals; - } - _ => panic!("We really expect our scope to be a scope!"), - } - Ok(None) - } bytecode::Instruction::UnpackSequence { size } => { let value = self.pop_value(); let elements = vm.extract_elements(&value)?; @@ -715,8 +705,10 @@ impl Frame { let obj = import_module(vm, current_path, module)?; for (k, v) in obj.get_key_value_pairs().iter() { - vm.ctx - .set_attr(&self.locals, &objstr::get_value(k), v.clone()); + &self + .scope + .locals + .set_item(&vm.ctx, &objstr::get_value(k), v.clone()); } Ok(None) } @@ -838,37 +830,35 @@ impl Frame { fn store_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult { let obj = self.pop_value(); - vm.ctx.set_attr(&self.locals, name, obj); + self.scope.locals.set_item(&vm.ctx, name, obj); Ok(None) } fn delete_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult { - let locals = match self.locals.payload { - PyObjectPayload::Scope { ref scope } => scope.borrow().locals.clone(), - _ => panic!("We really expect our scope to be a scope!"), - }; - - // Assume here that locals is a dict let name = vm.ctx.new_str(name.to_string()); - vm.call_method(&locals, "__delitem__", vec![name])?; + vm.call_method(&self.scope.locals, "__delitem__", vec![name])?; Ok(None) } fn load_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult { // Lookup name in scope and put it onto the stack! - let mut scope = self.locals.clone(); + let mut scope = self.scope.clone(); loop { - if scope.contains_key(name) { - let obj = scope.get_item(name).unwrap(); + if scope.locals.contains_key(name) { + let obj = scope.locals.get_item(name).unwrap(); self.push_value(obj); - break Ok(None); - } else if scope.has_parent() { - scope = scope.get_parent(); - } else { - let name_error_type = vm.ctx.exceptions.name_error.clone(); - let msg = format!("name '{}' is not defined", name); - let name_error = vm.new_exception(name_error_type, msg); - break Err(name_error); + return Ok(None); + } + match &scope.parent { + Some(parent_scope) => { + scope = parent_scope.clone(); + } + None => { + let name_error_type = vm.ctx.exceptions.name_error.clone(); + let msg = format!("name '{}' is not defined", name); + let name_error = vm.new_exception(name_error_type, msg); + return Err(name_error); + } } } } @@ -1119,21 +1109,18 @@ impl fmt::Debug for Frame { .map(|elem| format!("\n > {:?}", elem)) .collect::>() .join(""); - let local_str = match self.locals.payload { - PyObjectPayload::Scope { ref scope } => match scope.borrow().locals.payload { - PyObjectPayload::Dict { ref elements } => { - objdict::get_key_value_pairs_from_content(&elements.borrow()) - .iter() - .map(|elem| format!("\n {:?} = {:?}", elem.0, elem.1)) - .collect::>() - .join("") - } - ref unexpected => panic!( - "locals unexpectedly not wrapping a dict! instead: {:?}", - unexpected - ), - }, - ref unexpected => panic!("locals unexpectedly not a scope! instead: {:?}", unexpected), + let local_str = match self.scope.locals.payload { + PyObjectPayload::Dict { ref elements } => { + objdict::get_key_value_pairs_from_content(&elements.borrow()) + .iter() + .map(|elem| format!("\n {:?} = {:?}", elem.0, elem.1)) + .collect::>() + .join("") + } + ref unexpected => panic!( + "locals unexpectedly not wrapping a dict! instead: {:?}", + unexpected + ), }; write!( f, diff --git a/vm/src/import.rs b/vm/src/import.rs index 454cf84422..3dab1418ca 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -43,8 +43,9 @@ fn import_uncached_module( let builtins = vm.get_builtin_scope(); let scope = vm.ctx.new_scope(Some(builtins)); - vm.ctx - .set_attr(&scope, "__name__", vm.new_str(module.to_string())); + scope + .locals + .set_item(&vm.ctx, "__name__", vm.new_str(module.to_string())); vm.run_code_obj(code_obj, scope.clone())?; Ok(vm.ctx.new_module(module, scope)) } diff --git a/vm/src/obj/objframe.rs b/vm/src/obj/objframe.rs index d5cc8e0c53..37acd33385 100644 --- a/vm/src/obj/objframe.rs +++ b/vm/src/obj/objframe.rs @@ -30,13 +30,7 @@ fn frame_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn frame_flocals(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(frame, Some(vm.ctx.frame_type()))]); let frame = get_value(frame); - let py_scope = frame.locals.clone(); - - if let PyObjectPayload::Scope { scope } = &py_scope.payload { - Ok(scope.borrow().locals.clone()) - } else { - panic!("The scope isn't a scope!"); - } + Ok(frame.scope.locals.clone()) } fn frame_fcode(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 44c65d625a..585f7a9a86 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -155,8 +155,9 @@ pub struct PyContext { pub struct Scope { pub locals: PyObjectRef, // Variables // TODO: pub locals: RefCell, // Variables - pub parent: Option, // Parent scope + pub parent: Option>, // Parent scope } +pub type ScopeRef = Rc; fn _nothing() -> PyObjectRef { PyObject { @@ -549,21 +550,16 @@ impl PyContext { objtype::new(self.type_type(), name, vec![base], PyAttributes::new()).unwrap() } - pub fn new_scope(&self, parent: Option) -> PyObjectRef { + pub fn new_scope(&self, parent: Option) -> ScopeRef { let locals = self.new_dict(); - let scope = RefCell::new(Scope { locals, parent }); - PyObject { - payload: PyObjectPayload::Scope { scope }, - typ: None, - } - .into_ref() + Rc::new(Scope { locals, parent }) } - pub fn new_module(&self, name: &str, scope: PyObjectRef) -> PyObjectRef { + pub fn new_module(&self, name: &str, scope: ScopeRef) -> PyObjectRef { PyObject::new( PyObjectPayload::Module { name: name.to_string(), - dict: scope.clone(), + scope: scope, }, self.module_type.clone(), ) @@ -591,7 +587,7 @@ impl PyContext { ) } - pub fn new_frame(&self, code: PyObjectRef, scope: PyObjectRef) -> PyObjectRef { + pub fn new_frame(&self, code: PyObjectRef, scope: ScopeRef) -> PyObjectRef { PyObject::new( PyObjectPayload::Frame { frame: Frame::new(code, scope), @@ -617,7 +613,7 @@ impl PyContext { pub fn new_function( &self, code_obj: PyObjectRef, - scope: PyObjectRef, + scope: ScopeRef, defaults: PyObjectRef, ) -> PyObjectRef { PyObject::new( @@ -680,13 +676,12 @@ impl PyContext { pub fn set_attr(&self, obj: &PyObjectRef, attr_name: &str, value: PyObjectRef) { match obj.payload { - PyObjectPayload::Module { ref dict, .. } => self.set_attr(dict, attr_name, value), + PyObjectPayload::Module { ref scope, .. } => { + scope.locals.set_item(self, attr_name, value) + } PyObjectPayload::Instance { ref dict } | PyObjectPayload::Class { ref dict, .. } => { dict.borrow_mut().insert(attr_name.to_string(), value); } - PyObjectPayload::Scope { ref scope } => { - self.set_item(&scope.borrow().locals, attr_name, value); - } ref payload => unimplemented!("set_attr unimplemented for: {:?}", payload), }; } @@ -759,30 +754,6 @@ impl TypeProtocol for PyObject { } } -pub trait ParentProtocol { - fn has_parent(&self) -> bool; - fn get_parent(&self) -> PyObjectRef; -} - -impl ParentProtocol for PyObjectRef { - fn has_parent(&self) -> bool { - match self.payload { - PyObjectPayload::Scope { ref scope } => scope.borrow().parent.is_some(), - _ => panic!("Only scopes have parent (not {:?}", self), - } - } - - fn get_parent(&self) -> PyObjectRef { - match self.payload { - PyObjectPayload::Scope { ref scope } => match scope.borrow().parent { - Some(ref value) => value.clone(), - None => panic!("OMG"), - }, - _ => panic!("TODO"), - } - } -} - pub trait AttributeProtocol { fn get_attr(&self, attr_name: &str) -> Option; fn has_attr(&self, attr_name: &str) -> bool; @@ -805,7 +776,7 @@ fn class_has_item(class: &PyObjectRef, attr_name: &str) -> bool { impl AttributeProtocol for PyObjectRef { fn get_attr(&self, attr_name: &str) -> Option { match self.payload { - PyObjectPayload::Module { ref dict, .. } => dict.get_item(attr_name), + PyObjectPayload::Module { ref scope, .. } => scope.locals.get_item(attr_name), PyObjectPayload::Class { ref mro, .. } => { if let Some(item) = class_get_item(self, attr_name) { return Some(item); @@ -824,7 +795,7 @@ impl AttributeProtocol for PyObjectRef { fn has_attr(&self, attr_name: &str) -> bool { match self.payload { - PyObjectPayload::Module { ref dict, .. } => dict.contains_key(attr_name), + PyObjectPayload::Module { ref scope, .. } => scope.locals.contains_key(attr_name), PyObjectPayload::Class { ref mro, .. } => { class_has_item(self, attr_name) || mro.iter().any(|d| class_has_item(d, attr_name)) } @@ -847,7 +818,6 @@ impl DictProtocol for PyObjectRef { PyObjectPayload::Dict { ref elements } => { objdict::content_contains_key_str(&elements.borrow(), k) } - PyObjectPayload::Scope { ref scope } => scope.borrow().locals.contains_key(k), ref payload => unimplemented!("TODO {:?}", payload), } } @@ -857,8 +827,7 @@ impl DictProtocol for PyObjectRef { 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), + PyObjectPayload::Module { ref scope, .. } => scope.locals.get_item(k), ref k => panic!("TODO {:?}", k), } } @@ -866,8 +835,7 @@ impl DictProtocol for PyObjectRef { fn get_key_value_pairs(&self) -> Vec<(PyObjectRef, PyObjectRef)> { match self.payload { PyObjectPayload::Dict { .. } => objdict::get_key_value_pairs(self), - PyObjectPayload::Module { ref dict, .. } => dict.get_key_value_pairs(), - PyObjectPayload::Scope { ref scope } => scope.borrow().locals.get_key_value_pairs(), + PyObjectPayload::Module { ref scope, .. } => scope.locals.get_key_value_pairs(), _ => panic!("TODO"), } } @@ -879,11 +847,8 @@ impl DictProtocol for PyObjectRef { let key = ctx.new_str(key.to_string()); objdict::set_item_in_content(&mut elements.borrow_mut(), &key, &v); } - PyObjectPayload::Module { dict, .. } => { - dict.set_item(ctx, key, v); - } - PyObjectPayload::Scope { scope, .. } => { - scope.borrow().locals.set_item(ctx, key, v); + PyObjectPayload::Module { scope, .. } => { + scope.locals.set_item(ctx, key, v); } ref k => panic!("TODO {:?}", k), }; @@ -1247,7 +1212,7 @@ pub enum PyObjectPayload { }, Function { code: PyObjectRef, - scope: PyObjectRef, + scope: ScopeRef, defaults: PyObjectRef, }, Generator { @@ -1257,12 +1222,9 @@ pub enum PyObjectPayload { function: PyObjectRef, object: PyObjectRef, }, - Scope { - scope: RefCell, - }, Module { name: String, - dict: PyObjectRef, + scope: ScopeRef, }, None, NotImplemented, @@ -1316,7 +1278,6 @@ impl fmt::Debug for PyObjectPayload { ref object, } => write!(f, "bound-method: {:?} of {:?}", function, object), 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), diff --git a/vm/src/vm.rs b/vm/src/vm.rs index babeae24f0..fd107d0d6c 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -8,6 +8,7 @@ extern crate rustpython_parser; use std::collections::hash_map::HashMap; use std::collections::hash_set::HashSet; +use std::rc::Rc; use std::sync::{Mutex, MutexGuard}; use crate::builtins; @@ -23,7 +24,7 @@ use crate::obj::objstr; use crate::obj::objtype; use crate::pyobject::{ AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, - PyObjectRef, PyResult, TypeProtocol, + PyObjectRef, PyResult, Scope, ScopeRef, TypeProtocol, }; use crate::stdlib; use crate::sysmodule; @@ -68,7 +69,7 @@ impl VirtualMachine { } } - pub fn run_code_obj(&mut self, code: PyObjectRef, scope: PyObjectRef) -> PyResult { + pub fn run_code_obj(&mut self, code: PyObjectRef, scope: ScopeRef) -> PyResult { let frame = self.ctx.new_frame(code, scope); self.run_frame_full(frame) } @@ -187,11 +188,6 @@ impl VirtualMachine { self.new_exception(overflow_error, msg) } - pub fn new_scope(&mut self, parent_scope: Option) -> PyObjectRef { - // let parent_scope = self.current_frame_mut().locals.clone(); - self.ctx.new_scope(parent_scope) - } - pub fn get_none(&self) -> PyObjectRef { self.ctx.none() } @@ -221,10 +217,10 @@ impl VirtualMachine { &self.ctx } - pub fn get_builtin_scope(&mut self) -> PyObjectRef { + pub fn get_builtin_scope(&mut self) -> ScopeRef { let a2 = &*self.builtins; match a2.payload { - PyObjectPayload::Module { ref dict, .. } => dict.clone(), + PyObjectPayload::Module { ref scope, .. } => scope.clone(), _ => { panic!("OMG"); } @@ -329,13 +325,13 @@ impl VirtualMachine { fn invoke_python_function( &mut self, code: &PyObjectRef, - scope: &PyObjectRef, + scope: &ScopeRef, defaults: &PyObjectRef, args: PyFuncArgs, ) -> PyResult { 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)?; + self.fill_locals_from_args(&code_object, &scope.locals, args, defaults)?; // Construct frame: let frame = self.ctx.new_frame(code.clone(), scope); @@ -348,10 +344,30 @@ impl VirtualMachine { } } - fn fill_scope_from_args( + pub fn invoke_with_locals(&mut self, function: PyObjectRef, locals: PyObjectRef) -> PyResult { + if let PyObjectPayload::Function { + code, + scope, + defaults: _defaults, + } = &function.payload + { + let scope = Rc::new(Scope { + locals, + parent: Some(scope.clone()), + }); + let frame = self.ctx.new_frame(code.clone(), scope); + return self.run_frame_full(frame); + } + panic!( + "invoke_with_locals: expected python function, got: {:?}", + function + ); + } + + fn fill_locals_from_args( &mut self, code_object: &bytecode::CodeObject, - scope: &PyObjectRef, + locals: &PyObjectRef, args: PyFuncArgs, defaults: &PyObjectRef, ) -> Result<(), PyObjectRef> { @@ -374,7 +390,7 @@ impl VirtualMachine { for i in 0..n { let arg_name = &code_object.arg_names[i]; let arg = &args.args[i]; - self.ctx.set_attr(scope, arg_name, arg.clone()); + locals.set_item(&self.ctx, arg_name, arg.clone()); } // Pack other positional arguments in to *args: @@ -388,7 +404,7 @@ impl VirtualMachine { // If we have a name (not '*' only) then store it: if let Some(vararg_name) = vararg { - self.ctx.set_attr(scope, vararg_name, vararg_value); + locals.set_item(&self.ctx, vararg_name, vararg_value); } } else { // Check the number of positional arguments @@ -406,7 +422,7 @@ impl VirtualMachine { // Store when we have a name: if let Some(kwargs_name) = kwargs { - self.ctx.set_attr(scope, &kwargs_name, d.clone()); + locals.set_item(&self.ctx, &kwargs_name, d.clone()); } Some(d) @@ -419,13 +435,13 @@ impl VirtualMachine { // Check if we have a parameter with this name: if code_object.arg_names.contains(&name) || code_object.kwonlyarg_names.contains(&name) { - if scope.contains_key(&name) { + if locals.contains_key(&name) { return Err( self.new_type_error(format!("Got multiple values for argument '{}'", name)) ); } - self.ctx.set_attr(scope, &name, value); + locals.set_item(&self.ctx, &name, value); } else if let Some(d) = &kwargs { d.set_item(&self.ctx, &name, value); } else { @@ -450,7 +466,7 @@ impl VirtualMachine { let mut missing = vec![]; for i in 0..required_args { let variable_name = &code_object.arg_names[i]; - if !scope.contains_key(variable_name) { + if !locals.contains_key(variable_name) { missing.push(variable_name) } } @@ -466,9 +482,12 @@ impl VirtualMachine { // the default if we don't already have a value 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_attr(scope, arg_name, available_defaults[default_index].clone()); + if !locals.contains_key(arg_name) { + locals.set_item( + &self.ctx, + arg_name, + available_defaults[default_index].clone(), + ); } } }; @@ -476,7 +495,7 @@ impl VirtualMachine { // Check if kw only arguments are all present: let kwdefs: HashMap = HashMap::new(); for arg_name in &code_object.kwonlyarg_names { - if !scope.contains_key(arg_name) { + if !locals.contains_key(arg_name) { if kwdefs.contains_key(arg_name) { // If not yet specified, take the default value unimplemented!(); diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index d4f2c51974..a363b06407 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -4,7 +4,7 @@ use crate::wasm_builtins; use js_sys::{Object, SyntaxError, TypeError}; use rustpython_vm::{ compile, - pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult}, + pyobject::{DictProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, ScopeRef}, VirtualMachine, }; use std::cell::RefCell; @@ -14,7 +14,7 @@ use wasm_bindgen::prelude::*; pub(crate) struct StoredVirtualMachine { pub vm: VirtualMachine, - pub scope: PyObjectRef, + pub scope: ScopeRef, } impl StoredVirtualMachine { @@ -235,7 +235,7 @@ impl WASMVirtualMachine { ref mut scope, }| { let value = convert::js_to_py(vm, value); - vm.ctx.set_attr(scope, &name, value); + scope.locals.set_item(&vm.ctx, &name, value); }, ) } @@ -274,8 +274,9 @@ impl WASMVirtualMachine { ) .into()); }; - vm.ctx - .set_attr(scope, "print", vm.ctx.new_rustfunc_from_box(print_fn)); + scope + .locals + .set_item(&vm.ctx, "print", vm.ctx.new_rustfunc_from_box(print_fn)); Ok(()) }, )? From 9d6e9ac8896e4852b8a198c2fa029521aa9372a6 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Fri, 1 Mar 2019 18:11:41 +0000 Subject: [PATCH 107/380] Move Scope from pyobject to frame. --- src/main.rs | 3 ++- vm/src/builtins.rs | 4 ++-- vm/src/eval.rs | 3 ++- vm/src/frame.rs | 15 ++++++++++++++- vm/src/lib.rs | 2 +- vm/src/pyobject.rs | 14 +------------- vm/src/vm.rs | 3 ++- wasm/lib/src/vm_class.rs | 3 ++- 8 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/main.rs b/src/main.rs index cc71ecf126..1dae97a524 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,10 +12,11 @@ use rustpython_parser::error::ParseError; use rustpython_vm::{ compile, error::CompileError, + frame::ScopeRef, import, obj::objstr, print_exception, - pyobject::{AttributeProtocol, PyResult, ScopeRef}, + pyobject::{AttributeProtocol, PyResult}, util, VirtualMachine, }; use rustyline::{error::ReadlineError, Editor}; diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index a846bf6732..0e48315e98 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -14,9 +14,9 @@ use crate::obj::objiter; use crate::obj::objstr; use crate::obj::objtype; +use crate::frame::{Scope, ScopeRef}; use crate::pyobject::{ - AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, Scope, ScopeRef, - TypeProtocol, + AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol, }; use std::rc::Rc; diff --git a/vm/src/eval.rs b/vm/src/eval.rs index fe88623b2e..98e9c54717 100644 --- a/vm/src/eval.rs +++ b/vm/src/eval.rs @@ -3,7 +3,8 @@ extern crate rustpython_parser; use std::error::Error; use crate::compile; -use crate::pyobject::{PyResult, ScopeRef}; +use crate::frame::ScopeRef; +use crate::pyobject::PyResult; use crate::vm::VirtualMachine; pub fn eval(vm: &mut VirtualMachine, source: &str, scope: ScopeRef, source_path: &str) -> PyResult { diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 7a7e937116..41a83b19f3 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -17,10 +17,23 @@ use crate::obj::objstr; use crate::obj::objtype; use crate::pyobject::{ DictProtocol, IdProtocol, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, - ScopeRef, TypeProtocol, + TypeProtocol, }; use crate::vm::VirtualMachine; use num_bigint::BigInt; +use std::rc::Rc; + +/* + * So a scope is a linked list of scopes. + * When a name is looked up, it is check in its scope. + */ +#[derive(Debug)] +pub struct Scope { + pub locals: PyObjectRef, // Variables + // TODO: pub locals: RefCell, // Variables + pub parent: Option>, // Parent scope +} +pub type ScopeRef = Rc; #[derive(Clone, Debug)] struct Block { diff --git a/vm/src/lib.rs b/vm/src/lib.rs index f785e294d7..8d964fe391 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -37,7 +37,7 @@ pub mod error; pub mod eval; mod exceptions; pub mod format; -mod frame; +pub mod frame; pub mod import; pub mod obj; pub mod pyobject; diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 585f7a9a86..6cd5f786d4 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1,6 +1,6 @@ use crate::bytecode; use crate::exceptions; -use crate::frame::Frame; +use crate::frame::{Frame, Scope, ScopeRef}; use crate::obj::objbool; use crate::obj::objbytearray; use crate::obj::objbytes; @@ -147,18 +147,6 @@ pub struct PyContext { pub exceptions: exceptions::ExceptionZoo, } -/* - * So a scope is a linked list of scopes. - * When a name is looked up, it is check in its scope. - */ -#[derive(Debug)] -pub struct Scope { - pub locals: PyObjectRef, // Variables - // TODO: pub locals: RefCell, // Variables - pub parent: Option>, // Parent scope -} -pub type ScopeRef = Rc; - fn _nothing() -> PyObjectRef { PyObject { payload: PyObjectPayload::None, diff --git a/vm/src/vm.rs b/vm/src/vm.rs index fd107d0d6c..74cd50969d 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -14,6 +14,7 @@ use std::sync::{Mutex, MutexGuard}; use crate::builtins; use crate::bytecode; use crate::frame::ExecutionResult; +use crate::frame::{Scope, ScopeRef}; use crate::obj::objbool; use crate::obj::objcode; use crate::obj::objframe; @@ -24,7 +25,7 @@ use crate::obj::objstr; use crate::obj::objtype; use crate::pyobject::{ AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, - PyObjectRef, PyResult, Scope, ScopeRef, TypeProtocol, + PyObjectRef, PyResult, TypeProtocol, }; use crate::stdlib; use crate::sysmodule; diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index a363b06407..8a45ecece3 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -4,7 +4,8 @@ use crate::wasm_builtins; use js_sys::{Object, SyntaxError, TypeError}; use rustpython_vm::{ compile, - pyobject::{DictProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, ScopeRef}, + frame::ScopeRef, + pyobject::{DictProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult}, VirtualMachine, }; use std::cell::RefCell; From 60603d250ee57b146d125d211f078c0fcdac15b7 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Fri, 1 Mar 2019 18:06:50 -0600 Subject: [PATCH 108/380] Use alert() for the WASM example crate --- wasm/example/src/main.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/wasm/example/src/main.py b/wasm/example/src/main.py index 8ba2ec00b7..5447d078af 100644 --- a/wasm/example/src/main.py +++ b/wasm/example/src/main.py @@ -1,13 +1,12 @@ -from browser import fetch +from browser import fetch, alert def fetch_handler(repos): star_sum = 0 for repo in repos: star_sum += repo['stars'] - print(f'Average github trending star count: {star_sum / len(repos)}') - + alert(f'Average github trending star count: {star_sum / len(repos)}') fetch( 'https://github-trending-api.now.sh/repositories', response_format='json', -).then(fetch_handler) \ No newline at end of file +).then(fetch_handler, lambda err: alert(f"Error: {err}")) \ No newline at end of file From f364ea59433ef183d1d3ae547736030e9e22ba17 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Fri, 1 Mar 2019 18:41:45 -0600 Subject: [PATCH 109/380] Fix a bunch of clippy lints --- vm/src/bytecode.rs | 7 ++----- vm/src/compile.rs | 5 +---- vm/src/frame.rs | 6 ++---- vm/src/import.rs | 2 +- vm/src/lib.rs | 3 +++ vm/src/obj/objbool.rs | 6 ++---- vm/src/obj/objcomplex.rs | 4 ++-- vm/src/obj/objgenerator.rs | 2 +- vm/src/obj/objset.rs | 2 +- vm/src/obj/objstr.rs | 10 +++++----- vm/src/obj/objtype.rs | 2 +- vm/src/pyobject.rs | 8 +++++++- vm/src/stdlib/socket.rs | 36 ++++++++++++++++-------------------- vm/src/sysmodule.rs | 2 +- vm/src/vm.rs | 6 ++++++ 15 files changed, 51 insertions(+), 50 deletions(-) diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index b7cefe0b3e..c57f53d807 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -269,7 +269,7 @@ impl CodeObject { } } - pub fn get_constants<'a>(&'a self) -> impl Iterator { + pub fn get_constants(&self) -> impl Iterator { self.instructions.iter().filter_map(|x| { if let Instruction::LoadConst { value } = x { Some(value) @@ -362,10 +362,7 @@ impl Instruction { UnpackSequence { size } => w!(UnpackSequence, size), UnpackEx { before, after } => w!(UnpackEx, before, after), Unpack => w!(Unpack), - FormatValue { - conversion: _, - spec, - } => w!(FormatValue, spec), // TODO: write conversion + FormatValue { spec, .. } => w!(FormatValue, spec), // TODO: write conversion } } } diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 1fcce93c93..de02456fc3 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -48,10 +48,7 @@ pub fn compile( let code = compiler.pop_code_object(); trace!("Compilation completed: {:?}", code); - Ok(PyObject::new( - PyObjectPayload::Code { code: code }, - code_type, - )) + Ok(PyObject::new(PyObjectPayload::Code { code }, code_type)) } pub enum Mode { diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 7662f016d6..659c521984 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -94,7 +94,7 @@ impl Frame { let run_obj_name = &self.code.obj_name.to_string(); // Execute until return or exception: - let value = loop { + loop { let lineno = self.get_lineno(); let result = self.execute_instruction(vm); match result { @@ -136,9 +136,7 @@ impl Frame { } } } - }; - - value + } } pub fn fetch_instruction(&self) -> &bytecode::Instruction { diff --git a/vm/src/import.rs b/vm/src/import.rs index 454cf84422..109de50946 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -105,7 +105,7 @@ fn find_source(vm: &VirtualMachine, current_path: PathBuf, name: &str) -> Result } } - match file_paths.iter().filter(|p| p.exists()).next() { + match file_paths.iter().find(|p| p.exists()) { Some(path) => Ok(path.to_path_buf()), None => Err(format!("No module named '{}'", name)), } diff --git a/vm/src/lib.rs b/vm/src/lib.rs index f785e294d7..0583979212 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -5,6 +5,9 @@ //! - Import mechanics //! - Base objects +// for methods like vm.to_str(), not the typical use of 'to' as a method prefix +#![allow(clippy::wrong_self_convention)] + #[macro_use] extern crate bitflags; #[macro_use] diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index cf16e1f5f4..eddd744e5a 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -22,12 +22,10 @@ pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result { if let Ok(f) = vm.get_method(obj.clone(), "__bool__") { let bool_res = vm.invoke(f, PyFuncArgs::default())?; - let v = match bool_res.payload { + match bool_res.payload { PyObjectPayload::Integer { ref value } => !value.is_zero(), _ => return Err(vm.new_type_error(String::from("TypeError"))), - }; - - v + } } else { true } diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index 0b0d5ba7a2..c6be5e5f80 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -84,13 +84,13 @@ fn complex_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn complex_real(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.complex_type()))]); - let Complex64 { re, im: _ } = get_value(zelf); + let Complex64 { re, .. } = get_value(zelf); Ok(vm.ctx.new_float(re)) } fn complex_imag(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.complex_type()))]); - let Complex64 { re: _, im } = get_value(zelf); + let Complex64 { im, .. } = get_value(zelf); Ok(vm.ctx.new_float(im)) } diff --git a/vm/src/obj/objgenerator.rs b/vm/src/obj/objgenerator.rs index 73cdbab68a..05a7a60ae6 100644 --- a/vm/src/obj/objgenerator.rs +++ b/vm/src/obj/objgenerator.rs @@ -29,7 +29,7 @@ pub fn init(context: &PyContext) { pub fn new_generator(vm: &mut VirtualMachine, frame: PyObjectRef) -> PyResult { Ok(PyObject::new( - PyObjectPayload::Generator { frame: frame }, + PyObjectPayload::Generator { frame }, vm.ctx.generator_type.clone(), )) } diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index c041ce5673..2652532f59 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -547,7 +547,7 @@ fn set_ixor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn set_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.set_type()))]); - let items = get_elements(zelf).values().map(|x| x.clone()).collect(); + let items = get_elements(zelf).values().cloned().collect(); let set_list = vm.ctx.new_list(items); let iter_obj = PyObject::new( PyObjectPayload::Iterator { diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 580ab2b801..42f7152865 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -518,7 +518,7 @@ fn str_isupper(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { && value .chars() .filter(|x| !x.is_ascii_whitespace()) - .all(|c| c.is_uppercase()), + .all(char::is_uppercase), )) } @@ -530,7 +530,7 @@ fn str_islower(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { && value .chars() .filter(|x| !x.is_ascii_whitespace()) - .all(|c| c.is_lowercase()), + .all(char::is_lowercase), )) } @@ -895,7 +895,7 @@ fn str_isalnum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let value = get_value(&s); Ok(vm .ctx - .new_bool(!value.is_empty() && value.chars().all(|c| c.is_alphanumeric()))) + .new_bool(!value.is_empty() && value.chars().all(char::is_alphanumeric))) } fn str_isascii(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -959,7 +959,7 @@ fn str_isnumeric(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let value = get_value(&s); Ok(vm .ctx - .new_bool(!value.is_empty() && value.chars().all(|c| c.is_numeric()))) + .new_bool(!value.is_empty() && value.chars().all(char::is_numeric))) } fn str_isalpha(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -967,7 +967,7 @@ fn str_isalpha(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let value = get_value(&s); Ok(vm .ctx - .new_bool(!value.is_empty() && value.chars().all(|c| c.is_alphanumeric()))) + .new_bool(!value.is_empty() && value.chars().all(char::is_alphanumeric))) } fn str_isdigit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index e1d1ceaf5e..785ca1b87b 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -343,7 +343,7 @@ fn linearise_mro(mut bases: Vec>) -> Option> { debug!("Linearising MRO: {:?}", bases); let mut result = vec![]; loop { - if (&bases).iter().all(|x| x.is_empty()) { + if (&bases).iter().all(Vec::is_empty) { break; } match take_next_base(bases) { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 44c65d625a..f00d33a0b5 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -712,6 +712,12 @@ impl PyContext { } } +impl Default for PyContext { + fn default() -> Self { + PyContext::new() + } +} + /// This is an actual python object. It consists of a `typ` which is the /// python class, and carries some rust payload optionally. This rust /// payload can be a rust float or rust int in case of float and int objects. @@ -1091,7 +1097,7 @@ macro_rules! tuple_py_native_func_factory { let signature = Signature::new(parameters); Box::new(move |vm, mut args| { - signature.check(vm, &mut args)?; + signature.check(vm, &args)?; (self)(vm, $($T::from_py_func_args(&mut args)?,)+) .into_pyobject(&vm.ctx) diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index d3ee6a1316..b16032ba95 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -17,17 +17,17 @@ use num_traits::ToPrimitive; #[derive(Copy, Clone)] enum AddressFamily { - AfUnix = 1, - AfInet = 2, - AfInet6 = 3, + Unix = 1, + Inet = 2, + Inet6 = 3, } impl AddressFamily { fn from_i32(value: i32) -> AddressFamily { match value { - 1 => AddressFamily::AfUnix, - 2 => AddressFamily::AfInet, - 3 => AddressFamily::AfInet6, + 1 => AddressFamily::Unix, + 2 => AddressFamily::Inet, + 3 => AddressFamily::Inet6, _ => panic!("Unknown value: {}", value), } } @@ -35,15 +35,15 @@ impl AddressFamily { #[derive(Copy, Clone)] enum SocketKind { - SockStream = 1, - SockDgram = 2, + Stream = 1, + Dgram = 2, } impl SocketKind { fn from_i32(value: i32) -> SocketKind { match value { - 1 => SocketKind::SockStream, - 2 => SocketKind::SockDgram, + 1 => SocketKind::Stream, + 2 => SocketKind::Dgram, _ => panic!("Unknown value: {}", value), } } @@ -102,7 +102,7 @@ impl Socket { fn new(address_family: AddressFamily, socket_kind: SocketKind) -> Socket { Socket { address_family, - socket_kind: socket_kind, + socket_kind, con: None, } } @@ -286,8 +286,8 @@ fn socket_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { PyObjectPayload::Socket { ref socket } => { let mut socket = socket.borrow_mut(); match socket.address_family { - AddressFamily::AfInet => match socket.socket_kind { - SocketKind::SockStream => { + AddressFamily::Inet => match socket.socket_kind { + SocketKind::Stream => { socket.con = None; Ok(vm.get_none()) } @@ -320,7 +320,7 @@ fn socket_getsockname(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm.ctx.tuple_type(), )) } - _ => return Err(vm.new_type_error("".to_string())), + _ => Err(vm.new_type_error("".to_string())), } } _ => Err(vm.new_type_error("".to_string())), @@ -330,16 +330,12 @@ fn socket_getsockname(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn mk_module(ctx: &PyContext) -> PyObjectRef { let py_mod = ctx.new_module(&"socket".to_string(), ctx.new_scope(None)); - ctx.set_attr( - &py_mod, - "AF_INET", - ctx.new_int(AddressFamily::AfInet as i32), - ); + ctx.set_attr(&py_mod, "AF_INET", ctx.new_int(AddressFamily::Inet as i32)); ctx.set_attr( &py_mod, "SOCK_STREAM", - ctx.new_int(SocketKind::SockStream as i32), + ctx.new_int(SocketKind::Stream as i32), ); let socket = { diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index 5f5ca94704..d794792b8f 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -24,7 +24,7 @@ fn frame_idx(vm: &mut VirtualMachine, offset: Option<&PyObjectRef>) -> Result PyResult { diff --git a/vm/src/vm.rs b/vm/src/vm.rs index babeae24f0..bbf257111c 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -804,6 +804,12 @@ impl VirtualMachine { } } +impl Default for VirtualMachine { + fn default() -> Self { + VirtualMachine::new() + } +} + lazy_static! { static ref REPR_GUARDS: Mutex> = { Mutex::new(HashSet::new()) }; } From 58e1533b12585e8435df6b3396ebfc8111a1c3db Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Fri, 1 Mar 2019 20:12:54 -0600 Subject: [PATCH 110/380] Fix clippy lint for float_is_integer --- vm/src/obj/objfloat.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 4617364ad5..f4d4068a76 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -417,7 +417,7 @@ fn float_real(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn float_is_integer(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(i, Some(vm.ctx.float_type()))]); let v = get_value(i); - let result = v == v.round(); + let result = (v - v.round()).abs() < std::f64::EPSILON; Ok(vm.ctx.new_bool(result)) } From c99e843769c9db7e0a864ce64b740e864df33dfa Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Fri, 1 Mar 2019 20:13:50 -0600 Subject: [PATCH 111/380] Fix a clippy lint I missed --- parser/src/fstring.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser/src/fstring.rs b/parser/src/fstring.rs index e6e5586b06..cad885531e 100644 --- a/parser/src/fstring.rs +++ b/parser/src/fstring.rs @@ -121,7 +121,7 @@ impl<'a> FStringParser<'a> { } } - return Err(UnclosedLbrace); + Err(UnclosedLbrace) } fn parse(mut self) -> Result { From 5f8fb896d7ab55b248880d99421ef6272306337b Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Fri, 1 Mar 2019 20:22:14 -0600 Subject: [PATCH 112/380] Fix lints in rustpython_wasm --- wasm/lib/src/browser_module.rs | 8 ++++---- wasm/lib/src/convert.rs | 2 +- wasm/lib/src/lib.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index fdae97481e..9aa6006108 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -95,7 +95,7 @@ fn browser_fetch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }) .and_then(JsFuture::from); - Ok(PyPromise::new(promise_type, future_to_promise(future))) + Ok(PyPromise::new_obj(promise_type, future_to_promise(future))) } fn browser_request_animation_frame(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -159,7 +159,7 @@ pub struct PyPromise { } impl PyPromise { - pub fn new(promise_type: PyObjectRef, value: Promise) -> PyObjectRef { + pub fn new_obj(promise_type: PyObjectRef, value: Promise) -> PyObjectRef { PyObject::new( PyObjectPayload::AnyRustValue { value: Box::new(PyPromise { value }), @@ -230,7 +230,7 @@ fn promise_then(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let ret_promise = future_to_promise(ret_future); - Ok(PyPromise::new(promise_type, ret_promise)) + Ok(PyPromise::new_obj(promise_type, ret_promise)) } fn promise_catch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -265,7 +265,7 @@ fn promise_catch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let ret_promise = future_to_promise(ret_future); - Ok(PyPromise::new(promise_type, ret_promise)) + Ok(PyPromise::new_obj(promise_type, ret_promise)) } fn browser_alert(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index 1e02d4a0d3..cb37efe5a8 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -118,7 +118,7 @@ pub fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef { if let Some(promise) = js_val.dyn_ref::() { // the browser module might not be injected if let Ok(promise_type) = browser_module::import_promise_type(vm) { - return browser_module::PyPromise::new(promise_type, promise.clone()); + return browser_module::PyPromise::new_obj(promise_type, promise.clone()); } } if Array::is_array(&js_val) { diff --git a/wasm/lib/src/lib.rs b/wasm/lib/src/lib.rs index 4849f03bed..35d4595199 100644 --- a/wasm/lib/src/lib.rs +++ b/wasm/lib/src/lib.rs @@ -34,7 +34,7 @@ fn panic_hook(info: &panic::PanicInfo) { Ok(stack) => stack, Err(_) => return, }; - let _ = Reflect::set(&window, &"__RUSTPYTHON_ERROR_STACK".into(), &stack.into()); + let _ = Reflect::set(&window, &"__RUSTPYTHON_ERROR_STACK".into(), &stack); } #[wasm_bindgen(start)] From 8f52e15d32d0d9c4949d40a9ecc9cc16f406c681 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Sat, 2 Mar 2019 11:14:35 +0100 Subject: [PATCH 113/380] Minor clippy fixes. --- vm/src/compile.rs | 535 ++++++++++++++++++++++------------------- vm/src/dictdatatype.rs | 10 +- vm/src/obj/objiter.rs | 7 +- vm/src/pyobject.rs | 2 +- vm/src/vm.rs | 9 +- 5 files changed, 297 insertions(+), 266 deletions(-) diff --git a/vm/src/compile.rs b/vm/src/compile.rs index ad869f0fe7..761297b4a5 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -271,45 +271,7 @@ impl Compiler { iter, body, orelse, - } => { - // Start loop - let start_label = self.new_label(); - let else_label = self.new_label(); - let end_label = self.new_label(); - self.emit(Instruction::SetupLoop { - start: start_label, - end: end_label, - }); - - // The thing iterated: - for i in iter { - self.compile_expression(i)?; - } - - // Retrieve Iterator - self.emit(Instruction::GetIter); - - self.set_label(start_label); - self.emit(Instruction::ForIter { target: else_label }); - - // Start of loop iteration, set targets: - self.compile_store(target)?; - - let was_in_loop = self.in_loop; - self.in_loop = true; - self.compile_statements(body)?; - self.in_loop = was_in_loop; - - self.emit(Instruction::Jump { - target: start_label, - }); - self.set_label(else_label); - self.emit(Instruction::PopBlock); - if let Some(orelse) = orelse { - self.compile_statements(orelse)?; - } - self.set_label(end_label); - } + } => self.compile_for(target, iter, body, orelse)?, ast::Statement::Raise { exception, cause } => match exception { Some(value) => { self.compile_expression(value)?; @@ -332,227 +294,20 @@ impl Compiler { handlers, orelse, finalbody, - } => { - let mut handler_label = self.new_label(); - let finally_label = self.new_label(); - let else_label = self.new_label(); - // try: - self.emit(Instruction::SetupExcept { - handler: handler_label, - }); - self.compile_statements(body)?; - self.emit(Instruction::PopBlock); - self.emit(Instruction::Jump { target: else_label }); - - // except handlers: - self.set_label(handler_label); - // Exception is on top of stack now - handler_label = self.new_label(); - for handler in handlers { - // If we gave a typ, - // check if this handler can handle the exception: - if let Some(exc_type) = &handler.typ { - // Duplicate exception for test: - self.emit(Instruction::Duplicate); - - // Check exception type: - self.emit(Instruction::LoadName { - name: String::from("isinstance"), - }); - self.emit(Instruction::Rotate { amount: 2 }); - self.compile_expression(exc_type)?; - self.emit(Instruction::CallFunction { - typ: CallType::Positional(2), - }); - - // We cannot handle this exception type: - self.emit(Instruction::JumpIfFalse { - target: handler_label, - }); - - // We have a match, store in name (except x as y) - if let Some(alias) = &handler.name { - self.emit(Instruction::StoreName { - name: alias.clone(), - }); - } else { - // Drop exception from top of stack: - self.emit(Instruction::Pop); - } - } else { - // Catch all! - // Drop exception from top of stack: - self.emit(Instruction::Pop); - } - - // Handler code: - self.compile_statements(&handler.body)?; - self.emit(Instruction::Jump { - target: finally_label, - }); - - // Emit a new label for the next handler - self.set_label(handler_label); - handler_label = self.new_label(); - } - self.emit(Instruction::Jump { - target: handler_label, - }); - self.set_label(handler_label); - // If code flows here, we have an unhandled exception, - // emit finally code and raise again! - // Duplicate finally code here: - // TODO: this bytecode is now duplicate, could this be - // improved? - if let Some(statements) = finalbody { - self.compile_statements(statements)?; - } - self.emit(Instruction::Raise { argc: 1 }); - - // We successfully ran the try block: - // else: - self.set_label(else_label); - if let Some(statements) = orelse { - self.compile_statements(statements)?; - } - - // finally: - self.set_label(finally_label); - if let Some(statements) = finalbody { - self.compile_statements(statements)?; - } - - // unimplemented!(); - } + } => self.compile_try_statement(body, handlers, orelse, finalbody)?, ast::Statement::FunctionDef { name, args, body, decorator_list, - } => { - // Create bytecode for this function: - // remember to restore self.in_loop to the original after the function is compiled - let was_in_loop = self.in_loop; - let was_in_function_def = self.in_function_def; - self.in_loop = false; - self.in_function_def = true; - let flags = self.enter_function(name, args)?; - self.compile_statements(body)?; - - // Emit None at end: - self.emit(Instruction::LoadConst { - value: bytecode::Constant::None, - }); - self.emit(Instruction::ReturnValue); - let code = self.pop_code_object(); - - self.prepare_decorators(decorator_list)?; - self.emit(Instruction::LoadConst { - value: bytecode::Constant::Code { code }, - }); - self.emit(Instruction::LoadConst { - value: bytecode::Constant::String { - value: name.clone(), - }, - }); - - // Turn code object into function object: - self.emit(Instruction::MakeFunction { flags }); - self.apply_decorators(decorator_list); - - self.emit(Instruction::StoreName { - name: name.to_string(), - }); - self.in_loop = was_in_loop; - self.in_function_def = was_in_function_def; - } + } => self.compile_function_def(name, args, body, decorator_list)?, ast::Statement::ClassDef { name, body, bases, keywords, decorator_list, - } => { - let was_in_loop = self.in_loop; - self.in_loop = false; - 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![], - None, - vec![], - None, - self.source_path.clone().unwrap(), - line_number, - name.clone(), - )); - self.compile_statements(body)?; - self.emit(Instruction::LoadConst { - value: bytecode::Constant::None, - }); - self.emit(Instruction::ReturnValue); - - let code = self.pop_code_object(); - self.emit(Instruction::LoadConst { - value: bytecode::Constant::Code { code }, - }); - self.emit(Instruction::LoadConst { - value: bytecode::Constant::String { - value: name.clone(), - }, - }); - - // Turn code object into function object: - self.emit(Instruction::MakeFunction { - flags: bytecode::FunctionOpArg::empty(), - }); - - self.emit(Instruction::LoadConst { - value: bytecode::Constant::String { - value: name.clone(), - }, - }); - - for base in bases { - self.compile_expression(base)?; - } - - if !keywords.is_empty() { - let mut kwarg_names = vec![]; - for keyword in keywords { - if let Some(name) = &keyword.name { - kwarg_names.push(bytecode::Constant::String { - value: name.to_string(), - }); - } else { - // This means **kwargs! - panic!("name must be set"); - } - self.compile_expression(&keyword.value)?; - } - - self.emit(Instruction::LoadConst { - value: bytecode::Constant::Tuple { - elements: kwarg_names, - }, - }); - self.emit(Instruction::CallFunction { - typ: CallType::Keyword(2 + keywords.len() + bases.len()), - }); - } else { - self.emit(Instruction::CallFunction { - typ: CallType::Positional(2 + bases.len()), - }); - } - - self.apply_decorators(decorator_list); - - self.emit(Instruction::StoreName { - name: name.to_string(), - }); - self.in_loop = was_in_loop; - } + } => self.compile_class_def(name, body, bases, keywords, decorator_list)?, ast::Statement::Assert { test, msg } => { // TODO: if some flag, ignore all assert statements! @@ -723,6 +478,288 @@ impl Compiler { } } + fn compile_try_statement( + &mut self, + body: &[ast::LocatedStatement], + handlers: &[ast::ExceptHandler], + orelse: &Option>, + finalbody: &Option>, + ) -> Result<(), CompileError> { + let mut handler_label = self.new_label(); + let finally_label = self.new_label(); + let else_label = self.new_label(); + // try: + self.emit(Instruction::SetupExcept { + handler: handler_label, + }); + self.compile_statements(body)?; + self.emit(Instruction::PopBlock); + self.emit(Instruction::Jump { target: else_label }); + + // except handlers: + self.set_label(handler_label); + // Exception is on top of stack now + handler_label = self.new_label(); + for handler in handlers { + // If we gave a typ, + // check if this handler can handle the exception: + if let Some(exc_type) = &handler.typ { + // Duplicate exception for test: + self.emit(Instruction::Duplicate); + + // Check exception type: + self.emit(Instruction::LoadName { + name: String::from("isinstance"), + }); + self.emit(Instruction::Rotate { amount: 2 }); + self.compile_expression(exc_type)?; + self.emit(Instruction::CallFunction { + typ: CallType::Positional(2), + }); + + // We cannot handle this exception type: + self.emit(Instruction::JumpIfFalse { + target: handler_label, + }); + + // We have a match, store in name (except x as y) + if let Some(alias) = &handler.name { + self.emit(Instruction::StoreName { + name: alias.clone(), + }); + } else { + // Drop exception from top of stack: + self.emit(Instruction::Pop); + } + } else { + // Catch all! + // Drop exception from top of stack: + self.emit(Instruction::Pop); + } + + // Handler code: + self.compile_statements(&handler.body)?; + self.emit(Instruction::Jump { + target: finally_label, + }); + + // Emit a new label for the next handler + self.set_label(handler_label); + handler_label = self.new_label(); + } + self.emit(Instruction::Jump { + target: handler_label, + }); + self.set_label(handler_label); + // If code flows here, we have an unhandled exception, + // emit finally code and raise again! + // Duplicate finally code here: + // TODO: this bytecode is now duplicate, could this be + // improved? + if let Some(statements) = finalbody { + self.compile_statements(statements)?; + } + self.emit(Instruction::Raise { argc: 1 }); + + // We successfully ran the try block: + // else: + self.set_label(else_label); + if let Some(statements) = orelse { + self.compile_statements(statements)?; + } + + // finally: + self.set_label(finally_label); + if let Some(statements) = finalbody { + self.compile_statements(statements)?; + } + + // unimplemented!(); + Ok(()) + } + + fn compile_function_def( + &mut self, + name: &str, + args: &ast::Parameters, + body: &[ast::LocatedStatement], + decorator_list: &[ast::Expression], + ) -> Result<(), CompileError> { + // Create bytecode for this function: + // remember to restore self.in_loop to the original after the function is compiled + let was_in_loop = self.in_loop; + let was_in_function_def = self.in_function_def; + self.in_loop = false; + self.in_function_def = true; + let flags = self.enter_function(name, args)?; + self.compile_statements(body)?; + + // Emit None at end: + self.emit(Instruction::LoadConst { + value: bytecode::Constant::None, + }); + self.emit(Instruction::ReturnValue); + let code = self.pop_code_object(); + + self.prepare_decorators(decorator_list)?; + self.emit(Instruction::LoadConst { + value: bytecode::Constant::Code { code }, + }); + self.emit(Instruction::LoadConst { + value: bytecode::Constant::String { + value: name.to_string(), + }, + }); + + // Turn code object into function object: + self.emit(Instruction::MakeFunction { flags }); + self.apply_decorators(decorator_list); + + self.emit(Instruction::StoreName { + name: name.to_string(), + }); + self.in_loop = was_in_loop; + self.in_function_def = was_in_function_def; + Ok(()) + } + + fn compile_class_def( + &mut self, + name: &str, + body: &[ast::LocatedStatement], + bases: &[ast::Expression], + keywords: &[ast::Keyword], + decorator_list: &[ast::Expression], + ) -> Result<(), CompileError> { + let was_in_loop = self.in_loop; + self.in_loop = false; + 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![], + None, + vec![], + None, + self.source_path.clone().unwrap(), + line_number, + name.to_string(), + )); + self.compile_statements(body)?; + self.emit(Instruction::LoadConst { + value: bytecode::Constant::None, + }); + self.emit(Instruction::ReturnValue); + + let code = self.pop_code_object(); + self.emit(Instruction::LoadConst { + value: bytecode::Constant::Code { code }, + }); + self.emit(Instruction::LoadConst { + value: bytecode::Constant::String { + value: name.to_string(), + }, + }); + + // Turn code object into function object: + self.emit(Instruction::MakeFunction { + flags: bytecode::FunctionOpArg::empty(), + }); + + self.emit(Instruction::LoadConst { + value: bytecode::Constant::String { + value: name.to_string(), + }, + }); + + for base in bases { + self.compile_expression(base)?; + } + + if !keywords.is_empty() { + let mut kwarg_names = vec![]; + for keyword in keywords { + if let Some(name) = &keyword.name { + kwarg_names.push(bytecode::Constant::String { + value: name.to_string(), + }); + } else { + // This means **kwargs! + panic!("name must be set"); + } + self.compile_expression(&keyword.value)?; + } + + self.emit(Instruction::LoadConst { + value: bytecode::Constant::Tuple { + elements: kwarg_names, + }, + }); + self.emit(Instruction::CallFunction { + typ: CallType::Keyword(2 + keywords.len() + bases.len()), + }); + } else { + self.emit(Instruction::CallFunction { + typ: CallType::Positional(2 + bases.len()), + }); + } + + self.apply_decorators(decorator_list); + + self.emit(Instruction::StoreName { + name: name.to_string(), + }); + self.in_loop = was_in_loop; + Ok(()) + } + + fn compile_for( + &mut self, + target: &ast::Expression, + iter: &[ast::Expression], + body: &[ast::LocatedStatement], + orelse: &Option>, + ) -> Result<(), CompileError> { + // Start loop + let start_label = self.new_label(); + let else_label = self.new_label(); + let end_label = self.new_label(); + self.emit(Instruction::SetupLoop { + start: start_label, + end: end_label, + }); + + // The thing iterated: + for i in iter { + self.compile_expression(i)?; + } + + // Retrieve Iterator + self.emit(Instruction::GetIter); + + self.set_label(start_label); + self.emit(Instruction::ForIter { target: else_label }); + + // Start of loop iteration, set targets: + self.compile_store(target)?; + + let was_in_loop = self.in_loop; + self.in_loop = true; + self.compile_statements(body)?; + self.in_loop = was_in_loop; + + self.emit(Instruction::Jump { + target: start_label, + }); + self.set_label(else_label); + self.emit(Instruction::PopBlock); + if let Some(orelse) = orelse { + self.compile_statements(orelse)?; + } + self.set_label(end_label); + Ok(()) + } + fn compile_store(&mut self, target: &ast::Expression) -> Result<(), CompileError> { match target { ast::Expression::Identifier { name } => { diff --git a/vm/src/dictdatatype.rs b/vm/src/dictdatatype.rs index a942081040..2ba30c066b 100644 --- a/vm/src/dictdatatype.rs +++ b/vm/src/dictdatatype.rs @@ -36,7 +36,7 @@ impl Dict { vm: &mut VirtualMachine, key: &PyObjectRef, value: PyObjectRef, - ) -> Result<(), PyObjectRef> { + ) -> PyResult<()> { match self.lookup(vm, key)? { LookupResult::Existing(index) => { // Update existing key @@ -70,7 +70,7 @@ impl Dict { &self, vm: &mut VirtualMachine, key: &PyObjectRef, - ) -> Result { + ) -> PyResult { if let LookupResult::Existing(_index) = self.lookup(vm, key)? { Ok(true) } else { @@ -97,7 +97,7 @@ impl Dict { &mut self, vm: &mut VirtualMachine, key: &PyObjectRef, - ) -> Result<(), PyObjectRef> { + ) -> PyResult<()> { if let LookupResult::Existing(index) = self.lookup(vm, key)? { self.entries[index] = None; self.size -= 1; @@ -130,7 +130,7 @@ impl Dict { &self, vm: &mut VirtualMachine, key: &PyObjectRef, - ) -> Result { + ) -> PyResult { let hash_value = calc_hash(vm, key)?; let perturb = hash_value; let mut hash_index: usize = hash_value; @@ -181,7 +181,7 @@ enum LookupResult { Existing(usize), // Existing record, index into entries } -fn calc_hash(vm: &mut VirtualMachine, key: &PyObjectRef) -> Result { +fn calc_hash(vm: &mut VirtualMachine, key: &PyObjectRef) -> PyResult { let hash = vm.call_method(key, "__hash__", vec![])?; Ok(objint::get_value(&hash).to_usize().unwrap()) } diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index a72339fbb0..2010da906d 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -32,7 +32,7 @@ pub fn call_next(vm: &mut VirtualMachine, iter_obj: &PyObjectRef) -> PyResult { pub fn get_next_object( vm: &mut VirtualMachine, iter_obj: &PyObjectRef, -) -> Result, PyObjectRef> { +) -> PyResult> { let next_obj: PyResult = call_next(vm, iter_obj); match next_obj { @@ -49,10 +49,7 @@ pub fn get_next_object( } /* Retrieve all elements from an iterator */ -pub fn get_all( - vm: &mut VirtualMachine, - iter_obj: &PyObjectRef, -) -> Result, PyObjectRef> { +pub fn get_all(vm: &mut VirtualMachine, iter_obj: &PyObjectRef) -> PyResult> { let mut elements = vec![]; loop { let element = get_next_object(vm, iter_obj)?; diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index c4fdafca4e..e8a2b32c07 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -547,7 +547,7 @@ impl PyContext { PyObject::new( PyObjectPayload::Module { name: name.to_string(), - scope: scope, + scope, }, self.module_type.clone(), ) diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 375afc0bd9..9d91abf9e9 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -82,7 +82,7 @@ impl VirtualMachine { } } - pub fn run_frame(&mut self, frame: PyObjectRef) -> Result { + pub fn run_frame(&mut self, frame: PyObjectRef) -> PyResult { self.frames.push(frame.clone()); let frame = objframe::get_value(&frame); let result = frame.run(self); @@ -371,7 +371,7 @@ impl VirtualMachine { locals: &PyObjectRef, args: PyFuncArgs, defaults: &PyObjectRef, - ) -> Result<(), PyObjectRef> { + ) -> PyResult<()> { let nargs = args.args.len(); let nexpected_args = code_object.arg_names.len(); @@ -513,10 +513,7 @@ impl VirtualMachine { Ok(()) } - pub fn extract_elements( - &mut self, - value: &PyObjectRef, - ) -> Result, PyObjectRef> { + pub fn extract_elements(&mut self, value: &PyObjectRef) -> PyResult> { // Extract elements from item, if possible: let elements = if objtype::isinstance(value, &self.ctx.tuple_type()) || objtype::isinstance(value, &self.ctx.list_type()) From 7bb6f8fdaa3ff4fbdaaf2b7a040eb6d194eeae31 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Sat, 2 Mar 2019 11:22:40 +0100 Subject: [PATCH 114/380] Minor clippy fix. --- vm/src/bytecode.rs | 2 +- vm/src/compile.rs | 16 ++++++++++++---- vm/src/frame.rs | 3 +-- vm/src/pyobject.rs | 2 +- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index f07b38cc01..9c74799426 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -190,7 +190,7 @@ pub enum Constant { Boolean { value: bool }, String { value: String }, Bytes { value: Vec }, - Code { code: CodeObject }, + Code { code: Box }, Tuple { elements: Vec }, None, } diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 761297b4a5..1959cee83f 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -603,7 +603,9 @@ impl Compiler { self.prepare_decorators(decorator_list)?; self.emit(Instruction::LoadConst { - value: bytecode::Constant::Code { code }, + value: bytecode::Constant::Code { + code: Box::new(code), + }, }); self.emit(Instruction::LoadConst { value: bytecode::Constant::String { @@ -653,7 +655,9 @@ impl Compiler { let code = self.pop_code_object(); self.emit(Instruction::LoadConst { - value: bytecode::Constant::Code { code }, + value: bytecode::Constant::Code { + code: Box::new(code), + }, }); self.emit(Instruction::LoadConst { value: bytecode::Constant::String { @@ -1075,7 +1079,9 @@ impl Compiler { self.emit(Instruction::ReturnValue); let code = self.pop_code_object(); self.emit(Instruction::LoadConst { - value: bytecode::Constant::Code { code }, + value: bytecode::Constant::Code { + code: Box::new(code), + }, }); self.emit(Instruction::LoadConst { value: bytecode::Constant::String { value: name }, @@ -1360,7 +1366,9 @@ impl Compiler { // List comprehension code: self.emit(Instruction::LoadConst { - value: bytecode::Constant::Code { code }, + value: bytecode::Constant::Code { + code: Box::new(code), + }, }); // List comprehension function name: diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 50bbde150b..ec0c099238 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -716,8 +716,7 @@ impl Frame { let obj = import_module(vm, current_path, module)?; for (k, v) in obj.get_key_value_pairs().iter() { - &self - .scope + self.scope .locals .set_item(&vm.ctx, &objstr::get_value(k), v.clone()); } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index e8a2b32c07..9e0cdbb95e 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -682,7 +682,7 @@ impl PyContext { 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::Code { ref code } => self.new_code_object(*code.clone()), bytecode::Constant::Tuple { ref elements } => { let elements = elements .iter() From e4007f530e1f08a87215299b08f792f3f4adb893 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sat, 2 Mar 2019 10:52:24 +0200 Subject: [PATCH 115/380] Add get_socket function --- vm/src/stdlib/socket.rs | 188 +++++++++++++++++++--------------------- 1 file changed, 88 insertions(+), 100 deletions(-) diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index b16032ba95..f687efd358 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -3,6 +3,7 @@ use std::io; use std::io::Read; use std::io::Write; use std::net::{SocketAddr, TcpListener, TcpStream}; +use std::ops::DerefMut; use crate::obj::objbytes; use crate::obj::objint; @@ -108,6 +109,14 @@ impl Socket { } } +fn get_socket<'a>(obj: &'a PyObjectRef) -> impl DerefMut + 'a { + if let PyObjectPayload::Socket { ref socket } = obj.payload { + socket.borrow_mut() + } else { + panic!("Inner error getting socket {:?}", obj); + } +} + fn socket_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -139,17 +148,14 @@ fn socket_connect(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let address_string = get_address_string(vm, address)?; - match zelf.payload { - PyObjectPayload::Socket { ref socket } => { - if let Ok(stream) = TcpStream::connect(address_string) { - socket.borrow_mut().con = Some(Connection::TcpStream(stream)); - Ok(vm.get_none()) - } else { - // TODO: Socket error - Err(vm.new_type_error("socket failed".to_string())) - } - } - _ => Err(vm.new_type_error("".to_string())), + let mut socket = get_socket(zelf); + + if let Ok(stream) = TcpStream::connect(address_string) { + socket.con = Some(Connection::TcpStream(stream)); + Ok(vm.get_none()) + } else { + // TODO: Socket error + Err(vm.new_type_error("socket failed".to_string())) } } @@ -162,17 +168,14 @@ fn socket_bind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let address_string = get_address_string(vm, address)?; - match zelf.payload { - PyObjectPayload::Socket { ref socket } => { - if let Ok(stream) = TcpListener::bind(address_string) { - socket.borrow_mut().con = Some(Connection::TcpListener(stream)); - Ok(vm.get_none()) - } else { - // TODO: Socket error - Err(vm.new_type_error("socket failed".to_string())) - } - } - _ => Err(vm.new_type_error("".to_string())), + let mut socket = get_socket(zelf); + + if let Ok(stream) = TcpListener::bind(address_string) { + socket.con = Some(Connection::TcpListener(stream)); + Ok(vm.get_none()) + } else { + // TODO: Socket error + Err(vm.new_type_error("socket failed".to_string())) } } @@ -212,35 +215,32 @@ fn socket_listen(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn socket_accept(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, None)]); - match zelf.payload { - PyObjectPayload::Socket { ref socket } => { - let ret = match socket.borrow_mut().con { - Some(ref mut v) => v.accept(), - None => return Err(vm.new_type_error("".to_string())), - }; + let mut socket = get_socket(zelf); - let tcp_stream = match ret { - Ok((socket, _addr)) => socket, - _ => return Err(vm.new_type_error("".to_string())), - }; + let ret = match socket.con { + Some(ref mut v) => v.accept(), + None => return Err(vm.new_type_error("".to_string())), + }; - let socket = RefCell::new(Socket { - address_family: socket.borrow().address_family, - socket_kind: socket.borrow().socket_kind, - con: Some(Connection::TcpStream(tcp_stream)), - }); + let tcp_stream = match ret { + Ok((socket, _addr)) => socket, + _ => return Err(vm.new_type_error("".to_string())), + }; - let sock_obj = PyObject::new(PyObjectPayload::Socket { socket }, zelf.typ()); + let socket = RefCell::new(Socket { + address_family: socket.address_family, + socket_kind: socket.socket_kind, + con: Some(Connection::TcpStream(tcp_stream)), + }); - let elements = RefCell::new(vec![sock_obj, vm.get_none()]); + let sock_obj = PyObject::new(PyObjectPayload::Socket { socket }, zelf.typ()); - Ok(PyObject::new( - PyObjectPayload::Sequence { elements }, - vm.ctx.tuple_type(), - )) - } - _ => Err(vm.new_type_error("".to_string())), - } + let elements = RefCell::new(vec![sock_obj, vm.get_none()]); + + Ok(PyObject::new( + PyObjectPayload::Sequence { elements }, + vm.ctx.tuple_type(), + )) } fn socket_recv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -249,17 +249,14 @@ fn socket_recv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(zelf, None), (bufsize, Some(vm.ctx.int_type()))] ); - match zelf.payload { - PyObjectPayload::Socket { ref socket } => { - let mut buffer = vec![0u8; objint::get_value(bufsize).to_usize().unwrap()]; - match socket.borrow_mut().con { - Some(ref mut v) => v.read_exact(&mut buffer).unwrap(), - None => return Err(vm.new_type_error("".to_string())), - }; - Ok(vm.ctx.new_bytes(buffer)) - } - _ => Err(vm.new_type_error("".to_string())), - } + let mut socket = get_socket(zelf); + + let mut buffer = vec![0u8; objint::get_value(bufsize).to_usize().unwrap()]; + match socket.con { + Some(ref mut v) => v.read_exact(&mut buffer).unwrap(), + None => return Err(vm.new_type_error("".to_string())), + }; + Ok(vm.ctx.new_bytes(buffer)) } fn socket_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -268,60 +265,51 @@ fn socket_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(zelf, None), (bytes, Some(vm.ctx.bytes_type()))] ); - match zelf.payload { - PyObjectPayload::Socket { ref socket } => { - match socket.borrow_mut().con { - Some(ref mut v) => v.write(&objbytes::get_value(&bytes)).unwrap(), - None => return Err(vm.new_type_error("".to_string())), - }; - Ok(vm.get_none()) - } - _ => Err(vm.new_type_error("".to_string())), - } + let mut socket = get_socket(zelf); + + match socket.con { + Some(ref mut v) => v.write(&objbytes::get_value(&bytes)).unwrap(), + None => return Err(vm.new_type_error("".to_string())), + }; + Ok(vm.get_none()) } fn socket_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, None)]); - match zelf.payload { - PyObjectPayload::Socket { ref socket } => { - let mut socket = socket.borrow_mut(); - match socket.address_family { - AddressFamily::Inet => match socket.socket_kind { - SocketKind::Stream => { - socket.con = None; - Ok(vm.get_none()) - } - _ => Err(vm.new_type_error("".to_string())), - }, - _ => Err(vm.new_type_error("".to_string())), + + let mut socket = get_socket(zelf); + + match socket.address_family { + AddressFamily::Inet => match socket.socket_kind { + SocketKind::Stream => { + socket.con = None; + Ok(vm.get_none()) } - } + _ => Err(vm.new_type_error("".to_string())), + }, _ => Err(vm.new_type_error("".to_string())), } } fn socket_getsockname(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, None)]); - match zelf.payload { - PyObjectPayload::Socket { ref socket } => { - let addr = match socket.borrow_mut().con { - Some(ref mut v) => v.local_addr(), - None => return Err(vm.new_type_error("".to_string())), - }; - - match addr { - Ok(addr) => { - let port = vm.ctx.new_int(addr.port()); - let ip = vm.ctx.new_str(addr.ip().to_string()); - let elements = RefCell::new(vec![ip, port]); - - Ok(PyObject::new( - PyObjectPayload::Sequence { elements }, - vm.ctx.tuple_type(), - )) - } - _ => Err(vm.new_type_error("".to_string())), - } + let mut socket = get_socket(zelf); + + let addr = match socket.con { + Some(ref mut v) => v.local_addr(), + None => return Err(vm.new_type_error("".to_string())), + }; + + match addr { + Ok(addr) => { + let port = vm.ctx.new_int(addr.port()); + let ip = vm.ctx.new_str(addr.ip().to_string()); + let elements = RefCell::new(vec![ip, port]); + + Ok(PyObject::new( + PyObjectPayload::Sequence { elements }, + vm.ctx.tuple_type(), + )) } _ => Err(vm.new_type_error("".to_string())), } From aed657173f295946bd7edc8ba52f8630e015879b Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sat, 2 Mar 2019 10:53:25 +0200 Subject: [PATCH 116/380] Simplify socket_close --- vm/src/stdlib/socket.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index f687efd358..5b61eac6cf 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -278,17 +278,8 @@ fn socket_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, None)]); let mut socket = get_socket(zelf); - - match socket.address_family { - AddressFamily::Inet => match socket.socket_kind { - SocketKind::Stream => { - socket.con = None; - Ok(vm.get_none()) - } - _ => Err(vm.new_type_error("".to_string())), - }, - _ => Err(vm.new_type_error("".to_string())), - } + socket.con = None; + Ok(vm.get_none()) } fn socket_getsockname(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From af2f790d0dce7d122984bf4f98ba7ed71d2c8130 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sat, 2 Mar 2019 12:15:43 +0200 Subject: [PATCH 117/380] Use PyObjectPayload::AnyRustValue for socket --- vm/src/pyobject.rs | 5 ----- vm/src/stdlib/socket.rs | 20 ++++++++++++++------ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index e8a2b32c07..e9d791773c 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -29,7 +29,6 @@ use crate::obj::objsuper; use crate::obj::objtuple; use crate::obj::objtype; use crate::obj::objzip; -use crate::stdlib::socket::Socket; use crate::vm::VirtualMachine; use num_bigint::BigInt; use num_bigint::ToBigInt; @@ -1236,9 +1235,6 @@ pub enum PyObjectPayload { RustFunction { function: Box PyResult>, }, - Socket { - socket: RefCell, - }, AnyRustValue { value: Box, }, @@ -1278,7 +1274,6 @@ impl fmt::Debug for PyObjectPayload { PyObjectPayload::Instance { .. } => write!(f, "instance"), PyObjectPayload::RustFunction { .. } => write!(f, "rust function"), PyObjectPayload::Frame { .. } => write!(f, "frame"), - PyObjectPayload::Socket { .. } => write!(f, "socket"), PyObjectPayload::AnyRustValue { .. } => write!(f, "some rust value"), } } diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index 5b61eac6cf..30a1bbdc50 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -110,11 +110,12 @@ impl Socket { } fn get_socket<'a>(obj: &'a PyObjectRef) -> impl DerefMut + 'a { - if let PyObjectPayload::Socket { ref socket } = obj.payload { - socket.borrow_mut() - } else { - panic!("Inner error getting socket {:?}", obj); + if let PyObjectPayload::AnyRustValue { ref value } = obj.payload { + if let Some(socket) = value.downcast_ref::>() { + return socket.borrow_mut(); + } } + panic!("Inner error getting socket {:?}", obj); } fn socket_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -134,7 +135,9 @@ fn socket_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let socket = RefCell::new(Socket::new(address_family, kind)); Ok(PyObject::new( - PyObjectPayload::Socket { socket }, + PyObjectPayload::AnyRustValue { + value: Box::new(socket), + }, cls.clone(), )) } @@ -233,7 +236,12 @@ fn socket_accept(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { con: Some(Connection::TcpStream(tcp_stream)), }); - let sock_obj = PyObject::new(PyObjectPayload::Socket { socket }, zelf.typ()); + let sock_obj = PyObject::new( + PyObjectPayload::AnyRustValue { + value: Box::new(socket), + }, + zelf.typ(), + ); let elements = RefCell::new(vec![sock_obj, vm.get_none()]); From a70f251b6a0a93444b5ddcdd653065982f810662 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sat, 2 Mar 2019 03:47:37 -0800 Subject: [PATCH 118/380] PyNativeFuncFactory => IntoPyNativeFunc --- vm/src/pyobject.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index bbde769b24..9cff28f792 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -576,13 +576,13 @@ impl PyContext { ) } - pub fn new_rustfunc(&self, factory: F) -> PyObjectRef + pub fn new_rustfunc(&self, f: F) -> PyObjectRef where - F: PyNativeFuncFactory, + F: IntoPyNativeFunc, { PyObject::new( PyObjectPayload::RustFunction { - function: factory.create(), + function: f.into_func(), }, self.builtin_function_or_method_type(), ) @@ -1234,35 +1234,35 @@ tuple_from_py_func_args!(A, B, C, D, E); pub type PyNativeFunc = Box PyResult + 'static>; -pub trait PyNativeFuncFactory { - fn create(self) -> PyNativeFunc; +pub trait IntoPyNativeFunc { + fn into_func(self) -> PyNativeFunc; } -impl PyNativeFuncFactory for F +impl IntoPyNativeFunc for F where F: Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult + 'static, { - fn create(self) -> PyNativeFunc { + fn into_func(self) -> PyNativeFunc { Box::new(self) } } -impl PyNativeFuncFactory for PyNativeFunc { - fn create(self) -> PyNativeFunc { +impl IntoPyNativeFunc for PyNativeFunc { + fn into_func(self) -> PyNativeFunc { self } } -macro_rules! py_native_func_factory_tuple { +macro_rules! into_py_native_func_tuple { ($(($n:tt, $T:ident)),+) => { - impl PyNativeFuncFactory<($($T,)+), R> for F + impl IntoPyNativeFunc<($($T,)+), R> for F where F: Fn($($T,)+ &mut VirtualMachine) -> R + 'static, $($T: FromArgs,)+ ($($T,)+): FromArgs, R: IntoPyObject, { - fn create(self) -> PyNativeFunc { + fn into_func(self) -> PyNativeFunc { Box::new(move |vm, args| { let ($($n,)+) = args.bind::<($($T,)+)>(vm)?; @@ -1273,11 +1273,11 @@ macro_rules! py_native_func_factory_tuple { }; } -py_native_func_factory_tuple!((a, A)); -py_native_func_factory_tuple!((a, A), (b, B)); -py_native_func_factory_tuple!((a, A), (b, B), (c, C)); -py_native_func_factory_tuple!((a, A), (b, B), (c, C), (d, D)); -py_native_func_factory_tuple!((a, A), (b, B), (c, C), (d, D), (e, E)); +into_py_native_func_tuple!((a, A)); +into_py_native_func_tuple!((a, A), (b, B)); +into_py_native_func_tuple!((a, A), (b, B), (c, C)); +into_py_native_func_tuple!((a, A), (b, B), (c, C), (d, D)); +into_py_native_func_tuple!((a, A), (b, B), (c, C), (d, D), (e, E)); /// Rather than determining the type of a python object, this enum is more /// a holder for the rust payload of a python object. It is more a carrier From 30ddb4803790ff4b6ffc410b0bc8c1a25d24b494 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Sat, 2 Mar 2019 14:10:46 +0100 Subject: [PATCH 119/380] Use PyResult at more places. --- vm/src/obj/objcode.rs | 2 +- vm/src/obj/objfloat.rs | 2 +- vm/src/obj/objint.rs | 2 +- vm/src/stdlib/pystruct.rs | 24 ++++++++++++------------ 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index 178946de84..c9f244466b 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -58,7 +58,7 @@ fn code_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn member_code_obj( vm: &mut VirtualMachine, args: PyFuncArgs, -) -> Result { +) -> PyResult { arg_check!( vm, args, diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index f4d4068a76..b59e893e3e 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -62,7 +62,7 @@ pub fn get_value(obj: &PyObjectRef) -> f64 { } } -pub fn make_float(vm: &mut VirtualMachine, obj: &PyObjectRef) -> Result { +pub fn make_float(vm: &mut VirtualMachine, obj: &PyObjectRef) -> PyResult { if objtype::isinstance(obj, &vm.ctx.float_type()) { Ok(get_value(obj)) } else if let Ok(method) = vm.get_method(obj.clone(), "__float__") { diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 7e22f19be2..480eaf6753 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -67,7 +67,7 @@ pub fn to_int( vm: &mut VirtualMachine, obj: &PyObjectRef, base: u32, -) -> Result { +) -> PyResult { let val = if objtype::isinstance(obj, &vm.ctx.int_type()) { get_value(obj) } else if objtype::isinstance(obj, &vm.ctx.float_type()) { diff --git a/vm/src/stdlib/pystruct.rs b/vm/src/stdlib/pystruct.rs index c686b00063..5599e61a4c 100644 --- a/vm/src/stdlib/pystruct.rs +++ b/vm/src/stdlib/pystruct.rs @@ -42,7 +42,7 @@ fn parse_format_string(fmt: String) -> Vec { codes } -fn get_int(vm: &mut VirtualMachine, arg: &PyObjectRef) -> Result { +fn get_int(vm: &mut VirtualMachine, arg: &PyObjectRef) -> PyResult { objint::to_int(vm, arg, 10) } @@ -50,7 +50,7 @@ fn pack_i8( vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write, -) -> Result<(), PyObjectRef> { +) -> PyResult<()> { let v = get_int(vm, arg)?.to_i8().unwrap(); data.write_i8(v).unwrap(); Ok(()) @@ -60,7 +60,7 @@ fn pack_u8( vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write, -) -> Result<(), PyObjectRef> { +) -> PyResult<()> { let v = get_int(vm, arg)?.to_u8().unwrap(); data.write_u8(v).unwrap(); Ok(()) @@ -70,7 +70,7 @@ fn pack_bool( vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write, -) -> Result<(), PyObjectRef> { +) -> PyResult<()> { if objtype::isinstance(&arg, &vm.ctx.bool_type()) { let v = if objbool::get_value(arg) { 1 } else { 0 }; data.write_u8(v).unwrap(); @@ -84,7 +84,7 @@ fn pack_i16( vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write, -) -> Result<(), PyObjectRef> { +) -> PyResult<()> { let v = get_int(vm, arg)?.to_i16().unwrap(); data.write_i16::(v).unwrap(); Ok(()) @@ -94,7 +94,7 @@ fn pack_u16( vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write, -) -> Result<(), PyObjectRef> { +) -> PyResult<()> { let v = get_int(vm, arg)?.to_u16().unwrap(); data.write_u16::(v).unwrap(); Ok(()) @@ -104,7 +104,7 @@ fn pack_i32( vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write, -) -> Result<(), PyObjectRef> { +) -> PyResult<()> { let v = get_int(vm, arg)?.to_i32().unwrap(); data.write_i32::(v).unwrap(); Ok(()) @@ -114,7 +114,7 @@ fn pack_u32( vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write, -) -> Result<(), PyObjectRef> { +) -> PyResult<()> { let v = get_int(vm, arg)?.to_u32().unwrap(); data.write_u32::(v).unwrap(); Ok(()) @@ -124,7 +124,7 @@ fn pack_i64( vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write, -) -> Result<(), PyObjectRef> { +) -> PyResult<()> { let v = get_int(vm, arg)?.to_i64().unwrap(); data.write_i64::(v).unwrap(); Ok(()) @@ -134,7 +134,7 @@ fn pack_u64( vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write, -) -> Result<(), PyObjectRef> { +) -> PyResult<()> { let v = get_int(vm, arg)?.to_u64().unwrap(); data.write_u64::(v).unwrap(); Ok(()) @@ -144,7 +144,7 @@ fn pack_f32( vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write, -) -> Result<(), PyObjectRef> { +) -> PyResult<()> { if objtype::isinstance(&arg, &vm.ctx.float_type()) { let v = objfloat::get_value(arg) as f32; data.write_f32::(v).unwrap(); @@ -158,7 +158,7 @@ fn pack_f64( vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write, -) -> Result<(), PyObjectRef> { +) -> PyResult<()> { if objtype::isinstance(&arg, &vm.ctx.float_type()) { let v = objfloat::get_value(arg) as f64; data.write_f64::(v).unwrap(); From 4558dbf21047bbfb84f7c8d7647812c842b74dae Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sat, 2 Mar 2019 13:25:42 -0800 Subject: [PATCH 120/380] Fix build (rustfmt) --- vm/src/obj/objcode.rs | 5 +-- vm/src/obj/objint.rs | 6 +--- vm/src/stdlib/pystruct.rs | 66 +++++++-------------------------------- 3 files changed, 13 insertions(+), 64 deletions(-) diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index c9f244466b..53ea55f4c3 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -55,10 +55,7 @@ fn code_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_str(repr)) } -fn member_code_obj( - vm: &mut VirtualMachine, - args: PyFuncArgs, -) -> PyResult { +fn member_code_obj(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 8fb5bcdd1e..a79dfb6dd5 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -83,11 +83,7 @@ fn int_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } // Casting function: -pub fn to_int( - vm: &mut VirtualMachine, - obj: &PyObjectRef, - base: u32, -) -> PyResult { +pub fn to_int(vm: &mut VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult { let val = if objtype::isinstance(obj, &vm.ctx.int_type()) { get_value(obj) } else if objtype::isinstance(obj, &vm.ctx.float_type()) { diff --git a/vm/src/stdlib/pystruct.rs b/vm/src/stdlib/pystruct.rs index 5599e61a4c..c96bba736e 100644 --- a/vm/src/stdlib/pystruct.rs +++ b/vm/src/stdlib/pystruct.rs @@ -46,31 +46,19 @@ fn get_int(vm: &mut VirtualMachine, arg: &PyObjectRef) -> PyResult { objint::to_int(vm, arg, 10) } -fn pack_i8( - vm: &mut VirtualMachine, - arg: &PyObjectRef, - data: &mut Write, -) -> PyResult<()> { +fn pack_i8(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { let v = get_int(vm, arg)?.to_i8().unwrap(); data.write_i8(v).unwrap(); Ok(()) } -fn pack_u8( - vm: &mut VirtualMachine, - arg: &PyObjectRef, - data: &mut Write, -) -> PyResult<()> { +fn pack_u8(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { let v = get_int(vm, arg)?.to_u8().unwrap(); data.write_u8(v).unwrap(); Ok(()) } -fn pack_bool( - vm: &mut VirtualMachine, - arg: &PyObjectRef, - data: &mut Write, -) -> PyResult<()> { +fn pack_bool(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { if objtype::isinstance(&arg, &vm.ctx.bool_type()) { let v = if objbool::get_value(arg) { 1 } else { 0 }; data.write_u8(v).unwrap(); @@ -80,71 +68,43 @@ fn pack_bool( } } -fn pack_i16( - vm: &mut VirtualMachine, - arg: &PyObjectRef, - data: &mut Write, -) -> PyResult<()> { +fn pack_i16(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { let v = get_int(vm, arg)?.to_i16().unwrap(); data.write_i16::(v).unwrap(); Ok(()) } -fn pack_u16( - vm: &mut VirtualMachine, - arg: &PyObjectRef, - data: &mut Write, -) -> PyResult<()> { +fn pack_u16(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { let v = get_int(vm, arg)?.to_u16().unwrap(); data.write_u16::(v).unwrap(); Ok(()) } -fn pack_i32( - vm: &mut VirtualMachine, - arg: &PyObjectRef, - data: &mut Write, -) -> PyResult<()> { +fn pack_i32(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { let v = get_int(vm, arg)?.to_i32().unwrap(); data.write_i32::(v).unwrap(); Ok(()) } -fn pack_u32( - vm: &mut VirtualMachine, - arg: &PyObjectRef, - data: &mut Write, -) -> PyResult<()> { +fn pack_u32(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { let v = get_int(vm, arg)?.to_u32().unwrap(); data.write_u32::(v).unwrap(); Ok(()) } -fn pack_i64( - vm: &mut VirtualMachine, - arg: &PyObjectRef, - data: &mut Write, -) -> PyResult<()> { +fn pack_i64(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { let v = get_int(vm, arg)?.to_i64().unwrap(); data.write_i64::(v).unwrap(); Ok(()) } -fn pack_u64( - vm: &mut VirtualMachine, - arg: &PyObjectRef, - data: &mut Write, -) -> PyResult<()> { +fn pack_u64(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { let v = get_int(vm, arg)?.to_u64().unwrap(); data.write_u64::(v).unwrap(); Ok(()) } -fn pack_f32( - vm: &mut VirtualMachine, - arg: &PyObjectRef, - data: &mut Write, -) -> PyResult<()> { +fn pack_f32(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { if objtype::isinstance(&arg, &vm.ctx.float_type()) { let v = objfloat::get_value(arg) as f32; data.write_f32::(v).unwrap(); @@ -154,11 +114,7 @@ fn pack_f32( } } -fn pack_f64( - vm: &mut VirtualMachine, - arg: &PyObjectRef, - data: &mut Write, -) -> PyResult<()> { +fn pack_f64(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { if objtype::isinstance(&arg, &vm.ctx.float_type()) { let v = objfloat::get_value(arg) as f64; data.write_f64::(v).unwrap(); From e7fb11621079b5f559c0673c9b8cf488bb214ff3 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Thu, 28 Feb 2019 15:52:26 -0800 Subject: [PATCH 121/380] Convert more objects to `Any` payload --- vm/src/obj/objbool.rs | 5 +++- vm/src/obj/objfloat.rs | 34 ++++++++++++++++++----- vm/src/obj/objiter.rs | 62 +++++++++++++++++++++--------------------- vm/src/obj/objrange.rs | 42 ++++++++++++++++------------ vm/src/pyobject.rs | 17 +++++------- 5 files changed, 94 insertions(+), 66 deletions(-) diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index 3e0798ef50..84b048e3ae 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -1,3 +1,4 @@ +use super::objfloat::PyFloat; use super::objstr::PyString; use super::objtype; use crate::pyobject::{ @@ -16,9 +17,11 @@ pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result() { return Ok(!s.value.is_empty()); } + if let Some(value) = obj.payload::() { + return Ok(*value != PyFloat::from(0.0)); + } let result = match obj.payload { PyObjectPayload::Integer { ref value } => !value.is_zero(), - PyObjectPayload::Float { value } => value != 0.0, PyObjectPayload::Sequence { ref elements } => !elements.borrow().is_empty(), PyObjectPayload::Dict { ref elements } => !elements.borrow().is_empty(), PyObjectPayload::None { .. } => false, diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index b59e893e3e..a2038bffbc 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -3,12 +3,30 @@ use super::objint; use super::objstr; use super::objtype; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, + TypeProtocol, }; use crate::vm::VirtualMachine; use num_bigint::ToBigInt; use num_traits::ToPrimitive; +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct PyFloat { + value: f64, +} + +impl PyObjectPayload2 for PyFloat { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.float_type() + } +} + +impl From for PyFloat { + fn from(value: f64) -> Self { + PyFloat { value } + } +} + fn float_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(float, Some(vm.ctx.float_type()))]); let v = get_value(float); @@ -50,16 +68,18 @@ fn float_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let type_name = objtype::get_type_name(&arg.typ()); return Err(vm.new_type_error(format!("can't convert {} to float", type_name))); }; - Ok(PyObject::new(PyObjectPayload::Float { value }, cls.clone())) + + Ok(PyObject::new( + PyObjectPayload::AnyRustValue { + value: Box::new(PyFloat { value }), + }, + cls.clone(), + )) } // Retrieve inner float value: pub fn get_value(obj: &PyObjectRef) -> f64 { - if let PyObjectPayload::Float { value } = &obj.payload { - *value - } else { - panic!("Inner error getting float: {}", obj); - } + obj.payload::().unwrap().value } pub fn make_float(vm: &mut VirtualMachine, obj: &PyObjectRef) -> PyResult { diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 2010da906d..9985553fb1 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -2,13 +2,14 @@ * Various types to support iteration. */ -use super::objbool; use crate::pyobject::{ PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; -// use super::objstr; -use super::objtype; // Required for arg_check! to use isinstance + +use super::objbool; +use super::objrange::PyRange; +use super::objtype; /* * This helper function is called at multiple places. First, it is called @@ -129,38 +130,37 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { iterated_obj: ref iterated_obj_ref, } = iter.payload { - match iterated_obj_ref.payload { - PyObjectPayload::Sequence { ref elements } => { - if position.get() < elements.borrow().len() { - let obj_ref = elements.borrow()[position.get()].clone(); - position.set(position.get() + 1); - Ok(obj_ref) - } else { - Err(new_stop_iteration(vm)) - } + if let Some(range) = iterated_obj_ref.payload::() { + if let Some(int) = range.get(position.get()) { + position.set(position.get() + 1); + Ok(vm.ctx.new_int(int)) + } else { + Err(new_stop_iteration(vm)) } - - PyObjectPayload::Range { ref range } => { - if let Some(int) = range.get(position.get()) { - position.set(position.get() + 1); - Ok(vm.ctx.new_int(int)) - } else { - Err(new_stop_iteration(vm)) + } else { + match iterated_obj_ref.payload { + PyObjectPayload::Sequence { ref elements } => { + if position.get() < elements.borrow().len() { + let obj_ref = elements.borrow()[position.get()].clone(); + position.set(position.get() + 1); + Ok(obj_ref) + } else { + Err(new_stop_iteration(vm)) + } } - } - - PyObjectPayload::Bytes { ref value } => { - if position.get() < value.borrow().len() { - let obj_ref = vm.ctx.new_int(value.borrow()[position.get()]); - position.set(position.get() + 1); - Ok(obj_ref) - } else { - Err(new_stop_iteration(vm)) + PyObjectPayload::Bytes { ref value } => { + if position.get() < value.borrow().len() { + let obj_ref = vm.ctx.new_int(value.borrow()[position.get()]); + position.set(position.get() + 1); + Ok(obj_ref) + } else { + Err(new_stop_iteration(vm)) + } } - } - _ => { - panic!("NOT IMPL"); + _ => { + panic!("NOT IMPL"); + } } } } else { diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index daa5aab52f..41a4c69645 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -4,7 +4,8 @@ use std::ops::Mul; use super::objint; use super::objtype; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, + TypeProtocol, }; use crate::vm::VirtualMachine; use num_bigint::{BigInt, Sign}; @@ -12,7 +13,7 @@ use num_integer::Integer; use num_traits::{One, Signed, ToPrimitive, Zero}; #[derive(Debug, Clone)] -pub struct RangeType { +pub struct PyRange { // 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: BigInt, @@ -20,7 +21,13 @@ pub struct RangeType { pub step: BigInt, } -impl RangeType { +impl PyObjectPayload2 for PyRange { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.range_type() + } +} + +impl PyRange { #[inline] pub fn try_len(&self) -> Option { match self.step.sign() { @@ -116,12 +123,12 @@ impl RangeType { }; match self.step.sign() { - Sign::Plus => RangeType { + Sign::Plus => PyRange { start, end: &self.start - 1, step: -&self.step, }, - Sign::Minus => RangeType { + Sign::Minus => PyRange { start, end: &self.start + 1, step: -&self.step, @@ -139,12 +146,8 @@ impl RangeType { } } -pub fn get_value(obj: &PyObjectRef) -> RangeType { - if let PyObjectPayload::Range { range } = &obj.payload { - range.clone() - } else { - panic!("Inner error getting range {:?}", obj); - } +pub fn get_value(obj: &PyObjectRef) -> PyRange { + obj.payload::().unwrap().clone() } pub fn init(context: &PyContext) { @@ -223,8 +226,8 @@ fn range_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Err(vm.new_value_error("range with 0 step size".to_string())) } else { Ok(PyObject::new( - PyObjectPayload::Range { - range: RangeType { start, end, step }, + PyObjectPayload::AnyRustValue { + value: Box::new(PyRange { start, end, step }), }, cls.clone(), )) @@ -251,7 +254,12 @@ fn range_reversed(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(PyObject::new( PyObjectPayload::Iterator { position: Cell::new(0), - iterated_obj: PyObject::new(PyObjectPayload::Range { range }, vm.ctx.range_type()), + iterated_obj: PyObject::new( + PyObjectPayload::AnyRustValue { + value: Box::new(range), + }, + vm.ctx.range_type(), + ), }, vm.ctx.iter_type(), )) @@ -316,12 +324,12 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; Ok(PyObject::new( - PyObjectPayload::Range { - range: RangeType { + PyObjectPayload::AnyRustValue { + value: Box::new(PyRange { start: new_start, end: new_end, step: new_step, - }, + }), }, vm.ctx.range_type(), )) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index c39aa71ae6..bfb33871e8 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -459,8 +459,13 @@ impl PyContext { ) } - pub fn new_float(&self, i: f64) -> PyObjectRef { - PyObject::new(PyObjectPayload::Float { value: i }, self.float_type()) + pub fn new_float(&self, value: f64) -> PyObjectRef { + PyObject::new( + PyObjectPayload::AnyRustValue { + value: Box::new(objfloat::PyFloat::from(value)), + }, + self.float_type(), + ) } pub fn new_complex(&self, i: Complex64) -> PyObjectRef { @@ -1245,9 +1250,6 @@ pub enum PyObjectPayload { Integer { value: BigInt, }, - Float { - value: f64, - }, Complex { value: Complex64, }, @@ -1284,9 +1286,6 @@ pub enum PyObjectPayload { stop: Option, step: Option, }, - Range { - range: objrange::RangeType, - }, MemoryView { obj: PyObjectRef, }, @@ -1340,7 +1339,6 @@ impl fmt::Debug for PyObjectPayload { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { 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), @@ -1348,7 +1346,6 @@ impl fmt::Debug for PyObjectPayload { PyObjectPayload::Dict { .. } => write!(f, "dict"), PyObjectPayload::Set { .. } => write!(f, "set"), 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"), From 0a68b7a3da67f478103ab1adb05f933ce1a269db Mon Sep 17 00:00:00 2001 From: Abdul Arfan Date: Sun, 3 Mar 2019 09:18:08 +0700 Subject: [PATCH 122/380] fix typo in README.md, will to with --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b060a29e92..7e7b0bd70d 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ $ pipenv install $ pipenv run pytest -v ``` -There also are some unit tests, you can run those will cargo: +There also are some unit tests, you can run those with cargo: ```shell $ cargo test --all From 9271115db7f06afc3982bda571b9d8923f376f69 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Sun, 3 Mar 2019 14:25:00 +0100 Subject: [PATCH 123/380] Introduce py_class macro to define classes. (#589) * Introduce py_class macro to define classes. * rustfmt --- vm/src/dictdatatype.rs | 18 +----- vm/src/macros.rs | 13 +++++ vm/src/stdlib/io.rs | 126 +++++++++++++++-------------------------- vm/src/stdlib/re.rs | 14 ++--- 4 files changed, 68 insertions(+), 103 deletions(-) diff --git a/vm/src/dictdatatype.rs b/vm/src/dictdatatype.rs index 2ba30c066b..df6b3bb5a8 100644 --- a/vm/src/dictdatatype.rs +++ b/vm/src/dictdatatype.rs @@ -66,11 +66,7 @@ impl Dict { } } - pub fn contains( - &self, - vm: &mut VirtualMachine, - key: &PyObjectRef, - ) -> PyResult { + pub fn contains(&self, vm: &mut VirtualMachine, key: &PyObjectRef) -> PyResult { if let LookupResult::Existing(_index) = self.lookup(vm, key)? { Ok(true) } else { @@ -93,11 +89,7 @@ impl Dict { } /// Delete a key - pub fn delete( - &mut self, - vm: &mut VirtualMachine, - key: &PyObjectRef, - ) -> PyResult<()> { + pub fn delete(&mut self, vm: &mut VirtualMachine, key: &PyObjectRef) -> PyResult<()> { if let LookupResult::Existing(index) = self.lookup(vm, key)? { self.entries[index] = None; self.size -= 1; @@ -126,11 +118,7 @@ impl Dict { } /// Lookup the index for the given key. - fn lookup( - &self, - vm: &mut VirtualMachine, - key: &PyObjectRef, - ) -> PyResult { + fn lookup(&self, vm: &mut VirtualMachine, key: &PyObjectRef) -> PyResult { let hash_value = calc_hash(vm, key)?; let perturb = hash_value; let mut hash_index: usize = hash_value; diff --git a/vm/src/macros.rs b/vm/src/macros.rs index 2c48286d34..f228b6a7fb 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -124,3 +124,16 @@ macro_rules! py_module { } } } + +#[macro_export] +macro_rules! py_class { + ( $ctx:expr, $class_name:expr, $class_base:expr, { $($name:expr => $value:expr),* $(,)* }) => { + { + let py_class = $ctx.new_class($class_name, $class_base); + $( + $ctx.set_attr(&py_class, $name, $value); + )* + py_class + } + } +} diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 077f907d89..b63b6c8e55 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -346,101 +346,67 @@ 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 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 = { - 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), - ); - buffered_io_base - }; - ctx.set_attr(&py_mod, "BufferedIOBase", buffered_io_base.clone()); + + let buffered_io_base = py_class!(ctx, "BufferedIOBase", io_base.clone(), { + "__init__" => ctx.new_rustfunc(buffered_io_base_init) + }); //TextIO Base has no public constructor - let text_io_base = { - 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)); - text_io_base - }; - ctx.set_attr(&py_mod, "TextIOBase", text_io_base.clone()); + let text_io_base = py_class!(ctx, "TextIOBase", io_base.clone(), { + "read" => ctx.new_rustfunc(text_io_base_read) + }); // RawBaseIO Subclasses - let file_io = { - 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, "readinto", ctx.new_rustfunc(file_io_readinto)); - ctx.set_attr(&file_io, "write", ctx.new_rustfunc(file_io_write)); - file_io - }; - ctx.set_attr(&py_mod, "FileIO", file_io.clone()); + let file_io = py_class!(ctx, "FileIO", raw_io_base.clone(), { + "__init__" => ctx.new_rustfunc(file_io_init), + "name" => ctx.str_type(), + "read" => ctx.new_rustfunc(file_io_read), + "readinto" => ctx.new_rustfunc(file_io_readinto), + "write" => ctx.new_rustfunc(file_io_write) + }); // BufferedIOBase Subclasses - let buffered_reader = { - let buffered_reader = ctx.new_class("BufferedReader", buffered_io_base.clone()); - ctx.set_attr( - &buffered_reader, - "read", - ctx.new_rustfunc(buffered_reader_read), - ); - buffered_reader - }; - ctx.set_attr(&py_mod, "BufferedReader", buffered_reader.clone()); - - let buffered_writer = { - let buffered_writer = ctx.new_class("BufferedWriter", buffered_io_base.clone()); - ctx.set_attr( - &buffered_writer, - "write", - ctx.new_rustfunc(buffered_writer_write), - ); - buffered_writer - }; - ctx.set_attr(&py_mod, "BufferedWriter", buffered_writer.clone()); + let buffered_reader = py_class!(ctx, "BufferedReader", buffered_io_base.clone(), { + "read" => ctx.new_rustfunc(buffered_reader_read) + }); + + let buffered_writer = py_class!(ctx, "BufferedWriter", buffered_io_base.clone(), { + "write" => ctx.new_rustfunc(buffered_writer_write) + }); //TextIOBase Subclass - let text_io_wrapper = { - 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), - ); - text_io_wrapper - }; - ctx.set_attr(&py_mod, "TextIOWrapper", text_io_wrapper.clone()); + let text_io_wrapper = py_class!(ctx, "TextIOWrapper", text_io_base.clone(), { + "__init__" => ctx.new_rustfunc(text_io_wrapper_init) + }); //StringIO: in-memory text - let string_io = { - let string_io = ctx.new_class("StringIO", text_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 string_io = py_class!(ctx, "StringIO", text_io_base.clone(), { + "__init__" => ctx.new_rustfunc(string_io_init), + "getvalue" => ctx.new_rustfunc(string_io_getvalue) + }); //BytesIO: in-memory bytes - let bytes_io = { - let bytes_io = ctx.new_class("BytesIO", buffered_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 + let bytes_io = py_class!(ctx, "BytesIO", buffered_io_base.clone(), { + "__init__" => ctx.new_rustfunc(bytes_io_init), + "getvalue" => ctx.new_rustfunc(bytes_io_getvalue) + }); + + py_module!(ctx, "io", { + "open" => ctx.new_rustfunc(io_open), + "IOBase" => io_base.clone(), + "RawIOBase" => raw_io_base.clone(), + "BufferedIOBase" => buffered_io_base.clone(), + "TextIOBase" => text_io_base.clone(), + "FileIO" => file_io.clone(), + "BufferedReader" => buffered_reader.clone(), + "BufferedWriter" => buffered_writer.clone(), + "TextIOWrapper" => text_io_wrapper.clone(), + "StringIO" => string_io, + "BytesIO" => bytes_io, + }) } diff --git a/vm/src/stdlib/re.rs b/vm/src/stdlib/re.rs index 0c0cef934f..b262ff76ae 100644 --- a/vm/src/stdlib/re.rs +++ b/vm/src/stdlib/re.rs @@ -50,13 +50,11 @@ 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 = py_class!(ctx, "Match", ctx.object(), {}); - 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 + py_module!(ctx, "re", { + "Match" => match_type, + "match" => ctx.new_rustfunc(re_match), + "search" => ctx.new_rustfunc(re_search) + }) } From e5d1d11c3eaab4dd4114a8c7cda7c54a8417a61e Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sat, 2 Mar 2019 13:46:45 +0200 Subject: [PATCH 124/380] Add UDP to socket --- tests/snippets/stdlib_socket.py | 39 +++++++++++++++------ vm/src/stdlib/socket.rs | 61 +++++++++++++++++++++++++-------- 2 files changed, 76 insertions(+), 24 deletions(-) diff --git a/tests/snippets/stdlib_socket.py b/tests/snippets/stdlib_socket.py index e0ea6d1682..2a3c3a94e2 100644 --- a/tests/snippets/stdlib_socket.py +++ b/tests/snippets/stdlib_socket.py @@ -1,6 +1,10 @@ import socket from testutils import assertRaises +MESSAGE_A = b'aaaa' +MESSAGE_B= b'bbbbb' + +# TCP listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM) listener.bind(("127.0.0.1", 0)) @@ -10,16 +14,12 @@ connector.connect(("127.0.0.1", listener.getsockname()[1])) connection = listener.accept()[0] -message_a = b'aaaa' -message_b = b'bbbbb' - -connector.send(message_a) -connection.send(message_b) -recv_a = connection.recv(len(message_a)) -recv_b = connector.recv(len(message_b)) -assert recv_a == message_a -assert recv_b == message_b - +connector.send(MESSAGE_A) +connection.send(MESSAGE_B) +recv_a = connection.recv(len(MESSAGE_A)) +recv_b = connector.recv(len(MESSAGE_B)) +assert recv_a == MESSAGE_A +assert recv_b == MESSAGE_B connection.close() connector.close() listener.close() @@ -35,3 +35,22 @@ s.bind((888, 8888)) s.close() + +# UDP +sock1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +sock1.bind(("127.0.0.1", 0)) + +sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +sock2.bind(("127.0.0.1", 0)) + +sock1.connect(("127.0.0.1", sock2.getsockname()[1])) +sock2.connect(("127.0.0.1", sock1.getsockname()[1])) + +sock1.send(MESSAGE_A) +sock2.send(MESSAGE_B) +recv_a = sock2.recv(len(MESSAGE_A)) +recv_b = sock1.recv(len(MESSAGE_B)) +assert recv_a == MESSAGE_A +assert recv_b == MESSAGE_B +sock1.close() +sock2.close() diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index 30a1bbdc50..e85b22d815 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -2,7 +2,7 @@ use std::cell::RefCell; use std::io; use std::io::Read; use std::io::Write; -use std::net::{SocketAddr, TcpListener, TcpStream}; +use std::net::{SocketAddr, TcpListener, TcpStream, UdpSocket}; use std::ops::DerefMut; use crate::obj::objbytes; @@ -53,7 +53,7 @@ impl SocketKind { enum Connection { TcpListener(TcpListener), TcpStream(TcpStream), - // UdpSocket(UdpSocket), + UdpSocket(UdpSocket), } impl Connection { @@ -67,6 +67,7 @@ impl Connection { fn local_addr(&self) -> io::Result { match self { Connection::TcpListener(con) => con.local_addr(), + Connection::UdpSocket(con) => con.local_addr(), _ => Err(io::Error::new(io::ErrorKind::Other, "oh no!")), } } @@ -76,6 +77,7 @@ impl Read for Connection { fn read(&mut self, buf: &mut [u8]) -> io::Result { match self { Connection::TcpStream(con) => con.read(buf), + Connection::UdpSocket(con) => con.recv(buf), _ => Err(io::Error::new(io::ErrorKind::Other, "oh no!")), } } @@ -85,6 +87,7 @@ impl Write for Connection { fn write(&mut self, buf: &[u8]) -> io::Result { match self { Connection::TcpStream(con) => con.write(buf), + Connection::UdpSocket(con) => con.send(buf), _ => Err(io::Error::new(io::ErrorKind::Other, "oh no!")), } } @@ -153,12 +156,27 @@ fn socket_connect(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut socket = get_socket(zelf); - if let Ok(stream) = TcpStream::connect(address_string) { - socket.con = Some(Connection::TcpStream(stream)); - Ok(vm.get_none()) - } else { - // TODO: Socket error - Err(vm.new_type_error("socket failed".to_string())) + match socket.socket_kind { + SocketKind::Stream => { + if let Ok(stream) = TcpStream::connect(address_string) { + socket.con = Some(Connection::TcpStream(stream)); + Ok(vm.get_none()) + } else { + // TODO: Socket error + Err(vm.new_type_error("socket failed".to_string())) + } + } + SocketKind::Dgram => { + if let Some(Connection::UdpSocket(con)) = &socket.con { + match con.connect(address_string) { + Ok(_) => Ok(vm.get_none()), + // TODO: Socket error + Err(_) => Err(vm.new_type_error("socket failed".to_string())), + } + } else { + Err(vm.new_type_error("".to_string())) + } + } } } @@ -173,12 +191,25 @@ fn socket_bind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut socket = get_socket(zelf); - if let Ok(stream) = TcpListener::bind(address_string) { - socket.con = Some(Connection::TcpListener(stream)); - Ok(vm.get_none()) - } else { - // TODO: Socket error - Err(vm.new_type_error("socket failed".to_string())) + match socket.socket_kind { + SocketKind::Stream => { + if let Ok(stream) = TcpListener::bind(address_string) { + socket.con = Some(Connection::TcpListener(stream)); + Ok(vm.get_none()) + } else { + // TODO: Socket error + Err(vm.new_type_error("socket failed".to_string())) + } + } + SocketKind::Dgram => { + if let Ok(dgram) = UdpSocket::bind(address_string) { + socket.con = Some(Connection::UdpSocket(dgram)); + Ok(vm.get_none()) + } else { + // TODO: Socket error + Err(vm.new_type_error("socket failed".to_string())) + } + } } } @@ -325,6 +356,8 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.new_int(SocketKind::Stream as i32), ); + ctx.set_attr(&py_mod, "SOCK_DGRAM", ctx.new_int(SocketKind::Dgram as i32)); + let socket = { let socket = ctx.new_class("socket", ctx.object()); ctx.set_attr(&socket, "__new__", ctx.new_rustfunc(socket_new)); From 86b60faa61d80458e6304db5b6ad596da52f4183 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sat, 2 Mar 2019 17:12:13 +0200 Subject: [PATCH 125/380] Add socket.{sendto, recvfrom} --- tests/snippets/stdlib_socket.py | 6 ++- vm/src/stdlib/socket.rs | 87 +++++++++++++++++++++++++++++---- 2 files changed, 82 insertions(+), 11 deletions(-) diff --git a/tests/snippets/stdlib_socket.py b/tests/snippets/stdlib_socket.py index 2a3c3a94e2..af29b451c7 100644 --- a/tests/snippets/stdlib_socket.py +++ b/tests/snippets/stdlib_socket.py @@ -41,8 +41,12 @@ sock1.bind(("127.0.0.1", 0)) sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) -sock2.bind(("127.0.0.1", 0)) +sock2.sendto(MESSAGE_A, sock1.getsockname()) +(recv_a, addr) = sock1.recvfrom(len(MESSAGE_A)) +assert recv_a == MESSAGE_A + +sock2.bind(("127.0.0.1", 0)) sock1.connect(("127.0.0.1", sock2.getsockname()[1])) sock2.connect(("127.0.0.1", sock1.getsockname()[1])) diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index e85b22d815..37dc8a6fa2 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -71,6 +71,13 @@ impl Connection { _ => Err(io::Error::new(io::ErrorKind::Other, "oh no!")), } } + + fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + match self { + Connection::UdpSocket(con) => con.recv_from(buf), + _ => Err(io::Error::new(io::ErrorKind::Other, "oh no!")), + } + } } impl Read for Connection { @@ -298,6 +305,34 @@ fn socket_recv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bytes(buffer)) } +fn socket_recvfrom(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, None), (bufsize, Some(vm.ctx.int_type()))] + ); + + let mut socket = get_socket(zelf); + + let mut buffer = vec![0u8; objint::get_value(bufsize).to_usize().unwrap()]; + let ret = match socket.con { + Some(ref mut v) => v.recv_from(&mut buffer), + None => return Err(vm.new_type_error("".to_string())), + }; + + let addr = match ret { + Ok((_size, addr)) => addr, + _ => return Err(vm.new_type_error("".to_string())), + }; + + let elements = RefCell::new(vec![vm.ctx.new_bytes(buffer), get_addr_tuple(vm, addr)?]); + + Ok(PyObject::new( + PyObjectPayload::Sequence { elements }, + vm.ctx.tuple_type(), + )) +} + fn socket_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -313,6 +348,34 @@ fn socket_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.get_none()) } +fn socket_sendto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [ + (zelf, None), + (bytes, Some(vm.ctx.bytes_type())), + (address, Some(vm.ctx.tuple_type())) + ] + ); + let address_string = get_address_string(vm, address)?; + + let socket = get_socket(zelf); + + match socket.socket_kind { + SocketKind::Dgram => { + // We can't do sendto without bind in std::net::UdpSocket + if let Ok(dgram) = UdpSocket::bind("0.0.0.0:0") { + if let Ok(_) = dgram.send_to(&objbytes::get_value(&bytes), address_string) { + return Ok(vm.get_none()); + } + } + Err(vm.new_type_error("socket failed".to_string())) + } + _ => Err(vm.new_not_implemented_error("".to_string())), + } +} + fn socket_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, None)]); @@ -331,20 +394,22 @@ fn socket_getsockname(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; match addr { - Ok(addr) => { - let port = vm.ctx.new_int(addr.port()); - let ip = vm.ctx.new_str(addr.ip().to_string()); - let elements = RefCell::new(vec![ip, port]); - - Ok(PyObject::new( - PyObjectPayload::Sequence { elements }, - vm.ctx.tuple_type(), - )) - } + Ok(addr) => get_addr_tuple(vm, addr), _ => Err(vm.new_type_error("".to_string())), } } +fn get_addr_tuple(vm: &mut VirtualMachine, addr: SocketAddr) -> PyResult { + let port = vm.ctx.new_int(addr.port()); + let ip = vm.ctx.new_str(addr.ip().to_string()); + let elements = RefCell::new(vec![ip, port]); + + Ok(PyObject::new( + PyObjectPayload::Sequence { elements }, + vm.ctx.tuple_type(), + )) +} + pub fn mk_module(ctx: &PyContext) -> PyObjectRef { let py_mod = ctx.new_module(&"socket".to_string(), ctx.new_scope(None)); @@ -369,6 +434,8 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&socket, "listen", ctx.new_rustfunc(socket_listen)); ctx.set_attr(&socket, "close", ctx.new_rustfunc(socket_close)); ctx.set_attr(&socket, "getsockname", ctx.new_rustfunc(socket_getsockname)); + ctx.set_attr(&socket, "sendto", ctx.new_rustfunc(socket_sendto)); + ctx.set_attr(&socket, "recvfrom", ctx.new_rustfunc(socket_recvfrom)); socket }; ctx.set_attr(&py_mod, "socket", socket.clone()); From 9b5ba068afc88a439999f884bb8e8f4ddbc9ca0f Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sat, 2 Mar 2019 17:21:36 +0200 Subject: [PATCH 126/380] Return addr in accept --- tests/snippets/stdlib_socket.py | 3 ++- vm/src/stdlib/socket.rs | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/snippets/stdlib_socket.py b/tests/snippets/stdlib_socket.py index af29b451c7..54ae180c3e 100644 --- a/tests/snippets/stdlib_socket.py +++ b/tests/snippets/stdlib_socket.py @@ -12,7 +12,8 @@ connector = socket.socket(socket.AF_INET, socket.SOCK_STREAM) connector.connect(("127.0.0.1", listener.getsockname()[1])) -connection = listener.accept()[0] +(connection, addr) = listener.accept() +assert addr == connector.getsockname() connector.send(MESSAGE_A) connection.send(MESSAGE_B) diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index 37dc8a6fa2..f4b88b85f9 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -68,7 +68,7 @@ impl Connection { match self { Connection::TcpListener(con) => con.local_addr(), Connection::UdpSocket(con) => con.local_addr(), - _ => Err(io::Error::new(io::ErrorKind::Other, "oh no!")), + Connection::TcpStream(con) => con.local_addr(), } } @@ -263,8 +263,8 @@ fn socket_accept(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { None => return Err(vm.new_type_error("".to_string())), }; - let tcp_stream = match ret { - Ok((socket, _addr)) => socket, + let (tcp_stream, addr) = match ret { + Ok((socket, addr)) => (socket, addr), _ => return Err(vm.new_type_error("".to_string())), }; @@ -281,7 +281,7 @@ fn socket_accept(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { zelf.typ(), ); - let elements = RefCell::new(vec![sock_obj, vm.get_none()]); + let elements = RefCell::new(vec![sock_obj, get_addr_tuple(vm, addr)?]); Ok(PyObject::new( PyObjectPayload::Sequence { elements }, From f09f75ac8d5c1190aa6d7d89ecf652f786e62bc0 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sat, 2 Mar 2019 17:45:26 +0200 Subject: [PATCH 127/380] Do implicit bind in socket.sendto --- tests/snippets/stdlib_socket.py | 26 ++++++++++++++++++++------ vm/src/stdlib/socket.rs | 33 ++++++++++++++++++++++++++------- 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/tests/snippets/stdlib_socket.py b/tests/snippets/stdlib_socket.py index 54ae180c3e..f984515ea6 100644 --- a/tests/snippets/stdlib_socket.py +++ b/tests/snippets/stdlib_socket.py @@ -44,18 +44,32 @@ sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock2.sendto(MESSAGE_A, sock1.getsockname()) +(recv_a, addr1) = sock1.recvfrom(len(MESSAGE_A)) +assert recv_a == MESSAGE_A + +sock2.sendto(MESSAGE_B, sock1.getsockname()) +(recv_b, addr2) = sock1.recvfrom(len(MESSAGE_B)) +assert recv_b == MESSAGE_B +assert addr1[0] == addr2[0] +assert addr1[1] == addr2[1] + +sock2.close() + +sock3 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +sock3.bind(("127.0.0.1", 0)) +sock3.sendto(MESSAGE_A, sock1.getsockname()) (recv_a, addr) = sock1.recvfrom(len(MESSAGE_A)) assert recv_a == MESSAGE_A +assert addr == sock3.getsockname() -sock2.bind(("127.0.0.1", 0)) -sock1.connect(("127.0.0.1", sock2.getsockname()[1])) -sock2.connect(("127.0.0.1", sock1.getsockname()[1])) +sock1.connect(("127.0.0.1", sock3.getsockname()[1])) +sock3.connect(("127.0.0.1", sock1.getsockname()[1])) sock1.send(MESSAGE_A) -sock2.send(MESSAGE_B) -recv_a = sock2.recv(len(MESSAGE_A)) +sock3.send(MESSAGE_B) +recv_a = sock3.recv(len(MESSAGE_A)) recv_b = sock1.recv(len(MESSAGE_B)) assert recv_a == MESSAGE_A assert recv_b == MESSAGE_B sock1.close() -sock2.close() +sock3.close() diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index f4b88b85f9..d0ff61b37e 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -2,7 +2,7 @@ use std::cell::RefCell; use std::io; use std::io::Read; use std::io::Write; -use std::net::{SocketAddr, TcpListener, TcpStream, UdpSocket}; +use std::net::{SocketAddr, TcpListener, TcpStream, ToSocketAddrs, UdpSocket}; use std::ops::DerefMut; use crate::obj::objbytes; @@ -78,6 +78,13 @@ impl Connection { _ => Err(io::Error::new(io::ErrorKind::Other, "oh no!")), } } + + fn send_to(&self, buf: &[u8], addr: A) -> io::Result { + match self { + Connection::UdpSocket(con) => con.send_to(buf, addr), + _ => Err(io::Error::new(io::ErrorKind::Other, "oh no!")), + } + } } impl Read for Connection { @@ -360,17 +367,29 @@ fn socket_sendto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let address_string = get_address_string(vm, address)?; - let socket = get_socket(zelf); + let mut socket = get_socket(zelf); match socket.socket_kind { SocketKind::Dgram => { - // We can't do sendto without bind in std::net::UdpSocket - if let Ok(dgram) = UdpSocket::bind("0.0.0.0:0") { - if let Ok(_) = dgram.send_to(&objbytes::get_value(&bytes), address_string) { - return Ok(vm.get_none()); + match socket.con { + Some(ref mut v) => { + if let Ok(_) = v.send_to(&objbytes::get_value(&bytes), address_string) { + Ok(vm.get_none()) + } else { + Err(vm.new_type_error("socket failed".to_string())) + } + } + None => { + // Doing implicit bind + if let Ok(dgram) = UdpSocket::bind("0.0.0.0:0") { + if let Ok(_) = dgram.send_to(&objbytes::get_value(&bytes), address_string) { + socket.con = Some(Connection::UdpSocket(dgram)); + return Ok(vm.get_none()); + } + } + Err(vm.new_type_error("socket failed".to_string())) } } - Err(vm.new_type_error("socket failed".to_string())) } _ => Err(vm.new_not_implemented_error("".to_string())), } From 283b331323178e8a6c6f8f184d0a7e46d90b354a Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sun, 3 Mar 2019 18:22:20 +0200 Subject: [PATCH 128/380] Use vm.ctx.new_tuple --- vm/src/stdlib/socket.rs | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index d0ff61b37e..7626cc182a 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -288,12 +288,9 @@ fn socket_accept(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { zelf.typ(), ); - let elements = RefCell::new(vec![sock_obj, get_addr_tuple(vm, addr)?]); + let addr_tuple = get_addr_tuple(vm, addr)?; - Ok(PyObject::new( - PyObjectPayload::Sequence { elements }, - vm.ctx.tuple_type(), - )) + Ok(vm.ctx.new_tuple(vec![sock_obj, addr_tuple])) } fn socket_recv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -332,12 +329,9 @@ fn socket_recvfrom(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { _ => return Err(vm.new_type_error("".to_string())), }; - let elements = RefCell::new(vec![vm.ctx.new_bytes(buffer), get_addr_tuple(vm, addr)?]); + let addr_tuple = get_addr_tuple(vm, addr)?; - Ok(PyObject::new( - PyObjectPayload::Sequence { elements }, - vm.ctx.tuple_type(), - )) + Ok(vm.ctx.new_tuple(vec![vm.ctx.new_bytes(buffer), addr_tuple])) } fn socket_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -421,12 +415,8 @@ fn socket_getsockname(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn get_addr_tuple(vm: &mut VirtualMachine, addr: SocketAddr) -> PyResult { let port = vm.ctx.new_int(addr.port()); let ip = vm.ctx.new_str(addr.ip().to_string()); - let elements = RefCell::new(vec![ip, port]); - Ok(PyObject::new( - PyObjectPayload::Sequence { elements }, - vm.ctx.tuple_type(), - )) + Ok(vm.ctx.new_tuple(vec![ip, port])) } pub fn mk_module(ctx: &PyContext) -> PyObjectRef { From 4e822ed6cdf02a7826f733a5edf0c5e4f4f6765b Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sun, 3 Mar 2019 12:26:50 -0800 Subject: [PATCH 129/380] Improve error messages and add docs for new native function machinery --- vm/src/function.rs | 46 ++++++++++- vm/src/pyobject.rs | 192 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 222 insertions(+), 16 deletions(-) diff --git a/vm/src/function.rs b/vm/src/function.rs index 434f995bb5..131fce129d 100644 --- a/vm/src/function.rs +++ b/vm/src/function.rs @@ -2,17 +2,48 @@ use std::marker::PhantomData; use std::ops::Deref; use crate::obj::objtype; -use crate::pyobject::{PyObjectPayload2, PyObjectRef, PyResult, TryFromObject}; +use crate::pyobject::{ + IntoPyObject, PyContext, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, + TryFromObject, TypeProtocol, +}; use crate::vm::VirtualMachine; // TODO: Move PyFuncArgs, FromArgs, etc. here +// TODO: `PyRef` probably actually belongs in the pyobject module. + +/// A reference to the payload of a built-in object. +/// +/// Note that a `PyRef` can only deref to a shared / immutable reference. +/// It is the payload type's responsibility to handle (possibly concurrent) +/// mutability with locks or concurrent data structures if required. +/// +/// A `PyRef` can be directly returned from a built-in function to handle +/// situations (such as when implementing in-place methods such as `__iadd__`) +/// where a reference to the same object must be returned. pub struct PyRef { // invariant: this obj must always have payload of type T obj: PyObjectRef, _payload: PhantomData, } +impl PyRef +where + T: PyObjectPayload2, +{ + pub fn new(ctx: &PyContext, payload: T) -> Self { + PyRef { + obj: PyObject::new( + PyObjectPayload::AnyRustValue { + value: Box::new(payload), + }, + T::required_type(ctx), + ), + _payload: PhantomData, + } + } +} + impl Deref for PyRef where T: PyObjectPayload2, @@ -35,7 +66,18 @@ where _payload: PhantomData, }) } else { - Err(vm.new_type_error("wrong type".to_string())) // TODO: better message + let expected_type = vm.to_pystr(&T::required_type(&vm.ctx))?; + let actual_type = vm.to_pystr(&obj.typ())?; + Err(vm.new_type_error(format!( + "Expected type {}, not {}", + expected_type, actual_type, + ))) } } } + +impl IntoPyObject for PyRef { + fn into_pyobject(self, _ctx: &PyContext) -> PyResult { + Ok(self.obj) + } +} diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index c39aa71ae6..cb5bd7ed95 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -2,6 +2,7 @@ use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::fmt; use std::iter; +use std::ops::RangeInclusive; use std::rc::{Rc, Weak}; use crate::bytecode; @@ -937,6 +938,8 @@ impl PyFuncArgs { } } + /// Serializes these arguments into an iterator starting with the positional + /// arguments followed by keyword arguments. fn into_iter(self) -> impl Iterator { self.args.into_iter().map(PyArg::Positional).chain( self.kwargs @@ -945,30 +948,82 @@ impl PyFuncArgs { ) } + /// Binds these arguments to their respective values. + /// + /// If there is an insufficient number of arguments, there are leftover + /// arguments after performing the binding, or if an argument is not of + /// the expected type, a TypeError is raised. + /// + /// If the given `FromArgs` includes any conversions, exceptions raised + /// during the conversion will halt the binding and return the error. fn bind(self, vm: &mut VirtualMachine) -> PyResult { + let given_args = self.args.len(); let mut args = self.into_iter().peekable(); - let bound = T::from_args(vm, &mut args)?; + let bound = match T::from_args(vm, &mut args) { + Ok(args) => args, + Err(ArgumentError::TooFewArgs) => { + return Err(vm.new_type_error(format!( + "Expected at least {} arguments ({} given)", + T::arity().start(), + given_args, + ))); + } + Err(ArgumentError::Exception(ex)) => { + return Err(ex); + } + }; - if args.next().is_none() { - Ok(bound) - } else { - Err(vm.new_type_error("too many args".to_string())) // TODO: improve error message + match args.next() { + None => Ok(bound), + Some(PyArg::Positional(_)) => Err(vm.new_type_error(format!( + "Expected at most {} arguments ({} given)", + T::arity().end(), + given_args, + ))), + Some(PyArg::Keyword(name, _)) => { + Err(vm.new_type_error(format!("Unexpected keyword argument {}", name))) + } } } } +/// Implemented by any type that can be accepted as a parameter to a built-in +/// function. +/// pub trait FromArgs: Sized { - fn from_args(vm: &mut VirtualMachine, args: &mut iter::Peekable) -> PyResult + /// The range of positional arguments permitted by the function signature. + /// + /// Returns an empty range if not applicable. + fn arity() -> RangeInclusive { + 0..=0 + } + + /// Extracts this item from the next argument(s). + fn from_args( + vm: &mut VirtualMachine, + args: &mut iter::Peekable, + ) -> Result where I: Iterator; } -pub struct PyIterable { +/// An iterable Python object. +/// +/// `PyIterable` implements `FromArgs` so that a built-in function can accept +/// an object that is required to conform to the Python iterator protocol. +/// +/// PyIterable can optionally perform type checking and conversions on iterated +/// objects using a generic type parameter that implements `TryFromObject`. +pub struct PyIterable { method: PyObjectRef, _item: std::marker::PhantomData, } impl PyIterable { + /// Returns an iterator over this sequence of objects. + /// + /// This operation may fail if an exception is raised while invoking the + /// `__iter__` method of the iterable object. pub fn iter<'a>(&self, vm: &'a mut VirtualMachine) -> PyResult> { let iter_obj = vm.invoke( self.method.clone(), @@ -1037,13 +1092,24 @@ impl TryFromObject for PyObjectRef { } } -pub struct KwArgs(HashMap); +/// A map of keyword arguments to their values. +/// +/// A built-in function with a `KwArgs` parameter is analagous to a Python +/// function with `*kwargs`. All remaining keyword arguments are extracted +/// (and hence the function will permit an arbitrary number of them). +/// +/// `KwArgs` optionally accepts a generic type parameter to allow type checks +/// or conversions of each argument. +pub struct KwArgs(HashMap); impl FromArgs for KwArgs where T: TryFromObject, { - fn from_args(vm: &mut VirtualMachine, args: &mut iter::Peekable) -> PyResult + fn from_args( + vm: &mut VirtualMachine, + args: &mut iter::Peekable, + ) -> Result where I: Iterator, { @@ -1055,13 +1121,24 @@ where } } +/// A list of positional argument values. +/// +/// A built-in function with a `Args` parameter is analagous to a Python +/// function with `*args`. All remaining positional arguments are extracted +/// (and hence the function will permit an arbitrary number of them). +/// +/// `Args` optionally accepts a generic type parameter to allow type checks +/// or conversions of each argument. pub struct Args(Vec); impl FromArgs for Args where T: TryFromObject, { - fn from_args(vm: &mut VirtualMachine, args: &mut iter::Peekable) -> PyResult + fn from_args( + vm: &mut VirtualMachine, + args: &mut iter::Peekable, + ) -> Result where I: Iterator, { @@ -1077,14 +1154,21 @@ impl FromArgs for T where T: TryFromObject, { - fn from_args(vm: &mut VirtualMachine, args: &mut iter::Peekable) -> PyResult + fn arity() -> RangeInclusive { + 1..=1 + } + + fn from_args( + vm: &mut VirtualMachine, + args: &mut iter::Peekable, + ) -> Result where I: Iterator, { if let Some(PyArg::Positional(value)) = args.next() { Ok(T::try_from_object(vm, value)?) } else { - Err(vm.new_type_error("not enough args".to_string())) // TODO: improve error message + Err(ArgumentError::TooFewArgs) } } } @@ -1103,7 +1187,14 @@ impl FromArgs for OptArg where T: TryFromObject, { - fn from_args(vm: &mut VirtualMachine, args: &mut iter::Peekable) -> PyResult + fn arity() -> RangeInclusive { + 0..=1 + } + + fn from_args( + vm: &mut VirtualMachine, + args: &mut iter::Peekable, + ) -> Result where I: Iterator, { @@ -1125,10 +1216,31 @@ pub enum PyArg { Keyword(String, PyObjectRef), } +pub enum ArgumentError { + TooFewArgs, + Exception(PyObjectRef), +} + +impl From for ArgumentError { + fn from(ex: PyObjectRef) -> Self { + ArgumentError::Exception(ex) + } +} + +/// Implemented by any type that can be created from a Python object. +/// +/// Any type that implements `TryFromObject` is automatically `FromArgs`, and +/// so can be accepted as a argument to a built-in function. pub trait TryFromObject: Sized { + /// Attempt to convert a Python object to a value of this type. fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult; } +/// Implemented by any type that can be returned from a built-in Python function. +/// +/// `IntoPyObject` has a blanket implementation for any built-in object payload, +/// and should be implemented by many primitive Rust types, allowing a built-in +/// function to simply return a `bool` or a `usize` for example. pub trait IntoPyObject { fn into_pyobject(self, ctx: &PyContext) -> PyResult; } @@ -1148,12 +1260,22 @@ where } } +// This allows a built-in function to not return a value, mapping to +// Python's behavior of returning `None` in this situation. impl IntoPyObject for () { fn into_pyobject(self, ctx: &PyContext) -> PyResult { Ok(ctx.none()) } } +// TODO: Allow a built-in function to return an `Option`, i.e.: +// +// impl IntoPyObject for Option +// +// Option::None should map to a Python `None`. + +// Allows a built-in function to return any built-in object payload without +// explicitly implementing `IntoPyObject`. impl IntoPyObject for T where T: PyObjectPayload2 + Sized, @@ -1168,13 +1290,33 @@ where } } +// A tuple of types that each implement `FromArgs` represents a sequence of +// arguments that can be bound and passed to a built-in function. +// +// Technically, a tuple can contain tuples, which can contain tuples, and so on, +// so this actually represents a tree of values to be bound from arguments, but +// in practice this is only used for the top-level parameters. macro_rules! tuple_from_py_func_args { ($($T:ident),+) => { impl<$($T),+> FromArgs for ($($T,)+) where $($T: FromArgs),+ { - fn from_args(vm: &mut VirtualMachine, args: &mut iter::Peekable) -> PyResult + fn arity() -> RangeInclusive { + let mut min = 0; + let mut max = 0; + $( + let (start, end) = $T::arity().into_inner(); + min += start; + max += end; + )+ + min..=max + } + + fn from_args( + vm: &mut VirtualMachine, + args: &mut iter::Peekable + ) -> Result where I: Iterator { @@ -1184,14 +1326,32 @@ macro_rules! tuple_from_py_func_args { }; } +// Implement `FromArgs` for up to 5-tuples, allowing built-in functions to bind +// up to 5 top-level parameters (note that `Args`, `KwArgs`, nested tuples, etc. +// count as 1, so this should actually be more than enough). tuple_from_py_func_args!(A); tuple_from_py_func_args!(A, B); tuple_from_py_func_args!(A, B, C); tuple_from_py_func_args!(A, B, C, D); tuple_from_py_func_args!(A, B, C, D, E); +/// A built-in Python function. pub type PyNativeFunc = Box PyResult + 'static>; +/// Implemented by types that are or can generate built-in functions. +/// +/// For example, any function that: +/// +/// - Accepts a sequence of types that implement `FromArgs`, followed by a +/// `&mut VirtualMachine` +/// - Returns some type that implements `IntoPyObject` +/// +/// will generate a `PyNativeFunc` that performs the appropriate type and arity +/// checking, any requested conversions, and then if successful call the function +/// with the bound values. +/// +/// A bare `PyNativeFunc` also implements this trait, allowing the above to be +/// done manually, for rare situations that don't fit into this model. pub trait IntoPyNativeFunc { fn into_func(self) -> PyNativeFunc; } @@ -1211,6 +1371,10 @@ impl IntoPyNativeFunc for PyNativeFunc { } } +// This is the "magic" that allows rust functions of varying signatures to +// generate native python functions. +// +// Note that this could be done without a macro - it is simply to avoid repetition. macro_rules! into_py_native_func_tuple { ($(($n:tt, $T:ident)),+) => { impl IntoPyNativeFunc<($($T,)+), R> for F From 2d71f6de288cd8dce359f16499c2df56e0f03bb5 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sun, 3 Mar 2019 20:01:07 -0800 Subject: [PATCH 130/380] bytes and bytearray --- vm/src/obj/objbytearray.rs | 49 ++++++++++++++++++++++++++++---------- vm/src/obj/objbytes.rs | 49 ++++++++++++++++++++++++-------------- vm/src/obj/objiter.rs | 28 ++++++++++++++-------- vm/src/pyobject.rs | 12 ++++------ vm/src/stdlib/io.rs | 20 ++++++++-------- 5 files changed, 100 insertions(+), 58 deletions(-) diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index 0cabf23fdc..7e03d34d3d 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -1,17 +1,47 @@ //! Implementation of the python bytearray object. use std::cell::RefCell; +use std::ops::{Deref, DerefMut}; -use crate::pyobject::{PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyResult, TypeProtocol}; +use crate::pyobject::{ + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, + TypeProtocol, +}; use super::objint; -use super::objbytes::get_mut_value; -use super::objbytes::get_value; use super::objtype; use crate::vm::VirtualMachine; use num_traits::ToPrimitive; +#[derive(Debug)] +pub struct PyByteArray { + // TODO: shouldn't be public + pub value: RefCell>, +} + +impl PyByteArray { + pub fn new(data: Vec) -> Self { + PyByteArray { + value: RefCell::new(data), + } + } +} + +impl PyObjectPayload2 for PyByteArray { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.bytearray_type() + } +} + +pub fn get_value<'a>(obj: &'a PyObjectRef) -> impl Deref> + 'a { + obj.payload::().unwrap().value.borrow() +} + +pub fn get_mut_value<'a>(obj: &'a PyObjectRef) -> impl DerefMut> + 'a { + obj.payload::().unwrap().value.borrow_mut() +} + // Binary data support /// Fill bytearray class methods dictionary. @@ -143,8 +173,8 @@ fn bytearray_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vec![] }; Ok(PyObject::new( - PyObjectPayload::Bytes { - value: RefCell::new(value), + PyObjectPayload::AnyRustValue { + value: Box::new(PyByteArray::new(value)), }, cls.clone(), )) @@ -290,13 +320,8 @@ fn bytearray_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn bytearray_clear(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); - match zelf.payload { - PyObjectPayload::Bytes { ref value } => { - value.borrow_mut().clear(); - Ok(vm.get_none()) - } - _ => panic!("Bytearray has incorrect payload."), - } + get_mut_value(zelf).clear(); + Ok(vm.get_none()) } fn bytearray_pop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 6ab2f72af2..d285b86e9a 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -1,16 +1,41 @@ -use std::cell::{Cell, RefCell}; +use std::cell::Cell; use std::hash::{Hash, Hasher}; use std::ops::Deref; -use std::ops::DerefMut; use super::objint; use super::objtype; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, + TypeProtocol, }; use crate::vm::VirtualMachine; use num_traits::ToPrimitive; +#[derive(Debug)] +pub struct PyBytes { + value: Vec, +} + +impl PyBytes { + pub fn new(data: Vec) -> Self { + PyBytes { value: data } + } +} + +impl Deref for PyBytes { + type Target = [u8]; + + fn deref(&self) -> &[u8] { + &self.value + } +} + +impl PyObjectPayload2 for PyBytes { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.bytes_type() + } +} + // Binary data support // Fill bytes class methods: @@ -71,8 +96,8 @@ fn bytes_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; Ok(PyObject::new( - PyObjectPayload::Bytes { - value: RefCell::new(value), + PyObjectPayload::AnyRustValue { + value: Box::new(PyBytes::new(value)), }, cls.clone(), )) @@ -170,19 +195,7 @@ fn bytes_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn get_value<'a>(obj: &'a PyObjectRef) -> impl Deref> + 'a { - if let PyObjectPayload::Bytes { ref value } = obj.payload { - value.borrow() - } else { - panic!("Inner error getting bytearray {:?}", obj); - } -} - -pub fn get_mut_value<'a>(obj: &'a PyObjectRef) -> impl DerefMut> + 'a { - if let PyObjectPayload::Bytes { ref value } = obj.payload { - value.borrow_mut() - } else { - panic!("Inner error getting bytearray {:?}", obj); - } + &obj.payload::().unwrap().value } fn bytes_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 9985553fb1..049ea8be39 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -8,6 +8,8 @@ use crate::pyobject::{ use crate::vm::VirtualMachine; use super::objbool; +use super::objbytearray::PyByteArray; +use super::objbytes::PyBytes; use super::objrange::PyRange; use super::objtype; @@ -137,6 +139,22 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } else { Err(new_stop_iteration(vm)) } + } else if let Some(bytes) = iterated_obj_ref.payload::() { + if position.get() < bytes.len() { + let obj_ref = vm.ctx.new_int(bytes[position.get()]); + position.set(position.get() + 1); + Ok(obj_ref) + } else { + Err(new_stop_iteration(vm)) + } + } else if let Some(bytes) = iterated_obj_ref.payload::() { + if position.get() < bytes.value.borrow().len() { + let obj_ref = vm.ctx.new_int(bytes.value.borrow()[position.get()]); + position.set(position.get() + 1); + Ok(obj_ref) + } else { + Err(new_stop_iteration(vm)) + } } else { match iterated_obj_ref.payload { PyObjectPayload::Sequence { ref elements } => { @@ -148,16 +166,6 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Err(new_stop_iteration(vm)) } } - PyObjectPayload::Bytes { ref value } => { - if position.get() < value.borrow().len() { - let obj_ref = vm.ctx.new_int(value.borrow()[position.get()]); - position.set(position.get() + 1); - Ok(obj_ref) - } else { - Err(new_stop_iteration(vm)) - } - } - _ => { panic!("NOT IMPL"); } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index bfb33871e8..038eb1d0f8 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -483,8 +483,8 @@ impl PyContext { pub fn new_bytes(&self, data: Vec) -> PyObjectRef { PyObject::new( - PyObjectPayload::Bytes { - value: RefCell::new(data), + PyObjectPayload::AnyRustValue { + value: Box::new(objbytes::PyBytes::new(data)), }, self.bytes_type(), ) @@ -492,8 +492,8 @@ impl PyContext { pub fn new_bytearray(&self, data: Vec) -> PyObjectRef { PyObject::new( - PyObjectPayload::Bytes { - value: RefCell::new(data), + PyObjectPayload::AnyRustValue { + value: Box::new(objbytearray::PyByteArray::new(data)), }, self.bytearray_type(), ) @@ -1253,9 +1253,6 @@ pub enum PyObjectPayload { Complex { value: Complex64, }, - Bytes { - value: RefCell>, - }, Sequence { elements: RefCell>, }, @@ -1340,7 +1337,6 @@ impl fmt::Debug for PyObjectPayload { match self { PyObjectPayload::Integer { ref value } => write!(f, "int {}", 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 { .. } => write!(f, "list or tuple"), PyObjectPayload::Dict { .. } => write!(f, "dict"), diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 077f907d89..95f6a10a9e 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -15,13 +15,13 @@ use num_traits::ToPrimitive; //custom imports use super::os; +use crate::obj::objbytearray::PyByteArray; use crate::obj::objbytes; use crate::obj::objint; use crate::obj::objstr; use crate::pyobject::{ - AttributeProtocol, BufferProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, - PyResult, TypeProtocol, + AttributeProtocol, BufferProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol, }; use crate::import; @@ -86,8 +86,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 - if let PyObjectPayload::Bytes { ref value } = buffer.payload { - result.extend(value.borrow().iter().cloned()); + if let Some(bytes) = buffer.payload::() { + result.extend_from_slice(&bytes.value.borrow()); }; let len = vm.get_method(buffer.clone(), &"__len__".to_string()); @@ -169,10 +169,10 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let handle = os::rust_file(raw_fd); let mut f = handle.take(length); - if let PyObjectPayload::Bytes { ref value } = obj.payload { + if let Some(bytes) = obj.payload::() { //TODO: Implement for MemoryView - let mut value_mut = value.borrow_mut(); + let mut value_mut = bytes.value.borrow_mut(); value_mut.clear(); match f.read_to_end(&mut value_mut) { Ok(_) => {} @@ -200,9 +200,9 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //to support windows - i.e. raw file_handles let mut handle = os::rust_file(raw_fd); - match obj.payload { - PyObjectPayload::Bytes { ref value } => { - let value_mut = value.borrow(); + match obj.payload::() { + Some(bytes) => { + let value_mut = bytes.value.borrow(); match handle.write(&value_mut[..]) { Ok(len) => { //reset raw fd on the FileIO object @@ -215,7 +215,7 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Err(_) => Err(vm.new_value_error("Error Writing Bytes to Handle".to_string())), } } - _ => Err(vm.new_value_error("Expected Bytes Object".to_string())), + None => Err(vm.new_value_error("Expected Bytes Object".to_string())), } } From b1a070acc2468a9a66cb69c026e1d80f85e9ca5d Mon Sep 17 00:00:00 2001 From: ben Date: Mon, 4 Mar 2019 19:55:58 +1300 Subject: [PATCH 131/380] Only use real isinstance/issubclass for builtins and move the real versions to the vm. --- vm/src/builtins.rs | 6 +++--- vm/src/macros.rs | 2 +- vm/src/obj/objsuper.rs | 4 +--- vm/src/obj/objtype.rs | 30 ------------------------------ vm/src/pyobject.rs | 10 ++-------- vm/src/vm.rs | 20 ++++++++++++++++++++ 6 files changed, 27 insertions(+), 45 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 0e48315e98..e7a3bbb448 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -347,7 +347,7 @@ fn builtin_isinstance(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(obj, None), (typ, Some(vm.get_type()))] ); - let isinstance = objtype::real_isinstance(vm, obj, typ)?; + let isinstance = vm.isinstance(obj, typ)?; Ok(vm.new_bool(isinstance)) } @@ -358,7 +358,7 @@ fn builtin_issubclass(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(subclass, Some(vm.get_type())), (cls, Some(vm.get_type()))] ); - let issubclass = objtype::real_issubclass(vm, subclass, cls)?; + let issubclass = vm.issubclass(subclass, cls)?; Ok(vm.context().new_bool(issubclass)) } @@ -814,7 +814,7 @@ pub fn builtin_build_class_(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> Py let mut metaclass = args.get_kwarg("metaclass", vm.get_type()); for base in bases.clone() { - if objtype::real_issubclass(vm, &base.typ(), &metaclass)? { + if objtype::issubclass(&base.typ(), &metaclass) { metaclass = base.typ(); } else if !objtype::issubclass(&metaclass, &base.typ()) { return Err(vm.new_type_error("metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases".to_string())); diff --git a/vm/src/macros.rs b/vm/src/macros.rs index f228b6a7fb..f91cd7c614 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -19,7 +19,7 @@ macro_rules! type_check { if let Some(expected_type) = $arg_type { let arg = &$args.args[$arg_count]; - if !$crate::obj::objtype::real_isinstance($vm, arg, &expected_type)? { + 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)?; diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index b3d2184300..79c516d4c0 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -72,9 +72,7 @@ fn super_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; // Check obj type: - if !(objtype::real_isinstance(vm, &py_obj, &py_type)? - || objtype::real_issubclass(vm, &py_obj, &py_type)?) - { + if !(objtype::isinstance(&py_obj, &py_type) || objtype::issubclass(&py_obj, &py_type)) { return Err(vm.new_type_error( "super(type, obj): obj must be an instance or subtype of type".to_string(), )); diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 785ca1b87b..c800b7f522 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -1,4 +1,3 @@ -use super::objbool; use super::objdict; use super::objstr; use super::objtype; // Required for arg_check! to use isinstance @@ -9,7 +8,6 @@ use crate::pyobject::{ use crate::vm::VirtualMachine; use std::cell::RefCell; use std::collections::HashMap; -use std::rc::Rc; /* * The magical type type @@ -108,23 +106,6 @@ pub fn isinstance(obj: &PyObjectRef, cls: &PyObjectRef) -> bool { mro.into_iter().any(|c| c.is(&cls)) } -/// Determines if `obj` is an instance of `cls`, either directly, indirectly or virtually via the -/// __instancecheck__ magic method. -pub fn real_isinstance( - vm: &mut VirtualMachine, - obj: &PyObjectRef, - cls: &PyObjectRef, -) -> PyResult { - // cpython first does an exact check on the type, although documentation doesn't state that - // https://github.com/python/cpython/blob/a24107b04c1277e3c1105f98aff5bfa3a98b33a0/Objects/abstract.c#L2408 - if Rc::ptr_eq(&obj.typ(), cls) { - Ok(true) - } else { - let ret = vm.call_method(cls, "__instancecheck__", vec![obj.clone()])?; - objbool::boolval(vm, ret) - } -} - fn type_instance_check(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -142,17 +123,6 @@ pub fn issubclass(subclass: &PyObjectRef, cls: &PyObjectRef) -> bool { mro.into_iter().any(|c| c.is(&cls)) } -/// Determines if `subclass` is a subclass of `cls`, either directly, indirectly or virtually via -/// the __subclasscheck__ magic method. -pub fn real_issubclass( - vm: &mut VirtualMachine, - subclass: &PyObjectRef, - cls: &PyObjectRef, -) -> PyResult { - let ret = vm.call_method(cls, "__subclasscheck__", vec![subclass.clone()])?; - objbool::boolval(vm, ret) -} - fn type_subclass_check(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index c39aa71ae6..e3900627fc 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -922,7 +922,7 @@ impl PyFuncArgs { ) -> Result, PyObjectRef> { match self.get_optional_kwarg(key) { Some(kwarg) => { - if objtype::real_isinstance(vm, &kwarg, &ty)? { + if objtype::isinstance(&kwarg, &ty) { Ok(Some(kwarg)) } else { let expected_ty_name = vm.to_pystr(&ty)?; @@ -1003,13 +1003,7 @@ where Ok(value) => Some(T::try_from_object(self.vm, value)), Err(err) => { let stop_ex = self.vm.ctx.exceptions.stop_iteration.clone(); - let stop = match objtype::real_isinstance(self.vm, &err, &stop_ex) { - Ok(stop) => stop, - Err(e) => { - return Some(Err(e)); - } - }; - if stop { + if objtype::isinstance(&err, &stop_ex) { None } else { Some(Err(err)) diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 9d91abf9e9..c4b6c14ce8 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -242,6 +242,26 @@ impl VirtualMachine { self.call_method(obj, "__repr__", vec![]) } + /// Determines if `obj` is an instance of `cls`, either directly, indirectly or virtually via + /// the __instancecheck__ magic method. + pub fn isinstance(&mut self, obj: &PyObjectRef, cls: &PyObjectRef) -> PyResult { + // cpython first does an exact check on the type, although documentation doesn't state that + // https://github.com/python/cpython/blob/a24107b04c1277e3c1105f98aff5bfa3a98b33a0/Objects/abstract.c#L2408 + if Rc::ptr_eq(&obj.typ(), cls) { + Ok(true) + } else { + let ret = self.call_method(cls, "__instancecheck__", vec![obj.clone()])?; + objbool::boolval(self, ret) + } + } + + /// Determines if `subclass` is a subclass of `cls`, either directly, indirectly or virtually + /// via the __subclasscheck__ magic method. + pub fn issubclass(&mut self, subclass: &PyObjectRef, cls: &PyObjectRef) -> PyResult { + let ret = self.call_method(cls, "__subclasscheck__", vec![subclass.clone()])?; + objbool::boolval(self, ret) + } + pub fn call_get_descriptor(&mut self, attr: PyObjectRef, obj: PyObjectRef) -> PyResult { let attr_class = attr.typ(); if let Some(descriptor) = attr_class.get_attr("__get__") { From c5789a03a32d76e83cc5da0a9ed6e434997ff277 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Sun, 3 Mar 2019 14:06:36 +0000 Subject: [PATCH 132/380] Proper construction of scope for exec/eval. --- tests/snippets/test_exec.py | 12 ++++++++++++ vm/src/builtins.rs | 35 ++++++++++++++++++----------------- vm/src/frame.rs | 6 ++++++ vm/src/vm.rs | 8 +++++++- 4 files changed, 43 insertions(+), 18 deletions(-) create mode 100644 tests/snippets/test_exec.py diff --git a/tests/snippets/test_exec.py b/tests/snippets/test_exec.py new file mode 100644 index 0000000000..c610af565c --- /dev/null +++ b/tests/snippets/test_exec.py @@ -0,0 +1,12 @@ +exec("def square(x):\n return x * x\n") +assert 16 == square(4) + +d = {} +exec("def square(x):\n return x * x\n", {}, d) +assert 16 == d['square'](4) + +exec("assert 2 == x", {}, {'x': 2}) +exec("assert 2 == x", {'x': 2}, {}) +exec("assert 4 == x", {'x': 2}, {'x': 4}) + +exec("assert max(1, 2) == 2", {}, {}) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index e7a3bbb448..3b8ea0f2a0 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -18,7 +18,6 @@ use crate::frame::{Scope, ScopeRef}; use crate::pyobject::{ AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol, }; -use std::rc::Rc; #[cfg(not(target_arch = "wasm32"))] use crate::stdlib::io::io_open; @@ -192,7 +191,7 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(source, None)], optional = [ - (_globals, Some(vm.ctx.dict_type())), + (globals, Some(vm.ctx.dict_type())), (locals, Some(vm.ctx.dict_type())) ] ); @@ -215,7 +214,7 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { return Err(vm.new_type_error("code argument must be str or code object".to_string())); }; - let scope = make_scope(vm, locals); + let scope = make_scope(vm, globals, locals); // Run the source: vm.run_code_obj(code_obj.clone(), scope) @@ -229,7 +228,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(source, None)], optional = [ - (_globals, Some(vm.ctx.dict_type())), + (globals, Some(vm.ctx.dict_type())), (locals, Some(vm.ctx.dict_type())) ] ); @@ -252,26 +251,28 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { return Err(vm.new_type_error("source argument must be str or code object".to_string())); }; - let scope = make_scope(vm, locals); + let scope = make_scope(vm, globals, locals); // Run the code: vm.run_code_obj(code_obj, scope) } -fn make_scope(vm: &mut VirtualMachine, locals: Option<&PyObjectRef>) -> ScopeRef { - // handle optional global and locals - let locals = if let Some(locals) = locals { - locals.clone() - } else { - vm.new_dict() +fn make_scope( + vm: &mut VirtualMachine, + globals: Option<&PyObjectRef>, + locals: Option<&PyObjectRef>, +) -> ScopeRef { + let current_scope = vm.current_scope(); + let parent = match globals { + Some(dict) => Some(Scope::new(dict.clone(), Some(vm.get_builtin_scope()))), + None => current_scope.parent.clone(), + }; + let locals = match locals { + Some(dict) => dict.clone(), + None => current_scope.locals.clone(), }; - // TODO: handle optional globals - // Construct new scope: - Rc::new(Scope { - locals, - parent: None, - }) + Scope::new(locals, parent) } fn builtin_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/frame.rs b/vm/src/frame.rs index ec0c099238..c76552b76e 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -35,6 +35,12 @@ pub struct Scope { } pub type ScopeRef = Rc; +impl Scope { + pub fn new(locals: PyObjectRef, parent: Option) -> ScopeRef { + Rc::new(Scope { locals, parent }) + } +} + #[derive(Clone, Debug)] struct Block { /// The type of block. diff --git a/vm/src/vm.rs b/vm/src/vm.rs index c4b6c14ce8..60a260479b 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -90,6 +90,12 @@ impl VirtualMachine { result } + pub fn current_scope(&self) -> &ScopeRef { + let current_frame = &self.frames[self.frames.len() - 1]; + let frame = objframe::get_value(current_frame); + &frame.scope + } + /// Create a new python string object. pub fn new_str(&self, s: String) -> PyObjectRef { self.ctx.new_str(s) @@ -218,7 +224,7 @@ impl VirtualMachine { &self.ctx } - pub fn get_builtin_scope(&mut self) -> ScopeRef { + pub fn get_builtin_scope(&self) -> ScopeRef { let a2 = &*self.builtins; match a2.payload { PyObjectPayload::Module { ref scope, .. } => scope.clone(), From 4dd03047122d35ec35a40660b45f6c6a5bb0ec7b Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Sun, 3 Mar 2019 16:33:45 +0000 Subject: [PATCH 133/380] Add check_but_allow_none macro for optional argument checking. --- vm/src/macros.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/vm/src/macros.rs b/vm/src/macros.rs index f91cd7c614..167a305a18 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -35,6 +35,34 @@ macro_rules! type_check { }; } +#[macro_export] +macro_rules! check_but_allow_none { + ( $vm: ident, $arg: ident, $expected_type: expr ) => { + let $arg = match $arg { + Some(arg) => { + if arg.is(&$vm.get_none()) { + None + } else { + if $crate::obj::objtype::real_isinstance($vm, arg, &$expected_type)? { + Some(arg) + } else { + let arg_typ = arg.typ(); + let actual_type = $vm.to_pystr(&arg_typ)?; + let expected_type_name = $vm.to_pystr(&$expected_type)?; + return Err($vm.new_type_error(format!( + "{} must be a {}, not {}", + stringify!($arg), + expected_type_name, + actual_type + ))); + } + } + } + None => None, + }; + }; +} + #[macro_export] macro_rules! arg_check { ( $vm: ident, $args:ident ) => { From 6548c365cb7a9556dc2161b827b72fae8be9e57c Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Sun, 3 Mar 2019 16:45:48 +0000 Subject: [PATCH 134/380] Use check_but_allow_none for exec/eval. --- tests/snippets/test_exec.py | 9 +++++++++ vm/src/builtins.rs | 14 ++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/tests/snippets/test_exec.py b/tests/snippets/test_exec.py index c610af565c..852ef61432 100644 --- a/tests/snippets/test_exec.py +++ b/tests/snippets/test_exec.py @@ -10,3 +10,12 @@ exec("assert 4 == x", {'x': 2}, {'x': 4}) exec("assert max(1, 2) == 2", {}, {}) + +exec("max(1, 5, square(5)) == 25", None) + +try: + exec("", 1) +except TypeError: + pass +else: + raise TypeError("exec should fail unless globals is a dict or None") diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 3b8ea0f2a0..71d6112c57 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -190,12 +190,11 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm, args, required = [(source, None)], - optional = [ - (globals, Some(vm.ctx.dict_type())), - (locals, Some(vm.ctx.dict_type())) - ] + optional = [(globals, None), (locals, Some(vm.ctx.dict_type()))] ); + check_but_allow_none!(vm, globals, vm.ctx.dict_type()); + // Determine code object: let code_obj = if objtype::isinstance(source, &vm.ctx.code_type()) { source.clone() @@ -227,12 +226,11 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm, args, required = [(source, None)], - optional = [ - (globals, Some(vm.ctx.dict_type())), - (locals, Some(vm.ctx.dict_type())) - ] + optional = [(globals, None), (locals, Some(vm.ctx.dict_type()))] ); + check_but_allow_none!(vm, globals, vm.ctx.dict_type()); + // Determine code object: let code_obj = if objtype::isinstance(source, &vm.ctx.str_type()) { let mode = compile::Mode::Exec; From 0d4887abe6d6fe77ffdaae9dc669cf865778adfe Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Mon, 4 Mar 2019 14:22:48 +0000 Subject: [PATCH 135/380] Fix missing asserts and add (disabled) tests for things that don't work yet. --- tests/snippets/test_exec.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/tests/snippets/test_exec.py b/tests/snippets/test_exec.py index 852ef61432..37ba33ff13 100644 --- a/tests/snippets/test_exec.py +++ b/tests/snippets/test_exec.py @@ -11,7 +11,28 @@ exec("assert max(1, 2) == 2", {}, {}) -exec("max(1, 5, square(5)) == 25", None) +exec("assert max(1, 5, square(5)) == 25", None) + +# +# These doesn't work yet: +# +# Local environment shouldn't replace global environment: +# +# exec("assert max(1, 5, square(5)) == 25", None, {}) +# +# Closures aren't available if local scope is replaced: +# +# def g(): +# seven = "seven" +# def f(): +# try: +# exec("seven", None, {}) +# except NameError: +# pass +# else: +# raise NameError("seven shouldn't be in scope") +# f() +# g() try: exec("", 1) From c4953ee9ec51d0ec4fb0bfca0b1b2a1a99c8a339 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Mon, 4 Mar 2019 14:29:44 +0000 Subject: [PATCH 136/380] Remove new macro and inline check into make_scope. --- vm/src/builtins.rs | 34 ++++++++++++++++++++++++++-------- vm/src/macros.rs | 28 ---------------------------- 2 files changed, 26 insertions(+), 36 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 71d6112c57..b146fcf913 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -193,7 +193,7 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { optional = [(globals, None), (locals, Some(vm.ctx.dict_type()))] ); - check_but_allow_none!(vm, globals, vm.ctx.dict_type()); + let scope = make_scope(vm, globals, locals)?; // Determine code object: let code_obj = if objtype::isinstance(source, &vm.ctx.code_type()) { @@ -213,8 +213,6 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { return Err(vm.new_type_error("code argument must be str or code object".to_string())); }; - let scope = make_scope(vm, globals, locals); - // Run the source: vm.run_code_obj(code_obj.clone(), scope) } @@ -229,7 +227,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { optional = [(globals, None), (locals, Some(vm.ctx.dict_type()))] ); - check_but_allow_none!(vm, globals, vm.ctx.dict_type()); + let scope = make_scope(vm, globals, locals)?; // Determine code object: let code_obj = if objtype::isinstance(source, &vm.ctx.str_type()) { @@ -249,8 +247,6 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { return Err(vm.new_type_error("source argument must be str or code object".to_string())); }; - let scope = make_scope(vm, globals, locals); - // Run the code: vm.run_code_obj(code_obj, scope) } @@ -259,7 +255,29 @@ fn make_scope( vm: &mut VirtualMachine, globals: Option<&PyObjectRef>, locals: Option<&PyObjectRef>, -) -> ScopeRef { +) -> PyResult { + let dict_type = vm.ctx.dict_type(); + let globals = match globals { + Some(arg) => { + if arg.is(&vm.get_none()) { + None + } else { + if vm.isinstance(arg, &dict_type)? { + Some(arg) + } else { + let arg_typ = arg.typ(); + let actual_type = vm.to_pystr(&arg_typ)?; + let expected_type_name = vm.to_pystr(&dict_type)?; + return Err(vm.new_type_error(format!( + "globals must be a {}, not {}", + expected_type_name, actual_type + ))); + } + } + } + None => None, + }; + let current_scope = vm.current_scope(); let parent = match globals { Some(dict) => Some(Scope::new(dict.clone(), Some(vm.get_builtin_scope()))), @@ -270,7 +288,7 @@ fn make_scope( None => current_scope.locals.clone(), }; - Scope::new(locals, parent) + Ok(Scope::new(locals, parent)) } fn builtin_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/macros.rs b/vm/src/macros.rs index 167a305a18..f91cd7c614 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -35,34 +35,6 @@ macro_rules! type_check { }; } -#[macro_export] -macro_rules! check_but_allow_none { - ( $vm: ident, $arg: ident, $expected_type: expr ) => { - let $arg = match $arg { - Some(arg) => { - if arg.is(&$vm.get_none()) { - None - } else { - if $crate::obj::objtype::real_isinstance($vm, arg, &$expected_type)? { - Some(arg) - } else { - let arg_typ = arg.typ(); - let actual_type = $vm.to_pystr(&arg_typ)?; - let expected_type_name = $vm.to_pystr(&$expected_type)?; - return Err($vm.new_type_error(format!( - "{} must be a {}, not {}", - stringify!($arg), - expected_type_name, - actual_type - ))); - } - } - } - None => None, - }; - }; -} - #[macro_export] macro_rules! arg_check { ( $vm: ident, $args:ident ) => { From d2061004a3fc391d2868945c62f200f7468bcb15 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Mon, 4 Mar 2019 18:27:33 +0200 Subject: [PATCH 137/380] Use py_class macro --- vm/src/stdlib/socket.rs | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index 7626cc182a..50cab5481f 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -432,21 +432,19 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "SOCK_DGRAM", ctx.new_int(SocketKind::Dgram as i32)); - let socket = { - let socket = ctx.new_class("socket", ctx.object()); - ctx.set_attr(&socket, "__new__", ctx.new_rustfunc(socket_new)); - ctx.set_attr(&socket, "connect", ctx.new_rustfunc(socket_connect)); - ctx.set_attr(&socket, "recv", ctx.new_rustfunc(socket_recv)); - ctx.set_attr(&socket, "send", ctx.new_rustfunc(socket_send)); - ctx.set_attr(&socket, "bind", ctx.new_rustfunc(socket_bind)); - ctx.set_attr(&socket, "accept", ctx.new_rustfunc(socket_accept)); - ctx.set_attr(&socket, "listen", ctx.new_rustfunc(socket_listen)); - ctx.set_attr(&socket, "close", ctx.new_rustfunc(socket_close)); - ctx.set_attr(&socket, "getsockname", ctx.new_rustfunc(socket_getsockname)); - ctx.set_attr(&socket, "sendto", ctx.new_rustfunc(socket_sendto)); - ctx.set_attr(&socket, "recvfrom", ctx.new_rustfunc(socket_recvfrom)); - socket - }; + let socket = py_class!(ctx, "socket", ctx.object(), { + "__new__" => ctx.new_rustfunc(socket_new), + "connect" => ctx.new_rustfunc(socket_connect), + "recv" => ctx.new_rustfunc(socket_recv), + "send" => ctx.new_rustfunc(socket_send), + "bind" => ctx.new_rustfunc(socket_bind), + "accept" => ctx.new_rustfunc(socket_accept), + "listen" => ctx.new_rustfunc(socket_listen), + "close" => ctx.new_rustfunc(socket_close), + "getsockname" => ctx.new_rustfunc(socket_getsockname), + "sendto" => ctx.new_rustfunc(socket_sendto), + "recvfrom" => ctx.new_rustfunc(socket_recvfrom), + }); ctx.set_attr(&py_mod, "socket", socket.clone()); py_mod From 933c8dc792c016206c9797d736f8c181eb9310df Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Mon, 4 Mar 2019 18:30:37 +0200 Subject: [PATCH 138/380] Use py_module macro --- vm/src/stdlib/socket.rs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index 50cab5481f..4afe7a372f 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -420,18 +420,6 @@ fn get_addr_tuple(vm: &mut VirtualMachine, addr: SocketAddr) -> PyResult { } pub fn mk_module(ctx: &PyContext) -> PyObjectRef { - let py_mod = ctx.new_module(&"socket".to_string(), ctx.new_scope(None)); - - ctx.set_attr(&py_mod, "AF_INET", ctx.new_int(AddressFamily::Inet as i32)); - - ctx.set_attr( - &py_mod, - "SOCK_STREAM", - ctx.new_int(SocketKind::Stream as i32), - ); - - ctx.set_attr(&py_mod, "SOCK_DGRAM", ctx.new_int(SocketKind::Dgram as i32)); - let socket = py_class!(ctx, "socket", ctx.object(), { "__new__" => ctx.new_rustfunc(socket_new), "connect" => ctx.new_rustfunc(socket_connect), @@ -445,7 +433,11 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { "sendto" => ctx.new_rustfunc(socket_sendto), "recvfrom" => ctx.new_rustfunc(socket_recvfrom), }); - ctx.set_attr(&py_mod, "socket", socket.clone()); - py_mod + py_module!(ctx, "socket", { + "AF_INET" => ctx.new_int(AddressFamily::Inet as i32), + "SOCK_STREAM" => ctx.new_int(SocketKind::Stream as i32), + "SOCK_DGRAM" => ctx.new_int(SocketKind::Dgram as i32), + "socket" => socket.clone(), + }) } From f820aeb1ea46c46a0f302edfc14fbece2cfb540c Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Mon, 4 Mar 2019 08:46:40 -0800 Subject: [PATCH 139/380] Convert complex payload --- vm/src/obj/objcomplex.rs | 30 +++++++++++++++++++++++------- vm/src/pyobject.rs | 28 +++++++++++++++------------- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index c6be5e5f80..66205559c9 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -2,12 +2,30 @@ use super::objfloat; use super::objint; use super::objtype; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, + TypeProtocol, }; use crate::vm::VirtualMachine; use num_complex::Complex64; use num_traits::ToPrimitive; +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct PyComplex { + value: Complex64, +} + +impl PyObjectPayload2 for PyComplex { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.complex_type() + } +} + +impl From for PyComplex { + fn from(value: Complex64) -> Self { + PyComplex { value } + } +} + pub fn init(context: &PyContext) { let complex_type = &context.complex_type; @@ -45,11 +63,7 @@ pub fn init(context: &PyContext) { } pub fn get_value(obj: &PyObjectRef) -> Complex64 { - if let PyObjectPayload::Complex { value } = &obj.payload { - *value - } else { - panic!("Inner error getting complex"); - } + obj.payload::().unwrap().value } fn complex_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -77,7 +91,9 @@ fn complex_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let value = Complex64::new(real, imag); Ok(PyObject::new( - PyObjectPayload::Complex { value }, + PyObjectPayload::AnyRustValue { + value: Box::new(PyComplex { value }), + }, cls.clone(), )) } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 2e07e30f6e..e581b1fae0 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -5,6 +5,11 @@ use std::iter; use std::ops::RangeInclusive; use std::rc::{Rc, Weak}; +use num_bigint::BigInt; +use num_bigint::ToBigInt; +use num_complex::Complex64; +use num_traits::{One, Zero}; + use crate::bytecode; use crate::exceptions; use crate::frame::{Frame, Scope, ScopeRef}; @@ -12,11 +17,11 @@ use crate::obj::objbool; use crate::obj::objbytearray; use crate::obj::objbytes; use crate::obj::objcode; -use crate::obj::objcomplex; +use crate::obj::objcomplex::{self, PyComplex}; use crate::obj::objdict; use crate::obj::objenumerate; use crate::obj::objfilter; -use crate::obj::objfloat; +use crate::obj::objfloat::{self, PyFloat}; use crate::obj::objframe; use crate::obj::objfunction; use crate::obj::objgenerator; @@ -37,10 +42,6 @@ use crate::obj::objtuple; use crate::obj::objtype; use crate::obj::objzip; use crate::vm::VirtualMachine; -use num_bigint::BigInt; -use num_bigint::ToBigInt; -use num_complex::Complex64; -use num_traits::{One, Zero}; /* Python objects and references. @@ -463,14 +464,19 @@ impl PyContext { pub fn new_float(&self, value: f64) -> PyObjectRef { PyObject::new( PyObjectPayload::AnyRustValue { - value: Box::new(objfloat::PyFloat::from(value)), + value: Box::new(PyFloat::from(value)), }, self.float_type(), ) } - pub fn new_complex(&self, i: Complex64) -> PyObjectRef { - PyObject::new(PyObjectPayload::Complex { value: i }, self.complex_type()) + pub fn new_complex(&self, value: Complex64) -> PyObjectRef { + PyObject::new( + PyObjectPayload::AnyRustValue { + value: Box::new(PyComplex::from(value)), + }, + self.complex_type(), + ) } pub fn new_str(&self, s: String) -> PyObjectRef { @@ -1408,9 +1414,6 @@ pub enum PyObjectPayload { Integer { value: BigInt, }, - Complex { - value: Complex64, - }, Sequence { elements: RefCell>, }, @@ -1494,7 +1497,6 @@ impl fmt::Debug for PyObjectPayload { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { PyObjectPayload::Integer { ref value } => write!(f, "int {}", value), - PyObjectPayload::Complex { ref value } => write!(f, "complex {}", value), PyObjectPayload::MemoryView { ref obj } => write!(f, "bytes/bytearray {:?}", obj), PyObjectPayload::Sequence { .. } => write!(f, "list or tuple"), PyObjectPayload::Dict { .. } => write!(f, "dict"), From 6544f60d9b4555be365c25d055de2eb3da69aa74 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Mon, 4 Mar 2019 19:24:34 +0100 Subject: [PATCH 140/380] Add type annotations to parser. --- parser/src/ast.rs | 14 +++++++--- parser/src/python.lalrpop | 50 +++++++++++++++++++++--------------- tests/snippets/type_hints.py | 7 +++++ vm/src/compile.rs | 12 ++++++--- vm/src/stdlib/ast.rs | 2 +- 5 files changed, 55 insertions(+), 30 deletions(-) create mode 100644 tests/snippets/type_hints.py diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 749003d9a9..ee5ef8473e 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -269,14 +269,20 @@ impl Expression { */ #[derive(Debug, PartialEq, Default)] pub struct Parameters { - pub args: Vec, - pub kwonlyargs: Vec, - pub vararg: Option>, // Optionally we handle optionally named '*args' or '*' - pub kwarg: Option>, + pub args: Vec, + pub kwonlyargs: Vec, + pub vararg: Option>, // Optionally we handle optionally named '*args' or '*' + pub kwarg: Option>, pub defaults: Vec, pub kw_defaults: Vec>, } +#[derive(Debug, PartialEq, Default)] +pub struct Parameter { + pub arg: String, + pub annotation: Option>, +} + #[derive(Debug, PartialEq)] pub enum ComprehensionKind { GeneratorExpression { element: Expression }, diff --git a/parser/src/python.lalrpop b/parser/src/python.lalrpop index 2748877ddd..a47e7e9129 100644 --- a/parser/src/python.lalrpop +++ b/parser/src/python.lalrpop @@ -460,7 +460,7 @@ FuncDef: ast::LocatedStatement = { }; Parameters: ast::Parameters = { - "(" ")" => { + "(" )?> ")" => { match a { Some(a) => a, None => Default::default(), @@ -470,8 +470,10 @@ Parameters: ast::Parameters = { // parameters are (String, None), kwargs are (String, Some(Test)) where Test is // the default -TypedArgsList: ast::Parameters = { - => { +// Note that this is a macro which is used once for function defs, and +// once for lambda defs. +TypedArgsList: ast::Parameters = { + > )?> => { let (names, default_elements) = param1; // Now gather rest of parameters: @@ -489,7 +491,7 @@ TypedArgsList: ast::Parameters = { kw_defaults: kw_defaults, } }, - => { + > )> => { let (names, default_elements) = param1; // Now gather rest of parameters: @@ -507,7 +509,7 @@ TypedArgsList: ast::Parameters = { kw_defaults: kw_defaults, } }, - => { + > => { let (vararg, kwonlyargs, kw_defaults, kwarg) = params; ast::Parameters { args: vec![], @@ -518,7 +520,7 @@ TypedArgsList: ast::Parameters = { kw_defaults: kw_defaults, } }, - => { + > => { ast::Parameters { args: vec![], kwonlyargs: vec![], @@ -532,8 +534,8 @@ TypedArgsList: ast::Parameters = { // Use inline here to make sure the "," is not creating an ambiguity. #[inline] -TypedParameters: (Vec, Vec) = { - => { +TypedParameters: (Vec, Vec) = { + > )*> => { // Combine first parameters: let mut args = vec![param1]; args.extend(param2.into_iter().map(|x| x.1)); @@ -542,7 +544,6 @@ TypedParameters: (Vec, Vec) = { let mut default_elements = vec![]; for (name, default) in args.into_iter() { - names.push(name.clone()); if let Some(default) = default { default_elements.push(default); } else { @@ -551,28 +552,35 @@ TypedParameters: (Vec, Vec) = { // have defaults panic!( "non-default argument follows default argument: {}", - name + &name.arg ); } } + names.push(name); } (names, default_elements) } }; -TypedParameterDef: (String, Option) = { - => (i, None), - "=" => (i, Some(e)), +TypedParameterDef: (ast::Parameter, Option) = { + => (i, None), + "=" => (i, Some(e)), }; -// TODO: add type annotations here: -TypedParameter: String = { - Identifier, +UntypedParameter: ast::Parameter = { + => ast::Parameter { arg: i, annotation: None }, }; -ParameterListStarArgs: (Option>, Vec, Vec>, Option>) = { - "*" => { +TypedParameter: ast::Parameter = { + => { + let annotation = a.map(|x| Box::new(x.1)); + ast::Parameter { arg, annotation } + }, +}; + +ParameterListStarArgs: (Option>, Vec, Vec>, Option>) = { + "*" )*> )?> => { // Extract keyword arguments: let mut kwonlyargs = vec![]; let mut kw_defaults = vec![]; @@ -590,8 +598,8 @@ ParameterListStarArgs: (Option>, Vec, Vec = { - "**" => { +KwargParameter: Option = { + "**" => { kwarg } }; @@ -675,7 +683,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/type_hints.py b/tests/snippets/type_hints.py new file mode 100644 index 0000000000..374e3aa26b --- /dev/null +++ b/tests/snippets/type_hints.py @@ -0,0 +1,7 @@ + +# See also: https://github.com/RustPython/RustPython/issues/587 + +def curry(foo: int): # TODO: -> float: + return foo * 3.1415926 * 2 + +assert curry(2) > 10 diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 1959cee83f..2842b53d87 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -442,10 +442,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(), + args.args.iter().map(|a| a.arg.clone()).collect(), + args.vararg + .as_ref() + .map(|x| x.as_ref().map(|a| a.arg.clone())), + args.kwonlyargs.iter().map(|a| a.arg.clone()).collect(), + args.kwarg + .as_ref() + .map(|x| x.as_ref().map(|a| a.arg.clone())), self.source_path.clone().unwrap(), line_number, name.to_string(), diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index e4eba415a9..a35451eb25 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -540,7 +540,7 @@ fn parameters_to_ast(ctx: &PyContext, args: &ast::Parameters) -> PyObjectRef { ctx.new_list( args.args .iter() - .map(|a| ctx.new_str(a.to_string())) + .map(|a| ctx.new_str(a.arg.to_string())) .collect(), ), ); From d24f79b17d41681a6a62d4a23e58767ec14d3d4b Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Mon, 4 Mar 2019 21:27:03 +0200 Subject: [PATCH 141/380] dir show module content --- vm/src/obj/objtype.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index c800b7f522..086f7f28f0 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -2,8 +2,8 @@ use super::objdict; use super::objstr; use super::objtype; // Required for arg_check! to use isinstance use crate::pyobject::{ - AttributeProtocol, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObject, PyObjectPayload, - PyObjectRef, PyResult, TypeProtocol, + AttributeProtocol, DictProtocol, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObject, + PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use std::cell::RefCell; @@ -277,6 +277,13 @@ pub fn get_attributes(obj: &PyObjectRef) -> PyAttributes { attributes.insert(name.to_string(), value.clone()); } } + + // Get module attributes: + if let PyObjectPayload::Module { ref scope, .. } = &obj.payload { + for (name, value) in scope.locals.get_key_value_pairs().iter() { + attributes.insert(objstr::get_value(name).to_string(), value.clone()); + } + } attributes } From f7a22254a6c537179c5061d70115c48015670ad6 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Mon, 4 Mar 2019 20:29:34 +0100 Subject: [PATCH 142/380] Add return annotation and fix tests in parser. --- parser/src/ast.rs | 1 + parser/src/parser.rs | 29 ++++++++++++++++++++++++++--- parser/src/python.lalrpop | 4 +++- tests/snippets/type_hints.py | 2 +- vm/src/compile.rs | 4 +++- vm/src/stdlib/ast.rs | 31 +++++++++++++++++++++++++------ 6 files changed, 59 insertions(+), 12 deletions(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index ee5ef8473e..3ee2c070d5 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -122,6 +122,7 @@ pub enum Statement { // docstring: String, body: Vec, decorator_list: Vec, + returns: Option, }, } diff --git a/parser/src/parser.rs b/parser/src/parser.rs index 4587e457ec..fc783c2aab 100644 --- a/parser/src/parser.rs +++ b/parser/src/parser.rs @@ -244,7 +244,16 @@ mod tests { node: ast::Statement::Expression { expression: ast::Expression::Lambda { args: ast::Parameters { - args: vec![String::from("x"), String::from("y")], + args: vec![ + ast::Parameter { + arg: String::from("x"), + annotation: None, + }, + ast::Parameter { + arg: String::from("y"), + annotation: None, + } + ], kwonlyargs: vec![], vararg: None, kwarg: None, @@ -330,7 +339,10 @@ mod tests { node: ast::Statement::FunctionDef { name: String::from("__init__"), args: ast::Parameters { - args: vec![String::from("self")], + args: vec![ast::Parameter { + arg: String::from("self"), + annotation: None, + }], kwonlyargs: vec![], vararg: None, kwarg: None, @@ -342,6 +354,7 @@ mod tests { node: ast::Statement::Pass, }], decorator_list: vec![], + returns: None, } }, ast::LocatedStatement { @@ -349,7 +362,16 @@ mod tests { node: ast::Statement::FunctionDef { name: String::from("method_with_default"), args: ast::Parameters { - args: vec![String::from("self"), String::from("arg"),], + args: vec![ + ast::Parameter { + arg: String::from("self"), + annotation: None, + }, + ast::Parameter { + arg: String::from("arg"), + annotation: None, + } + ], kwonlyargs: vec![], vararg: None, kwarg: None, @@ -365,6 +387,7 @@ mod tests { node: ast::Statement::Pass, }], decorator_list: vec![], + returns: None, } } ], diff --git a/parser/src/python.lalrpop b/parser/src/python.lalrpop index a47e7e9129..eb2605a657 100644 --- a/parser/src/python.lalrpop +++ b/parser/src/python.lalrpop @@ -446,7 +446,7 @@ WithItem: ast::WithItem = { }; FuncDef: ast::LocatedStatement = { - "def" ":" => { + "def" " Test)?> ":" => { ast::LocatedStatement { location: loc, node: ast::Statement::FunctionDef { @@ -454,6 +454,7 @@ FuncDef: ast::LocatedStatement = { args: a, body: s, decorator_list: d, + returns: r.map(|x| x.1), } } }, @@ -1094,6 +1095,7 @@ extern { "<=" => lexer::Tok::LessEqual, ">" => lexer::Tok::Greater, ">=" => lexer::Tok::GreaterEqual, + "->" => lexer::Tok::Rarrow, "and" => lexer::Tok::And, "as" => lexer::Tok::As, "assert" => lexer::Tok::Assert, diff --git a/tests/snippets/type_hints.py b/tests/snippets/type_hints.py index 374e3aa26b..8576e72352 100644 --- a/tests/snippets/type_hints.py +++ b/tests/snippets/type_hints.py @@ -1,7 +1,7 @@ # See also: https://github.com/RustPython/RustPython/issues/587 -def curry(foo: int): # TODO: -> float: +def curry(foo: int) -> float: return foo * 3.1415926 * 2 assert curry(2) > 10 diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 2842b53d87..20d3bb5317 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -300,7 +300,8 @@ impl Compiler { args, body, decorator_list, - } => self.compile_function_def(name, args, body, decorator_list)?, + returns, + } => self.compile_function_def(name, args, body, decorator_list, returns)?, ast::Statement::ClassDef { name, body, @@ -588,6 +589,7 @@ impl Compiler { args: &ast::Parameters, body: &[ast::LocatedStatement], decorator_list: &[ast::Expression], + _returns: &Option, // TODO: use type hint somehow.. ) -> Result<(), CompileError> { // Create bytecode for this function: // remember to restore self.in_loop to the original after the function is compiled diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index a35451eb25..178f074fb4 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -82,6 +82,7 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj args, body, decorator_list, + returns, } => { let node = create_node(ctx, "FunctionDef"); @@ -96,6 +97,13 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj let py_decorator_list = expressions_to_ast(ctx, decorator_list); ctx.set_attr(&node, "decorator_list", py_decorator_list); + + let py_returns = if let Some(hint) = returns { + expression_to_ast(ctx, hint) + } else { + ctx.none() + }; + ctx.set_attr(&node, "returns", py_returns); node } ast::Statement::Continue => create_node(ctx, "Continue"), @@ -537,17 +545,28 @@ fn parameters_to_ast(ctx: &PyContext, args: &ast::Parameters) -> PyObjectRef { ctx.set_attr( &node, "args", - ctx.new_list( - args.args - .iter() - .map(|a| ctx.new_str(a.arg.to_string())) - .collect(), - ), + ctx.new_list(args.args.iter().map(|a| parameter_to_ast(ctx, a)).collect()), ); node } +fn parameter_to_ast(ctx: &PyContext, parameter: &ast::Parameter) -> PyObjectRef { + let node = create_node(ctx, "arg"); + + let py_arg = ctx.new_str(parameter.arg.to_string()); + ctx.set_attr(&node, "arg", py_arg); + + let py_annotation = if let Some(annotation) = ¶meter.annotation { + expression_to_ast(ctx, annotation) + } else { + ctx.none() + }; + ctx.set_attr(&node, "annotation", py_annotation); + + node +} + fn comprehension_to_ast(ctx: &PyContext, comprehension: &ast::Comprehension) -> PyObjectRef { let node = create_node(ctx, "comprehension"); From 2d31e35844dc8426dbae76dbc73e190ddd941575 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Mon, 4 Mar 2019 21:31:20 +0200 Subject: [PATCH 143/380] Add test dir --- tests/snippets/builtin_dir.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/snippets/builtin_dir.py diff --git a/tests/snippets/builtin_dir.py b/tests/snippets/builtin_dir.py new file mode 100644 index 0000000000..b0c3949fa0 --- /dev/null +++ b/tests/snippets/builtin_dir.py @@ -0,0 +1,12 @@ + +class A: + def test(): + pass + +a = A() + +assert "test" in dir(a) + +import socket + +assert "AF_INET" in dir(socket) From da577306926e961af40de48fc83de9953d02d192 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Mon, 4 Mar 2019 21:52:20 +0200 Subject: [PATCH 144/380] Fix get_locals --- tests/snippets/builtin_locals.py | 8 ++++++++ vm/src/vm.rs | 12 ++---------- 2 files changed, 10 insertions(+), 10 deletions(-) create mode 100644 tests/snippets/builtin_locals.py diff --git a/tests/snippets/builtin_locals.py b/tests/snippets/builtin_locals.py new file mode 100644 index 0000000000..8f17d2248b --- /dev/null +++ b/tests/snippets/builtin_locals.py @@ -0,0 +1,8 @@ + +a = 5 +b = 6 + +loc = locals() + +assert loc['a'] == 5 +assert loc['b'] == 6 diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 60a260479b..6c6659d4db 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -208,16 +208,8 @@ impl VirtualMachine { } pub fn get_locals(&self) -> PyObjectRef { - // let scope = &self.frames.last().unwrap().locals; - // scope.clone() - // TODO: fix this! - self.get_none() - /* - match (*scope).payload { - PyObjectPayload::Scope { scope } => { scope.locals.clone() }, - _ => { panic!("Should be scope") }, - } // .clone() - */ + let scope = self.current_scope(); + scope.locals.clone() } pub fn context(&self) -> &PyContext { From eed0b3ca454d52f6a3cc20096868514839c130f6 Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Mon, 4 Mar 2019 20:52:56 +0100 Subject: [PATCH 145/380] Add float.as_integer_ratio() --- Cargo.lock | 12 ++++++++++++ tests/snippets/floats.py | 19 +++++++++++++++++++ vm/Cargo.toml | 1 + vm/src/obj/objfloat.rs | 26 ++++++++++++++++++++++++++ 4 files changed, 58 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 2fa56aaa77..9833a0a918 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -514,6 +514,16 @@ dependencies = [ "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "num-rational" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-bigint 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)", +] + [[package]] name = "num-traits" version = "0.2.6" @@ -756,6 +766,7 @@ dependencies = [ "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-rational 0.2.1 (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)", @@ -1285,6 +1296,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum num-bigint 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "10b8423ea72ec64751198856a853e07b37087cfc9b53a87ecb19bff67b6d1320" "checksum num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "107b9be86cd2481930688277b675b0114578227f034674726605b8a482d8baf8" "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" +"checksum num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e96f040177bb3da242b5b1ecf3f54b5d5af3efbbfb18608977a5d2767b22f10" "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" "checksum ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" "checksum petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" diff --git a/tests/snippets/floats.py b/tests/snippets/floats.py index ac24a4dbfa..6675615c56 100644 --- a/tests/snippets/floats.py +++ b/tests/snippets/floats.py @@ -87,3 +87,22 @@ assert (1.7).real == 1.7 assert (1.3).is_integer() == False assert (1.0).is_integer() == True + +assert (0.875).as_integer_ratio() == (7, 8) +assert (-0.875).as_integer_ratio() == (-7, 8) +assert (0.0).as_integer_ratio() == (0, 1) +assert (11.5).as_integer_ratio() == (23, 2) +assert (0.0).as_integer_ratio() == (0, 1) +assert (2.5).as_integer_ratio() == (5, 2) +assert (0.5).as_integer_ratio() == (1, 2) +assert (2.1).as_integer_ratio() == (4728779608739021, 2251799813685248) +assert (-2.1).as_integer_ratio() == (-4728779608739021, 2251799813685248) +assert (-2100.0).as_integer_ratio() == (-2100, 1) +assert (2.220446049250313e-16).as_integer_ratio() == (1, 4503599627370496) +assert (1.7976931348623157e+308).as_integer_ratio() == (179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368, 1) +assert (2.2250738585072014e-308).as_integer_ratio() == (1, 44942328371557897693232629769725618340449424473557664318357520289433168951375240783177119330601884005280028469967848339414697442203604155623211857659868531094441973356216371319075554900311523529863270738021251442209537670585615720368478277635206809290837627671146574559986811484619929076208839082406056034304) + +assert_raises(OverflowError, float('inf').as_integer_ratio) +assert_raises(OverflowError, float('-inf').as_integer_ratio) +assert_raises(ValueError, float('nan').as_integer_ratio) + diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 85e0bf936e..e245aadbcc 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -10,6 +10,7 @@ num-complex = "0.2" num-bigint = "0.2.1" num-traits = "0.2" num-integer = "0.1.39" +num-rational = "0.2.1" rand = "0.5" log = "0.3" rustpython_parser = {path = "../parser"} diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index a2038bffbc..d2bf03ef5b 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -2,12 +2,14 @@ use super::objbytes; use super::objint; use super::objstr; use super::objtype; +use crate::function::PyRef; use crate::pyobject::{ PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use num_bigint::ToBigInt; +use num_rational::Ratio; use num_traits::ToPrimitive; #[derive(Debug, Copy, Clone, PartialEq)] @@ -27,6 +29,25 @@ impl From for PyFloat { } } +impl PyFloat { + fn as_integer_ratio(zelf: PyRef, vm: &mut VirtualMachine) -> PyResult { + let value = zelf.value; + if value.is_infinite() { + return Err( + vm.new_overflow_error("cannot convert Infinity to integer ratio".to_string()) + ); + } + if value.is_nan() { + return Err(vm.new_value_error("cannot convert NaN to integer ratio".to_string())); + } + + let ratio = Ratio::from_float(value).unwrap(); + let numer = vm.ctx.new_int(ratio.numer().clone()); + let denom = vm.ctx.new_int(ratio.denom().clone()); + Ok(vm.ctx.new_tuple(vec![numer, denom])) + } +} + fn float_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(float, Some(vm.ctx.float_type()))]); let v = get_value(float); @@ -494,4 +515,9 @@ pub fn init(context: &PyContext) { "is_integer", context.new_rustfunc(float_is_integer), ); + context.set_attr( + &float_type, + "as_integer_ratio", + context.new_rustfunc(PyFloat::as_integer_ratio), + ); } From e25742f18434c0baba61802902685b4593566f93 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Mon, 4 Mar 2019 21:09:03 +0100 Subject: [PATCH 146/380] Add ellipsis syntax. --- parser/src/ast.rs | 2 ++ parser/src/lexer.rs | 10 ++++++++-- parser/src/python.lalrpop | 2 ++ vm/src/bytecode.rs | 2 ++ vm/src/compile.rs | 5 +++++ vm/src/pyobject.rs | 14 ++++++++++++++ vm/src/stdlib/ast.rs | 3 +++ 7 files changed, 36 insertions(+), 2 deletions(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 749003d9a9..df035c696f 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -217,6 +217,7 @@ pub enum Expression { True, False, None, + Ellipsis, } impl Expression { @@ -259,6 +260,7 @@ impl Expression { Lambda { .. } => "lambda", IfExpression { .. } => "conditional expression", True | False | None => "keyword", + Ellipsis => "ellipsis" } } } diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index 0c3d887806..83f0f0c2f8 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -1011,8 +1011,14 @@ where Some('.') => { let tok_start = self.get_pos(); self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Dot, tok_end))); + if let (Some('.'), Some('.')) = (&self.chr0, &self.chr1) { + self.next_char();self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Ellipsis, tok_end))); + } else { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Dot, tok_end))); + } } Some('\n') => { let tok_start = self.get_pos(); diff --git a/parser/src/python.lalrpop b/parser/src/python.lalrpop index 2748877ddd..fb144604e5 100644 --- a/parser/src/python.lalrpop +++ b/parser/src/python.lalrpop @@ -839,6 +839,7 @@ Atom: ast::Expression = { "True" => ast::Expression::True, "False" => ast::Expression::False, "None" => ast::Expression::None, + "..." => ast::Expression::Ellipsis, }; TestListComp: Vec = { @@ -1048,6 +1049,7 @@ extern { "~" => lexer::Tok::Tilde, ":" => lexer::Tok::Colon, "." => lexer::Tok::Dot, + "..." => lexer::Tok::Ellipsis, "," => lexer::Tok::Comma, "*" => lexer::Tok::Star, "**" => lexer::Tok::DoubleStar, diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index 9c74799426..59135ef64f 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -193,6 +193,7 @@ pub enum Constant { Code { code: Box }, Tuple { elements: Vec }, None, + Ellipsis, } #[derive(Debug, Clone, PartialEq)] @@ -385,6 +386,7 @@ impl fmt::Display for Constant { .join(", ") ), Constant::None => write!(f, "None"), + Constant::Ellipsis => write!(f, "Ellipsis"), } } } diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 1959cee83f..cc2dc44d1e 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -1056,6 +1056,11 @@ impl Compiler { value: bytecode::Constant::None, }); } + ast::Expression::Ellipsis => { + self.emit(Instruction::LoadConst { + value: bytecode::Constant::Ellipsis, + }); + } ast::Expression::String { value } => { self.compile_string(value)?; } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index e581b1fae0..f08c33a1a5 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 ellipsis: PyObjectRef, pub not_implemented: PyObjectRef, pub tuple_type: PyObjectRef, pub set_type: PyObjectRef, @@ -223,6 +224,12 @@ impl PyContext { create_type("NoneType", &type_type, &object_type, &dict_type), ); + // TODO: implement proper ellipsis class? + let ellipsis = PyObject::new( + PyObjectPayload::None, + create_type("EllipsisType", &type_type, &object_type, &dict_type), + ); + let not_implemented = PyObject::new( PyObjectPayload::NotImplemented, create_type("NotImplementedType", &type_type, &object_type, &dict_type), @@ -263,6 +270,7 @@ impl PyContext { zip_type, dict_type, none, + ellipsis, not_implemented, str_type, range_type, @@ -441,6 +449,11 @@ impl PyContext { pub fn none(&self) -> PyObjectRef { self.none.clone() } + + pub fn ellipsis(&self) -> PyObjectRef { + self.ellipsis.clone() + } + pub fn not_implemented(&self) -> PyObjectRef { self.not_implemented.clone() } @@ -699,6 +712,7 @@ impl PyContext { self.new_tuple(elements) } bytecode::Constant::None => self.none(), + bytecode::Constant::Ellipsis => self.ellipsis(), } } } diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index e4eba415a9..93b70b28d0 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -395,6 +395,9 @@ fn expression_to_ast(ctx: &PyContext, expression: &ast::Expression) -> PyObjectR node } + ast::Expression::Ellipsis => { + create_node(ctx, "Ellipsis") + } ast::Expression::List { elements } => { let node = create_node(ctx, "List"); From a545e4392a0284d8d2c528f6cb543c7db8b7e7f9 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Mon, 4 Mar 2019 22:17:41 +0200 Subject: [PATCH 147/380] Test locals indside a function --- tests/snippets/builtin_locals.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/snippets/builtin_locals.py b/tests/snippets/builtin_locals.py index 8f17d2248b..6f3fd847c4 100644 --- a/tests/snippets/builtin_locals.py +++ b/tests/snippets/builtin_locals.py @@ -6,3 +6,14 @@ assert loc['a'] == 5 assert loc['b'] == 6 + +def f(): + c = 4 + a = 7 + + loc = locals() + + assert loc['a'] == 4 + assert loc['c'] == 7 + assert not 'b' in loc + From a26401707f501c604cd25fb0c55bb4645f6abf96 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Mon, 4 Mar 2019 21:25:35 +0100 Subject: [PATCH 148/380] Minor lexer simplifications. --- parser/src/lexer.rs | 102 ++++++++++++++++++-------------------------- 1 file changed, 42 insertions(+), 60 deletions(-) diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index 0c3d887806..e49ef5148e 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -719,16 +719,13 @@ where Some('+') => { let tok_start = self.get_pos(); self.next_char(); - match self.chr0 { - Some('=') => { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::PlusEqual, tok_end))); - } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Plus, tok_end))); - } + if let Some('=') = self.chr0 { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::PlusEqual, tok_end))); + } else { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Plus, tok_end))); } } Some('*') => { @@ -792,61 +789,49 @@ where Some('%') => { let tok_start = self.get_pos(); self.next_char(); - match self.chr0 { - Some('=') => { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::PercentEqual, tok_end))); - } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Percent, tok_end))); - } + if let Some('=') = self.chr0 { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::PercentEqual, tok_end))); + } else { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Percent, tok_end))); } } Some('|') => { let tok_start = self.get_pos(); self.next_char(); - match self.chr0 { - Some('=') => { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::VbarEqual, tok_end))); - } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Vbar, tok_end))); - } + if let Some('=') = self.chr0 { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::VbarEqual, tok_end))); + } else { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Vbar, tok_end))); } } Some('^') => { let tok_start = self.get_pos(); self.next_char(); - match self.chr0 { - Some('=') => { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::CircumflexEqual, tok_end))); - } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::CircumFlex, tok_end))); - } + if let Some('=') = self.chr0 { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::CircumflexEqual, tok_end))); + } else { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::CircumFlex, tok_end))); } } Some('&') => { let tok_start = self.get_pos(); self.next_char(); - match self.chr0 { - Some('=') => { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::AmperEqual, tok_end))); - } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Amper, tok_end))); - } + if let Some('=') = self.chr0 { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::AmperEqual, tok_end))); + } else { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Amper, tok_end))); } } Some('-') => { @@ -872,16 +857,13 @@ where Some('@') => { let tok_start = self.get_pos(); self.next_char(); - match self.chr0 { - Some('=') => { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::AtEqual, tok_end))); - } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::At, tok_end))); - } + if let Some('=') = self.chr0 { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::AtEqual, tok_end))); + } else { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::At, tok_end))); } } Some('!') => { From 0584b85d860ff18ac50da4d44a1a384e738ee4b1 Mon Sep 17 00:00:00 2001 From: holygits Date: Fri, 1 Mar 2019 10:03:38 +1300 Subject: [PATCH 149/380] Bytearray hex formatting --- vm/src/obj/objbytearray.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index 7e03d34d3d..295d98be1d 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -2,6 +2,7 @@ use std::cell::RefCell; use std::ops::{Deref, DerefMut}; +use std::fmt::Write; use crate::pyobject::{ PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, @@ -311,10 +312,19 @@ fn set_value(obj: &PyObjectRef, value: Vec) { } */ +/// Return a lowercase hex representation of a bytearray +fn bytearray_to_hex(bytearray: &[u8]) -> String { + bytearray.iter().fold(String::new(), |mut s, b| { + let _ = write!(s, "\\x{:02x}", b); + s + }) +} + fn bytearray_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, Some(vm.ctx.bytearray_type()))]); let value = get_value(obj); - let data = String::from_utf8(value.to_vec()).unwrap(); + let data = + String::from_utf8(value.to_vec()).unwrap_or_else(|_| bytearray_to_hex(&value.to_vec())); Ok(vm.new_str(format!("bytearray(b'{}')", data))) } @@ -346,3 +356,13 @@ fn bytearray_upper(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let value = get_value(obj).to_vec().to_ascii_uppercase(); Ok(vm.ctx.new_bytearray(value)) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn bytearray_to_hex_formatting() { + assert_eq!(&bytearray_to_hex(&[11u8, 222u8]), "\\x0b\\xde"); + } +} From 55cc653d9b9cf8fc68e1ac4c5bcfcadbcd71a9da Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Mon, 4 Mar 2019 16:50:30 -0600 Subject: [PATCH 150/380] Prepare the wasm crate for publishing on npm --- Cargo.toml | 2 +- wasm/lib/README.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a1f366cd43..33c278da33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustpython" -version = "0.0.1" +version = "0.0.1-pre-alpha.1" authors = ["Windel Bouwman", "Shing Lyu "] edition = "2018" diff --git a/wasm/lib/README.md b/wasm/lib/README.md index 6d2f89d932..af0c4d9977 100644 --- a/wasm/lib/README.md +++ b/wasm/lib/README.md @@ -7,6 +7,10 @@ A Python-3 (CPython >= 3.5.0) Interpreter written in Rust. [![Contributors](https://img.shields.io/github/contributors/RustPython/RustPython.svg)](https://github.com/RustPython/RustPython/graphs/contributors) [![Gitter](https://badges.gitter.im/RustPython/Lobby.svg)](https://gitter.im/rustpython/Lobby) +# WARNING: this project is still in an pre-alpha state! + +**Using this in a production project is inadvisable. Please only do so if you understand the risks.** + ## Usage ### Check out our [online demo](https://rustpython.github.io/demo/) running on WebAssembly. From 006e975bc5947f9e7e68c7f6d266db1d64dbc48e Mon Sep 17 00:00:00 2001 From: Joey Date: Mon, 4 Mar 2019 17:16:02 -0600 Subject: [PATCH 151/380] Update wasm/lib/README.md Co-Authored-By: coolreader18 <33094578+coolreader18@users.noreply.github.com> --- wasm/lib/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wasm/lib/README.md b/wasm/lib/README.md index af0c4d9977..989e5952e3 100644 --- a/wasm/lib/README.md +++ b/wasm/lib/README.md @@ -7,7 +7,7 @@ A Python-3 (CPython >= 3.5.0) Interpreter written in Rust. [![Contributors](https://img.shields.io/github/contributors/RustPython/RustPython.svg)](https://github.com/RustPython/RustPython/graphs/contributors) [![Gitter](https://badges.gitter.im/RustPython/Lobby.svg)](https://gitter.im/rustpython/Lobby) -# WARNING: this project is still in an pre-alpha state! +# WARNING: this project is still in a pre-alpha state! **Using this in a production project is inadvisable. Please only do so if you understand the risks.** From eefde87594afa21cae574a0138203b60560fb0b0 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Mon, 4 Mar 2019 21:17:29 -0600 Subject: [PATCH 152/380] Cargo.lock update --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 2fa56aaa77..2d615ee654 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -720,7 +720,7 @@ dependencies = [ [[package]] name = "rustpython" -version = "0.0.1" +version = "0.0.1-pre-alpha.1" dependencies = [ "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", From bf1fe9e5f20f7c53744be68a1a364ebcfae4d585 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Mon, 4 Mar 2019 21:46:17 -0800 Subject: [PATCH 153/380] Convert set payload --- vm/src/obj/objset.rs | 108 +++++++++++++++++++++++++------------------ vm/src/pyobject.rs | 10 ++-- 2 files changed, 65 insertions(+), 53 deletions(-) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 2652532f59..f194e20f23 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -13,18 +13,26 @@ use super::objiter; use super::objstr; use super::objtype; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, + TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; -pub fn get_elements(obj: &PyObjectRef) -> HashMap { - if let PyObjectPayload::Set { elements } = &obj.payload { - elements.borrow().clone() - } else { - panic!("Cannot extract set elements from non-set"); +#[derive(Debug, Default)] +pub struct PySet { + elements: RefCell>, +} + +impl PyObjectPayload2 for PySet { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.set_type() } } +pub fn get_elements(obj: &PyObjectRef) -> HashMap { + obj.payload::().unwrap().elements.borrow().clone() +} + fn perform_action_with_hash( vm: &mut VirtualMachine, elements: &mut HashMap, @@ -62,12 +70,10 @@ fn set_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [(s, Some(vm.ctx.set_type())), (item, None)] + required = [(zelf, Some(vm.ctx.set_type())), (item, None)] ); - match s.payload { - PyObjectPayload::Set { ref elements } => { - insert_into_set(vm, &mut elements.borrow_mut(), item) - } + match zelf.payload::() { + Some(set) => insert_into_set(vm, &mut set.elements.borrow_mut(), item), _ => Err(vm.new_type_error("set.add is called with no item".to_string())), } } @@ -79,8 +85,8 @@ fn set_remove(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(s, Some(vm.ctx.set_type())), (item, None)] ); - match s.payload { - PyObjectPayload::Set { ref elements } => { + match s.payload::() { + Some(set) => { fn remove( vm: &mut VirtualMachine, elements: &mut HashMap, @@ -95,7 +101,7 @@ fn set_remove(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Some(_) => Ok(vm.get_none()), } } - perform_action_with_hash(vm, &mut elements.borrow_mut(), item, &remove) + perform_action_with_hash(vm, &mut set.elements.borrow_mut(), item, &remove) } _ => Err(vm.new_type_error("set.remove is called with no item".to_string())), } @@ -108,8 +114,8 @@ fn set_discard(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(s, Some(vm.ctx.set_type())), (item, None)] ); - match s.payload { - PyObjectPayload::Set { ref elements } => { + match s.payload::() { + Some(set) => { fn discard( vm: &mut VirtualMachine, elements: &mut HashMap, @@ -119,21 +125,21 @@ fn set_discard(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { elements.remove(&key); Ok(vm.get_none()) } - perform_action_with_hash(vm, &mut elements.borrow_mut(), item, &discard) + perform_action_with_hash(vm, &mut set.elements.borrow_mut(), item, &discard) } - _ => Err(vm.new_type_error("set.discard is called with no item".to_string())), + None => Err(vm.new_type_error("set.discard is called with no item".to_string())), } } fn set_clear(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("set.clear called"); arg_check!(vm, args, required = [(s, Some(vm.ctx.set_type()))]); - match s.payload { - PyObjectPayload::Set { ref elements } => { - elements.borrow_mut().clear(); + match s.payload::() { + Some(set) => { + set.elements.borrow_mut().clear(); Ok(vm.get_none()) } - _ => Err(vm.new_type_error("".to_string())), + None => Err(vm.new_type_error("".to_string())), } } @@ -163,8 +169,10 @@ fn set_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; Ok(PyObject::new( - PyObjectPayload::Set { - elements: RefCell::new(elements), + PyObjectPayload::AnyRustValue { + value: Box::new(PySet { + elements: RefCell::new(elements), + }), }, cls.clone(), )) @@ -182,8 +190,10 @@ fn set_copy(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.set_type()))]); let elements = get_elements(s); Ok(PyObject::new( - PyObjectPayload::Set { - elements: RefCell::new(elements), + PyObjectPayload::AnyRustValue { + value: Box::new(PySet { + elements: RefCell::new(elements), + }), }, vm.ctx.set_type(), )) @@ -336,8 +346,10 @@ fn set_union(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { elements.extend(get_elements(other).clone()); Ok(PyObject::new( - PyObjectPayload::Set { - elements: RefCell::new(elements), + PyObjectPayload::AnyRustValue { + value: Box::new(PySet { + elements: RefCell::new(elements), + }), }, vm.ctx.set_type(), )) @@ -378,8 +390,10 @@ fn set_symmetric_difference(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResu } Ok(PyObject::new( - PyObjectPayload::Set { - elements: RefCell::new(elements), + PyObjectPayload::AnyRustValue { + value: Box::new(PySet { + elements: RefCell::new(elements), + }), }, vm.ctx.set_type(), )) @@ -418,8 +432,10 @@ fn set_combine_inner( } Ok(PyObject::new( - PyObjectPayload::Set { - elements: RefCell::new(elements), + PyObjectPayload::AnyRustValue { + value: Box::new(PySet { + elements: RefCell::new(elements), + }), }, vm.ctx.set_type(), )) @@ -428,9 +444,9 @@ fn set_combine_inner( fn set_pop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.set_type()))]); - match s.payload { - PyObjectPayload::Set { ref elements } => { - let mut elements = elements.borrow_mut(); + match s.payload::() { + Some(set) => { + let mut elements = set.elements.borrow_mut(); match elements.clone().keys().next() { Some(key) => Ok(elements.remove(key).unwrap()), None => Err(vm.new_key_error("pop from an empty set".to_string())), @@ -452,11 +468,11 @@ fn set_ior(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.set_type())), (iterable, None)] ); - match zelf.payload { - PyObjectPayload::Set { ref elements } => { + match zelf.payload::() { + Some(set) => { let iterator = objiter::get_iter(vm, iterable)?; while let Ok(v) = vm.call_method(&iterator, "__next__", vec![]) { - insert_into_set(vm, &mut elements.borrow_mut(), &v)?; + insert_into_set(vm, &mut set.elements.borrow_mut(), &v)?; } } _ => return Err(vm.new_type_error("set.update is called with no other".to_string())), @@ -493,9 +509,9 @@ fn set_combine_update_inner( required = [(zelf, Some(vm.ctx.set_type())), (iterable, None)] ); - match zelf.payload { - PyObjectPayload::Set { ref elements } => { - let mut elements = elements.borrow_mut(); + match zelf.payload::() { + Some(set) => { + let mut elements = set.elements.borrow_mut(); for element in elements.clone().iter() { let value = vm.call_method(iterable, "__contains__", vec![element.1.clone()])?; let should_remove = match op { @@ -524,17 +540,17 @@ fn set_ixor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, Some(vm.ctx.set_type())), (iterable, None)] ); - match zelf.payload { - PyObjectPayload::Set { ref elements } => { - let elements_original = elements.borrow().clone(); + match zelf.payload::() { + Some(set) => { + let elements_original = set.elements.borrow().clone(); let iterator = objiter::get_iter(vm, iterable)?; while let Ok(v) = vm.call_method(&iterator, "__next__", vec![]) { - insert_into_set(vm, &mut elements.borrow_mut(), &v)?; + insert_into_set(vm, &mut set.elements.borrow_mut(), &v)?; } for element in elements_original.iter() { let value = vm.call_method(iterable, "__contains__", vec![element.1.clone()])?; if objbool::get_value(&value) { - elements.borrow_mut().remove(&element.0.clone()); + set.elements.borrow_mut().remove(&element.0.clone()); } } } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index e581b1fae0..fd332234a2 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -34,7 +34,7 @@ use crate::obj::objnone; use crate::obj::objobject; use crate::obj::objproperty; use crate::obj::objrange; -use crate::obj::objset; +use crate::obj::objset::{self, PySet}; use crate::obj::objslice; use crate::obj::objstr; use crate::obj::objsuper; @@ -536,8 +536,8 @@ 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. PyObject::new( - PyObjectPayload::Set { - elements: RefCell::new(HashMap::new()), + PyObjectPayload::AnyRustValue { + value: Box::new(PySet::default()), }, self.set_type(), ) @@ -1476,9 +1476,6 @@ pub enum PyObjectPayload { dict: RefCell, mro: Vec, }, - Set { - elements: RefCell>, - }, WeakRef { referent: PyObjectWeakRef, }, @@ -1500,7 +1497,6 @@ impl fmt::Debug for PyObjectPayload { PyObjectPayload::MemoryView { ref obj } => write!(f, "bytes/bytearray {:?}", obj), PyObjectPayload::Sequence { .. } => write!(f, "list or tuple"), PyObjectPayload::Dict { .. } => write!(f, "dict"), - PyObjectPayload::Set { .. } => write!(f, "set"), PyObjectPayload::WeakRef { .. } => write!(f, "weakref"), PyObjectPayload::Iterator { .. } => write!(f, "iterator"), PyObjectPayload::EnumerateIterator { .. } => write!(f, "enumerate"), From d9c35f94c2fffa1200772de3e7796b5d9f9ad706 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Mon, 4 Mar 2019 21:46:21 -0800 Subject: [PATCH 154/380] Convert dict payload --- vm/src/frame.rs | 20 +++++------- vm/src/obj/objbool.rs | 5 ++- vm/src/obj/objdict.rs | 37 +++++++++------------- vm/src/pyobject.rs | 73 +++++++++++++++++++++---------------------- 4 files changed, 62 insertions(+), 73 deletions(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index c76552b76e..9777596070 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -11,6 +11,7 @@ use crate::import::{import, import_module}; use crate::obj::objbool; use crate::obj::objcode; use crate::obj::objdict; +use crate::obj::objdict::PyDict; use crate::obj::objiter; use crate::obj::objlist; use crate::obj::objstr; @@ -1125,18 +1126,13 @@ impl fmt::Debug for Frame { .map(|elem| format!("\n > {:?}", elem)) .collect::>() .join(""); - let local_str = match self.scope.locals.payload { - PyObjectPayload::Dict { ref elements } => { - objdict::get_key_value_pairs_from_content(&elements.borrow()) - .iter() - .map(|elem| format!("\n {:?} = {:?}", elem.0, elem.1)) - .collect::>() - .join("") - } - ref unexpected => panic!( - "locals unexpectedly not wrapping a dict! instead: {:?}", - unexpected - ), + let local_str = match self.scope.locals.payload::() { + Some(dict) => objdict::get_key_value_pairs_from_content(&dict.entries.borrow()) + .iter() + .map(|elem| format!("\n {:?} = {:?}", elem.0, elem.1)) + .collect::>() + .join(""), + None => panic!("locals unexpectedly not wrapping a dict!",), }; write!( f, diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index 84b048e3ae..e45018ea60 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -1,3 +1,4 @@ +use super::objdict::PyDict; use super::objfloat::PyFloat; use super::objstr::PyString; use super::objtype; @@ -20,10 +21,12 @@ pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result() { return Ok(*value != PyFloat::from(0.0)); } + if let Some(dict) = obj.payload::() { + return Ok(!dict.entries.borrow().is_empty()); + } let result = match obj.payload { PyObjectPayload::Integer { ref value } => !value.is_zero(), PyObjectPayload::Sequence { ref elements } => !elements.borrow().is_empty(), - PyObjectPayload::Dict { ref elements } => !elements.borrow().is_empty(), PyObjectPayload::None { .. } => false, _ => { if let Ok(f) = vm.get_method(obj.clone(), "__bool__") { diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 03a307b4c3..f6287c0655 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -6,40 +6,31 @@ use super::objiter; use super::objstr; use super::objtype; use crate::pyobject::{ - PyAttributes, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, - TypeProtocol, + PyAttributes, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, + PyResult, TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; -// This typedef abstracts the actual dict type used. -// pub type DictContentType = HashMap>; -// pub type DictContentType = HashMap; pub type DictContentType = HashMap; -// pub type DictContentType = HashMap>; -pub fn new(dict_type: PyObjectRef) -> PyObjectRef { - PyObject::new( - PyObjectPayload::Dict { - elements: RefCell::new(HashMap::new()), - }, - dict_type.clone(), - ) +#[derive(Default, Debug)] +pub struct PyDict { + // TODO: should be private + pub entries: RefCell, } -pub fn get_elements<'a>(obj: &'a PyObjectRef) -> impl Deref + 'a { - if let PyObjectPayload::Dict { ref elements } = obj.payload { - elements.borrow() - } else { - panic!("Cannot extract dict elements"); +impl PyObjectPayload2 for PyDict { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.dict_type() } } +pub fn get_elements<'a>(obj: &'a PyObjectRef) -> impl Deref + 'a { + obj.payload::().unwrap().entries.borrow() +} + fn get_mut_elements<'a>(obj: &'a PyObjectRef) -> impl DerefMut + 'a { - if let PyObjectPayload::Dict { ref elements } = obj.payload { - elements.borrow_mut() - } else { - panic!("Cannot extract dict elements"); - } + obj.payload::().unwrap().entries.borrow_mut() } pub fn set_item( diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index fd332234a2..b4e5295c6c 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -18,7 +18,7 @@ use crate::obj::objbytearray; use crate::obj::objbytes; use crate::obj::objcode; use crate::obj::objcomplex::{self, PyComplex}; -use crate::obj::objdict; +use crate::obj::objdict::{self, PyDict}; use crate::obj::objenumerate; use crate::obj::objfilter; use crate::obj::objfloat::{self, PyFloat}; @@ -545,8 +545,8 @@ impl PyContext { pub fn new_dict(&self) -> PyObjectRef { PyObject::new( - PyObjectPayload::Dict { - elements: RefCell::new(HashMap::new()), + PyObjectPayload::AnyRustValue { + value: Box::new(PyDict::default()), }, self.dict_type(), ) @@ -654,12 +654,11 @@ impl PyContext { // Item set/get: pub fn set_item(&self, obj: &PyObjectRef, key: &str, v: PyObjectRef) { - match obj.payload { - PyObjectPayload::Dict { ref elements } => { - let key = self.new_str(key.to_string()); - objdict::set_item_in_content(&mut elements.borrow_mut(), &key, &v); - } - ref k => panic!("TODO {:?}", k), + if let Some(dict) = obj.payload::() { + let key = self.new_str(key.to_string()); + objdict::set_item_in_content(&mut dict.entries.borrow_mut(), &key, &v); + } else { + unimplemented!() }; } @@ -816,44 +815,48 @@ pub trait DictProtocol { impl DictProtocol for PyObjectRef { fn contains_key(&self, k: &str) -> bool { - match self.payload { - PyObjectPayload::Dict { ref elements } => { - objdict::content_contains_key_str(&elements.borrow(), k) - } - ref payload => unimplemented!("TODO {:?}", payload), + if let Some(dict) = self.payload::() { + objdict::content_contains_key_str(&dict.entries.borrow(), k) + } else { + unimplemented!() } } fn get_item(&self, k: &str) -> Option { - match self.payload { - PyObjectPayload::Dict { ref elements } => { - objdict::content_get_key_str(&elements.borrow(), k) + if let Some(dict) = self.payload::() { + objdict::content_get_key_str(&dict.entries.borrow(), k) + } else { + match self.payload { + PyObjectPayload::Module { ref scope, .. } => scope.locals.get_item(k), + ref k => panic!("TODO {:?}", k), } - PyObjectPayload::Module { ref scope, .. } => scope.locals.get_item(k), - ref k => panic!("TODO {:?}", k), } } fn get_key_value_pairs(&self) -> Vec<(PyObjectRef, PyObjectRef)> { - match self.payload { - PyObjectPayload::Dict { .. } => objdict::get_key_value_pairs(self), - PyObjectPayload::Module { ref scope, .. } => scope.locals.get_key_value_pairs(), - _ => panic!("TODO"), + if let Some(_) = self.payload::() { + objdict::get_key_value_pairs(self) + } else { + match self.payload { + PyObjectPayload::Module { ref scope, .. } => scope.locals.get_key_value_pairs(), + _ => panic!("TODO"), + } } } // Item set/get: fn set_item(&self, ctx: &PyContext, key: &str, v: PyObjectRef) { - match &self.payload { - PyObjectPayload::Dict { elements } => { - let key = ctx.new_str(key.to_string()); - objdict::set_item_in_content(&mut elements.borrow_mut(), &key, &v); - } - PyObjectPayload::Module { scope, .. } => { - scope.locals.set_item(ctx, key, v); - } - ref k => panic!("TODO {:?}", k), - }; + if let Some(dict) = self.payload::() { + let key = ctx.new_str(key.to_string()); + objdict::set_item_in_content(&mut dict.entries.borrow_mut(), &key, &v); + } else { + match &self.payload { + PyObjectPayload::Module { scope, .. } => { + scope.locals.set_item(ctx, key, v); + } + ref k => panic!("TODO {:?}", k), + }; + } } } @@ -1417,9 +1420,6 @@ pub enum PyObjectPayload { Sequence { elements: RefCell>, }, - Dict { - elements: RefCell, - }, Iterator { position: Cell, iterated_obj: PyObjectRef, @@ -1496,7 +1496,6 @@ impl fmt::Debug for PyObjectPayload { PyObjectPayload::Integer { ref value } => write!(f, "int {}", value), PyObjectPayload::MemoryView { ref obj } => write!(f, "bytes/bytearray {:?}", obj), PyObjectPayload::Sequence { .. } => write!(f, "list or tuple"), - PyObjectPayload::Dict { .. } => write!(f, "dict"), PyObjectPayload::WeakRef { .. } => write!(f, "weakref"), PyObjectPayload::Iterator { .. } => write!(f, "iterator"), PyObjectPayload::EnumerateIterator { .. } => write!(f, "enumerate"), From f4ba94a397b9b3c7919767cf429d1e6976b2df2d Mon Sep 17 00:00:00 2001 From: holygits Date: Tue, 5 Mar 2019 19:03:25 +1300 Subject: [PATCH 155/380] Fix formatting --- vm/src/obj/objbytearray.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index 295d98be1d..77b1c14ebc 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -1,8 +1,8 @@ //! Implementation of the python bytearray object. use std::cell::RefCell; -use std::ops::{Deref, DerefMut}; use std::fmt::Write; +use std::ops::{Deref, DerefMut}; use crate::pyobject::{ PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, From 05929b3d7bac9b91694aa35aa9ba280b0612eef1 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Tue, 5 Mar 2019 07:19:00 +0100 Subject: [PATCH 156/380] Add ellipsis type, test and do rustfmt. --- parser/src/ast.rs | 2 +- parser/src/lexer.rs | 3 ++- tests/snippets/ellipsis.py | 8 ++++++++ vm/src/obj/mod.rs | 1 + vm/src/obj/objellipsis.rs | 22 ++++++++++++++++++++++ vm/src/pyobject.rs | 11 ++++++----- vm/src/stdlib/ast.rs | 4 +--- 7 files changed, 41 insertions(+), 10 deletions(-) create mode 100644 tests/snippets/ellipsis.py create mode 100644 vm/src/obj/objellipsis.rs diff --git a/parser/src/ast.rs b/parser/src/ast.rs index df035c696f..f20d3a9507 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -260,7 +260,7 @@ impl Expression { Lambda { .. } => "lambda", IfExpression { .. } => "conditional expression", True | False | None => "keyword", - Ellipsis => "ellipsis" + Ellipsis => "ellipsis", } } } diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index 83f0f0c2f8..8e14704848 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -1012,7 +1012,8 @@ where let tok_start = self.get_pos(); self.next_char(); if let (Some('.'), Some('.')) = (&self.chr0, &self.chr1) { - self.next_char();self.next_char(); + self.next_char(); + self.next_char(); let tok_end = self.get_pos(); return Some(Ok((tok_start, Tok::Ellipsis, tok_end))); } else { diff --git a/tests/snippets/ellipsis.py b/tests/snippets/ellipsis.py new file mode 100644 index 0000000000..88b1c965d8 --- /dev/null +++ b/tests/snippets/ellipsis.py @@ -0,0 +1,8 @@ + + +a = ... +b = ... +c = type(a)() # Test singleton behavior + +assert a is b +assert b is c diff --git a/vm/src/obj/mod.rs b/vm/src/obj/mod.rs index f5dff7563c..95aacba417 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 objellipsis; pub mod objenumerate; pub mod objfilter; pub mod objfloat; diff --git a/vm/src/obj/objellipsis.rs b/vm/src/obj/objellipsis.rs new file mode 100644 index 0000000000..48120ea771 --- /dev/null +++ b/vm/src/obj/objellipsis.rs @@ -0,0 +1,22 @@ +use crate::pyobject::{PyContext, PyFuncArgs, PyResult, TypeProtocol}; +use crate::vm::VirtualMachine; + +pub fn init(context: &PyContext) { + let ellipsis_type = &context.ellipsis_type; + context.set_attr(ellipsis_type, "__new__", context.new_rustfunc(ellipsis_new)); + context.set_attr( + ellipsis_type, + "__repr__", + context.new_rustfunc(ellipsis_repr), + ); +} + +fn ellipsis_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(_cls, None)]); + Ok(vm.ctx.ellipsis()) +} + +fn ellipsis_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(_cls, None)]); + Ok(vm.new_str("Ellipsis".to_string())) +} diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index f08c33a1a5..58976c2d21 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -19,6 +19,7 @@ use crate::obj::objbytes; use crate::obj::objcode; use crate::obj::objcomplex::{self, PyComplex}; use crate::obj::objdict; +use crate::obj::objellipsis; use crate::obj::objenumerate; use crate::obj::objfilter; use crate::obj::objfloat::{self, PyFloat}; @@ -115,6 +116,7 @@ pub struct PyContext { pub classmethod_type: PyObjectRef, pub code_type: PyObjectRef, pub dict_type: PyObjectRef, + pub ellipsis_type: PyObjectRef, pub enumerate_type: PyObjectRef, pub filter_type: PyObjectRef, pub float_type: PyObjectRef, @@ -208,6 +210,7 @@ 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 ellipsis_type = create_type("EllipsisType", &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); @@ -224,11 +227,7 @@ impl PyContext { create_type("NoneType", &type_type, &object_type, &dict_type), ); - // TODO: implement proper ellipsis class? - let ellipsis = PyObject::new( - PyObjectPayload::None, - create_type("EllipsisType", &type_type, &object_type, &dict_type), - ); + let ellipsis = PyObject::new(PyObjectPayload::None, ellipsis_type.clone()); let not_implemented = PyObject::new( PyObjectPayload::NotImplemented, @@ -264,6 +263,7 @@ impl PyContext { false_value, tuple_type, iter_type, + ellipsis_type, enumerate_type, filter_type, map_type, @@ -308,6 +308,7 @@ impl PyContext { objsuper::init(&context); objtuple::init(&context); objiter::init(&context); + objellipsis::init(&context); objenumerate::init(&context); objfilter::init(&context); objmap::init(&context); diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index 93b70b28d0..2c898a656b 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -395,9 +395,7 @@ fn expression_to_ast(ctx: &PyContext, expression: &ast::Expression) -> PyObjectR node } - ast::Expression::Ellipsis => { - create_node(ctx, "Ellipsis") - } + ast::Expression::Ellipsis => create_node(ctx, "Ellipsis"), ast::Expression::List { elements } => { let node = create_node(ctx, "List"); From 0292146a4217b2742ce185aa0d374549b07b98af Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Tue, 5 Mar 2019 17:28:15 +0100 Subject: [PATCH 157/380] Add subscript list in grammar. --- parser/src/python.lalrpop | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/parser/src/python.lalrpop b/parser/src/python.lalrpop index 629f9cc26e..b9c8672c23 100644 --- a/parser/src/python.lalrpop +++ b/parser/src/python.lalrpop @@ -791,10 +791,24 @@ Power: ast::Expression = { AtomExpr: ast::Expression = { => e, "(" ")" => ast::Expression::Call { function: Box::new(f), args: a.0, keywords: a.1 }, - "[" "]" => ast::Expression::Subscript { a: Box::new(e), b: Box::new(s) }, + "[" "]" => ast::Expression::Subscript { a: Box::new(e), b: Box::new(s) }, "." => ast::Expression::Attribute { value: Box::new(e), name: n }, }; +SubscriptList: ast::Expression = { + ","? => { + if s2.is_empty() { + s1 + } else { + let mut dims = vec![s1]; + for x in s2 { + dims.push(x.1) + } + ast::Expression::Tuple { elements: dims } + } + } +}; + Subscript: ast::Expression = { => e, ":" => { From c1a5e31c3ca0213514a602238c0daf202cc47590 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Tue, 5 Mar 2019 20:24:48 +0100 Subject: [PATCH 158/380] Extend re module. --- tests/snippets/test_re.py | 6 +- vm/src/stdlib/re.rs | 206 ++++++++++++++++++++++++++++++++------ 2 files changed, 183 insertions(+), 29 deletions(-) diff --git a/tests/snippets/test_re.py b/tests/snippets/test_re.py index 24a2ee3b4a..d9914b38d5 100644 --- a/tests/snippets/test_re.py +++ b/tests/snippets/test_re.py @@ -4,5 +4,9 @@ haystack = "Hello world" needle = 'ello' -print(re.search(needle, haystack)) +mo = re.search(needle, haystack) +print(mo) +assert isinstance(mo, re.Match) +assert mo.start() == 1 +assert mo.end() == 5 diff --git a/vm/src/stdlib/re.rs b/vm/src/stdlib/re.rs index b262ff76ae..b257bd8183 100644 --- a/vm/src/stdlib/re.rs +++ b/vm/src/stdlib/re.rs @@ -5,19 +5,59 @@ * system. */ -extern crate regex; -use self::regex::Regex; +// extern crate regex; +use crate::import; +use regex::{Match, Regex}; +use std::path::PathBuf; use crate::obj::objstr; -use crate::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; +use crate::pyobject::{ + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, +}; use crate::VirtualMachine; +/// Create the python `re` module with all its members. +pub fn mk_module(ctx: &PyContext) -> PyObjectRef { + let match_type = py_class!(ctx, "Match", ctx.object(), { + "start" => ctx.new_rustfunc(match_start), + "end" => ctx.new_rustfunc(match_end) + }); + + let pattern_type = py_class!(ctx, "Pattern", ctx.object(), { + "match" => ctx.new_rustfunc(pattern_match), + "search" => ctx.new_rustfunc(pattern_search) + }); + + py_module!(ctx, "re", { + "compile" => ctx.new_rustfunc(re_compile), + "Match" => match_type, + "match" => ctx.new_rustfunc(re_match), + "Pattern" => pattern_type, + "search" => ctx.new_rustfunc(re_search) + }) +} + +/// Implement re.match +/// See also: +/// https://docs.python.org/3/library/re.html#re.match fn re_match(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - // TODO: - error!("TODO: implement match"); - re_search(vm, args) + arg_check!( + vm, + args, + required = [ + (pattern, Some(vm.ctx.str_type())), + (string, Some(vm.ctx.str_type())) + ] + ); + let regex = make_regex(vm, pattern)?; + let search_text = objstr::get_value(string); + + do_match(vm, ®ex, search_text) } +/// Implement re.search +/// See also: +/// https://docs.python.org/3/library/re.html#re.search fn re_search(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -28,33 +68,143 @@ fn re_search(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ] ); - let pattern_str = objstr::get_value(&pattern); - let search_text = objstr::get_value(&string); + // let pattern_str = objstr::get_value(&pattern); + let regex = make_regex(vm, pattern)?; + let search_text = objstr::get_value(string); + + do_search(vm, ®ex, search_text) +} + +fn do_match(vm: &mut VirtualMachine, regex: &Regex, search_text: String) -> PyResult { + // TODO: implement match! + do_search(vm, regex, search_text) +} + +fn do_search(vm: &mut VirtualMachine, regex: &Regex, search_text: String) -> PyResult { + match regex.find(&search_text) { + None => Ok(vm.get_none()), + Some(result) => create_match(vm, &result), + } +} + +fn make_regex(vm: &mut VirtualMachine, pattern: &PyObjectRef) -> PyResult { + let pattern_str = objstr::get_value(pattern); match Regex::new(&pattern_str) { - Ok(regex) => { - // Now use regex to search: - match regex.find(&search_text) { - None => Ok(vm.get_none()), - Some(result) => { - // Return match object: - // TODO: implement match object - // TODO: how to refer to match object defined in this - // module? - Ok(vm.ctx.new_str(result.as_str().to_string())) - } - } - } + Ok(regex) => Ok(regex), Err(err) => Err(vm.new_value_error(format!("Error in regex: {:?}", err))), } } -pub fn mk_module(ctx: &PyContext) -> PyObjectRef { - let match_type = py_class!(ctx, "Match", ctx.object(), {}); +/// Inner data for a match object. +struct PyMatch { + start: usize, + end: usize, +} - py_module!(ctx, "re", { - "Match" => match_type, - "match" => ctx.new_rustfunc(re_match), - "search" => ctx.new_rustfunc(re_search) - }) +/// Take a found regular expression and convert it to proper match object. +fn create_match(vm: &mut VirtualMachine, match_value: &Match) -> PyResult { + // Return match object: + // TODO: implement match object + // TODO: how to refer to match object defined in this + let module = import::import_module(vm, PathBuf::default(), "re").unwrap(); + let match_class = vm.ctx.get_attr(&module, "Match").unwrap(); + + // let mo = vm.invoke(match_class, PyFuncArgs::default())?; + // let txt = vm.ctx.new_str(result.as_str().to_string()); + // vm.ctx.set_attr(&mo, "str", txt); + let match_value = PyMatch { + start: match_value.start(), + end: match_value.end(), + }; + + Ok(PyObject::new( + PyObjectPayload::AnyRustValue { + value: Box::new(match_value), + }, + match_class.clone(), + )) +} + +/// Compile a regular expression into a Pattern object. +/// See also: +/// https://docs.python.org/3/library/re.html#re.compile +fn re_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(pattern, Some(vm.ctx.str_type()))] // TODO: flags=0 + ); + + let regex = make_regex(vm, pattern)?; + // TODO: retrieval of this module is akward: + let module = import::import_module(vm, PathBuf::default(), "re").unwrap(); + let pattern_class = vm.ctx.get_attr(&module, "Pattern").unwrap(); + + Ok(PyObject::new( + PyObjectPayload::AnyRustValue { + value: Box::new(regex), + }, + pattern_class.clone(), + )) +} + +fn pattern_match(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, None), (text, Some(vm.ctx.str_type()))] + ); + + let regex = get_regex(zelf); + let search_text = objstr::get_value(text); + do_match(vm, ®ex, search_text) +} + +fn pattern_search(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, None), (text, Some(vm.ctx.str_type()))] + ); + + let regex = get_regex(zelf); + let search_text = objstr::get_value(text); + do_search(vm, ®ex, search_text) +} + +/// Returns start of match +/// see: https://docs.python.org/3/library/re.html#re.Match.start +fn match_start(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, None)]); + // TODO: implement groups + let m = get_match(zelf); + Ok(vm.new_int(m.start)) +} + +fn match_end(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, None)]); + // TODO: implement groups + let m = get_match(zelf); + Ok(vm.new_int(m.end)) +} + +/// Retrieve inner rust regex from python object: +fn get_regex<'a>(obj: &'a PyObjectRef) -> &'a Regex { + if let PyObjectPayload::AnyRustValue { ref value } = obj.payload { + if let Some(regex) = value.downcast_ref::() { + return regex; + } + } + panic!("Inner error getting regex {:?}", obj); +} + +/// Retrieve inner rust match from python object: +fn get_match<'a>(obj: &'a PyObjectRef) -> &'a PyMatch { + if let PyObjectPayload::AnyRustValue { ref value } = obj.payload { + if let Some(value) = value.downcast_ref::() { + return value; + } + } + panic!("Inner error getting match {:?}", obj); } From e4e8b135efbcfbd14db13fbd3cc350e85e70b839 Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Sun, 3 Mar 2019 21:13:00 +0100 Subject: [PATCH 159/380] Convert a bunch of string methods to new-args-style. Mostly skipped the ones dealing with ints. --- vm/src/function.rs | 7 + vm/src/obj/objstr.rs | 795 +++++++++++++++++-------------------------- 2 files changed, 326 insertions(+), 476 deletions(-) diff --git a/vm/src/function.rs b/vm/src/function.rs index 131fce129d..2194dac9c8 100644 --- a/vm/src/function.rs +++ b/vm/src/function.rs @@ -1,3 +1,4 @@ +use std::fmt; use std::marker::PhantomData; use std::ops::Deref; @@ -81,3 +82,9 @@ impl IntoPyObject for PyRef { Ok(self.obj) } } + +impl fmt::Display for PyRef { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.obj.fmt(f) + } +} diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 2283a0e8d6..7fc8fa71bc 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -4,8 +4,8 @@ use super::objtype; use crate::format::{FormatParseError, FormatPart, FormatString}; use crate::function::PyRef; use crate::pyobject::{ - OptArg, PyContext, PyFuncArgs, PyIterable, PyObjectPayload, PyObjectPayload2, PyObjectRef, - PyResult, TypeProtocol, + IntoPyObject, OptArg, PyContext, PyFuncArgs, PyIterable, PyObjectPayload, PyObjectPayload2, + PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use num_traits::ToPrimitive; @@ -25,7 +25,137 @@ pub struct PyString { } impl PyString { - pub fn endswith( + fn add(zelf: PyRef, rhs: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&rhs, &vm.ctx.str_type()) { + Ok(format!("{}{}", zelf.value, get_value(&rhs))) + } else { + Err(vm.new_type_error(format!("Cannot add {} and {}", zelf, rhs))) + } + } + + fn eq(zelf: PyRef, rhs: PyObjectRef, vm: &mut VirtualMachine) -> bool { + if objtype::isinstance(&rhs, &vm.ctx.str_type()) { + zelf.value == get_value(&rhs) + } else { + false + } + } + + fn contains(zelf: PyRef, needle: PyRef, _vm: &mut VirtualMachine) -> bool { + zelf.value.contains(&needle.value) + } + + fn getitem(zelf: PyRef, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + subscript(vm, &zelf.value, needle) + } + + fn gt(zelf: PyRef, rhs: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&rhs, &vm.ctx.str_type()) { + Ok(zelf.value > get_value(&rhs)) + } else { + Err(vm.new_type_error(format!("Cannot compare {} and {}", zelf, rhs))) + } + } + + fn ge(zelf: PyRef, rhs: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&rhs, &vm.ctx.str_type()) { + Ok(zelf.value >= get_value(&rhs)) + } else { + Err(vm.new_type_error(format!("Cannot compare {} and {}", zelf, rhs))) + } + } + + fn lt(zelf: PyRef, rhs: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&rhs, &vm.ctx.str_type()) { + Ok(zelf.value < get_value(&rhs)) + } else { + Err(vm.new_type_error(format!("Cannot compare {} and {}", zelf, rhs))) + } + } + + fn le(zelf: PyRef, rhs: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&rhs, &vm.ctx.str_type()) { + Ok(zelf.value <= get_value(&rhs)) + } else { + Err(vm.new_type_error(format!("Cannot compare {} and {}", zelf, rhs))) + } + } + + fn hash(zelf: PyRef, _vm: &mut VirtualMachine) -> usize { + let mut hasher = std::collections::hash_map::DefaultHasher::new(); + zelf.value.hash(&mut hasher); + hasher.finish() as usize + } + + fn len(zelf: PyRef, _vm: &mut VirtualMachine) -> usize { + zelf.value.chars().count() + } + + fn str(zelf: PyRef, _vm: &mut VirtualMachine) -> PyRef { + zelf + } + + fn repr(zelf: PyRef, _vm: &mut VirtualMachine) -> String { + let value = &zelf.value; + let quote_char = if count_char(value, '\'') > count_char(value, '"') { + '"' + } else { + '\'' + }; + let mut formatted = String::new(); + formatted.push(quote_char); + for c in value.chars() { + if c == quote_char || c == '\\' { + formatted.push('\\'); + formatted.push(c); + } else if c == '\n' { + formatted.push('\\'); + formatted.push('n'); + } else if c == '\t' { + formatted.push('\\'); + formatted.push('t'); + } else if c == '\r' { + formatted.push('\\'); + formatted.push('r'); + } else { + formatted.push(c); + } + } + formatted.push(quote_char); + formatted + } + + fn lower(zelf: PyRef, _vm: &mut VirtualMachine) -> String { + zelf.value.to_lowercase() + } + + // casefold is much more aggressive than lower + fn casefold(zelf: PyRef, _vm: &mut VirtualMachine) -> String { + caseless::default_case_fold_str(&zelf.value) + } + + fn upper(zelf: PyRef, _vm: &mut VirtualMachine) -> String { + zelf.value.to_uppercase() + } + + fn capitalize(zelf: PyRef, _vm: &mut VirtualMachine) -> String { + let (first_part, lower_str) = zelf.value.split_at(1); + format!("{}{}", first_part.to_uppercase(), lower_str) + } + + fn strip(zelf: PyRef, _vm: &mut VirtualMachine) -> String { + zelf.value.trim().to_string() + } + + fn lstrip(zelf: PyRef, _vm: &mut VirtualMachine) -> String { + zelf.value.trim_start().to_string() + } + + fn rstrip(zelf: PyRef, _vm: &mut VirtualMachine) -> String { + zelf.value.trim_end().to_string() + } + + fn endswith( zelf: PyRef, suffix: PyRef, start: OptArg, @@ -37,7 +167,7 @@ impl PyString { zelf.value[start..end].ends_with(&suffix.value) } - pub fn startswith( + fn startswith( zelf: PyRef, prefix: PyRef, start: OptArg, @@ -49,23 +179,104 @@ impl PyString { zelf.value[start..end].starts_with(&prefix.value) } - fn upper(zelf: PyRef, _vm: &mut VirtualMachine) -> PyString { - PyString { - value: zelf.value.to_uppercase(), + fn isalnum(zelf: PyRef, _vm: &mut VirtualMachine) -> bool { + !zelf.value.is_empty() && zelf.value.chars().all(char::is_alphanumeric) + } + + fn isnumeric(zelf: PyRef, _vm: &mut VirtualMachine) -> bool { + !zelf.value.is_empty() && zelf.value.chars().all(char::is_numeric) + } + + fn isdigit(zelf: PyRef, _vm: &mut VirtualMachine) -> bool { + // python's isdigit also checks if exponents are digits, these are the unicodes for exponents + let valid_unicodes: [u16; 10] = [ + 0x2070, 0x00B9, 0x00B2, 0x00B3, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079, + ]; + + if zelf.value.is_empty() { + false + } else { + zelf.value + .chars() + .filter(|c| !c.is_digit(10)) + .all(|c| valid_unicodes.contains(&(c as u16))) } } - fn lower(zelf: PyRef, _vm: &mut VirtualMachine) -> PyString { - PyString { - value: zelf.value.to_lowercase(), + fn isdecimal(zelf: PyRef, _vm: &mut VirtualMachine) -> bool { + if zelf.value.is_empty() { + false + } else { + zelf.value.chars().all(|c| c.is_ascii_digit()) } } + fn title(zelf: PyRef, _vm: &mut VirtualMachine) -> String { + make_title(&zelf.value) + } + + fn swapcase(zelf: PyRef, _vm: &mut VirtualMachine) -> String { + let mut swapped_str = String::with_capacity(zelf.value.len()); + for c in zelf.value.chars() { + // to_uppercase returns an iterator, to_ascii_uppercase returns the char + if c.is_lowercase() { + swapped_str.push(c.to_ascii_uppercase()); + } else if c.is_uppercase() { + swapped_str.push(c.to_ascii_lowercase()); + } else { + swapped_str.push(c); + } + } + swapped_str + } + + fn isalpha(zelf: PyRef, _vm: &mut VirtualMachine) -> bool { + !zelf.value.is_empty() && zelf.value.chars().all(char::is_alphanumeric) + } + + // cpython's isspace ignores whitespace, including \t and \n, etc, unless the whole string is empty + // which is why isspace is using is_ascii_whitespace. Same for isupper & islower + fn isspace(zelf: PyRef, _vm: &mut VirtualMachine) -> bool { + !zelf.value.is_empty() && zelf.value.chars().all(|c| c.is_ascii_whitespace()) + } + + fn isupper(zelf: PyRef, _vm: &mut VirtualMachine) -> bool { + !zelf.value.is_empty() + && zelf + .value + .chars() + .filter(|x| !x.is_ascii_whitespace()) + .all(char::is_uppercase) + } + + fn islower(zelf: PyRef, _vm: &mut VirtualMachine) -> bool { + !zelf.value.is_empty() + && zelf + .value + .chars() + .filter(|x| !x.is_ascii_whitespace()) + .all(char::is_lowercase) + } + + fn isascii(zelf: PyRef, _vm: &mut VirtualMachine) -> bool { + !zelf.value.is_empty() && zelf.value.chars().all(|c| c.is_ascii()) + } + + // doesn't implement keep new line delimiter just yet + fn splitlines(zelf: PyRef, vm: &mut VirtualMachine) -> PyObjectRef { + let elements = zelf + .value + .split('\n') + .map(|e| vm.ctx.new_str(e.to_string())) + .collect(); + vm.ctx.new_list(elements) + } + fn join( zelf: PyRef, iterable: PyIterable>, vm: &mut VirtualMachine, - ) -> PyResult { + ) -> PyResult { let mut joined = String::new(); for (idx, elem) in iterable.iter(vm)?.enumerate() { @@ -76,7 +287,61 @@ impl PyString { joined.push_str(&elem.value) } - Ok(PyString { value: joined }) + Ok(joined) + } + + fn partition(zelf: PyRef, sub: PyRef, vm: &mut VirtualMachine) -> PyObjectRef { + let value = &zelf.value; + let sub = &sub.value; + let mut new_tup = Vec::new(); + if value.contains(sub) { + new_tup = value + .splitn(2, sub) + .map(|s| vm.ctx.new_str(s.to_string())) + .collect(); + new_tup.insert(1, vm.ctx.new_str(sub.clone())); + } else { + new_tup.push(vm.ctx.new_str(value.clone())); + new_tup.push(vm.ctx.new_str("".to_string())); + new_tup.push(vm.ctx.new_str("".to_string())); + } + vm.ctx.new_tuple(new_tup) + } + + fn rpartition(zelf: PyRef, sub: PyRef, vm: &mut VirtualMachine) -> PyObjectRef { + let value = &zelf.value; + let sub = &sub.value; + let mut new_tup = Vec::new(); + if value.contains(sub) { + new_tup = value + .rsplitn(2, sub) + .map(|s| vm.ctx.new_str(s.to_string())) + .collect(); + new_tup.swap(0, 1); // so it's in the right order + new_tup.insert(1, vm.ctx.new_str(sub.clone())); + } else { + new_tup.push(vm.ctx.new_str(value.clone())); + new_tup.push(vm.ctx.new_str("".to_string())); + new_tup.push(vm.ctx.new_str("".to_string())); + } + vm.ctx.new_tuple(new_tup) + } + + fn isidentifier(zelf: PyRef, _vm: &mut VirtualMachine) -> bool { + let value = &zelf.value; + // a string is not an identifier if it has whitespace or starts with a number + if !value.chars().any(|c| c.is_ascii_whitespace()) + && !value.chars().nth(0).unwrap().is_digit(10) + { + for c in value.chars() { + if c != "_".chars().nth(0).unwrap() && !c.is_digit(10) && !c.is_alphabetic() { + return false; + } + } + true + } else { + false + } } } @@ -86,94 +351,69 @@ impl PyObjectPayload2 for PyString { } } +impl IntoPyObject for String { + fn into_pyobject(self, ctx: &PyContext) -> PyResult { + Ok(ctx.new_str(self)) + } +} + +#[rustfmt::skip] // to avoid line splitting pub fn init(context: &PyContext) { 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( - &str_type, - "__contains__", - context.new_rustfunc(str_contains), - ); - 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, "__add__", context.new_rustfunc(PyString::add)); + context.set_attr(&str_type, "__eq__", context.new_rustfunc(PyString::eq)); + context.set_attr(&str_type, "__contains__", context.new_rustfunc(PyString::contains)); + context.set_attr(&str_type, "__getitem__", context.new_rustfunc(PyString::getitem)); + context.set_attr(&str_type, "__gt__", context.new_rustfunc(PyString::gt)); + context.set_attr(&str_type, "__ge__", context.new_rustfunc(PyString::ge)); + context.set_attr(&str_type, "__lt__", context.new_rustfunc(PyString::lt)); + context.set_attr(&str_type, "__le__", context.new_rustfunc(PyString::le)); + context.set_attr(&str_type, "__hash__", context.new_rustfunc(PyString::hash)); + context.set_attr(&str_type, "__len__", context.new_rustfunc(PyString::len)); context.set_attr(&str_type, "__mul__", context.new_rustfunc(str_mul)); context.set_attr(&str_type, "__new__", context.new_rustfunc(str_new)); - context.set_attr(&str_type, "__str__", context.new_rustfunc(str_str)); - context.set_attr(&str_type, "__repr__", context.new_rustfunc(str_repr)); + context.set_attr(&str_type, "__str__", context.new_rustfunc(PyString::str)); + context.set_attr(&str_type, "__repr__", context.new_rustfunc(PyString::repr)); context.set_attr(&str_type, "format", context.new_rustfunc(str_format)); context.set_attr(&str_type, "lower", context.new_rustfunc(PyString::lower)); - context.set_attr(&str_type, "casefold", context.new_rustfunc(str_casefold)); + context.set_attr(&str_type, "casefold", context.new_rustfunc(PyString::casefold)); context.set_attr(&str_type, "upper", context.new_rustfunc(PyString::upper)); - context.set_attr( - &str_type, - "capitalize", - context.new_rustfunc(str_capitalize), - ); + context.set_attr(&str_type, "capitalize", context.new_rustfunc(PyString::capitalize)); context.set_attr(&str_type, "split", context.new_rustfunc(str_split)); context.set_attr(&str_type, "rsplit", context.new_rustfunc(str_rsplit)); - context.set_attr(&str_type, "strip", context.new_rustfunc(str_strip)); - context.set_attr(&str_type, "lstrip", context.new_rustfunc(str_lstrip)); - context.set_attr(&str_type, "rstrip", context.new_rustfunc(str_rstrip)); - context.set_attr( - &str_type, - "endswith", - context.new_rustfunc(PyString::endswith), - ); - context.set_attr( - &str_type, - "startswith", - context.new_rustfunc(PyString::startswith), - ); - 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)); + context.set_attr(&str_type, "strip", context.new_rustfunc(PyString::strip)); + context.set_attr(&str_type, "lstrip", context.new_rustfunc(PyString::lstrip)); + context.set_attr(&str_type, "rstrip", context.new_rustfunc(PyString::rstrip)); + context.set_attr(&str_type, "endswith", context.new_rustfunc(PyString::endswith)); + context.set_attr(&str_type, "startswith", context.new_rustfunc(PyString::startswith)); + context.set_attr(&str_type, "isalnum", context.new_rustfunc(PyString::isalnum)); + context.set_attr(&str_type, "isnumeric", context.new_rustfunc(PyString::isnumeric)); + context.set_attr(&str_type, "isdigit", context.new_rustfunc(PyString::isdigit)); + context.set_attr(&str_type, "isdecimal", context.new_rustfunc(PyString::isdecimal)); + context.set_attr(&str_type, "title", context.new_rustfunc(PyString::title)); + context.set_attr(&str_type, "swapcase", context.new_rustfunc(PyString::swapcase)); + context.set_attr(&str_type, "isalpha", context.new_rustfunc(PyString::isalpha)); context.set_attr(&str_type, "replace", context.new_rustfunc(str_replace)); context.set_attr(&str_type, "center", context.new_rustfunc(str_center)); - context.set_attr(&str_type, "isspace", context.new_rustfunc(str_isspace)); - context.set_attr(&str_type, "isupper", context.new_rustfunc(str_isupper)); - context.set_attr(&str_type, "islower", context.new_rustfunc(str_islower)); - context.set_attr(&str_type, "isascii", context.new_rustfunc(str_isascii)); - context.set_attr( - &str_type, - "splitlines", - context.new_rustfunc(str_splitlines), - ); + context.set_attr(&str_type, "isspace", context.new_rustfunc(PyString::isspace)); + context.set_attr(&str_type, "isupper", context.new_rustfunc(PyString::isupper)); + context.set_attr(&str_type, "islower", context.new_rustfunc(PyString::islower)); + context.set_attr(&str_type, "isascii", context.new_rustfunc(PyString::isascii)); + context.set_attr(&str_type, "splitlines", context.new_rustfunc(PyString::splitlines)); context.set_attr(&str_type, "join", context.new_rustfunc(PyString::join)); context.set_attr(&str_type, "find", context.new_rustfunc(str_find)); context.set_attr(&str_type, "rfind", context.new_rustfunc(str_rfind)); context.set_attr(&str_type, "index", context.new_rustfunc(str_index)); context.set_attr(&str_type, "rindex", context.new_rustfunc(str_rindex)); - context.set_attr(&str_type, "partition", context.new_rustfunc(str_partition)); - context.set_attr( - &str_type, - "rpartition", - context.new_rustfunc(str_rpartition), - ); + context.set_attr(&str_type, "partition", context.new_rustfunc(PyString::partition)); + context.set_attr(&str_type, "rpartition", context.new_rustfunc(PyString::rpartition)); context.set_attr(&str_type, "istitle", context.new_rustfunc(str_istitle)); context.set_attr(&str_type, "count", context.new_rustfunc(str_count)); context.set_attr(&str_type, "zfill", context.new_rustfunc(str_zfill)); context.set_attr(&str_type, "ljust", context.new_rustfunc(str_ljust)); context.set_attr(&str_type, "rjust", context.new_rustfunc(str_rjust)); - context.set_attr( - &str_type, - "expandtabs", - context.new_rustfunc(str_expandtabs), - ); - context.set_attr( - &str_type, - "isidentifier", - context.new_rustfunc(str_isidentifier), - ); + context.set_attr(&str_type, "expandtabs", context.new_rustfunc(str_expandtabs)); + context.set_attr(&str_type, "isidentifier", context.new_rustfunc(PyString::isidentifier)); } pub fn get_value(obj: &PyObjectRef) -> String { @@ -184,136 +424,10 @@ pub fn borrow_value(obj: &PyObjectRef) -> &str { &obj.payload::().unwrap().value } -fn str_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(a, Some(vm.ctx.str_type())), (b, None)] - ); - - let result = if objtype::isinstance(b, &vm.ctx.str_type()) { - get_value(a) == get_value(b) - } else { - false - }; - Ok(vm.ctx.new_bool(result)) -} - -fn str_gt(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, i2))) - } -} - -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, i2))) - } -} - -fn str_lt(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, i2))) - } -} - -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, i2))) - } -} - -fn str_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - Ok(s.clone()) -} - fn count_char(s: &str, c: char) -> usize { s.chars().filter(|x| *x == c).count() } -fn str_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let value = get_value(s); - let quote_char = if count_char(&value, '\'') > count_char(&value, '"') { - '"' - } else { - '\'' - }; - let mut formatted = String::new(); - formatted.push(quote_char); - for c in value.chars() { - if c == quote_char || c == '\\' { - formatted.push('\\'); - formatted.push(c); - } else if c == '\n' { - formatted.push('\\'); - formatted.push('n'); - } else if c == '\t' { - formatted.push('\\'); - formatted.push('t'); - } else if c == '\r' { - formatted.push('\\'); - formatted.push('r'); - } else { - formatted.push(c); - } - } - formatted.push(quote_char); - Ok(vm.ctx.new_str(formatted)) -} - -fn str_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(s, Some(vm.ctx.str_type())), (s2, None)] - ); - if objtype::isinstance(s2, &vm.ctx.str_type()) { - Ok(vm - .ctx - .new_str(format!("{}{}", get_value(&s), get_value(&s2)))) - } else { - Err(vm.new_type_error(format!("Cannot add {} and {}", s, s2))) - } -} - fn str_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if args.args.is_empty() { return Err( @@ -409,21 +523,6 @@ fn perform_format( Ok(vm.ctx.new_str(final_string)) } -fn str_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(zelf, Some(vm.ctx.str_type()))]); - let value = get_value(zelf); - let mut hasher = std::collections::hash_map::DefaultHasher::new(); - value.hash(&mut hasher); - let hash = hasher.finish(); - 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())) -} - fn str_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -443,14 +542,6 @@ fn str_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -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(), lower_str); - Ok(vm.ctx.new_str(capitalized)) -} - fn str_rsplit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -503,87 +594,6 @@ fn str_split(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_list(elements)) } -fn str_strip(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let value = get_value(&s).trim().to_string(); - Ok(vm.ctx.new_str(value)) -} - -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_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_end().to_string(); - Ok(vm.ctx.new_str(value)) -} - -fn str_isidentifier(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_identifier: bool = true; - // a string is not an identifier if it has whitespace or starts with a number - if !value.chars().any(|c| c.is_ascii_whitespace()) - && !value.chars().nth(0).unwrap().is_digit(10) - { - for c in value.chars() { - if c != "_".chars().nth(0).unwrap() && !c.is_digit(10) && !c.is_alphabetic() { - is_identifier = false; - } - } - } else { - is_identifier = false; - } - Ok(vm.ctx.new_bool(is_identifier)) -} - -// cpython's isspace ignores whitespace, including \t and \n, etc, unless the whole string is empty -// 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 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 value = get_value(&s); - Ok(vm.ctx.new_bool( - !value.is_empty() - && value - .chars() - .filter(|x| !x.is_ascii_whitespace()) - .all(char::is_uppercase), - )) -} - -fn str_islower(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let value = get_value(&s); - Ok(vm.ctx.new_bool( - !value.is_empty() - && value - .chars() - .filter(|x| !x.is_ascii_whitespace()) - .all(char::is_lowercase), - )) -} - -// 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) - .split('\n') - .map(|e| vm.ctx.new_str(e.to_string())) - .collect(); - Ok(vm.ctx.new_list(elements)) -} - fn str_zfill(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -668,31 +678,6 @@ fn str_find(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_int(ind)) } -// casefold is much more aggressive than lower -fn str_casefold(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let value = get_value(&s); - let folded_str: String = caseless::default_case_fold_str(&value); - Ok(vm.ctx.new_str(folded_str)) -} - -fn str_swapcase(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let value = get_value(&s); - let mut swapped_str = String::with_capacity(value.len()); - for c in value.chars() { - // to_uppercase returns an iterator, to_ascii_uppercase returns the char - if c.is_lowercase() { - swapped_str.push(c.to_ascii_uppercase()); - } else if c.is_uppercase() { - swapped_str.push(c.to_ascii_lowercase()); - } else { - swapped_str.push(c); - } - } - Ok(vm.ctx.new_str(swapped_str)) -} - fn str_replace(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -748,58 +733,6 @@ fn str_expandtabs(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_str(expanded_str)) } -fn str_rpartition(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(s, Some(vm.ctx.str_type())), (sub, Some(vm.ctx.str_type()))] - ); - let value = get_value(&s); - let sub = get_value(&sub); - let mut new_tup = Vec::new(); - if value.contains(&sub) { - new_tup = value - .rsplitn(2, &sub) - .map(|s| vm.ctx.new_str(s.to_string())) - .collect(); - new_tup.swap(0, 1); // so it's in the right order - new_tup.insert(1, vm.ctx.new_str(sub)); - } else { - new_tup.push(vm.ctx.new_str(value)); - new_tup.push(vm.ctx.new_str("".to_string())); - new_tup.push(vm.ctx.new_str("".to_string())); - } - Ok(vm.ctx.new_tuple(new_tup)) -} - -fn str_partition(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(s, Some(vm.ctx.str_type())), (sub, Some(vm.ctx.str_type()))] - ); - let value = get_value(&s); - let sub = get_value(&sub); - let mut new_tup = Vec::new(); - if value.contains(&sub) { - new_tup = value - .splitn(2, &sub) - .map(|s| vm.ctx.new_str(s.to_string())) - .collect(); - new_tup.insert(1, vm.ctx.new_str(sub)); - } else { - new_tup.push(vm.ctx.new_str(value)); - new_tup.push(vm.ctx.new_str("".to_string())); - new_tup.push(vm.ctx.new_str("".to_string())); - } - Ok(vm.ctx.new_tuple(new_tup)) -} - -fn str_title(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - Ok(vm.ctx.new_str(make_title(&get_value(&s)))) -} - fn str_rjust(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -889,36 +822,6 @@ fn str_center(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_str(new_str)) } -fn str_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (s, Some(vm.ctx.str_type())), - (needle, Some(vm.ctx.str_type())) - ] - ); - let value = get_value(&s); - let needle = get_value(&needle); - Ok(vm.ctx.new_bool(value.contains(needle.as_str()))) -} - -fn str_isalnum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let value = get_value(&s); - Ok(vm - .ctx - .new_bool(!value.is_empty() && value.chars().all(char::is_alphanumeric))) -} - -fn str_isascii(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - 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 { arg_check!( vm, @@ -967,66 +870,6 @@ fn str_rfind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_int(ind)) } -fn str_isnumeric(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let value = get_value(&s); - Ok(vm - .ctx - .new_bool(!value.is_empty() && value.chars().all(char::is_numeric))) -} - -fn str_isalpha(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let value = get_value(&s); - Ok(vm - .ctx - .new_bool(!value.is_empty() && value.chars().all(char::is_alphanumeric))) -} - -fn str_isdigit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let value = get_value(&s); - // python's isdigit also checks if exponents are digits, these are the unicodes for exponents - let valid_unicodes: [u16; 10] = [ - 0x2070, 0x00B9, 0x00B2, 0x00B3, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079, - ]; - - let is_digit = if value.is_empty() { - false - } else { - value - .chars() - .filter(|c| !c.is_digit(10)) - .all(|c| valid_unicodes.contains(&(c as u16))) - }; - - 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, - args, - required = [(s, Some(vm.ctx.str_type())), (needle, None)] - ); - let value = get_value(&s); - subscript(vm, &value, needle.clone()) -} - // TODO: should with following format // class str(object='') // class str(object=b'', encoding='utf-8', errors='strict') From 37118c7d750967051d8c75a1887a49a456070e21 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Tue, 5 Mar 2019 22:34:45 +0200 Subject: [PATCH 160/380] Add dict.values --- tests/snippets/dict.py | 6 ++++++ vm/src/obj/objdict.rs | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/tests/snippets/dict.py b/tests/snippets/dict.py index 1e4acc3e3d..a451c4f39b 100644 --- a/tests/snippets/dict.py +++ b/tests/snippets/dict.py @@ -17,3 +17,9 @@ def dict_eq(d1, d2): a.clear() assert len(a) == 0 + +a = {'a': 5, 'b': 6} +res = set() +for num in a.values(): + res.add(num) +assert res == set([5,6]) diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 03a307b4c3..b735cc198e 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -268,6 +268,27 @@ fn dict_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(iter_obj) } +fn dict_values(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(dict, Some(vm.ctx.dict_type()))]); + + let values = get_elements(dict) + .values() + .map(|(_k, v)| v) + .cloned() + .collect(); + let values_list = vm.ctx.new_list(values); + + let iter_obj = PyObject::new( + PyObjectPayload::Iterator { + position: Cell::new(0), + iterated_obj: values_list, + }, + vm.ctx.iter_type(), + ); + + Ok(iter_obj) +} + fn dict_setitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -345,4 +366,5 @@ pub fn init(context: &PyContext) { context.new_rustfunc(dict_setitem), ); context.set_attr(&dict_type, "clear", context.new_rustfunc(dict_clear)); + context.set_attr(&dict_type, "values", context.new_rustfunc(dict_values)); } From 475476df71f856f1e963837db41be7920a07463c Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Tue, 5 Mar 2019 22:41:42 +0200 Subject: [PATCH 161/380] Add dict.items --- tests/snippets/dict.py | 10 ++++++++-- vm/src/obj/objdict.rs | 24 ++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/tests/snippets/dict.py b/tests/snippets/dict.py index a451c4f39b..3e20d0a3a1 100644 --- a/tests/snippets/dict.py +++ b/tests/snippets/dict.py @@ -20,6 +20,12 @@ def dict_eq(d1, d2): a = {'a': 5, 'b': 6} res = set() -for num in a.values(): - res.add(num) +for value in a.values(): + res.add(value) assert res == set([5,6]) + +count = 0 +for (key, value) in a.items(): + assert a[key] == value + count += 1 +assert count == len(a) diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index b735cc198e..91b4806788 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -273,8 +273,7 @@ fn dict_values(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let values = get_elements(dict) .values() - .map(|(_k, v)| v) - .cloned() + .map(|(_k, v)| v.clone()) .collect(); let values_list = vm.ctx.new_list(values); @@ -289,6 +288,26 @@ fn dict_values(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(iter_obj) } +fn dict_items(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(dict, Some(vm.ctx.dict_type()))]); + + let items = get_elements(dict) + .values() + .map(|(k, v)| vm.ctx.new_tuple(vec![k.clone(), v.clone()])) + .collect(); + let items_list = vm.ctx.new_list(items); + + let iter_obj = PyObject::new( + PyObjectPayload::Iterator { + position: Cell::new(0), + iterated_obj: items_list, + }, + vm.ctx.iter_type(), + ); + + Ok(iter_obj) +} + fn dict_setitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -367,4 +386,5 @@ pub fn init(context: &PyContext) { ); context.set_attr(&dict_type, "clear", context.new_rustfunc(dict_clear)); context.set_attr(&dict_type, "values", context.new_rustfunc(dict_values)); + context.set_attr(&dict_type, "items", context.new_rustfunc(dict_items)); } From 24dcc06c245ab0e582f6151aecbe7a2fb1335496 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Tue, 5 Mar 2019 22:46:13 +0200 Subject: [PATCH 162/380] add dict.keys --- tests/snippets/dict.py | 5 +++++ vm/src/obj/objdict.rs | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/snippets/dict.py b/tests/snippets/dict.py index 3e20d0a3a1..170a6492dc 100644 --- a/tests/snippets/dict.py +++ b/tests/snippets/dict.py @@ -29,3 +29,8 @@ def dict_eq(d1, d2): assert a[key] == value count += 1 assert count == len(a) + +res = set() +for key in a.keys(): + res.add(key) +assert res == set(['a','b']) diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 91b4806788..189684eb05 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -252,8 +252,8 @@ fn dict_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(dict, Some(vm.ctx.dict_type()))]); let keys = get_elements(dict) - .keys() - .map(|k| vm.ctx.new_str(k.to_string())) + .values() + .map(|(k, _v)| k.clone()) .collect(); let key_list = vm.ctx.new_list(keys); @@ -387,4 +387,5 @@ pub fn init(context: &PyContext) { context.set_attr(&dict_type, "clear", context.new_rustfunc(dict_clear)); context.set_attr(&dict_type, "values", context.new_rustfunc(dict_values)); context.set_attr(&dict_type, "items", context.new_rustfunc(dict_items)); + context.set_attr(&dict_type, "keys", context.new_rustfunc(dict_iter)); } From b5248fbd824d7a07b7005552d910e076ec637532 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 5 Mar 2019 17:27:56 -0600 Subject: [PATCH 163/380] Fix Cargo.toml versions --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- wasm/lib/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f3e2639df5..484e7a9c5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -730,7 +730,7 @@ dependencies = [ [[package]] name = "rustpython" -version = "0.0.1-pre-alpha.1" +version = "0.0.1" dependencies = [ "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -781,7 +781,7 @@ dependencies = [ [[package]] name = "rustpython_wasm" -version = "0.1.0" +version = "0.1.0-pre-alpha.1" 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)", diff --git a/Cargo.toml b/Cargo.toml index 33c278da33..a1f366cd43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustpython" -version = "0.0.1-pre-alpha.1" +version = "0.0.1" authors = ["Windel Bouwman", "Shing Lyu "] edition = "2018" diff --git a/wasm/lib/Cargo.toml b/wasm/lib/Cargo.toml index 5c4e6c86d9..3f89ba9acb 100644 --- a/wasm/lib/Cargo.toml +++ b/wasm/lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustpython_wasm" -version = "0.1.0" +version = "0.1.0-pre-alpha.1" authors = ["Ryan Liddle "] license = "MIT" description = "A Python-3 (CPython >= 3.5.0) Interpreter written in Rust, compiled to WASM" From c68dbcc1ababe2aa5e5e79dcbbcf252a19e32c7b Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Tue, 5 Mar 2019 19:56:19 -0800 Subject: [PATCH 164/380] Improve readability of methods on PyRef - Introduce PyStringRef type alias - Impl python methods on PyStringRef to allow receiver to just be `self` --- vm/src/obj/objstr.rs | 258 +++++++++++++++++++++---------------------- 1 file changed, 128 insertions(+), 130 deletions(-) diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 7fc8fa71bc..4ee7f38f54 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -24,79 +24,81 @@ pub struct PyString { pub value: String, } -impl PyString { - fn add(zelf: PyRef, rhs: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { +pub type PyStringRef = PyRef; + +impl PyStringRef { + fn add(self, rhs: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { if objtype::isinstance(&rhs, &vm.ctx.str_type()) { - Ok(format!("{}{}", zelf.value, get_value(&rhs))) + Ok(format!("{}{}", self.value, get_value(&rhs))) } else { - Err(vm.new_type_error(format!("Cannot add {} and {}", zelf, rhs))) + Err(vm.new_type_error(format!("Cannot add {} and {}", self, rhs))) } } - fn eq(zelf: PyRef, rhs: PyObjectRef, vm: &mut VirtualMachine) -> bool { + fn eq(self, rhs: PyObjectRef, vm: &mut VirtualMachine) -> bool { if objtype::isinstance(&rhs, &vm.ctx.str_type()) { - zelf.value == get_value(&rhs) + self.value == get_value(&rhs) } else { false } } - fn contains(zelf: PyRef, needle: PyRef, _vm: &mut VirtualMachine) -> bool { - zelf.value.contains(&needle.value) + fn contains(self, needle: PyStringRef, _vm: &mut VirtualMachine) -> bool { + self.value.contains(&needle.value) } - fn getitem(zelf: PyRef, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { - subscript(vm, &zelf.value, needle) + fn getitem(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + subscript(vm, &self.value, needle) } - fn gt(zelf: PyRef, rhs: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn gt(self, rhs: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { if objtype::isinstance(&rhs, &vm.ctx.str_type()) { - Ok(zelf.value > get_value(&rhs)) + Ok(self.value > get_value(&rhs)) } else { - Err(vm.new_type_error(format!("Cannot compare {} and {}", zelf, rhs))) + Err(vm.new_type_error(format!("Cannot compare {} and {}", self, rhs))) } } - fn ge(zelf: PyRef, rhs: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn ge(self, rhs: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { if objtype::isinstance(&rhs, &vm.ctx.str_type()) { - Ok(zelf.value >= get_value(&rhs)) + Ok(self.value >= get_value(&rhs)) } else { - Err(vm.new_type_error(format!("Cannot compare {} and {}", zelf, rhs))) + Err(vm.new_type_error(format!("Cannot compare {} and {}", self, rhs))) } } - fn lt(zelf: PyRef, rhs: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn lt(self, rhs: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { if objtype::isinstance(&rhs, &vm.ctx.str_type()) { - Ok(zelf.value < get_value(&rhs)) + Ok(self.value < get_value(&rhs)) } else { - Err(vm.new_type_error(format!("Cannot compare {} and {}", zelf, rhs))) + Err(vm.new_type_error(format!("Cannot compare {} and {}", self, rhs))) } } - fn le(zelf: PyRef, rhs: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn le(self, rhs: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { if objtype::isinstance(&rhs, &vm.ctx.str_type()) { - Ok(zelf.value <= get_value(&rhs)) + Ok(self.value <= get_value(&rhs)) } else { - Err(vm.new_type_error(format!("Cannot compare {} and {}", zelf, rhs))) + Err(vm.new_type_error(format!("Cannot compare {} and {}", self, rhs))) } } - fn hash(zelf: PyRef, _vm: &mut VirtualMachine) -> usize { + fn hash(self, _vm: &mut VirtualMachine) -> usize { let mut hasher = std::collections::hash_map::DefaultHasher::new(); - zelf.value.hash(&mut hasher); + self.value.hash(&mut hasher); hasher.finish() as usize } - fn len(zelf: PyRef, _vm: &mut VirtualMachine) -> usize { - zelf.value.chars().count() + fn len(self, _vm: &mut VirtualMachine) -> usize { + self.value.chars().count() } - fn str(zelf: PyRef, _vm: &mut VirtualMachine) -> PyRef { - zelf + fn str(self, _vm: &mut VirtualMachine) -> PyStringRef { + self } - fn repr(zelf: PyRef, _vm: &mut VirtualMachine) -> String { - let value = &zelf.value; + fn repr(self, _vm: &mut VirtualMachine) -> String { + let value = &self.value; let quote_char = if count_char(value, '\'') > count_char(value, '"') { '"' } else { @@ -125,99 +127,99 @@ impl PyString { formatted } - fn lower(zelf: PyRef, _vm: &mut VirtualMachine) -> String { - zelf.value.to_lowercase() + fn lower(self, _vm: &mut VirtualMachine) -> String { + self.value.to_lowercase() } // casefold is much more aggressive than lower - fn casefold(zelf: PyRef, _vm: &mut VirtualMachine) -> String { - caseless::default_case_fold_str(&zelf.value) + fn casefold(self, _vm: &mut VirtualMachine) -> String { + caseless::default_case_fold_str(&self.value) } - fn upper(zelf: PyRef, _vm: &mut VirtualMachine) -> String { - zelf.value.to_uppercase() + fn upper(self, _vm: &mut VirtualMachine) -> String { + self.value.to_uppercase() } - fn capitalize(zelf: PyRef, _vm: &mut VirtualMachine) -> String { - let (first_part, lower_str) = zelf.value.split_at(1); + fn capitalize(self, _vm: &mut VirtualMachine) -> String { + let (first_part, lower_str) = self.value.split_at(1); format!("{}{}", first_part.to_uppercase(), lower_str) } - fn strip(zelf: PyRef, _vm: &mut VirtualMachine) -> String { - zelf.value.trim().to_string() + fn strip(self, _vm: &mut VirtualMachine) -> String { + self.value.trim().to_string() } - fn lstrip(zelf: PyRef, _vm: &mut VirtualMachine) -> String { - zelf.value.trim_start().to_string() + fn lstrip(self, _vm: &mut VirtualMachine) -> String { + self.value.trim_start().to_string() } - fn rstrip(zelf: PyRef, _vm: &mut VirtualMachine) -> String { - zelf.value.trim_end().to_string() + fn rstrip(self, _vm: &mut VirtualMachine) -> String { + self.value.trim_end().to_string() } fn endswith( - zelf: PyRef, - suffix: PyRef, + self, + suffix: PyStringRef, start: OptArg, end: OptArg, _vm: &mut VirtualMachine, ) -> bool { let start = start.unwrap_or(0); - let end = end.unwrap_or(zelf.value.len()); - zelf.value[start..end].ends_with(&suffix.value) + let end = end.unwrap_or(self.value.len()); + self.value[start..end].ends_with(&suffix.value) } fn startswith( - zelf: PyRef, - prefix: PyRef, + self, + prefix: PyStringRef, start: OptArg, end: OptArg, _vm: &mut VirtualMachine, ) -> bool { let start = start.unwrap_or(0); - let end = end.unwrap_or(zelf.value.len()); - zelf.value[start..end].starts_with(&prefix.value) + let end = end.unwrap_or(self.value.len()); + self.value[start..end].starts_with(&prefix.value) } - fn isalnum(zelf: PyRef, _vm: &mut VirtualMachine) -> bool { - !zelf.value.is_empty() && zelf.value.chars().all(char::is_alphanumeric) + fn isalnum(self, _vm: &mut VirtualMachine) -> bool { + !self.value.is_empty() && self.value.chars().all(char::is_alphanumeric) } - fn isnumeric(zelf: PyRef, _vm: &mut VirtualMachine) -> bool { - !zelf.value.is_empty() && zelf.value.chars().all(char::is_numeric) + fn isnumeric(self, _vm: &mut VirtualMachine) -> bool { + !self.value.is_empty() && self.value.chars().all(char::is_numeric) } - fn isdigit(zelf: PyRef, _vm: &mut VirtualMachine) -> bool { + fn isdigit(self, _vm: &mut VirtualMachine) -> bool { // python's isdigit also checks if exponents are digits, these are the unicodes for exponents let valid_unicodes: [u16; 10] = [ 0x2070, 0x00B9, 0x00B2, 0x00B3, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079, ]; - if zelf.value.is_empty() { + if self.value.is_empty() { false } else { - zelf.value + self.value .chars() .filter(|c| !c.is_digit(10)) .all(|c| valid_unicodes.contains(&(c as u16))) } } - fn isdecimal(zelf: PyRef, _vm: &mut VirtualMachine) -> bool { - if zelf.value.is_empty() { + fn isdecimal(self, _vm: &mut VirtualMachine) -> bool { + if self.value.is_empty() { false } else { - zelf.value.chars().all(|c| c.is_ascii_digit()) + self.value.chars().all(|c| c.is_ascii_digit()) } } - fn title(zelf: PyRef, _vm: &mut VirtualMachine) -> String { - make_title(&zelf.value) + fn title(self, _vm: &mut VirtualMachine) -> String { + make_title(&self.value) } - fn swapcase(zelf: PyRef, _vm: &mut VirtualMachine) -> String { - let mut swapped_str = String::with_capacity(zelf.value.len()); - for c in zelf.value.chars() { + fn swapcase(self, _vm: &mut VirtualMachine) -> String { + let mut swapped_str = String::with_capacity(self.value.len()); + for c in self.value.chars() { // to_uppercase returns an iterator, to_ascii_uppercase returns the char if c.is_lowercase() { swapped_str.push(c.to_ascii_uppercase()); @@ -230,41 +232,41 @@ impl PyString { swapped_str } - fn isalpha(zelf: PyRef, _vm: &mut VirtualMachine) -> bool { - !zelf.value.is_empty() && zelf.value.chars().all(char::is_alphanumeric) + fn isalpha(self, _vm: &mut VirtualMachine) -> bool { + !self.value.is_empty() && self.value.chars().all(char::is_alphanumeric) } // cpython's isspace ignores whitespace, including \t and \n, etc, unless the whole string is empty // which is why isspace is using is_ascii_whitespace. Same for isupper & islower - fn isspace(zelf: PyRef, _vm: &mut VirtualMachine) -> bool { - !zelf.value.is_empty() && zelf.value.chars().all(|c| c.is_ascii_whitespace()) + fn isspace(self, _vm: &mut VirtualMachine) -> bool { + !self.value.is_empty() && self.value.chars().all(|c| c.is_ascii_whitespace()) } - fn isupper(zelf: PyRef, _vm: &mut VirtualMachine) -> bool { - !zelf.value.is_empty() - && zelf + fn isupper(self, _vm: &mut VirtualMachine) -> bool { + !self.value.is_empty() + && self .value .chars() .filter(|x| !x.is_ascii_whitespace()) .all(char::is_uppercase) } - fn islower(zelf: PyRef, _vm: &mut VirtualMachine) -> bool { - !zelf.value.is_empty() - && zelf + fn islower(self, _vm: &mut VirtualMachine) -> bool { + !self.value.is_empty() + && self .value .chars() .filter(|x| !x.is_ascii_whitespace()) .all(char::is_lowercase) } - fn isascii(zelf: PyRef, _vm: &mut VirtualMachine) -> bool { - !zelf.value.is_empty() && zelf.value.chars().all(|c| c.is_ascii()) + fn isascii(self, _vm: &mut VirtualMachine) -> bool { + !self.value.is_empty() && self.value.chars().all(|c| c.is_ascii()) } // doesn't implement keep new line delimiter just yet - fn splitlines(zelf: PyRef, vm: &mut VirtualMachine) -> PyObjectRef { - let elements = zelf + fn splitlines(self, vm: &mut VirtualMachine) -> PyObjectRef { + let elements = self .value .split('\n') .map(|e| vm.ctx.new_str(e.to_string())) @@ -272,17 +274,13 @@ impl PyString { vm.ctx.new_list(elements) } - fn join( - zelf: PyRef, - iterable: PyIterable>, - vm: &mut VirtualMachine, - ) -> PyResult { + fn join(self, iterable: PyIterable, vm: &mut VirtualMachine) -> PyResult { let mut joined = String::new(); for (idx, elem) in iterable.iter(vm)?.enumerate() { let elem = elem?; if idx != 0 { - joined.push_str(&zelf.value); + joined.push_str(&self.value); } joined.push_str(&elem.value) } @@ -290,8 +288,8 @@ impl PyString { Ok(joined) } - fn partition(zelf: PyRef, sub: PyRef, vm: &mut VirtualMachine) -> PyObjectRef { - let value = &zelf.value; + fn partition(self, sub: PyStringRef, vm: &mut VirtualMachine) -> PyObjectRef { + let value = &self.value; let sub = &sub.value; let mut new_tup = Vec::new(); if value.contains(sub) { @@ -308,8 +306,8 @@ impl PyString { vm.ctx.new_tuple(new_tup) } - fn rpartition(zelf: PyRef, sub: PyRef, vm: &mut VirtualMachine) -> PyObjectRef { - let value = &zelf.value; + fn rpartition(self, sub: PyStringRef, vm: &mut VirtualMachine) -> PyObjectRef { + let value = &self.value; let sub = &sub.value; let mut new_tup = Vec::new(); if value.contains(sub) { @@ -327,8 +325,8 @@ impl PyString { vm.ctx.new_tuple(new_tup) } - fn isidentifier(zelf: PyRef, _vm: &mut VirtualMachine) -> bool { - let value = &zelf.value; + fn isidentifier(self, _vm: &mut VirtualMachine) -> bool { + let value = &self.value; // a string is not an identifier if it has whitespace or starts with a number if !value.chars().any(|c| c.is_ascii_whitespace()) && !value.chars().nth(0).unwrap().is_digit(10) @@ -360,60 +358,60 @@ impl IntoPyObject for String { #[rustfmt::skip] // to avoid line splitting pub fn init(context: &PyContext) { let str_type = &context.str_type; - context.set_attr(&str_type, "__add__", context.new_rustfunc(PyString::add)); - context.set_attr(&str_type, "__eq__", context.new_rustfunc(PyString::eq)); - context.set_attr(&str_type, "__contains__", context.new_rustfunc(PyString::contains)); - context.set_attr(&str_type, "__getitem__", context.new_rustfunc(PyString::getitem)); - context.set_attr(&str_type, "__gt__", context.new_rustfunc(PyString::gt)); - context.set_attr(&str_type, "__ge__", context.new_rustfunc(PyString::ge)); - context.set_attr(&str_type, "__lt__", context.new_rustfunc(PyString::lt)); - context.set_attr(&str_type, "__le__", context.new_rustfunc(PyString::le)); - context.set_attr(&str_type, "__hash__", context.new_rustfunc(PyString::hash)); - context.set_attr(&str_type, "__len__", context.new_rustfunc(PyString::len)); + context.set_attr(&str_type, "__add__", context.new_rustfunc(PyStringRef::add)); + context.set_attr(&str_type, "__eq__", context.new_rustfunc(PyStringRef::eq)); + context.set_attr(&str_type, "__contains__", context.new_rustfunc(PyStringRef::contains)); + context.set_attr(&str_type, "__getitem__", context.new_rustfunc(PyStringRef::getitem)); + context.set_attr(&str_type, "__gt__", context.new_rustfunc(PyStringRef::gt)); + context.set_attr(&str_type, "__ge__", context.new_rustfunc(PyStringRef::ge)); + context.set_attr(&str_type, "__lt__", context.new_rustfunc(PyStringRef::lt)); + context.set_attr(&str_type, "__le__", context.new_rustfunc(PyStringRef::le)); + context.set_attr(&str_type, "__hash__", context.new_rustfunc(PyStringRef::hash)); + context.set_attr(&str_type, "__len__", context.new_rustfunc(PyStringRef::len)); context.set_attr(&str_type, "__mul__", context.new_rustfunc(str_mul)); context.set_attr(&str_type, "__new__", context.new_rustfunc(str_new)); - context.set_attr(&str_type, "__str__", context.new_rustfunc(PyString::str)); - context.set_attr(&str_type, "__repr__", context.new_rustfunc(PyString::repr)); + context.set_attr(&str_type, "__str__", context.new_rustfunc(PyStringRef::str)); + context.set_attr(&str_type, "__repr__", context.new_rustfunc(PyStringRef::repr)); context.set_attr(&str_type, "format", context.new_rustfunc(str_format)); - context.set_attr(&str_type, "lower", context.new_rustfunc(PyString::lower)); - context.set_attr(&str_type, "casefold", context.new_rustfunc(PyString::casefold)); - context.set_attr(&str_type, "upper", context.new_rustfunc(PyString::upper)); - context.set_attr(&str_type, "capitalize", context.new_rustfunc(PyString::capitalize)); + context.set_attr(&str_type, "lower", context.new_rustfunc(PyStringRef::lower)); + context.set_attr(&str_type, "casefold", context.new_rustfunc(PyStringRef::casefold)); + context.set_attr(&str_type, "upper", context.new_rustfunc(PyStringRef::upper)); + context.set_attr(&str_type, "capitalize", context.new_rustfunc(PyStringRef::capitalize)); context.set_attr(&str_type, "split", context.new_rustfunc(str_split)); context.set_attr(&str_type, "rsplit", context.new_rustfunc(str_rsplit)); - context.set_attr(&str_type, "strip", context.new_rustfunc(PyString::strip)); - context.set_attr(&str_type, "lstrip", context.new_rustfunc(PyString::lstrip)); - context.set_attr(&str_type, "rstrip", context.new_rustfunc(PyString::rstrip)); - context.set_attr(&str_type, "endswith", context.new_rustfunc(PyString::endswith)); - context.set_attr(&str_type, "startswith", context.new_rustfunc(PyString::startswith)); - context.set_attr(&str_type, "isalnum", context.new_rustfunc(PyString::isalnum)); - context.set_attr(&str_type, "isnumeric", context.new_rustfunc(PyString::isnumeric)); - context.set_attr(&str_type, "isdigit", context.new_rustfunc(PyString::isdigit)); - context.set_attr(&str_type, "isdecimal", context.new_rustfunc(PyString::isdecimal)); - context.set_attr(&str_type, "title", context.new_rustfunc(PyString::title)); - context.set_attr(&str_type, "swapcase", context.new_rustfunc(PyString::swapcase)); - context.set_attr(&str_type, "isalpha", context.new_rustfunc(PyString::isalpha)); + context.set_attr(&str_type, "strip", context.new_rustfunc(PyStringRef::strip)); + context.set_attr(&str_type, "lstrip", context.new_rustfunc(PyStringRef::lstrip)); + context.set_attr(&str_type, "rstrip", context.new_rustfunc(PyStringRef::rstrip)); + context.set_attr(&str_type, "endswith", context.new_rustfunc(PyStringRef::endswith)); + context.set_attr(&str_type, "startswith", context.new_rustfunc(PyStringRef::startswith)); + context.set_attr(&str_type, "isalnum", context.new_rustfunc(PyStringRef::isalnum)); + context.set_attr(&str_type, "isnumeric", context.new_rustfunc(PyStringRef::isnumeric)); + context.set_attr(&str_type, "isdigit", context.new_rustfunc(PyStringRef::isdigit)); + context.set_attr(&str_type, "isdecimal", context.new_rustfunc(PyStringRef::isdecimal)); + context.set_attr(&str_type, "title", context.new_rustfunc(PyStringRef::title)); + context.set_attr(&str_type, "swapcase", context.new_rustfunc(PyStringRef::swapcase)); + context.set_attr(&str_type, "isalpha", context.new_rustfunc(PyStringRef::isalpha)); context.set_attr(&str_type, "replace", context.new_rustfunc(str_replace)); context.set_attr(&str_type, "center", context.new_rustfunc(str_center)); - context.set_attr(&str_type, "isspace", context.new_rustfunc(PyString::isspace)); - context.set_attr(&str_type, "isupper", context.new_rustfunc(PyString::isupper)); - context.set_attr(&str_type, "islower", context.new_rustfunc(PyString::islower)); - context.set_attr(&str_type, "isascii", context.new_rustfunc(PyString::isascii)); - context.set_attr(&str_type, "splitlines", context.new_rustfunc(PyString::splitlines)); - context.set_attr(&str_type, "join", context.new_rustfunc(PyString::join)); + context.set_attr(&str_type, "isspace", context.new_rustfunc(PyStringRef::isspace)); + context.set_attr(&str_type, "isupper", context.new_rustfunc(PyStringRef::isupper)); + context.set_attr(&str_type, "islower", context.new_rustfunc(PyStringRef::islower)); + context.set_attr(&str_type, "isascii", context.new_rustfunc(PyStringRef::isascii)); + context.set_attr(&str_type, "splitlines", context.new_rustfunc(PyStringRef::splitlines)); + context.set_attr(&str_type, "join", context.new_rustfunc(PyStringRef::join)); context.set_attr(&str_type, "find", context.new_rustfunc(str_find)); context.set_attr(&str_type, "rfind", context.new_rustfunc(str_rfind)); context.set_attr(&str_type, "index", context.new_rustfunc(str_index)); context.set_attr(&str_type, "rindex", context.new_rustfunc(str_rindex)); - context.set_attr(&str_type, "partition", context.new_rustfunc(PyString::partition)); - context.set_attr(&str_type, "rpartition", context.new_rustfunc(PyString::rpartition)); + context.set_attr(&str_type, "partition", context.new_rustfunc(PyStringRef::partition)); + context.set_attr(&str_type, "rpartition", context.new_rustfunc(PyStringRef::rpartition)); context.set_attr(&str_type, "istitle", context.new_rustfunc(str_istitle)); context.set_attr(&str_type, "count", context.new_rustfunc(str_count)); context.set_attr(&str_type, "zfill", context.new_rustfunc(str_zfill)); context.set_attr(&str_type, "ljust", context.new_rustfunc(str_ljust)); context.set_attr(&str_type, "rjust", context.new_rustfunc(str_rjust)); context.set_attr(&str_type, "expandtabs", context.new_rustfunc(str_expandtabs)); - context.set_attr(&str_type, "isidentifier", context.new_rustfunc(PyString::isidentifier)); + context.set_attr(&str_type, "isidentifier", context.new_rustfunc(PyStringRef::isidentifier)); } pub fn get_value(obj: &PyObjectRef) -> String { From f3791a386748504808d82ddffc109b44c1a18b52 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Wed, 6 Mar 2019 11:25:16 +0100 Subject: [PATCH 165/380] Fix test script for python3.6 --- tests/snippets/test_re.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/snippets/test_re.py b/tests/snippets/test_re.py index d9914b38d5..2463f380b5 100644 --- a/tests/snippets/test_re.py +++ b/tests/snippets/test_re.py @@ -7,6 +7,7 @@ mo = re.search(needle, haystack) print(mo) -assert isinstance(mo, re.Match) +# Does not work on python 3.6: +# assert isinstance(mo, re.Match) assert mo.start() == 1 assert mo.end() == 5 From 6d84b4d4e9462db12b47cba55c7a7c00e1459321 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Wed, 6 Mar 2019 12:02:01 +0100 Subject: [PATCH 166/380] Less explicit PyFuncArgs is better. implemented a From trait for PyFuncArgs --- vm/src/builtins.rs | 28 +++++---------- vm/src/obj/objfilter.rs | 8 +---- vm/src/obj/objtype.rs | 24 ++----------- vm/src/pyobject.rs | 10 ++++++ vm/src/vm.rs | 65 ++++++++-------------------------- wasm/lib/src/browser_module.rs | 5 +-- 6 files changed, 39 insertions(+), 101 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index b146fcf913..eee98100eb 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -178,7 +178,7 @@ fn builtin_dir(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn builtin_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(x, None), (y, None)]); match vm.get_method(x.clone(), "__divmod__") { - Ok(attrib) => vm.invoke(attrib, PyFuncArgs::new(vec![y.clone()], vec![])), + Ok(attrib) => vm.invoke(attrib, vec![y.clone()]), Err(..) => Err(vm.new_type_error("unsupported operand type(s) for divmod".to_string())), } } @@ -429,16 +429,14 @@ fn builtin_max(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // TODO: this key function looks pretty duplicate. Maybe we can create // a local function? let mut x_key = if let Some(f) = &key_func { - let args = PyFuncArgs::new(vec![x.clone()], vec![]); - vm.invoke(f.clone(), args)? + vm.invoke(f.clone(), vec![x.clone()])? } else { x.clone() }; for y in candidates_iter { let y_key = if let Some(f) = &key_func { - let args = PyFuncArgs::new(vec![y.clone()], vec![]); - vm.invoke(f.clone(), args)? + vm.invoke(f.clone(), vec![y.clone()])? } else { y.clone() }; @@ -479,16 +477,14 @@ fn builtin_min(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // TODO: this key function looks pretty duplicate. Maybe we can create // a local function? let mut x_key = if let Some(f) = &key_func { - let args = PyFuncArgs::new(vec![x.clone()], vec![]); - vm.invoke(f.clone(), args)? + vm.invoke(f.clone(), vec![x.clone()])? } else { x.clone() }; for y in candidates_iter { let y_key = if let Some(f) = &key_func { - let args = PyFuncArgs::new(vec![y.clone()], vec![]); - vm.invoke(f.clone(), args)? + vm.invoke(f.clone(), vec![y.clone()])? } else { y.clone() }; @@ -566,7 +562,7 @@ fn builtin_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); 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![])), + Ok(attrib) => vm.invoke(attrib, vec![y.clone()]), Err(..) => Err(vm.new_type_error("unsupported operand type(s) for pow".to_string())), }; //Check if the 3rd argument is defined and perform modulus on the result @@ -576,7 +572,7 @@ fn builtin_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Some(mod_value) => { 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![])), + Ok(value) => vm.invoke(value, vec![mod_value.clone()]), Err(..) => { Err(vm.new_type_error("unsupported operand type(s) for mod".to_string())) } @@ -709,7 +705,7 @@ fn builtin_sorted(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { let lst = vm.ctx.new_list(items); args.shift(); - vm.call_method_pyargs(&lst, "sort", args)?; + vm.call_method(&lst, "sort", args)?; Ok(lst) } @@ -843,13 +839,7 @@ pub fn builtin_build_class_(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> Py // Prepare uses full __getattribute__ resolution chain. let prepare_name = vm.new_str("__prepare__".to_string()); let prepare = vm.get_attribute(metaclass.clone(), prepare_name)?; - let namespace = vm.invoke( - prepare, - PyFuncArgs { - args: vec![name_arg.clone(), bases.clone()], - kwargs: vec![], - }, - )?; + let namespace = vm.invoke(prepare, vec![name_arg.clone(), bases.clone()])?; vm.invoke_with_locals(function, namespace.clone())?; diff --git a/vm/src/obj/objfilter.rs b/vm/src/obj/objfilter.rs index 998d51c9b8..b4338192f9 100644 --- a/vm/src/obj/objfilter.rs +++ b/vm/src/obj/objfilter.rs @@ -36,13 +36,7 @@ fn filter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } 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![], - }, - )? + vm.invoke(predicate.clone(), vec![next_obj.clone()])? }; if objbool::boolval(vm, predicate_value)? { return Ok(next_obj); diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 086f7f28f0..5364dab3c7 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -210,13 +210,7 @@ pub fn type_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult let attr_class = attr.typ(); if attr_class.has_attr("__set__") { if let Some(descriptor) = attr_class.get_attr("__get__") { - return vm.invoke( - descriptor, - PyFuncArgs { - args: vec![attr, cls.clone(), mcl], - kwargs: vec![], - }, - ); + return vm.invoke(descriptor, vec![attr, cls.clone(), mcl]); } } } @@ -225,13 +219,7 @@ pub fn type_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult let attr_class = attr.typ(); if let Some(descriptor) = attr_class.get_attr("__get__") { let none = vm.get_none(); - return vm.invoke( - descriptor, - PyFuncArgs { - args: vec![attr, none, cls.clone()], - kwargs: vec![], - }, - ); + return vm.invoke(descriptor, vec![attr, none, cls.clone()]); } } @@ -240,13 +228,7 @@ pub fn type_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult } 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![], - }, - ) + vm.invoke(getter, vec![mcl, name_str.clone()]) } else { let attribute_error = vm.context().exceptions.attribute_error.clone(); Err(vm.new_exception( diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 6e0ab27011..9fc1c0f48c 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -904,6 +904,16 @@ pub struct PyFuncArgs { pub kwargs: Vec<(String, PyObjectRef)>, } +/// Conversion from vector of python objects to function arguments. +impl From> for PyFuncArgs { + fn from(args: Vec) -> Self { + PyFuncArgs { + args: args, + kwargs: vec![], + } + } +} + impl PyFuncArgs { pub fn new(mut args: Vec, kwarg_names: Vec) -> PyFuncArgs { let mut kwargs = vec![]; diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 6c6659d4db..709a9f46c7 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -117,10 +117,7 @@ impl VirtualMachine { pub fn new_empty_exception(&mut self, exc_type: PyObjectRef) -> PyResult { info!("New exception created: no msg"); - let args = PyFuncArgs { - args: vec![], - kwargs: vec![], - }; + let args = PyFuncArgs::default(); self.invoke(exc_type, args) } @@ -130,10 +127,6 @@ impl VirtualMachine { info!("New exception created: {}", msg); let pymsg = self.new_str(msg); let args: Vec = vec![pymsg]; - let args = PyFuncArgs { - args, - kwargs: vec![], - }; // Call function: self.invoke(exc_type, args).unwrap() @@ -264,40 +257,16 @@ impl VirtualMachine { let attr_class = attr.typ(); if let Some(descriptor) = attr_class.get_attr("__get__") { let cls = obj.typ(); - self.invoke( - descriptor, - PyFuncArgs { - args: vec![attr, obj.clone(), cls], - kwargs: vec![], - }, - ) + self.invoke(descriptor, vec![attr, obj.clone(), cls]) } else { Ok(attr) } } - pub fn call_method( - &mut self, - obj: &PyObjectRef, - method_name: &str, - args: Vec, - ) -> PyResult { - self.call_method_pyargs( - obj, - method_name, - PyFuncArgs { - args, - kwargs: vec![], - }, - ) - } - - pub fn call_method_pyargs( - &mut self, - obj: &PyObjectRef, - method_name: &str, - args: PyFuncArgs, - ) -> PyResult { + pub fn call_method(&mut self, obj: &PyObjectRef, method_name: &str, args: T) -> PyResult + where + T: Into, + { // This is only used in the vm for magic methods, which use a greatly simplified attribute lookup. let cls = obj.typ(); match cls.get_attr(method_name) { @@ -316,7 +285,11 @@ impl VirtualMachine { } } - pub fn invoke(&mut self, func_ref: PyObjectRef, args: PyFuncArgs) -> PyResult { + pub fn invoke(&mut self, func_ref: PyObjectRef, args: T) -> PyResult + where + T: Into, + { + let args = args.into(); trace!("Invoke: {:?} {:?}", func_ref, args); match func_ref.payload { PyObjectPayload::RustFunction { ref function } => function(self, args), @@ -325,18 +298,16 @@ impl VirtualMachine { ref scope, ref defaults, } => self.invoke_python_function(code, scope, defaults, args), - PyObjectPayload::Class { .. } => self.call_method_pyargs(&func_ref, "__call__", args), + PyObjectPayload::Class { .. } => self.call_method(&func_ref, "__call__", args), PyObjectPayload::BoundMethod { ref function, ref object, } => self.invoke(function.clone(), args.insert(object.clone())), - PyObjectPayload::Instance { .. } => { - self.call_method_pyargs(&func_ref, "__call__", args) - } + PyObjectPayload::Instance { .. } => self.call_method(&func_ref, "__call__", args), ref payload => { // TODO: is it safe to just invoke __call__ otherwise? trace!("invoke __call__ for: {:?}", payload); - self.call_method_pyargs(&func_ref, "__call__", args) + self.call_method(&func_ref, "__call__", args) } } } @@ -579,13 +550,7 @@ impl VirtualMachine { F: Fn(&mut VirtualMachine, PyObjectRef, PyObjectRef) -> PyResult, { if let Ok(method) = self.get_method(obj.clone(), method) { - let result = self.invoke( - method, - PyFuncArgs { - args: vec![arg.clone()], - kwargs: vec![], - }, - )?; + let result = self.invoke(method, vec![arg.clone()])?; if !result.is(&self.ctx.not_implemented()) { return Ok(result); } diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index 9aa6006108..ea21d30fda 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -118,10 +118,7 @@ fn browser_request_animation_frame(vm: &mut VirtualMachine, args: PyFuncArgs) -> .upgrade() .expect("that the vm is valid from inside of request_animation_frame"); let func = func.clone(); - let args = PyFuncArgs { - args: vec![vm.ctx.new_float(time)], - kwargs: vec![], - }; + let args = vec![vm.ctx.new_float(time)]; let _ = vm.invoke(func, args); let closure = f.borrow_mut().take(); From 69efc7f223e84a71a68cdec39d04d511de631763 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Wed, 6 Mar 2019 13:48:30 +0100 Subject: [PATCH 167/380] Compile type annotations on function. --- tests/snippets/type_hints.py | 4 ++-- vm/src/bytecode.rs | 1 + vm/src/compile.rs | 41 ++++++++++++++++++++++++++++++++++-- vm/src/frame.rs | 16 ++++++++++++++ 4 files changed, 58 insertions(+), 4 deletions(-) diff --git a/tests/snippets/type_hints.py b/tests/snippets/type_hints.py index 8576e72352..0a36e36d6f 100644 --- a/tests/snippets/type_hints.py +++ b/tests/snippets/type_hints.py @@ -1,7 +1,7 @@ # See also: https://github.com/RustPython/RustPython/issues/587 -def curry(foo: int) -> float: - return foo * 3.1415926 * 2 +def curry(foo: int, bla=2) -> float: + return foo * 3.1415926 * bla assert curry(2) > 10 diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index 59135ef64f..ff6ec873ce 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -31,6 +31,7 @@ pub struct CodeObject { bitflags! { pub struct FunctionOpArg: u8 { const HAS_DEFAULTS = 0x01; + const HAS_ANNOTATIONS = 0x04; } } diff --git a/vm/src/compile.rs b/vm/src/compile.rs index a5e01ab2f8..72e2186580 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -589,7 +589,7 @@ impl Compiler { args: &ast::Parameters, body: &[ast::LocatedStatement], decorator_list: &[ast::Expression], - _returns: &Option, // TODO: use type hint somehow.. + returns: &Option, // TODO: use type hint somehow.. ) -> Result<(), CompileError> { // Create bytecode for this function: // remember to restore self.in_loop to the original after the function is compiled @@ -597,7 +597,7 @@ impl Compiler { let was_in_function_def = self.in_function_def; self.in_loop = false; self.in_function_def = true; - let flags = self.enter_function(name, args)?; + let mut flags = self.enter_function(name, args)?; self.compile_statements(body)?; // Emit None at end: @@ -608,6 +608,43 @@ impl Compiler { let code = self.pop_code_object(); self.prepare_decorators(decorator_list)?; + + // Prepare type annotations: + let mut num_annotations = 0; + + // Return annotation: + if let Some(annotation) = returns { + // key: + self.emit(Instruction::LoadConst { + value: bytecode::Constant::String { + value: "returns".to_string(), + }, + }); + // value: + self.compile_expression(annotation)?; + num_annotations += 1; + } + + for arg in args.args.iter() { + if let Some(annotation) = &arg.annotation { + self.emit(Instruction::LoadConst { + value: bytecode::Constant::String { + value: arg.arg.to_string(), + }, + }); + self.compile_expression(&annotation)?; + num_annotations += 1; + } + } + + if num_annotations > 0 { + flags |= bytecode::FunctionOpArg::HAS_ANNOTATIONS; + self.emit(Instruction::BuildMap { + size: num_annotations, + unpack: false, + }); + } + self.emit(Instruction::LoadConst { value: bytecode::Constant::Code { code: Box::new(code), diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 9777596070..11cd261692 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -447,15 +447,31 @@ impl Frame { bytecode::Instruction::MakeFunction { flags } => { let _qualified_name = self.pop_value(); let code_obj = self.pop_value(); + + let _annotations = if flags.contains(bytecode::FunctionOpArg::HAS_ANNOTATIONS) { + self.pop_value() + } else { + vm.new_dict() + }; + let defaults = if flags.contains(bytecode::FunctionOpArg::HAS_DEFAULTS) { self.pop_value() } else { vm.get_none() }; + // pop argc arguments // argument: name, args, globals let scope = self.scope.clone(); let obj = vm.ctx.new_function(code_obj, scope, defaults); + + let annotation_repr = vm.to_pystr(&_annotations)?; + + warn!( + "Type annotation must be stored in attribute! {:?}", + annotation_repr + ); + // TODO: use annotations with set_attr here! self.push_value(obj); Ok(None) } From 5afbfafa8b8f872dd4fc708f1edf34350c4187ed Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Tue, 5 Mar 2019 15:05:52 +0000 Subject: [PATCH 168/380] Remove special case for None payload from boolval. --- tests/snippets/bools.py | 3 +++ vm/src/obj/objbool.rs | 3 +-- vm/src/obj/objnone.rs | 6 ++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/snippets/bools.py b/tests/snippets/bools.py index 2aa817ca46..0c277143bc 100644 --- a/tests/snippets/bools.py +++ b/tests/snippets/bools.py @@ -16,6 +16,9 @@ assert bool(1) == True assert bool({}) == False +assert bool(NotImplemented) == True +assert bool(...) == True + if not 1: raise BaseException diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index e45018ea60..7a96effd25 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -14,7 +14,7 @@ impl IntoPyObject for bool { } } -pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result { +pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { if let Some(s) = obj.payload::() { return Ok(!s.value.is_empty()); } @@ -27,7 +27,6 @@ pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result !value.is_zero(), PyObjectPayload::Sequence { ref elements } => !elements.borrow().is_empty(), - PyObjectPayload::None { .. } => false, _ => { if let Ok(f) = vm.get_method(obj.clone(), "__bool__") { let bool_res = vm.invoke(f, PyFuncArgs::default())?; diff --git a/vm/src/obj/objnone.rs b/vm/src/obj/objnone.rs index 2aceea9151..bc94f9cb92 100644 --- a/vm/src/obj/objnone.rs +++ b/vm/src/obj/objnone.rs @@ -5,6 +5,7 @@ pub fn init(context: &PyContext) { let none_type = &context.none.typ(); context.set_attr(&none_type, "__new__", context.new_rustfunc(none_new)); context.set_attr(&none_type, "__repr__", context.new_rustfunc(none_repr)); + context.set_attr(&none_type, "__bool__", context.new_rustfunc(none_bool)); } fn none_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -20,3 +21,8 @@ fn none_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(_zelf, Some(vm.ctx.none().typ()))]); Ok(vm.ctx.new_str("None".to_string())) } + +fn none_bool(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(_zelf, Some(vm.ctx.none().typ()))]); + Ok(vm.ctx.new_bool(false)) +} From a72af4d9674c0f7a4f6c74bb3f25c75a66d7d202 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Tue, 5 Mar 2019 15:50:07 +0000 Subject: [PATCH 169/380] Remove None and NotImplemented payloads and replace with NoPayload --- vm/src/frame.rs | 33 +++++++++++++++++---------------- vm/src/pyobject.rs | 14 ++++++-------- vm/src/stdlib/json.rs | 4 ++-- vm/src/vm.rs | 10 ++++++---- 4 files changed, 31 insertions(+), 30 deletions(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 9777596070..f5dc38ff07 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -286,10 +286,14 @@ impl Frame { let mut out: Vec> = elements .into_iter() - .map(|x| match x.payload { - PyObjectPayload::Integer { ref value } => Some(value.clone()), - PyObjectPayload::None => None, - _ => panic!("Expect Int or None as BUILD_SLICE arguments, got {:?}", x), + .map(|x| { + if x.is(&vm.get_none()) { + None + } else if let PyObjectPayload::Integer { ref value } = x.payload { + Some(value.clone()) + } else { + panic!("Expect Int or None as BUILD_SLICE arguments, got {:?}", x); + } }) .collect(); @@ -575,18 +579,15 @@ impl Frame { } bytecode::Instruction::PrintExpr => { let expr = self.pop_value(); - match expr.payload { - PyObjectPayload::None => (), - _ => { - let repr = vm.to_repr(&expr)?; - builtins::builtin_print( - vm, - PyFuncArgs { - args: vec![repr], - kwargs: vec![], - }, - )?; - } + if !expr.is(&vm.get_none()) { + let repr = vm.to_repr(&expr)?; + builtins::builtin_print( + vm, + PyFuncArgs { + args: vec![repr], + kwargs: vec![], + }, + )?; } Ok(None) } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 6e0ab27011..f644e16987 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -155,7 +155,7 @@ pub struct PyContext { fn _nothing() -> PyObjectRef { PyObject { - payload: PyObjectPayload::None, + payload: PyObjectPayload::NoPayload, typ: None, } .into_ref() @@ -223,14 +223,14 @@ impl PyContext { let exceptions = exceptions::ExceptionZoo::new(&type_type, &object_type, &dict_type); let none = PyObject::new( - PyObjectPayload::None, + PyObjectPayload::NoPayload, create_type("NoneType", &type_type, &object_type, &dict_type), ); - let ellipsis = PyObject::new(PyObjectPayload::None, ellipsis_type.clone()); + let ellipsis = PyObject::new(PyObjectPayload::NoPayload, ellipsis_type.clone()); let not_implemented = PyObject::new( - PyObjectPayload::NotImplemented, + PyObjectPayload::NoPayload, create_type("NotImplementedType", &type_type, &object_type, &dict_type), ); @@ -1484,8 +1484,7 @@ pub enum PyObjectPayload { name: String, scope: ScopeRef, }, - None, - NotImplemented, + NoPayload, Class { name: String, dict: RefCell, @@ -1526,8 +1525,7 @@ impl fmt::Debug for PyObjectPayload { ref object, } => write!(f, "bound-method: {:?} of {:?}", function, object), PyObjectPayload::Module { .. } => write!(f, "module"), - PyObjectPayload::None => write!(f, "None"), - PyObjectPayload::NotImplemented => write!(f, "NotImplemented"), + PyObjectPayload::NoPayload => write!(f, "NoPayload"), PyObjectPayload::Class { ref name, .. } => write!(f, "class {:?}", name), PyObjectPayload::Instance { .. } => write!(f, "instance"), PyObjectPayload::RustFunction { .. } => write!(f, "rust function"), diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index 7403c8d8c0..0d0bc23dc5 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -11,7 +11,7 @@ use crate::obj::{ objtype, }; use crate::pyobject::{ - create_type, DictProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, + create_type, DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol, }; use crate::VirtualMachine; @@ -69,7 +69,7 @@ impl<'s> serde::Serialize for PyObjectSerializer<'s> { map.serialize_entry(&key, &self.clone_with_object(&e.1))?; } map.end() - } else if let PyObjectPayload::None = self.pyobject.payload { + } else if self.pyobject.is(&self.vm.get_none()) { serializer.serialize_none() } else { Err(serde::ser::Error::custom(format!( diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 6c6659d4db..1d4aab0a11 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -473,10 +473,12 @@ impl VirtualMachine { // Add missing positional arguments, if we have fewer positional arguments than the // function definition calls for if nargs < nexpected_args { - let available_defaults = match defaults.payload { - PyObjectPayload::Sequence { ref elements } => elements.borrow().clone(), - PyObjectPayload::None => vec![], - _ => panic!("function defaults not tuple or None"), + let available_defaults = if defaults.is(&self.get_none()) { + vec![] + } else if let PyObjectPayload::Sequence { ref elements } = defaults.payload { + elements.borrow().clone() + } else { + panic!("function defaults not tuple or None"); }; // Given the number of defaults available, check all the arguments for which we From 2a593aa1007e3efd1d1be60ef4f2394ee7f163a2 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Wed, 6 Mar 2019 18:11:52 +0000 Subject: [PATCH 170/380] Add objmodule. --- vm/src/obj/mod.rs | 1 + vm/src/obj/objmodule.rs | 30 ++++++++++++++++++++++++++++++ vm/src/pyobject.rs | 6 ++++++ 3 files changed, 37 insertions(+) create mode 100644 vm/src/obj/objmodule.rs diff --git a/vm/src/obj/mod.rs b/vm/src/obj/mod.rs index 95aacba417..6cbb5fa66f 100644 --- a/vm/src/obj/mod.rs +++ b/vm/src/obj/mod.rs @@ -18,6 +18,7 @@ pub mod objiter; pub mod objlist; pub mod objmap; pub mod objmemory; +pub mod objmodule; pub mod objnone; pub mod objobject; pub mod objproperty; diff --git a/vm/src/obj/objmodule.rs b/vm/src/obj/objmodule.rs new file mode 100644 index 0000000000..ef6cf2d6df --- /dev/null +++ b/vm/src/obj/objmodule.rs @@ -0,0 +1,30 @@ +use crate::frame::ScopeRef; +use crate::pyobject::{ + DictProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, +}; +use crate::vm::VirtualMachine; + +fn module_dir(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(obj, Some(vm.ctx.module_type()))]); + let scope = get_scope(obj); + let keys = scope + .locals + .get_key_value_pairs() + .iter() + .map(|(k, _v)| k.clone()) + .collect(); + Ok(vm.ctx.new_list(keys)) +} + +pub fn init(context: &PyContext) { + let module_type = &context.module_type; + context.set_attr(&module_type, "__dir__", context.new_rustfunc(module_dir)); +} + +fn get_scope(obj: &PyObjectRef) -> &ScopeRef { + if let PyObjectPayload::Module { ref scope, .. } = &obj.payload { + &scope + } else { + panic!("Can't get scope from non-module.") + } +} diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 6e0ab27011..00bce8458c 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -31,6 +31,7 @@ use crate::obj::objiter; use crate::obj::objlist; use crate::obj::objmap; use crate::obj::objmemory; +use crate::obj::objmodule; use crate::obj::objnone; use crate::obj::objobject; use crate::obj::objproperty; @@ -317,6 +318,7 @@ impl PyContext { objcode::init(&context); objframe::init(&context); objnone::init(&context); + objmodule::init(&context); exceptions::init(&context); context } @@ -357,6 +359,10 @@ impl PyContext { self.list_type.clone() } + pub fn module_type(&self) -> PyObjectRef { + self.module_type.clone() + } + pub fn set_type(&self) -> PyObjectRef { self.set_type.clone() } From 10bbfc6a3437ceeaed6c8b2146c7742de797c6c3 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Wed, 6 Mar 2019 18:13:27 +0000 Subject: [PATCH 171/380] Implement dir via __dir__. --- tests/snippets/builtin_dir.py | 13 ++++++++++++- vm/src/builtins.rs | 16 +++------------- vm/src/obj/objobject.rs | 31 +++++++++++++++++++++++++++++-- vm/src/obj/objtype.rs | 34 ++++++++++++++++------------------ 4 files changed, 60 insertions(+), 34 deletions(-) diff --git a/tests/snippets/builtin_dir.py b/tests/snippets/builtin_dir.py index b0c3949fa0..a121fb718c 100644 --- a/tests/snippets/builtin_dir.py +++ b/tests/snippets/builtin_dir.py @@ -5,7 +5,18 @@ def test(): a = A() -assert "test" in dir(a) +assert "test" in dir(a), "test not in a" +assert "test" in dir(A), "test not in A" + +class B(A): + def __dir__(self): + return ('q', 'h') + +# Gets sorted and turned into a list +assert ['h', 'q'] == dir(B()) + +# This calls type.__dir__ so isn't changed (but inheritance works)! +assert 'test' in dir(A) import socket diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index b146fcf913..bcc5755a1f 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -40,18 +40,6 @@ fn dir_locals(vm: &mut VirtualMachine) -> PyObjectRef { get_locals(vm) } -fn dir_object(vm: &mut VirtualMachine, obj: &PyObjectRef) -> PyObjectRef { - // Gather all members here: - let attributes = objtype::get_attributes(obj); - let mut members: Vec = attributes.into_iter().map(|(n, _o)| n).collect(); - - // Sort members: - members.sort(); - - let members_pystr = members.into_iter().map(|m| vm.ctx.new_str(m)).collect(); - vm.ctx.new_list(members_pystr) -} - fn builtin_abs(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(x, None)]); match vm.get_method(x.clone(), "__abs__") { @@ -171,7 +159,9 @@ fn builtin_dir(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(dir_locals(vm)) } else { let obj = args.args.into_iter().next().unwrap(); - Ok(dir_object(vm, &obj)) + let seq = vm.call_method(&obj, "__dir__", vec![])?; + let sorted = builtin_sorted(vm, PyFuncArgs::new(vec![seq], vec![]))?; + Ok(sorted) } } diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 73a4df608f..e21941cda6 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, PyObject, PyObjectPayload, - PyObjectRef, PyResult, TypeProtocol, + AttributeProtocol, DictProtocol, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObject, + PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use std::cell::RefCell; @@ -127,6 +127,18 @@ fn object_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_str(format!("<{} object at 0x{:x}>", type_name, address))) } +pub fn object_dir(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(obj, None)]); + + let attributes = get_attributes(&obj); + Ok(vm.ctx.new_list( + attributes + .keys() + .map(|k| vm.ctx.new_str(k.to_string())) + .collect(), + )) +} + fn object_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -161,6 +173,7 @@ pub fn init(context: &PyContext) { "__dict__", context.new_member_descriptor(object_dict), ); + context.set_attr(&object, "__dir__", context.new_rustfunc(object_dir)); 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)); @@ -238,3 +251,17 @@ fn object_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { )) } } + +pub fn get_attributes(obj: &PyObjectRef) -> PyAttributes { + // Get class attributes: + let mut attributes = objtype::get_attributes(&obj.typ()); + + // Get instance attributes: + if let PyObjectPayload::Instance { dict } = &obj.payload { + for (name, value) in dict.borrow().iter() { + attributes.insert(name.to_string(), value.clone()); + } + } + + attributes +} diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 086f7f28f0..3962fd2856 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -1,9 +1,8 @@ use super::objdict; use super::objstr; -use super::objtype; // Required for arg_check! to use isinstance use crate::pyobject::{ - AttributeProtocol, DictProtocol, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObject, - PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + AttributeProtocol, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObject, PyObjectPayload, + PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use std::cell::RefCell; @@ -67,6 +66,7 @@ pub fn init(context: &PyContext) { context.new_rustfunc(type_subclass_check), ); context.set_attr(&type_type, "__doc__", context.new_str(type_doc.to_string())); + context.set_attr(&type_type, "__dir__", context.new_rustfunc(type_dir)); } fn type_mro(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -256,12 +256,23 @@ pub fn type_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult } } +pub fn type_dir(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(obj, None)]); + + let attributes = get_attributes(&obj); + Ok(vm.ctx.new_list( + attributes + .keys() + .map(|k| vm.ctx.new_str(k.to_string())) + .collect(), + )) +} + pub fn get_attributes(obj: &PyObjectRef) -> PyAttributes { // Gather all members here: let mut attributes = PyAttributes::new(); - // Get class attributes: - let mut base_classes = objtype::base_classes(obj); + let mut base_classes = _mro(obj.clone()).expect("Type get_attributes on non-type"); base_classes.reverse(); for bc in base_classes { if let PyObjectPayload::Class { dict, .. } = &bc.payload { @@ -271,19 +282,6 @@ pub fn get_attributes(obj: &PyObjectRef) -> PyAttributes { } } - // Get instance attributes: - if let PyObjectPayload::Instance { dict } = &obj.payload { - for (name, value) in dict.borrow().iter() { - attributes.insert(name.to_string(), value.clone()); - } - } - - // Get module attributes: - if let PyObjectPayload::Module { ref scope, .. } = &obj.payload { - for (name, value) in scope.locals.get_key_value_pairs().iter() { - attributes.insert(objstr::get_value(name).to_string(), value.clone()); - } - } attributes } From d769fa93175ca2792bd89a66e89894ce62000e48 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Wed, 6 Mar 2019 19:08:37 +0000 Subject: [PATCH 172/380] Remove unused function. --- vm/src/obj/objtype.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 3962fd2856..750ccff2d6 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -95,10 +95,6 @@ fn _mro(cls: PyObjectRef) -> Option> { } } -pub fn base_classes(obj: &PyObjectRef) -> Vec { - _mro(obj.typ()).unwrap() -} - /// Determines if `obj` actually an instance of `cls`, this doesn't call __instancecheck__, so only /// use this if `cls` is known to have not overridden the base __instancecheck__ magic method. pub fn isinstance(obj: &PyObjectRef, cls: &PyObjectRef) -> bool { From e0c398cbd5eb67087bae5a8d627d42c923d97d44 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Wed, 6 Mar 2019 20:57:03 +0000 Subject: [PATCH 173/380] Eliminate NoPayload and just use a boxed (). --- vm/src/pyobject.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index f644e16987..0e8d31e653 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -155,7 +155,9 @@ pub struct PyContext { fn _nothing() -> PyObjectRef { PyObject { - payload: PyObjectPayload::NoPayload, + payload: PyObjectPayload::AnyRustValue { + value: Box::new(()), + }, typ: None, } .into_ref() @@ -223,14 +225,23 @@ impl PyContext { let exceptions = exceptions::ExceptionZoo::new(&type_type, &object_type, &dict_type); let none = PyObject::new( - PyObjectPayload::NoPayload, + PyObjectPayload::AnyRustValue { + value: Box::new(()), + }, create_type("NoneType", &type_type, &object_type, &dict_type), ); - let ellipsis = PyObject::new(PyObjectPayload::NoPayload, ellipsis_type.clone()); + let ellipsis = PyObject::new( + PyObjectPayload::AnyRustValue { + value: Box::new(()), + }, + ellipsis_type.clone(), + ); let not_implemented = PyObject::new( - PyObjectPayload::NoPayload, + PyObjectPayload::AnyRustValue { + value: Box::new(()), + }, create_type("NotImplementedType", &type_type, &object_type, &dict_type), ); @@ -1484,7 +1495,6 @@ pub enum PyObjectPayload { name: String, scope: ScopeRef, }, - NoPayload, Class { name: String, dict: RefCell, @@ -1525,7 +1535,6 @@ impl fmt::Debug for PyObjectPayload { ref object, } => write!(f, "bound-method: {:?} of {:?}", function, object), PyObjectPayload::Module { .. } => write!(f, "module"), - PyObjectPayload::NoPayload => write!(f, "NoPayload"), PyObjectPayload::Class { ref name, .. } => write!(f, "class {:?}", name), PyObjectPayload::Instance { .. } => write!(f, "instance"), PyObjectPayload::RustFunction { .. } => write!(f, "rust function"), From d7f8961a120f0dbe6f65c23707464846c77d0688 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Wed, 6 Mar 2019 22:30:11 +0100 Subject: [PATCH 174/380] Simplify some PyFuncArgs occurences. --- vm/src/obj/objfloat.rs | 8 +------- vm/src/obj/objlist.rs | 8 +------- vm/src/obj/objmap.rs | 8 +------- vm/src/obj/objobject.rs | 16 ++-------------- 4 files changed, 5 insertions(+), 35 deletions(-) diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index d2bf03ef5b..71cb803fac 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -107,13 +107,7 @@ pub fn make_float(vm: &mut VirtualMachine, obj: &PyObjectRef) -> PyResult { if objtype::isinstance(obj, &vm.ctx.float_type()) { Ok(get_value(obj)) } else if let Ok(method) = vm.get_method(obj.clone(), "__float__") { - let res = vm.invoke( - method, - PyFuncArgs { - args: vec![], - kwargs: vec![], - }, - )?; + let res = vm.invoke(method, vec![])?; Ok(get_value(&res)) } else { Err(vm.new_type_error(format!("Cannot cast {} to float", obj))) diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index e09f15a8b1..5f2643e785 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -386,13 +386,7 @@ fn do_sort( 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![], - }, - )?, + Some(ref func) => vm.invoke((*func).clone(), vec![x.clone()])?, }); } diff --git a/vm/src/obj/objmap.rs b/vm/src/obj/objmap.rs index 78cbdcf74d..5ecf171234 100644 --- a/vm/src/obj/objmap.rs +++ b/vm/src/obj/objmap.rs @@ -38,13 +38,7 @@ fn map_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .collect::, _>>()?; // the mapper itself can raise StopIteration which does stop the map iteration - vm.invoke( - mapper.clone(), - PyFuncArgs { - args: next_objs, - kwargs: vec![], - }, - ) + vm.invoke(mapper.clone(), next_objs) } else { panic!("map doesn't have correct payload"); } diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index e21941cda6..a387028cbb 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -220,13 +220,7 @@ fn object_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let attr_class = attr.typ(); if attr_class.has_attr("__set__") { if let Some(descriptor) = attr_class.get_attr("__get__") { - return vm.invoke( - descriptor, - PyFuncArgs { - args: vec![attr, obj.clone(), cls], - kwargs: vec![], - }, - ); + return vm.invoke(descriptor, vec![attr, obj.clone(), cls]); } } } @@ -236,13 +230,7 @@ fn object_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } 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![], - }, - ) + vm.invoke(getter, vec![cls, name_str.clone()]) } else { let attribute_error = vm.context().exceptions.attribute_error.clone(); Err(vm.new_exception( From 7b8c8900bcfc456c8524b7db4a8582ccccb4ca7c Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Wed, 6 Mar 2019 18:54:57 -0600 Subject: [PATCH 175/380] Allow JS errors to get the row and column info of the error --- wasm/lib/src/vm_class.rs | 44 +++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index 0da541ade0..f950f2c86e 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -1,7 +1,7 @@ use crate::browser_module::setup_browser_module; use crate::convert; use crate::wasm_builtins; -use js_sys::{Object, SyntaxError, TypeError}; +use js_sys::{Object, Reflect, SyntaxError, TypeError}; use rustpython_vm::{ compile, frame::ScopeRef, @@ -318,10 +318,44 @@ impl WASMVirtualMachine { }| { source.push('\n'); let code = - compile::compile(&source, &mode, "".to_string(), vm.ctx.code_type()) - .map_err(|err| { - SyntaxError::new(&format!("Error parsing Python code: {}", err)) - })?; + compile::compile(&source, &mode, "".to_string(), vm.ctx.code_type()); + let code = code.map_err(|err| { + let js_err = SyntaxError::new(&format!("Error parsing Python code: {}", err)); + if let rustpython_vm::error::CompileError::Parse(ref parse_error) = err { + use rustpython_parser::error::ParseError; + if let ParseError::EOF(Some(ref loc)) + | ParseError::ExtraToken((ref loc, ..)) + | ParseError::InvalidToken(ref loc) + | ParseError::UnrecognizedToken((ref loc, ..), _) = parse_error + { + let _ = Reflect::set( + &js_err, + &"row".into(), + &(loc.get_row() as u32).into(), + ); + let _ = Reflect::set( + &js_err, + &"col".into(), + &(loc.get_column() as u32).into(), + ); + } + if let ParseError::ExtraToken((_, _, ref loc)) + | ParseError::UnrecognizedToken((_, _, ref loc), _) = parse_error + { + let _ = Reflect::set( + &js_err, + &"endrow".into(), + &(loc.get_row() as u32).into(), + ); + let _ = Reflect::set( + &js_err, + &"endcol".into(), + &(loc.get_column() as u32).into(), + ); + } + } + js_err + })?; let result = vm.run_code_obj(code, scope.clone()); convert::pyresult_to_jsresult(vm, result) }, From ecc92ffe33b20b45f9c550dbad97cade93346240 Mon Sep 17 00:00:00 2001 From: Joey Date: Wed, 6 Mar 2019 19:25:54 -0800 Subject: [PATCH 176/380] Convert int to Any payload --- vm/src/frame.rs | 23 +++++++++------- vm/src/obj/objbool.rs | 29 ++++++++++---------- vm/src/obj/objint.rs | 56 ++++++++++++++++++++++----------------- vm/src/obj/objrange.rs | 27 ++++++++++--------- vm/src/obj/objsequence.rs | 11 ++++---- vm/src/pyobject.rs | 18 ++++++------- 6 files changed, 90 insertions(+), 74 deletions(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 9777596070..3186ae564b 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -1,9 +1,11 @@ -extern crate rustpython_parser; - -use self::rustpython_parser::ast; use std::cell::RefCell; use std::fmt; use std::path::PathBuf; +use std::rc::Rc; + +use num_bigint::BigInt; + +use rustpython_parser::ast; use crate::builtins; use crate::bytecode; @@ -12,6 +14,7 @@ use crate::obj::objbool; use crate::obj::objcode; use crate::obj::objdict; use crate::obj::objdict::PyDict; +use crate::obj::objint::PyInt; use crate::obj::objiter; use crate::obj::objlist; use crate::obj::objstr; @@ -21,8 +24,6 @@ use crate::pyobject::{ TypeProtocol, }; use crate::vm::VirtualMachine; -use num_bigint::BigInt; -use std::rc::Rc; /* * So a scope is a linked list of scopes. @@ -286,10 +287,14 @@ impl Frame { let mut out: Vec> = elements .into_iter() - .map(|x| match x.payload { - PyObjectPayload::Integer { ref value } => Some(value.clone()), - PyObjectPayload::None => None, - _ => panic!("Expect Int or None as BUILD_SLICE arguments, got {:?}", x), + .map(|x| { + if x.is(&vm.ctx.none()) { + None + } else if let Some(i) = x.payload::() { + Some(i.value.clone()) + } else { + panic!("Expect Int or None as BUILD_SLICE arguments") + } }) .collect(); diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index e45018ea60..0f272d1710 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -1,12 +1,15 @@ -use super::objdict::PyDict; -use super::objfloat::PyFloat; -use super::objstr::PyString; -use super::objtype; +use num_traits::Zero; + use crate::pyobject::{ IntoPyObject, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; -use num_traits::Zero; + +use super::objdict::PyDict; +use super::objfloat::PyFloat; +use super::objint::PyInt; +use super::objstr::PyString; +use super::objtype; impl IntoPyObject for bool { fn into_pyobject(self, ctx: &PyContext) -> PyResult { @@ -24,16 +27,18 @@ pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result() { return Ok(!dict.entries.borrow().is_empty()); } + if let Some(i) = obj.payload::() { + return Ok(!i.value.is_zero()); + } let result = match obj.payload { - PyObjectPayload::Integer { ref value } => !value.is_zero(), PyObjectPayload::Sequence { ref elements } => !elements.borrow().is_empty(), PyObjectPayload::None { .. } => false, _ => { if let Ok(f) = vm.get_method(obj.clone(), "__bool__") { let bool_res = vm.invoke(f, PyFuncArgs::default())?; - match bool_res.payload { - PyObjectPayload::Integer { ref value } => !value.is_zero(), - _ => return Err(vm.new_type_error(String::from("TypeError"))), + match bool_res.payload::() { + Some(i) => !i.value.is_zero(), + None => return Err(vm.new_type_error(String::from("TypeError"))), } } else { true @@ -67,11 +72,7 @@ pub fn not(vm: &mut VirtualMachine, obj: &PyObjectRef) -> PyResult { // Retrieve inner int value: pub fn get_value(obj: &PyObjectRef) -> bool { - if let PyObjectPayload::Integer { value } = &obj.payload { - !value.is_zero() - } else { - panic!("Inner error getting inner boolean"); - } + !obj.payload::().unwrap().value.is_zero() } fn bool_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result { diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index a79dfb6dd5..d2fe571cac 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -1,29 +1,39 @@ -use super::objfloat; -use super::objstr; -use super::objtype; +use std::hash::{Hash, Hasher}; + +use num_bigint::{BigInt, ToBigInt}; +use num_integer::Integer; +use num_traits::{Pow, Signed, ToPrimitive, Zero}; + use crate::format::FormatSpec; use crate::pyobject::{ - FromPyObjectRef, IntoPyObject, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, - PyResult, TryFromObject, TypeProtocol, + FromPyObjectRef, IntoPyObject, PyContext, PyFuncArgs, PyObject, PyObjectPayload, + PyObjectPayload2, PyObjectRef, PyResult, TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; -use num_bigint::{BigInt, ToBigInt}; -use num_integer::Integer; -use num_traits::{Pow, Signed, ToPrimitive, Zero}; -use std::hash::{Hash, Hasher}; -// This proxy allows for easy switching between types. -type IntType = BigInt; +use super::objfloat; +use super::objstr; +use super::objtype; -pub type PyInt = BigInt; +#[derive(Debug)] +pub struct PyInt { + // TODO: shouldn't be public + pub value: BigInt, +} -impl IntoPyObject for PyInt { - fn into_pyobject(self, ctx: &PyContext) -> PyResult { - Ok(ctx.new_int(self)) +impl PyInt { + pub fn new(i: T) -> Self { + PyInt { + value: i.to_bigint().unwrap(), + } } } -// TODO: macro to impl for all primitive ints +impl PyObjectPayload2 for PyInt { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.int_type() + } +} impl IntoPyObject for usize { fn into_pyobject(self, ctx: &PyContext) -> PyResult { @@ -77,13 +87,15 @@ fn int_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { None => Zero::zero(), }; Ok(PyObject::new( - PyObjectPayload::Integer { value: val }, + PyObjectPayload::AnyRustValue { + value: Box::new(PyInt::new(val)), + }, cls.clone(), )) } // Casting function: -pub fn to_int(vm: &mut VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult { +pub fn to_int(vm: &mut VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult { let val = if objtype::isinstance(obj, &vm.ctx.int_type()) { get_value(obj) } else if objtype::isinstance(obj, &vm.ctx.float_type()) { @@ -111,12 +123,8 @@ pub fn to_int(vm: &mut VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult } // Retrieve inner int value: -pub fn get_value(obj: &PyObjectRef) -> IntType { - if let PyObjectPayload::Integer { value } = &obj.payload { - value.clone() - } else { - panic!("Inner error getting int {:?}", obj); - } +pub fn get_value(obj: &PyObjectRef) -> BigInt { + obj.payload::().unwrap().value.clone() } impl FromPyObjectRef for BigInt { diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 41a4c69645..55f736da56 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -1,16 +1,18 @@ use std::cell::Cell; use std::ops::Mul; -use super::objint; -use super::objtype; +use num_bigint::{BigInt, Sign}; +use num_integer::Integer; +use num_traits::{One, Signed, ToPrimitive, Zero}; + use crate::pyobject::{ PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; -use num_bigint::{BigInt, Sign}; -use num_integer::Integer; -use num_traits::{One, Signed, ToPrimitive, Zero}; + +use super::objint::{self, PyInt}; +use super::objtype; #[derive(Debug, Clone)] pub struct PyRange { @@ -284,14 +286,15 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let range = get_value(zelf); + if let Some(i) = subscript.payload::() { + return if let Some(int) = range.get(i.value.clone()) { + Ok(vm.ctx.new_int(int)) + } else { + Err(vm.new_index_error("range object index out of range".to_string())) + }; + } + match subscript.payload { - PyObjectPayload::Integer { ref 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())) - } - } PyObjectPayload::Slice { ref start, ref stop, diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index 46912c1b8f..5f875a5b07 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -3,7 +3,7 @@ use std::marker::Sized; use std::ops::{Deref, DerefMut, Range}; use super::objbool; -use super::objint; +use super::objint::{self, PyInt}; use crate::pyobject::{IdProtocol, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; use num_bigint::BigInt; @@ -141,8 +141,8 @@ pub fn get_item( elements: &[PyObjectRef], subscript: PyObjectRef, ) -> PyResult { - match &subscript.payload { - PyObjectPayload::Integer { value } => match value.to_i32() { + if let Some(i) = subscript.payload::() { + return match i.value.to_i32() { Some(value) => { if let Some(pos_index) = elements.to_vec().get_pos(value) { let obj = elements[pos_index].clone(); @@ -154,8 +154,9 @@ pub fn get_item( None => { Err(vm.new_index_error("cannot fit 'int' into an index-sized integer".to_string())) } - }, - + }; + } + match &subscript.payload { PyObjectPayload::Slice { .. } => Ok(PyObject::new( match &sequence.payload { PyObjectPayload::Sequence { .. } => PyObjectPayload::Sequence { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 6a618cd7b6..9ab66373ac 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -26,7 +26,7 @@ use crate::obj::objfloat::{self, PyFloat}; use crate::obj::objframe; use crate::obj::objfunction; use crate::obj::objgenerator; -use crate::obj::objint; +use crate::obj::objint::{self, PyInt}; use crate::obj::objiter; use crate::obj::objlist; use crate::obj::objmap; @@ -236,12 +236,14 @@ impl PyContext { ); let true_value = PyObject::new( - PyObjectPayload::Integer { value: One::one() }, + PyObjectPayload::AnyRustValue { + value: Box::new(PyInt::new(BigInt::one())), + }, bool_type.clone(), ); let false_value = PyObject::new( - PyObjectPayload::Integer { - value: Zero::zero(), + PyObjectPayload::AnyRustValue { + value: Box::new(PyInt::new(BigInt::zero())), }, bool_type.clone(), ); @@ -474,8 +476,8 @@ impl PyContext { pub fn new_int(&self, i: T) -> PyObjectRef { PyObject::new( - PyObjectPayload::Integer { - value: i.to_bigint().unwrap(), + PyObjectPayload::AnyRustValue { + value: Box::new(PyInt::new(i)), }, self.int_type(), ) @@ -1445,9 +1447,6 @@ into_py_native_func_tuple!((a, A), (b, B), (c, C), (d, D), (e, E)); /// of rust data for a particular python object. Determine the python type /// by using for example the `.typ()` method on a python object. pub enum PyObjectPayload { - Integer { - value: BigInt, - }, Sequence { elements: RefCell>, }, @@ -1524,7 +1523,6 @@ pub enum PyObjectPayload { impl fmt::Debug for PyObjectPayload { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - PyObjectPayload::Integer { ref value } => write!(f, "int {}", value), PyObjectPayload::MemoryView { ref obj } => write!(f, "bytes/bytearray {:?}", obj), PyObjectPayload::Sequence { .. } => write!(f, "list or tuple"), PyObjectPayload::WeakRef { .. } => write!(f, "weakref"), From 4a000c424c66c27c4f6318622de2817abb82da23 Mon Sep 17 00:00:00 2001 From: Joey Date: Wed, 6 Mar 2019 19:53:03 -0800 Subject: [PATCH 177/380] Improvements to OptionalArg extractor --- vm/src/obj/objstr.rs | 20 ++++++++++---------- vm/src/pyobject.rs | 29 +++++++++++++++++++---------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 4ee7f38f54..6d9d622d04 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -4,8 +4,8 @@ use super::objtype; use crate::format::{FormatParseError, FormatPart, FormatString}; use crate::function::PyRef; use crate::pyobject::{ - IntoPyObject, OptArg, PyContext, PyFuncArgs, PyIterable, PyObjectPayload, PyObjectPayload2, - PyObjectRef, PyResult, TypeProtocol, + IntoPyObject, OptionalArg, PyContext, PyFuncArgs, PyIterable, PyObjectPayload, + PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use num_traits::ToPrimitive; @@ -160,24 +160,24 @@ impl PyStringRef { fn endswith( self, suffix: PyStringRef, - start: OptArg, - end: OptArg, + start: OptionalArg, + end: OptionalArg, _vm: &mut VirtualMachine, ) -> bool { - let start = start.unwrap_or(0); - let end = end.unwrap_or(self.value.len()); + let start = start.into_option().unwrap_or(0); + let end = end.into_option().unwrap_or(self.value.len()); self.value[start..end].ends_with(&suffix.value) } fn startswith( self, prefix: PyStringRef, - start: OptArg, - end: OptArg, + start: OptionalArg, + end: OptionalArg, _vm: &mut VirtualMachine, ) -> bool { - let start = start.unwrap_or(0); - let end = end.unwrap_or(self.value.len()); + let start = start.into_option().unwrap_or(0); + let end = end.into_option().unwrap_or(self.value.len()); self.value[start..end].starts_with(&prefix.value) } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 6a618cd7b6..925c564810 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1212,17 +1212,26 @@ where } } -pub struct OptArg(Option); +/// An argument that may or may not be provided by the caller. +/// +/// This style of argument is not possible in pure Python. +pub enum OptionalArg { + Present(T), + Missing, +} -impl std::ops::Deref for OptArg { - type Target = Option; +use self::OptionalArg::*; - fn deref(&self) -> &Option { - &self.0 +impl OptionalArg { + pub fn into_option(self) -> Option { + match self { + Present(value) => Some(value), + Missing => None, + } } } -impl FromArgs for OptArg +impl FromArgs for OptionalArg where T: TryFromObject, { @@ -1237,16 +1246,16 @@ where where I: Iterator, { - Ok(OptArg(if let Some(PyArg::Positional(_)) = args.peek() { + Ok(if let Some(PyArg::Positional(_)) = args.peek() { let value = if let Some(PyArg::Positional(value)) = args.next() { value } else { unreachable!() }; - Some(T::try_from_object(vm, value)?) + Present(T::try_from_object(vm, value)?) } else { - None - })) + Missing + }) } } From f9ab27296ac07605f1cc8479b8b494b39c2c1864 Mon Sep 17 00:00:00 2001 From: Joey Date: Wed, 6 Mar 2019 20:38:52 -0800 Subject: [PATCH 178/380] Add conversions for all primitive ints --- vm/src/obj/objint.rs | 66 +++++++++++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index d2fe571cac..8e5f3f59c6 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -5,6 +5,7 @@ use num_integer::Integer; use num_traits::{Pow, Signed, ToPrimitive, Zero}; use crate::format::FormatSpec; +use crate::function::PyRef; use crate::pyobject::{ FromPyObjectRef, IntoPyObject, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, TryFromObject, TypeProtocol, @@ -21,6 +22,8 @@ pub struct PyInt { pub value: BigInt, } +pub type PyIntRef = PyRef; + impl PyInt { pub fn new(i: T) -> Self { PyInt { @@ -35,31 +38,48 @@ impl PyObjectPayload2 for PyInt { } } -impl IntoPyObject for usize { - fn into_pyobject(self, ctx: &PyContext) -> PyResult { - Ok(ctx.new_int(self)) - } -} - -impl TryFromObject for usize { - fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { - // FIXME: don't use get_value - match get_value(&obj).to_usize() { - Some(value) => Ok(value), - None => Err(vm.new_overflow_error("Int value cannot fit into Rust usize".to_string())), +macro_rules! impl_into_pyobject_int { + ($($t:ty)*) => {$( + impl IntoPyObject for $t { + fn into_pyobject(self, ctx: &PyContext) -> PyResult { + Ok(ctx.new_int(self)) + } } - } -} - -impl TryFromObject for isize { - fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { - // FIXME: don't use get_value - match get_value(&obj).to_isize() { - Some(value) => Ok(value), - None => Err(vm.new_overflow_error("Int value cannot fit into Rust isize".to_string())), + )*}; +} + +impl_into_pyobject_int!(isize i8 i16 i32 i64 usize u8 u16 u32 u64) ; + +macro_rules! impl_try_from_object_int { + ($(($t:ty, $to_prim:ident),)*) => {$( + impl TryFromObject for $t { + fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { + match PyRef::::try_from_object(vm, obj)?.value.$to_prim() { + Some(value) => Ok(value), + None => Err( + vm.new_overflow_error(concat!( + "Int value cannot fit into Rust ", + stringify!($t) + ).to_string()) + ), + } + } } - } -} + )*}; +} + +impl_try_from_object_int!( + (isize, to_isize), + (i8, to_i8), + (i16, to_i16), + (i32, to_i32), + (i64, to_i64), + (usize, to_usize), + (u8, to_u8), + (u16, to_u16), + (u32, to_u32), + (u64, to_u64), +); fn int_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(int, Some(vm.ctx.int_type()))]); From 9d03abf652dca9f1606121570a82d63527b458c2 Mon Sep 17 00:00:00 2001 From: ben Date: Thu, 7 Mar 2019 20:05:32 +1300 Subject: [PATCH 179/380] Move __class__ attribute to object to make it work in more situations --- tests/snippets/types_snippet.py | 9 +++++++++ vm/src/obj/objfunction.rs | 34 +++++++++++++++++++++++++++++++++ vm/src/obj/objobject.rs | 20 +++++++++++++++++++ vm/src/obj/objtype.rs | 5 ----- vm/src/pyobject.rs | 22 +++++++++++++++++++++ 5 files changed, 85 insertions(+), 5 deletions(-) diff --git a/tests/snippets/types_snippet.py b/tests/snippets/types_snippet.py index 3fc27b71c0..ac715751ef 100644 --- a/tests/snippets/types_snippet.py +++ b/tests/snippets/types_snippet.py @@ -15,5 +15,14 @@ assert type(cls) is metaclass assert type(metaclass) is type +assert issubclass(metaclass, type) +assert isinstance(cls, type) + +assert inst.__class__ is cls +assert cls.__class__ is metaclass +assert metaclass.__class__ is type +assert type.__class__ is type +assert None.__class__ is type(None) + assert isinstance(type, type) assert issubclass(type, type) diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index a2092c308f..82c06fac63 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -27,6 +27,18 @@ pub fn init(context: &PyContext) { context.new_rustfunc(member_get), ); + let data_descriptor_type = &context.data_descriptor_type; + context.set_attr( + &data_descriptor_type, + "__get__", + context.new_rustfunc(data_get), + ); + context.set_attr( + &data_descriptor_type, + "__set__", + context.new_rustfunc(data_set), + ); + let classmethod_type = &context.classmethod_type; context.set_attr( &classmethod_type, @@ -83,6 +95,28 @@ fn member_get(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { } } +fn data_get(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { + match args.shift().get_attr("fget") { + Some(function) => vm.invoke(function, args), + None => { + println!("A"); + let attribute_error = vm.context().exceptions.attribute_error.clone(); + Err(vm.new_exception(attribute_error, String::from("Attribute Error"))) + } + } +} + +fn data_set(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { + match args.shift().get_attr("fset") { + Some(function) => vm.invoke(function, args), + None => { + println!("B"); + let attribute_error = vm.context().exceptions.attribute_error.clone(); + Err(vm.new_exception(attribute_error, String::from("Attribute Error"))) + } + } +} + // Classmethod type methods: fn classmethod_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("classmethod.__get__ {:?}", args.args); diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index a387028cbb..99df1f28dd 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -161,6 +161,11 @@ pub fn init(context: &PyContext) { context.set_attr(&object, "__new__", context.new_rustfunc(new_instance)); context.set_attr(&object, "__init__", context.new_rustfunc(object_init)); + context.set_attr( + &object, + "__class__", + context.new_data_descriptor(object_class, object_class_setter), + ); 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)); @@ -190,6 +195,21 @@ fn object_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { Ok(vm.ctx.none()) } +fn object_class(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(_obj, None), (owner, Some(vm.ctx.type_type()))] + ); + Ok(owner.clone()) +} + +fn object_class_setter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(instance, None), (_value, None)]); + let type_repr = vm.to_pystr(&instance.typ())?; + Err(vm.new_type_error(format!("can't change class of type '{}'", type_repr))) +} + fn object_dict(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match args.args[0].payload { PyObjectPayload::Class { ref dict, .. } | PyObjectPayload::Instance { ref dict, .. } => { diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index b30c77226d..dfe11510bc 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -39,11 +39,6 @@ pub fn init(context: &PyContext) { "__mro__", context.new_member_descriptor(type_mro), ); - context.set_attr( - &type_type, - "__class__", - context.new_member_descriptor(type_new), - ); context.set_attr(&type_type, "__repr__", context.new_rustfunc(type_repr)); context.set_attr( &type_type, diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 6a618cd7b6..34a27548f5 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -150,6 +150,7 @@ pub struct PyContext { pub module_type: PyObjectRef, pub bound_method_type: PyObjectRef, pub member_descriptor_type: PyObjectRef, + pub data_descriptor_type: PyObjectRef, pub object: PyObjectRef, pub exceptions: exceptions::ExceptionZoo, } @@ -199,6 +200,8 @@ impl PyContext { let bound_method_type = create_type("method", &type_type, &object_type, &dict_type); let member_descriptor_type = create_type("member_descriptor", &type_type, &object_type, &dict_type); + let data_descriptor_type = + create_type("data_descriptor", &type_type, &object_type, &dict_type); let str_type = create_type("str", &type_type, &object_type, &dict_type); let list_type = create_type("list", &type_type, &object_type, &dict_type); let set_type = create_type("set", &type_type, &object_type, &dict_type); @@ -285,6 +288,7 @@ impl PyContext { module_type, bound_method_type, member_descriptor_type, + data_descriptor_type, type_type, exceptions, }; @@ -449,6 +453,10 @@ impl PyContext { pub fn member_descriptor_type(&self) -> PyObjectRef { self.member_descriptor_type.clone() } + pub fn data_descriptor_type(&self) -> PyObjectRef { + self.data_descriptor_type.clone() + } + pub fn type_type(&self) -> PyObjectRef { self.type_type.clone() } @@ -658,6 +666,20 @@ impl PyContext { self.new_instance(self.member_descriptor_type(), Some(dict)) } + pub fn new_data_descriptor< + G: 'static + Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult, + S: 'static + Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult, + >( + &self, + getter: G, + setter: S, + ) -> PyObjectRef { + let mut dict = PyAttributes::new(); + dict.insert("fget".to_string(), self.new_rustfunc(getter)); + dict.insert("fset".to_string(), self.new_rustfunc(setter)); + self.new_instance(self.data_descriptor_type(), Some(dict)) + } + pub fn new_instance(&self, class: PyObjectRef, dict: Option) -> PyObjectRef { let dict = if let Some(dict) = dict { dict From 720bec2f4f480b8520736b7c5a95fa4b406855a9 Mon Sep 17 00:00:00 2001 From: ben Date: Thu, 7 Mar 2019 20:46:07 +1300 Subject: [PATCH 180/380] Convert __class__ descriptor to use new style functions --- vm/src/obj/objobject.rs | 16 +++++++--------- vm/src/pyobject.rs | 6 ++++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 99df1f28dd..6b01fd662d 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -195,17 +195,15 @@ fn object_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { Ok(vm.ctx.none()) } -fn object_class(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(_obj, None), (owner, Some(vm.ctx.type_type()))] - ); - Ok(owner.clone()) +fn object_class(_obj: PyObjectRef, owner: PyObjectRef, _vm: &mut VirtualMachine) -> PyObjectRef { + owner } -fn object_class_setter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(instance, None), (_value, None)]); +fn object_class_setter( + instance: PyObjectRef, + _value: PyObjectRef, + vm: &mut VirtualMachine, +) -> PyResult { let type_repr = vm.to_pystr(&instance.typ())?; Err(vm.new_type_error(format!("can't change class of type '{}'", type_repr))) } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 34a27548f5..accab331a1 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -667,8 +667,10 @@ impl PyContext { } pub fn new_data_descriptor< - G: 'static + Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult, - S: 'static + Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult, + G: IntoPyNativeFunc<(I, PyObjectRef), T>, + S: IntoPyNativeFunc<(I, T), PyResult>, + T, + I, >( &self, getter: G, From 22811c0d566a16772e64f1b1d5652eea1cf0e6b4 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Thu, 7 Mar 2019 17:00:24 +0000 Subject: [PATCH 181/380] Add PyNone and new style definition for NoneType methods. --- vm/src/obj/objnone.rs | 57 ++++++++++++++++++++++++++++++++----------- vm/src/pyobject.rs | 10 +------- 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/vm/src/obj/objnone.rs b/vm/src/obj/objnone.rs index bc94f9cb92..a701d7d488 100644 --- a/vm/src/obj/objnone.rs +++ b/vm/src/obj/objnone.rs @@ -1,11 +1,35 @@ -use crate::pyobject::{PyContext, PyFuncArgs, PyResult, TypeProtocol}; +use crate::function::PyRef; +use crate::pyobject::{ + IntoPyObject, PyContext, PyFuncArgs, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, +}; use crate::vm::VirtualMachine; -pub fn init(context: &PyContext) { - let none_type = &context.none.typ(); - context.set_attr(&none_type, "__new__", context.new_rustfunc(none_new)); - context.set_attr(&none_type, "__repr__", context.new_rustfunc(none_repr)); - context.set_attr(&none_type, "__bool__", context.new_rustfunc(none_bool)); +#[derive(Clone, Debug)] +pub struct PyNone; +pub type PyNoneRef = PyRef; + +impl PyObjectPayload2 for PyNone { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.none().typ() + } +} + +// This allows a built-in function to not return a value, mapping to +// Python's behavior of returning `None` in this situation. +impl IntoPyObject for () { + fn into_pyobject(self, ctx: &PyContext) -> PyResult { + Ok(ctx.none()) + } +} + +impl PyNoneRef { + fn repr(self, _vm: &mut VirtualMachine) -> PyResult { + Ok("None".to_string()) + } + + fn bool(self, _vm: &mut VirtualMachine) -> PyResult { + Ok(false) + } } fn none_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -17,12 +41,17 @@ fn none_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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())) -} - -fn none_bool(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(_zelf, Some(vm.ctx.none().typ()))]); - Ok(vm.ctx.new_bool(false)) +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(PyNoneRef::repr), + ); + context.set_attr( + &none_type, + "__bool__", + context.new_rustfunc(PyNoneRef::bool), + ); } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index f498a54018..0931d4a758 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -227,7 +227,7 @@ impl PyContext { let none = PyObject::new( PyObjectPayload::AnyRustValue { - value: Box::new(()), + value: Box::new(objnone::PyNone), }, create_type("NoneType", &type_type, &object_type, &dict_type), ); @@ -1319,14 +1319,6 @@ where } } -// This allows a built-in function to not return a value, mapping to -// Python's behavior of returning `None` in this situation. -impl IntoPyObject for () { - fn into_pyobject(self, ctx: &PyContext) -> PyResult { - Ok(ctx.none()) - } -} - // TODO: Allow a built-in function to return an `Option`, i.e.: // // impl IntoPyObject for Option From f2f0f1d7429d01dc434de122c4293c5bf1d1d673 Mon Sep 17 00:00:00 2001 From: ben Date: Fri, 8 Mar 2019 06:03:13 +1300 Subject: [PATCH 182/380] Remove debug statements --- vm/src/obj/objfunction.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index 82c06fac63..26fee403fa 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -99,7 +99,6 @@ fn data_get(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { match args.shift().get_attr("fget") { Some(function) => vm.invoke(function, args), None => { - println!("A"); let attribute_error = vm.context().exceptions.attribute_error.clone(); Err(vm.new_exception(attribute_error, String::from("Attribute Error"))) } @@ -110,7 +109,6 @@ fn data_set(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { match args.shift().get_attr("fset") { Some(function) => vm.invoke(function, args), None => { - println!("B"); let attribute_error = vm.context().exceptions.attribute_error.clone(); Err(vm.new_exception(attribute_error, String::from("Attribute Error"))) } From 289c86de6703555c5836ce96cb2f7f156862fce5 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Thu, 7 Mar 2019 17:06:11 +0000 Subject: [PATCH 183/380] Add extend_class! macro to neaten definitions of builtins types. --- vm/src/macros.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/vm/src/macros.rs b/vm/src/macros.rs index f91cd7c614..861e1db42a 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -137,3 +137,13 @@ macro_rules! py_class { } } } + +#[macro_export] +macro_rules! extend_class { + ( $ctx:expr, $class:expr, { $($name:expr => $value:expr),* $(,)* }) => { + let class = $class; + $( + $ctx.set_attr(&class, $name, $value); + )* + } +} From adf473faf4e8eee537c6ec851fe771d589900825 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Thu, 7 Mar 2019 17:06:34 +0000 Subject: [PATCH 184/380] Use macro for NoneType. --- vm/src/obj/objnone.rs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/vm/src/obj/objnone.rs b/vm/src/obj/objnone.rs index a701d7d488..80980f5283 100644 --- a/vm/src/obj/objnone.rs +++ b/vm/src/obj/objnone.rs @@ -42,16 +42,9 @@ fn none_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } 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(PyNoneRef::repr), - ); - context.set_attr( - &none_type, - "__bool__", - context.new_rustfunc(PyNoneRef::bool), - ); + extend_class!(context, &context.none.typ(), { + "__new__" => context.new_rustfunc(none_new), + "__repr__" => context.new_rustfunc(PyNoneRef::repr), + "__bool__" => context.new_rustfunc(PyNoneRef::bool), + }); } From 303f9b95a0061aa04ac0968c14ac55edf0bbadde Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Tue, 5 Mar 2019 16:27:51 +0000 Subject: [PATCH 185/380] Add (unused) dict element to PyObject struct. --- vm/src/pyobject.rs | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index a296ea32d8..7a18566c5b 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -155,13 +155,8 @@ pub struct PyContext { } fn _nothing() -> PyObjectRef { - PyObject { - payload: PyObjectPayload::AnyRustValue { - value: Box::new(()), - }, - typ: None, - } - .into_ref() + let obj: PyObject = Default::default(); + obj.into_ref() } pub fn create_type( @@ -745,10 +740,11 @@ impl Default for PyContext { /// This is an actual python object. It consists of a `typ` which is the /// python class, and carries some rust payload optionally. This rust /// payload can be a rust float or rust int in case of float and int objects. +#[derive(Default)] pub struct PyObject { pub payload: PyObjectPayload, pub typ: Option, - // pub dict: HashMap, // __dict__ member + pub dict: Option>, // __dict__ member } pub trait IdProtocol { @@ -1538,6 +1534,14 @@ pub enum PyObjectPayload { }, } +impl Default for PyObjectPayload { + fn default() -> Self { + PyObjectPayload::AnyRustValue { + value: Box::new(()), + } + } +} + impl fmt::Debug for PyObjectPayload { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { @@ -1568,14 +1572,11 @@ impl fmt::Debug for PyObjectPayload { } impl PyObject { - pub fn new( - payload: PyObjectPayload, - /* dict: PyObjectRef,*/ typ: PyObjectRef, - ) -> PyObjectRef { + pub fn new(payload: PyObjectPayload, typ: PyObjectRef) -> PyObjectRef { PyObject { payload, typ: Some(typ), - // dict: HashMap::new(), // dict, + dict: None, } .into_ref() } From 5b1384814c45d8cecfb814cd568b1c483b98aeb1 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Tue, 5 Mar 2019 17:09:48 +0000 Subject: [PATCH 186/380] Move attributes dictionary to PyObject. --- vm/src/obj/objdict.rs | 2 +- vm/src/obj/objobject.rs | 25 ++++++++-------- vm/src/obj/objtype.rs | 15 +++++----- vm/src/pyobject.rs | 65 +++++++++++++++++++++++------------------ vm/src/vm.rs | 1 - 5 files changed, 57 insertions(+), 51 deletions(-) diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 4fd99ae7ea..0a7416ccaa 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -342,9 +342,9 @@ pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: unsafe { (*ptr).payload = PyObjectPayload::Class { name: String::from("dict"), - dict: RefCell::new(HashMap::new()), mro: vec![object_type], }; + (*ptr).dict = Some(RefCell::new(HashMap::new())); (*ptr).typ = Some(type_type.clone()); } } diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index a387028cbb..6545a211f6 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -21,9 +21,9 @@ pub fn create_object(type_type: PyObjectRef, object_type: PyObjectRef, _dict_typ unsafe { (*ptr).payload = PyObjectPayload::Class { name: String::from("object"), - dict: RefCell::new(HashMap::new()), mro: vec![], }; + (*ptr).dict = Some(RefCell::new(HashMap::new())); (*ptr).typ = Some(type_type.clone()); } } @@ -105,13 +105,13 @@ fn object_delattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ] ); - match zelf.payload { - PyObjectPayload::Class { ref dict, .. } | PyObjectPayload::Instance { ref dict, .. } => { + match zelf.dict { + Some(ref dict) => { let attr_name = objstr::get_value(attr); dict.borrow_mut().remove(&attr_name); Ok(vm.get_none()) } - _ => Err(vm.new_type_error("TypeError: no dictionary.".to_string())), + None => Err(vm.new_type_error("TypeError: no dictionary.".to_string())), } } @@ -191,15 +191,14 @@ fn object_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { } fn object_dict(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - match args.args[0].payload { - PyObjectPayload::Class { ref dict, .. } | PyObjectPayload::Instance { ref dict, .. } => { - let new_dict = vm.new_dict(); - for (attr, value) in dict.borrow().iter() { - new_dict.set_item(&vm.ctx, &attr, value.clone()); - } - Ok(new_dict) + if let Some(ref dict) = args.args[0].dict { + let new_dict = vm.new_dict(); + for (attr, value) in dict.borrow().iter() { + new_dict.set_item(&vm.ctx, &attr, value.clone()); } - _ => Err(vm.new_type_error("TypeError: no dictionary.".to_string())), + Ok(new_dict) + } else { + Err(vm.new_type_error("TypeError: no dictionary.".to_string())) } } @@ -245,7 +244,7 @@ pub fn get_attributes(obj: &PyObjectRef) -> PyAttributes { let mut attributes = objtype::get_attributes(&obj.typ()); // Get instance attributes: - if let PyObjectPayload::Instance { dict } = &obj.payload { + if let Some(dict) = &obj.dict { for (name, value) in dict.borrow().iter() { attributes.insert(name.to_string(), value.clone()); } diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index b30c77226d..1ced9d2a3b 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -18,9 +18,9 @@ pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, _dict_type: unsafe { (*ptr).payload = PyObjectPayload::Class { name: String::from("type"), - dict: RefCell::new(PyAttributes::new()), mro: vec![object_type], }; + (*ptr).dict = Some(RefCell::new(PyAttributes::new())); (*ptr).typ = Some(type_type); } } @@ -253,7 +253,7 @@ pub fn get_attributes(obj: &PyObjectRef) -> PyAttributes { let mut base_classes = _mro(obj.clone()).expect("Type get_attributes on non-type"); base_classes.reverse(); for bc in base_classes { - if let PyObjectPayload::Class { dict, .. } = &bc.payload { + if let Some(ref dict) = &bc.dict { for (name, value) in dict.borrow().iter() { attributes.insert(name.to_string(), value.clone()); } @@ -318,14 +318,15 @@ pub fn new( ) -> PyResult { let mros = bases.into_iter().map(|x| _mro(x).unwrap()).collect(); let mro = linearise_mro(mros).unwrap(); - Ok(PyObject::new( - PyObjectPayload::Class { + Ok(PyObject { + payload: PyObjectPayload::Class { name: String::from(name), - dict: RefCell::new(dict), mro, }, - typ, - )) + dict: Some(RefCell::new(dict)), + typ: Some(typ), + } + .into_ref()) } fn type_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 7a18566c5b..5d784172bc 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -672,12 +672,14 @@ impl PyContext { } else { PyAttributes::new() }; - PyObject::new( - PyObjectPayload::Instance { - dict: RefCell::new(dict), + PyObject { + payload: PyObjectPayload::AnyRustValue { + value: Box::new(()), }, - class, - ) + typ: Some(class), + dict: Some(RefCell::new(dict)), + } + .into_ref() } // Item set/get: @@ -698,14 +700,12 @@ impl PyContext { } pub fn set_attr(&self, obj: &PyObjectRef, attr_name: &str, value: PyObjectRef) { - match obj.payload { - PyObjectPayload::Module { ref scope, .. } => { - scope.locals.set_item(self, attr_name, value) - } - PyObjectPayload::Instance { ref dict } | PyObjectPayload::Class { ref dict, .. } => { - dict.borrow_mut().insert(attr_name.to_string(), value); - } - ref payload => unimplemented!("set_attr unimplemented for: {:?}", payload), + if let PyObjectPayload::Module { ref scope, .. } = obj.payload { + scope.locals.set_item(self, attr_name, value) + } else if let Some(ref dict) = obj.dict { + dict.borrow_mut().insert(attr_name.to_string(), value); + } else { + unimplemented!("set_attr unimplemented for: {:?}", obj); }; } @@ -744,7 +744,7 @@ impl Default for PyContext { pub struct PyObject { pub payload: PyObjectPayload, pub typ: Option, - pub dict: Option>, // __dict__ member + pub dict: Option>, // __dict__ member } pub trait IdProtocol { @@ -791,16 +791,18 @@ pub trait AttributeProtocol { } fn class_get_item(class: &PyObjectRef, attr_name: &str) -> Option { - match class.payload { - PyObjectPayload::Class { ref dict, .. } => dict.borrow().get(attr_name).cloned(), - _ => panic!("Only classes should be in MRO!"), + if let Some(ref dict) = class.dict { + dict.borrow().get(attr_name).cloned() + } else { + panic!("Only classes should be in MRO!"); } } fn class_has_item(class: &PyObjectRef, attr_name: &str) -> bool { - match class.payload { - PyObjectPayload::Class { ref dict, .. } => dict.borrow().contains_key(attr_name), - _ => panic!("Only classes should be in MRO!"), + if let Some(ref dict) = class.dict { + dict.borrow().contains_key(attr_name) + } else { + panic!("Only classes should be in MRO!"); } } @@ -819,8 +821,13 @@ impl AttributeProtocol for PyObjectRef { } None } - PyObjectPayload::Instance { ref dict } => dict.borrow().get(attr_name).cloned(), - _ => None, + _ => { + if let Some(ref dict) = self.dict { + dict.borrow().get(attr_name).cloned() + } else { + None + } + } } } @@ -830,8 +837,13 @@ impl AttributeProtocol for PyObjectRef { PyObjectPayload::Class { ref mro, .. } => { class_has_item(self, attr_name) || mro.iter().any(|d| class_has_item(d, attr_name)) } - PyObjectPayload::Instance { ref dict } => dict.borrow().contains_key(attr_name), - _ => false, + _ => { + if let Some(ref dict) = self.dict { + dict.borrow().contains_key(attr_name) + } else { + false + } + } } } } @@ -1517,15 +1529,11 @@ pub enum PyObjectPayload { }, Class { name: String, - dict: RefCell, mro: Vec, }, WeakRef { referent: PyObjectWeakRef, }, - Instance { - dict: RefCell, - }, RustFunction { function: PyNativeFunc, }, @@ -1563,7 +1571,6 @@ impl fmt::Debug for PyObjectPayload { } => write!(f, "bound-method: {:?} of {:?}", function, object), PyObjectPayload::Module { .. } => write!(f, "module"), PyObjectPayload::Class { ref name, .. } => write!(f, "class {:?}", name), - PyObjectPayload::Instance { .. } => write!(f, "instance"), PyObjectPayload::RustFunction { .. } => write!(f, "rust function"), PyObjectPayload::Frame { .. } => write!(f, "frame"), PyObjectPayload::AnyRustValue { .. } => write!(f, "some rust value"), diff --git a/vm/src/vm.rs b/vm/src/vm.rs index e3d06d5fae..aa96d4cfd1 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -303,7 +303,6 @@ impl VirtualMachine { ref function, ref object, } => self.invoke(function.clone(), args.insert(object.clone())), - PyObjectPayload::Instance { .. } => self.call_method(&func_ref, "__call__", args), ref payload => { // TODO: is it safe to just invoke __call__ otherwise? trace!("invoke __call__ for: {:?}", payload); From 995212040a784b77e1b434208565f160fb8e4191 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Thu, 7 Mar 2019 18:42:46 +0000 Subject: [PATCH 187/380] Use PyInstance as the payload rather than (). --- vm/src/obj/objobject.rs | 5 +++++ vm/src/pyobject.rs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 6545a211f6..00acdb7ef1 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -1,5 +1,6 @@ use super::objstr; use super::objtype; +use crate::function::PyRef; use crate::pyobject::{ AttributeProtocol, DictProtocol, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, @@ -8,6 +9,10 @@ use crate::vm::VirtualMachine; use std::cell::RefCell; use std::collections::HashMap; +#[derive(Clone, Debug)] +pub struct PyInstance; +pub type PyInstanceRef = PyRef; + pub fn new_instance(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { // more or less __new__ operator let type_ref = args.shift(); diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 5d784172bc..a5f8a5725e 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -674,7 +674,7 @@ impl PyContext { }; PyObject { payload: PyObjectPayload::AnyRustValue { - value: Box::new(()), + value: Box::new(objobject::PyInstance), }, typ: Some(class), dict: Some(RefCell::new(dict)), From 1601da0692b3ec13179cc2332c777f012f0cbf48 Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Wed, 6 Mar 2019 22:14:09 +0100 Subject: [PATCH 188/380] Migrate PyFloat to new method style --- vm/src/function.rs | 1 + vm/src/obj/objfloat.rs | 736 +++++++++++++++++------------------------ vm/src/pyobject.rs | 12 +- 3 files changed, 312 insertions(+), 437 deletions(-) diff --git a/vm/src/function.rs b/vm/src/function.rs index 2194dac9c8..993167ffc9 100644 --- a/vm/src/function.rs +++ b/vm/src/function.rs @@ -22,6 +22,7 @@ use crate::vm::VirtualMachine; /// A `PyRef` can be directly returned from a built-in function to handle /// situations (such as when implementing in-place methods such as `__iadd__`) /// where a reference to the same object must be returned. +#[derive(Clone)] pub struct PyRef { // invariant: this obj must always have payload of type T obj: PyObjectRef, diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 71cb803fac..64c26973e9 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -4,7 +4,7 @@ use super::objstr; use super::objtype; use crate::function::PyRef; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, + IntoPyObject, PyContext, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -23,495 +23,369 @@ impl PyObjectPayload2 for PyFloat { } } +impl IntoPyObject for f64 { + fn into_pyobject(self, ctx: &PyContext) -> PyResult { + Ok(ctx.new_float(self)) + } +} + impl From for PyFloat { fn from(value: f64) -> Self { PyFloat { value } } } -impl PyFloat { - fn as_integer_ratio(zelf: PyRef, vm: &mut VirtualMachine) -> PyResult { - let value = zelf.value; - if value.is_infinite() { - return Err( - vm.new_overflow_error("cannot convert Infinity to integer ratio".to_string()) - ); - } - if value.is_nan() { - return Err(vm.new_value_error("cannot convert NaN to integer ratio".to_string())); - } - - let ratio = Ratio::from_float(value).unwrap(); - let numer = vm.ctx.new_int(ratio.numer().clone()); - let denom = vm.ctx.new_int(ratio.denom().clone()); - Ok(vm.ctx.new_tuple(vec![numer, denom])) +pub type PyFloatRef = PyRef; + +impl PyFloatRef { + fn eq(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + let value = self.value; + let result = if objtype::isinstance(&other, &vm.ctx.float_type()) { + let other = get_value(&other); + value == other + } else if objtype::isinstance(&other, &vm.ctx.int_type()) { + let other_int = objint::get_value(&other); + + if let (Some(self_int), Some(other_float)) = (value.to_bigint(), other_int.to_f64()) { + value == other_float && self_int == other_int + } else { + false + } + } else { + return vm.ctx.not_implemented(); + }; + vm.ctx.new_bool(result) } -} - -fn float_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(float, Some(vm.ctx.float_type()))]); - let v = get_value(float); - Ok(vm.new_str(v.to_string())) -} -fn float_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(cls, None), (arg, None)]); - let value = if objtype::isinstance(arg, &vm.ctx.float_type()) { - get_value(arg) - } else if objtype::isinstance(arg, &vm.ctx.int_type()) { - 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)) - ); - } + fn lt(self, i2: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + let v1 = self.value; + if objtype::isinstance(&i2, &vm.ctx.float_type()) { + vm.ctx.new_bool(v1 < get_value(&i2)) + } else if objtype::isinstance(&i2, &vm.ctx.int_type()) { + vm.ctx + .new_bool(v1 < objint::get_value(&i2).to_f64().unwrap()) + } else { + vm.ctx.not_implemented() } - } else { - let type_name = objtype::get_type_name(&arg.typ()); - return Err(vm.new_type_error(format!("can't convert {} to float", type_name))); - }; - - Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyFloat { value }), - }, - cls.clone(), - )) -} - -// Retrieve inner float value: -pub fn get_value(obj: &PyObjectRef) -> f64 { - obj.payload::().unwrap().value -} + } -pub fn make_float(vm: &mut VirtualMachine, obj: &PyObjectRef) -> PyResult { - if objtype::isinstance(obj, &vm.ctx.float_type()) { - Ok(get_value(obj)) - } else if let Ok(method) = vm.get_method(obj.clone(), "__float__") { - let res = vm.invoke(method, vec![])?; - Ok(get_value(&res)) - } else { - Err(vm.new_type_error(format!("Cannot cast {} to float", obj))) + fn le(self, i2: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + let v1 = self.value; + if objtype::isinstance(&i2, &vm.ctx.float_type()) { + vm.ctx.new_bool(v1 <= get_value(&i2)) + } else if objtype::isinstance(&i2, &vm.ctx.int_type()) { + vm.ctx + .new_bool(v1 <= objint::get_value(&i2).to_f64().unwrap()) + } else { + vm.ctx.not_implemented() + } } -} -fn float_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.float_type())), (other, None)] - ); - let zelf = get_value(zelf); - let result = if objtype::isinstance(other, &vm.ctx.float_type()) { - let other = get_value(other); - zelf == other - } else if objtype::isinstance(other, &vm.ctx.int_type()) { - 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 + fn gt(self, i2: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + let v1 = self.value; + if objtype::isinstance(&i2, &vm.ctx.float_type()) { + vm.ctx.new_bool(v1 > get_value(&i2)) + } else if objtype::isinstance(&i2, &vm.ctx.int_type()) { + vm.ctx + .new_bool(v1 > objint::get_value(&i2).to_f64().unwrap()) } else { - false + vm.ctx.not_implemented() } - } else { - return Ok(vm.ctx.not_implemented()); - }; - Ok(vm.ctx.new_bool(result)) -} + } -fn float_lt(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_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 { - Ok(vm.ctx.not_implemented()) + fn ge(self, i2: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + let v1 = self.value; + if objtype::isinstance(&i2, &vm.ctx.float_type()) { + vm.ctx.new_bool(v1 >= get_value(&i2)) + } else if objtype::isinstance(&i2, &vm.ctx.int_type()) { + vm.ctx + .new_bool(v1 >= objint::get_value(&i2).to_f64().unwrap()) + } else { + vm.ctx.not_implemented() + } } -} -fn float_le(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_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 { - Ok(vm.ctx.not_implemented()) + fn abs(self, _vm: &mut VirtualMachine) -> f64 { + self.value.abs() } -} -fn float_gt(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_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 { - Ok(vm.ctx.not_implemented()) + fn add(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + let v1 = self.value; + if objtype::isinstance(&other, &vm.ctx.float_type()) { + vm.ctx.new_float(v1 + get_value(&other)) + } else if objtype::isinstance(&other, &vm.ctx.int_type()) { + vm.ctx + .new_float(v1 + objint::get_value(&other).to_f64().unwrap()) + } else { + vm.ctx.not_implemented() + } } -} -fn float_ge(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_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 { - Ok(vm.ctx.not_implemented()) + fn divmod(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&other, &vm.ctx.float_type()) + || objtype::isinstance(&other, &vm.ctx.int_type()) + { + let r1 = PyFloatRef::floordiv(self.clone(), other.clone(), vm)?; + let r2 = PyFloatRef::mod_(self, other, vm)?; + Ok(vm.ctx.new_tuple(vec![r1, r2])) + } else { + Ok(vm.ctx.not_implemented()) + } } -} -fn float_abs(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(i, Some(vm.ctx.float_type()))]); - Ok(vm.ctx.new_float(get_value(i).abs())) -} + fn floordiv(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + let v1 = self.value; + let v2 = if objtype::isinstance(&other, &vm.ctx.float_type) { + get_value(&other) + } else if objtype::isinstance(&other, &vm.ctx.int_type) { + objint::get_value(&other).to_f64().ok_or_else(|| { + vm.new_overflow_error("int too large to convert to float".to_string()) + })? + } else { + return Ok(vm.ctx.not_implemented()); + }; -fn float_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.float_type())), (other, None)] - ); - - let v1 = get_value(zelf); - if objtype::isinstance(other, &vm.ctx.float_type()) { - Ok(vm.ctx.new_float(v1 + get_value(other))) - } else if objtype::isinstance(other, &vm.ctx.int_type()) { - Ok(vm - .ctx - .new_float(v1 + objint::get_value(other).to_f64().unwrap())) - } else { - Ok(vm.ctx.not_implemented()) + 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())) + } } -} - -fn float_radd(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - float_add(vm, args) -} -fn float_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(i, Some(vm.ctx.float_type())), (i2, None)] - ); - 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, r2])) - } else { - Ok(vm.ctx.not_implemented()) + fn new_str(cls: PyObjectRef, arg: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + let value = if objtype::isinstance(&arg, &vm.ctx.float_type()) { + get_value(&arg) + } else if objtype::isinstance(&arg, &vm.ctx.int_type()) { + 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 { + let type_name = objtype::get_type_name(&arg.typ()); + return Err(vm.new_type_error(format!("can't convert {} to float", type_name))); + }; + Ok(PyObject::new( + PyObjectPayload::AnyRustValue { + value: Box::new(PyFloat { value }), + }, + cls.clone(), + )) } -} -fn float_floordiv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(i, Some(vm.ctx.float_type())), (i2, None)] - ); - - 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 Ok(vm.ctx.not_implemented()); - }; + fn mod_(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + let v1 = self.value; + let v2 = if objtype::isinstance(&other, &vm.ctx.float_type) { + get_value(&other) + } else if objtype::isinstance(&other, &vm.ctx.int_type) { + objint::get_value(&other).to_f64().ok_or_else(|| { + vm.new_overflow_error("int too large to convert to float".to_string()) + })? + } else { + return Ok(vm.ctx.not_implemented()); + }; - 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())) + if v2 != 0.0 { + Ok(vm.ctx.new_float(v1 % v2)) + } else { + Err(vm.new_zero_division_error("float mod by zero".to_string())) + } } -} -fn float_sub(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.float_type())), (other, None)] - ); - let v1 = get_value(zelf); - if objtype::isinstance(other, &vm.ctx.float_type()) { - Ok(vm.ctx.new_float(v1 - get_value(other))) - } else if objtype::isinstance(other, &vm.ctx.int_type()) { - Ok(vm - .ctx - .new_float(v1 - objint::get_value(other).to_f64().unwrap())) - } else { - Ok(vm.ctx.not_implemented()) + fn neg(self, _vm: &mut VirtualMachine) -> f64 { + -self.value } -} -fn float_rsub(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.float_type())), (other, None)] - ); - let v1 = get_value(zelf); - if objtype::isinstance(other, &vm.ctx.float_type()) { - Ok(vm.ctx.new_float(get_value(other) - v1)) - } else if objtype::isinstance(other, &vm.ctx.int_type()) { - Ok(vm - .ctx - .new_float(objint::get_value(other).to_f64().unwrap() - v1)) - } else { - Ok(vm.ctx.not_implemented()) + fn pow(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + let v1 = self.value; + if objtype::isinstance(&other, &vm.ctx.float_type()) { + vm.ctx.new_float(v1.powf(get_value(&other))) + } else if objtype::isinstance(&other, &vm.ctx.int_type()) { + let result = v1.powf(objint::get_value(&other).to_f64().unwrap()); + vm.ctx.new_float(result) + } else { + vm.ctx.not_implemented() + } } -} -fn float_mod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(i, Some(vm.ctx.float_type())), (i2, None)] - ); - - 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 Ok(vm.ctx.not_implemented()); - }; + fn sub(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + let v1 = self.value; + if objtype::isinstance(&other, &vm.ctx.float_type()) { + Ok(vm.ctx.new_float(v1 - get_value(&other))) + } else if objtype::isinstance(&other, &vm.ctx.int_type()) { + Ok(vm + .ctx + .new_float(v1 - objint::get_value(&other).to_f64().unwrap())) + } else { + Ok(vm.ctx.not_implemented()) + } + } - if v2 != 0.0 { - Ok(vm.ctx.new_float(v1 % v2)) - } else { - Err(vm.new_zero_division_error("float mod by zero".to_string())) + fn rsub(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + let v1 = self.value; + if objtype::isinstance(&other, &vm.ctx.float_type()) { + Ok(vm.ctx.new_float(get_value(&other) - v1)) + } else if objtype::isinstance(&other, &vm.ctx.int_type()) { + Ok(vm + .ctx + .new_float(objint::get_value(&other).to_f64().unwrap() - v1)) + } else { + Ok(vm.ctx.not_implemented()) + } } -} -fn float_neg(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(i, Some(vm.ctx.float_type()))]); + fn repr(self, _vm: &mut VirtualMachine) -> String { + self.value.to_string() + } - let v1 = get_value(i); - Ok(vm.ctx.new_float(-v1)) -} + fn truediv(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + let v1 = self.value; + let v2 = if objtype::isinstance(&other, &vm.ctx.float_type) { + get_value(&other) + } else if objtype::isinstance(&other, &vm.ctx.int_type) { + objint::get_value(&other).to_f64().ok_or_else(|| { + vm.new_overflow_error("int too large to convert to float".to_string()) + })? + } else { + return Ok(vm.ctx.not_implemented()); + }; -fn float_pow(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()) { - let result = v1.powf(get_value(i2)); - Ok(vm.ctx.new_float(result)) - } else if objtype::isinstance(i2, &vm.ctx.int_type()) { - let result = v1.powf(objint::get_value(i2).to_f64().unwrap()); - Ok(vm.ctx.new_float(result)) - } else { - Ok(vm.ctx.not_implemented()) + if v2 != 0.0 { + Ok(vm.ctx.new_float(v1 / v2)) + } else { + Err(vm.new_zero_division_error("float division by zero".to_string())) + } } -} -fn float_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.float_type())), (other, None)] - ); - - let v1 = get_value(zelf); - let v2 = if objtype::isinstance(other, &vm.ctx.float_type) { - get_value(other) - } else if objtype::isinstance(other, &vm.ctx.int_type) { - objint::get_value(other) - .to_f64() - .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))? - } else { - return Ok(vm.ctx.not_implemented()); - }; + fn rtruediv(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + let v1 = self.value; + let v2 = if objtype::isinstance(&other, &vm.ctx.float_type) { + get_value(&other) + } else if objtype::isinstance(&other, &vm.ctx.int_type) { + objint::get_value(&other).to_f64().ok_or_else(|| { + vm.new_overflow_error("int too large to convert to float".to_string()) + })? + } else { + return Ok(vm.ctx.not_implemented()); + }; - if v2 != 0.0 { - Ok(vm.ctx.new_float(v1 / v2)) - } else { - Err(vm.new_zero_division_error("float division by zero".to_string())) + if v1 != 0.0 { + Ok(vm.ctx.new_float(v2 / v1)) + } else { + Err(vm.new_zero_division_error("float division by zero".to_string())) + } } -} -fn float_rtruediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.float_type())), (other, None)] - ); - - let v1 = get_value(zelf); - let v2 = if objtype::isinstance(other, &vm.ctx.float_type) { - get_value(other) - } else if objtype::isinstance(other, &vm.ctx.int_type) { - objint::get_value(other) - .to_f64() - .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))? - } else { - return Ok(vm.ctx.not_implemented()); - }; + fn mul(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + let v1 = self.value; + if objtype::isinstance(&other, &vm.ctx.float_type) { + Ok(vm.ctx.new_float(v1 * get_value(&other))) + } else if objtype::isinstance(&other, &vm.ctx.int_type) { + Ok(vm + .ctx + .new_float(v1 * objint::get_value(&other).to_f64().unwrap())) + } else { + Ok(vm.ctx.not_implemented()) + } + } - if v1 != 0.0 { - Ok(vm.ctx.new_float(v2 / v1)) - } else { - Err(vm.new_zero_division_error("float division by zero".to_string())) + fn is_integer(self, _vm: &mut VirtualMachine) -> bool { + let v = self.value; + (v - v.round()).abs() < std::f64::EPSILON } -} -fn float_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.float_type())), (other, None)] - ); - let v1 = get_value(zelf); - if objtype::isinstance(other, &vm.ctx.float_type) { - Ok(vm.ctx.new_float(v1 * get_value(other))) - } else if objtype::isinstance(other, &vm.ctx.int_type) { - Ok(vm - .ctx - .new_float(v1 * objint::get_value(other).to_f64().unwrap())) - } else { - Ok(vm.ctx.not_implemented()) + fn real(self, _vm: &mut VirtualMachine) -> Self { + self } -} -fn float_rmul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - float_mul(vm, args) + fn as_integer_ratio(self, vm: &mut VirtualMachine) -> PyResult { + let value = self.value; + if value.is_infinite() { + return Err( + vm.new_overflow_error("cannot convert Infinity to integer ratio".to_string()) + ); + } + if value.is_nan() { + return Err(vm.new_value_error("cannot convert NaN to integer ratio".to_string())); + } + + let ratio = Ratio::from_float(value).unwrap(); + let numer = vm.ctx.new_int(ratio.numer().clone()); + let denom = vm.ctx.new_int(ratio.denom().clone()); + Ok(vm.ctx.new_tuple(vec![numer, denom])) + } } -fn float_real(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(i, Some(vm.ctx.float_type()))]); - let v = get_value(i); - Ok(vm.ctx.new_float(v)) +// Retrieve inner float value: +pub fn get_value(obj: &PyObjectRef) -> f64 { + obj.payload::().unwrap().value } -fn float_is_integer(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(i, Some(vm.ctx.float_type()))]); - let v = get_value(i); - let result = (v - v.round()).abs() < std::f64::EPSILON; - Ok(vm.ctx.new_bool(result)) +pub fn make_float(vm: &mut VirtualMachine, obj: &PyObjectRef) -> PyResult { + if objtype::isinstance(obj, &vm.ctx.float_type()) { + Ok(get_value(obj)) + } else if let Ok(method) = vm.get_method(obj.clone(), "__float__") { + let res = vm.invoke(method, vec![])?; + Ok(get_value(&res)) + } else { + Err(vm.new_type_error(format!("Cannot cast {} to float", obj))) + } } +#[rustfmt::skip] // to avoid line splitting 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)); - context.set_attr(&float_type, "__gt__", context.new_rustfunc(float_gt)); - context.set_attr(&float_type, "__ge__", context.new_rustfunc(float_ge)); - context.set_attr(&float_type, "__abs__", context.new_rustfunc(float_abs)); - context.set_attr(&float_type, "__add__", context.new_rustfunc(float_add)); - context.set_attr(&float_type, "__radd__", context.new_rustfunc(float_radd)); - context.set_attr( - &float_type, - "__divmod__", - context.new_rustfunc(float_divmod), - ); - context.set_attr( - &float_type, - "__floordiv__", - context.new_rustfunc(float_floordiv), - ); - context.set_attr(&float_type, "__new__", context.new_rustfunc(float_new)); - context.set_attr(&float_type, "__mod__", context.new_rustfunc(float_mod)); - context.set_attr(&float_type, "__neg__", context.new_rustfunc(float_neg)); - 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, "__rsub__", context.new_rustfunc(float_rsub)); - context.set_attr(&float_type, "__repr__", context.new_rustfunc(float_repr)); - context.set_attr( - &float_type, - "__doc__", - context.new_str(float_doc.to_string()), - ); - context.set_attr( - &float_type, - "__truediv__", - context.new_rustfunc(float_truediv), - ); - context.set_attr( - &float_type, - "__rtruediv__", - context.new_rustfunc(float_rtruediv), - ); - context.set_attr(&float_type, "__mul__", context.new_rustfunc(float_mul)); - context.set_attr(&float_type, "__rmul__", context.new_rustfunc(float_rmul)); - context.set_attr(&float_type, "real", context.new_property(float_real)); - context.set_attr( - &float_type, - "is_integer", - context.new_rustfunc(float_is_integer), - ); - context.set_attr( - &float_type, - "as_integer_ratio", - context.new_rustfunc(PyFloat::as_integer_ratio), - ); + context.set_attr(&float_type, "__eq__", context.new_rustfunc(PyFloatRef::eq)); + context.set_attr(&float_type, "__lt__", context.new_rustfunc(PyFloatRef::lt)); + context.set_attr(&float_type, "__le__", context.new_rustfunc(PyFloatRef::le)); + context.set_attr(&float_type, "__gt__", context.new_rustfunc(PyFloatRef::gt)); + context.set_attr(&float_type, "__ge__", context.new_rustfunc(PyFloatRef::ge)); + context.set_attr(&float_type, "__abs__", context.new_rustfunc(PyFloatRef::abs)); + context.set_attr(&float_type, "__add__", context.new_rustfunc(PyFloatRef::add)); + context.set_attr(&float_type, "__radd__", context.new_rustfunc(PyFloatRef::add)); + context.set_attr(&float_type, "__divmod__", context.new_rustfunc(PyFloatRef::divmod)); + context.set_attr(&float_type, "__floordiv__", context.new_rustfunc(PyFloatRef::floordiv)); + context.set_attr(&float_type, "__new__", context.new_rustfunc(PyFloatRef::new_str)); + context.set_attr(&float_type, "__mod__", context.new_rustfunc(PyFloatRef::mod_)); + context.set_attr(&float_type, "__neg__", context.new_rustfunc(PyFloatRef::neg)); + context.set_attr(&float_type, "__pow__", context.new_rustfunc(PyFloatRef::pow)); + context.set_attr(&float_type, "__sub__", context.new_rustfunc(PyFloatRef::sub)); + context.set_attr(&float_type, "__rsub__", context.new_rustfunc(PyFloatRef::rsub)); + context.set_attr(&float_type, "__repr__", context.new_rustfunc(PyFloatRef::repr)); + context.set_attr(&float_type, "__doc__", context.new_str(float_doc.to_string())); + context.set_attr(&float_type, "__truediv__", context.new_rustfunc(PyFloatRef::truediv)); + context.set_attr(&float_type, "__rtruediv__", context.new_rustfunc(PyFloatRef::rtruediv)); + context.set_attr(&float_type, "__mul__", context.new_rustfunc(PyFloatRef::mul)); + context.set_attr(&float_type, "__rmul__", context.new_rustfunc(PyFloatRef::mul)); + context.set_attr(&float_type, "real", context.new_property(PyFloatRef::real)); + context.set_attr(&float_type, "is_integer", context.new_rustfunc(PyFloatRef::is_integer)); + context.set_attr(&float_type, "as_integer_ratio", context.new_rustfunc(PyFloatRef::as_integer_ratio)); } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index a296ea32d8..8e1e45c8c3 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -625,13 +625,13 @@ impl PyContext { ) } - pub fn new_property PyResult>( - &self, - function: F, - ) -> PyObjectRef { - let fget = self.new_rustfunc(function); + pub fn new_property(&self, f: F) -> PyObjectRef + where + F: IntoPyNativeFunc, + { + let fget = self.new_rustfunc(f); let py_obj = self.new_instance(self.property_type(), None); - self.set_attr(&py_obj, "fget", fget.clone()); + self.set_attr(&py_obj, "fget", fget); py_obj } From 4b783b15235edbb8e252669bc427ca8f4c61d43b Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Thu, 7 Mar 2019 20:00:02 +0100 Subject: [PATCH 189/380] Add a code of conduct --- README.md | 4 +++ code-of-conduct.md | 77 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 code-of-conduct.md diff --git a/README.md b/README.md index 7e7b0bd70d..41bde43e07 100644 --- a/README.md +++ b/README.md @@ -201,6 +201,10 @@ The code style used is the default rustfmt codestyle. Please format your code ac Chat with us on [gitter][gitter]. +# Code of conduct + +Our code of conduct [can be found here](code-of-conduct.md). + # Credit The initial work was based on [windelbouwman/rspython](https://github.com/windelbouwman/rspython) and [shinglyu/RustPython](https://github.com/shinglyu/RustPython) diff --git a/code-of-conduct.md b/code-of-conduct.md new file mode 100644 index 0000000000..a783b67225 --- /dev/null +++ b/code-of-conduct.md @@ -0,0 +1,77 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at windel.bouwman@gmail.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq + From b81a0a16d870f8be9fde7b077fc587fc9f43396c Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Thu, 7 Mar 2019 20:37:39 +0100 Subject: [PATCH 190/380] Convert most remaining string methods to implicit args Also support out-of-range start/end params for some functions. --- tests/snippets/strings.py | 39 +++ vm/src/obj/objstr.rs | 689 +++++++++++++++++--------------------- 2 files changed, 341 insertions(+), 387 deletions(-) diff --git a/tests/snippets/strings.py b/tests/snippets/strings.py index 45fbfa0c7c..4483b37f62 100644 --- a/tests/snippets/strings.py +++ b/tests/snippets/strings.py @@ -62,6 +62,45 @@ assert c.title() == 'Hallo' assert c.count('l') == 2 +assert 'aaa'.count('a') == 3 +assert 'aaa'.count('a', 1) == 2 +assert 'aaa'.count('a', 1, 2) == 1 +assert 'aaa'.count('a', 2, 2) == 0 +assert 'aaa'.count('a', 2, 1) == 0 + +assert '___a__'.find('a') == 3 +assert '___a__'.find('a', -10) == 3 +assert '___a__'.find('a', -3) == 3 +assert '___a__'.find('a', -2) == -1 +assert '___a__'.find('a', -1) == -1 +assert '___a__'.find('a', 0) == 3 +assert '___a__'.find('a', 3) == 3 +assert '___a__'.find('a', 4) == -1 +assert '___a__'.find('a', 10) == -1 +assert '___a__'.rfind('a', 3) == 3 +assert '___a__'.index('a', 3) == 3 + +assert '___a__'.find('a', 0, -10) == -1 +assert '___a__'.find('a', 0, -3) == -1 +assert '___a__'.find('a', 0, -2) == 3 +assert '___a__'.find('a', 0, -1) == 3 +assert '___a__'.find('a', 0, 0) == -1 +assert '___a__'.find('a', 0, 3) == -1 +assert '___a__'.find('a', 0, 4) == 3 +assert '___a__'.find('a', 0, 10) == 3 + +assert '___a__'.find('a', 3, 3) == -1 +assert '___a__'.find('a', 3, 4) == 3 +assert '___a__'.find('a', 4, 3) == -1 + +assert 'abcd'.startswith('b', 1) +assert not 'abcd'.startswith('b', -4) +assert 'abcd'.startswith('b', -3) + +assert not 'abcd'.startswith('b', 3, 3) +assert 'abcd'.startswith('', 3, 3) +assert not 'abcd'.startswith('', 4, 3) + assert ' '.isspace() assert 'hello\nhallo\nHallo'.splitlines() == ['hello', 'hallo', 'Hallo'] assert 'abc\t12345\txyz'.expandtabs() == 'abc 12345 xyz' diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 6d9d622d04..ba42740091 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -93,6 +93,20 @@ impl PyStringRef { self.value.chars().count() } + fn mul(self, val: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&val, &vm.ctx.int_type()) { + let value = &self.value; + let multiplier = objint::get_value(&val).to_i32().unwrap(); + let mut result = String::new(); + for _x in 0..multiplier { + result.push_str(value.as_str()); + } + Ok(result) + } else { + Err(vm.new_type_error(format!("Cannot multiply {} and {}", self, val))) + } + } + fn str(self, _vm: &mut VirtualMachine) -> PyStringRef { self } @@ -145,6 +159,48 @@ impl PyStringRef { format!("{}{}", first_part.to_uppercase(), lower_str) } + fn split( + self, + pattern: OptionalArg, + num: OptionalArg, + vm: &mut VirtualMachine, + ) -> PyObjectRef { + let value = &self.value; + let pattern = match pattern { + OptionalArg::Present(ref s) => &s.value, + OptionalArg::Missing => " ", + }; + let num_splits = num + .into_option() + .unwrap_or_else(|| value.split(pattern).count()); + let elements = value + .splitn(num_splits + 1, pattern) + .map(|o| vm.ctx.new_str(o.to_string())) + .collect(); + vm.ctx.new_list(elements) + } + + fn rsplit( + self, + pattern: OptionalArg, + num: OptionalArg, + vm: &mut VirtualMachine, + ) -> PyObjectRef { + let value = &self.value; + let pattern = match pattern { + OptionalArg::Present(ref s) => &s.value, + OptionalArg::Missing => " ", + }; + let num_splits = num + .into_option() + .unwrap_or_else(|| value.split(pattern).count()); + let elements = value + .rsplitn(num_splits + 1, pattern) + .map(|o| vm.ctx.new_str(o.to_string())) + .collect(); + vm.ctx.new_list(elements) + } + fn strip(self, _vm: &mut VirtualMachine) -> String { self.value.trim().to_string() } @@ -160,25 +216,29 @@ impl PyStringRef { fn endswith( self, suffix: PyStringRef, - start: OptionalArg, - end: OptionalArg, + start: OptionalArg, + end: OptionalArg, _vm: &mut VirtualMachine, ) -> bool { - let start = start.into_option().unwrap_or(0); - let end = end.into_option().unwrap_or(self.value.len()); - self.value[start..end].ends_with(&suffix.value) + if let Some((start, end)) = adjust_indices(start, end, self.value.len()) { + self.value[start..end].ends_with(&suffix.value) + } else { + false + } } fn startswith( self, prefix: PyStringRef, - start: OptionalArg, - end: OptionalArg, + start: OptionalArg, + end: OptionalArg, _vm: &mut VirtualMachine, ) -> bool { - let start = start.into_option().unwrap_or(0); - let end = end.into_option().unwrap_or(self.value.len()); - self.value[start..end].starts_with(&prefix.value) + if let Some((start, end)) = adjust_indices(start, end, self.value.len()) { + self.value[start..end].starts_with(&prefix.value) + } else { + false + } } fn isalnum(self, _vm: &mut VirtualMachine) -> bool { @@ -236,6 +296,19 @@ impl PyStringRef { !self.value.is_empty() && self.value.chars().all(char::is_alphanumeric) } + fn replace( + self, + old: Self, + new: Self, + num: OptionalArg, + _vm: &mut VirtualMachine, + ) -> String { + match num.into_option() { + Some(num) => self.value.replacen(&old.value, &new.value, num), + None => self.value.replace(&old.value, &new.value), + } + } + // cpython's isspace ignores whitespace, including \t and \n, etc, unless the whole string is empty // which is why isspace is using is_ascii_whitespace. Same for isupper & islower fn isspace(self, _vm: &mut VirtualMachine) -> bool { @@ -288,6 +361,78 @@ impl PyStringRef { Ok(joined) } + fn find( + self, + sub: Self, + start: OptionalArg, + end: OptionalArg, + _vm: &mut VirtualMachine, + ) -> isize { + let value = &self.value; + if let Some((start, end)) = adjust_indices(start, end, value.len()) { + match value[start..end].find(&sub.value) { + Some(num) => (start + num) as isize, + None => -1 as isize, + } + } else { + -1 as isize + } + } + + fn rfind( + self, + sub: Self, + start: OptionalArg, + end: OptionalArg, + _vm: &mut VirtualMachine, + ) -> isize { + let value = &self.value; + if let Some((start, end)) = adjust_indices(start, end, value.len()) { + match value[start..end].rfind(&sub.value) { + Some(num) => (start + num) as isize, + None => -1 as isize, + } + } else { + -1 as isize + } + } + + fn index( + self, + sub: Self, + start: OptionalArg, + end: OptionalArg, + vm: &mut VirtualMachine, + ) -> PyResult { + let value = &self.value; + if let Some((start, end)) = adjust_indices(start, end, value.len()) { + match value[start..end].find(&sub.value) { + Some(num) => Ok(start + num), + None => Err(vm.new_value_error("substring not found".to_string())), + } + } else { + Err(vm.new_value_error("substring not found".to_string())) + } + } + + fn rindex( + self, + sub: Self, + start: OptionalArg, + end: OptionalArg, + vm: &mut VirtualMachine, + ) -> PyResult { + let value = &self.value; + if let Some((start, end)) = adjust_indices(start, end, value.len()) { + match value[start..end].rfind(&sub.value) { + Some(num) => Ok(start + num), + None => Err(vm.new_value_error("substring not found".to_string())), + } + } else { + Err(vm.new_value_error("substring not found".to_string())) + } + } + fn partition(self, sub: PyStringRef, vm: &mut VirtualMachine) -> PyObjectRef { let value = &self.value; let sub = &sub.value; @@ -325,6 +470,115 @@ impl PyStringRef { vm.ctx.new_tuple(new_tup) } + fn istitle(self, _vm: &mut VirtualMachine) -> bool { + if self.value.is_empty() { + false + } else { + self.value.split(' ').all(|word| word == make_title(word)) + } + } + + fn count( + self, + sub: Self, + start: OptionalArg, + end: OptionalArg, + _vm: &mut VirtualMachine, + ) -> usize { + let value = &self.value; + if let Some((start, end)) = adjust_indices(start, end, value.len()) { + self.value[start..end].matches(&sub.value).count() + } else { + 0 + } + } + + fn zfill(self, len: usize, _vm: &mut VirtualMachine) -> String { + let value = &self.value; + if len <= value.len() { + value.to_string() + } else { + format!("{}{}", "0".repeat(len - value.len()), value) + } + } + + fn get_fill_char<'a>(rep: &'a OptionalArg, vm: &mut VirtualMachine) -> PyResult<&'a str> { + let rep_str = match rep { + OptionalArg::Present(ref st) => &st.value, + OptionalArg::Missing => " ", + }; + if rep_str.len() == 1 { + Ok(rep_str) + } else { + Err(vm.new_type_error( + "The fill character must be exactly one character long".to_string(), + )) + } + } + + fn ljust( + self, + len: usize, + rep: OptionalArg, + vm: &mut VirtualMachine, + ) -> PyResult { + let value = &self.value; + let rep_char = PyStringRef::get_fill_char(&rep, vm)?; + Ok(format!("{}{}", value, rep_char.repeat(len))) + } + + fn rjust( + self, + len: usize, + rep: OptionalArg, + vm: &mut VirtualMachine, + ) -> PyResult { + let value = &self.value; + let rep_char = PyStringRef::get_fill_char(&rep, vm)?; + Ok(format!("{}{}", rep_char.repeat(len), value)) + } + + fn center( + self, + len: usize, + rep: OptionalArg, + vm: &mut VirtualMachine, + ) -> PyResult { + let value = &self.value; + let rep_char = PyStringRef::get_fill_char(&rep, vm)?; + let left_buff: usize = (len - value.len()) / 2; + let right_buff = len - value.len() - left_buff; + Ok(format!( + "{}{}{}", + rep_char.repeat(left_buff), + value, + rep_char.repeat(right_buff) + )) + } + + fn expandtabs(self, tab_stop: OptionalArg, _vm: &mut VirtualMachine) -> String { + let tab_stop = tab_stop.into_option().unwrap_or(8 as usize); + let mut expanded_str = String::new(); + let mut tab_size = tab_stop; + let mut col_count = 0 as usize; + for ch in self.value.chars() { + // 0x0009 is tab + if ch == 0x0009 as char { + let num_spaces = tab_size - col_count; + col_count += num_spaces; + let expand = " ".repeat(num_spaces); + expanded_str.push_str(&expand); + } else { + expanded_str.push(ch); + col_count += 1; + } + if col_count >= tab_size { + tab_size += tab_stop; + } + } + expanded_str + } + fn isidentifier(self, _vm: &mut VirtualMachine) -> bool { let value = &self.value; // a string is not an identifier if it has whitespace or starts with a number @@ -368,7 +622,7 @@ pub fn init(context: &PyContext) { context.set_attr(&str_type, "__le__", context.new_rustfunc(PyStringRef::le)); context.set_attr(&str_type, "__hash__", context.new_rustfunc(PyStringRef::hash)); context.set_attr(&str_type, "__len__", context.new_rustfunc(PyStringRef::len)); - context.set_attr(&str_type, "__mul__", context.new_rustfunc(str_mul)); + context.set_attr(&str_type, "__mul__", context.new_rustfunc(PyStringRef::mul)); context.set_attr(&str_type, "__new__", context.new_rustfunc(str_new)); context.set_attr(&str_type, "__str__", context.new_rustfunc(PyStringRef::str)); context.set_attr(&str_type, "__repr__", context.new_rustfunc(PyStringRef::repr)); @@ -377,8 +631,8 @@ pub fn init(context: &PyContext) { context.set_attr(&str_type, "casefold", context.new_rustfunc(PyStringRef::casefold)); context.set_attr(&str_type, "upper", context.new_rustfunc(PyStringRef::upper)); context.set_attr(&str_type, "capitalize", context.new_rustfunc(PyStringRef::capitalize)); - context.set_attr(&str_type, "split", context.new_rustfunc(str_split)); - context.set_attr(&str_type, "rsplit", context.new_rustfunc(str_rsplit)); + context.set_attr(&str_type, "split", context.new_rustfunc(PyStringRef::split)); + context.set_attr(&str_type, "rsplit", context.new_rustfunc(PyStringRef::rsplit)); context.set_attr(&str_type, "strip", context.new_rustfunc(PyStringRef::strip)); context.set_attr(&str_type, "lstrip", context.new_rustfunc(PyStringRef::lstrip)); context.set_attr(&str_type, "rstrip", context.new_rustfunc(PyStringRef::rstrip)); @@ -391,26 +645,26 @@ pub fn init(context: &PyContext) { context.set_attr(&str_type, "title", context.new_rustfunc(PyStringRef::title)); context.set_attr(&str_type, "swapcase", context.new_rustfunc(PyStringRef::swapcase)); context.set_attr(&str_type, "isalpha", context.new_rustfunc(PyStringRef::isalpha)); - context.set_attr(&str_type, "replace", context.new_rustfunc(str_replace)); - context.set_attr(&str_type, "center", context.new_rustfunc(str_center)); + context.set_attr(&str_type, "replace", context.new_rustfunc(PyStringRef::replace)); context.set_attr(&str_type, "isspace", context.new_rustfunc(PyStringRef::isspace)); context.set_attr(&str_type, "isupper", context.new_rustfunc(PyStringRef::isupper)); context.set_attr(&str_type, "islower", context.new_rustfunc(PyStringRef::islower)); context.set_attr(&str_type, "isascii", context.new_rustfunc(PyStringRef::isascii)); context.set_attr(&str_type, "splitlines", context.new_rustfunc(PyStringRef::splitlines)); context.set_attr(&str_type, "join", context.new_rustfunc(PyStringRef::join)); - context.set_attr(&str_type, "find", context.new_rustfunc(str_find)); - context.set_attr(&str_type, "rfind", context.new_rustfunc(str_rfind)); - context.set_attr(&str_type, "index", context.new_rustfunc(str_index)); - context.set_attr(&str_type, "rindex", context.new_rustfunc(str_rindex)); + context.set_attr(&str_type, "find", context.new_rustfunc(PyStringRef::find)); + context.set_attr(&str_type, "rfind", context.new_rustfunc(PyStringRef::rfind)); + context.set_attr(&str_type, "index", context.new_rustfunc(PyStringRef::index)); + context.set_attr(&str_type, "rindex", context.new_rustfunc(PyStringRef::rindex)); context.set_attr(&str_type, "partition", context.new_rustfunc(PyStringRef::partition)); context.set_attr(&str_type, "rpartition", context.new_rustfunc(PyStringRef::rpartition)); - context.set_attr(&str_type, "istitle", context.new_rustfunc(str_istitle)); - context.set_attr(&str_type, "count", context.new_rustfunc(str_count)); - context.set_attr(&str_type, "zfill", context.new_rustfunc(str_zfill)); - context.set_attr(&str_type, "ljust", context.new_rustfunc(str_ljust)); - context.set_attr(&str_type, "rjust", context.new_rustfunc(str_rjust)); - context.set_attr(&str_type, "expandtabs", context.new_rustfunc(str_expandtabs)); + context.set_attr(&str_type, "istitle", context.new_rustfunc(PyStringRef::istitle)); + context.set_attr(&str_type, "count", context.new_rustfunc(PyStringRef::count)); + context.set_attr(&str_type, "zfill", context.new_rustfunc(PyStringRef::zfill)); + context.set_attr(&str_type, "ljust", context.new_rustfunc(PyStringRef::ljust)); + context.set_attr(&str_type, "rjust", context.new_rustfunc(PyStringRef::rjust)); + context.set_attr(&str_type, "center", context.new_rustfunc(PyStringRef::center)); + context.set_attr(&str_type, "expandtabs", context.new_rustfunc(PyStringRef::expandtabs)); context.set_attr(&str_type, "isidentifier", context.new_rustfunc(PyStringRef::isidentifier)); } @@ -521,353 +775,6 @@ fn perform_format( Ok(vm.ctx.new_str(final_string)) } -fn str_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(s, Some(vm.ctx.str_type())), (s2, None)] - ); - if objtype::isinstance(s2, &vm.ctx.int_type()) { - let value1 = get_value(&s); - let value2 = objint::get_value(s2).to_i32().unwrap(); - let mut result = String::new(); - for _x in 0..value2 { - result.push_str(value1.as_str()); - } - Ok(vm.ctx.new_str(result)) - } else { - Err(vm.new_type_error(format!("Cannot multiply {} and {}", s, s2))) - } -} - -fn str_rsplit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(s, Some(vm.ctx.str_type()))], - optional = [ - (pat, Some(vm.ctx.str_type())), - (num, Some(vm.ctx.int_type())) - ] - ); - let value = get_value(&s); - let pat = match pat { - Some(s) => get_value(&s), - None => " ".to_string(), - }; - let num_splits = match num { - Some(n) => objint::get_value(&n).to_usize().unwrap(), - None => value.split(&pat).count(), - }; - let elements = value - .rsplitn(num_splits + 1, &pat) - .map(|o| vm.ctx.new_str(o.to_string())) - .collect(); - Ok(vm.ctx.new_list(elements)) -} - -fn str_split(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(s, Some(vm.ctx.str_type()))], - optional = [ - (pat, Some(vm.ctx.str_type())), - (num, Some(vm.ctx.int_type())) - ] - ); - let value = get_value(&s); - let pat = match pat { - Some(s) => get_value(&s), - None => " ".to_string(), - }; - let num_splits = match num { - Some(n) => objint::get_value(&n).to_usize().unwrap(), - None => value.split(&pat).count(), - }; - let elements = value - .splitn(num_splits + 1, &pat) - .map(|o| vm.ctx.new_str(o.to_string())) - .collect(); - Ok(vm.ctx.new_list(elements)) -} - -fn str_zfill(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(s, Some(vm.ctx.str_type())), (len, Some(vm.ctx.int_type()))] - ); - let value = get_value(&s); - let len = objint::get_value(&len).to_usize().unwrap(); - let new_str = if len <= value.len() { - value - } else { - format!("{}{}", "0".repeat(len - value.len()), value) - }; - Ok(vm.ctx.new_str(new_str)) -} - -fn str_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(s, Some(vm.ctx.str_type())), (sub, Some(vm.ctx.str_type()))], - optional = [ - (start, Some(vm.ctx.int_type())), - (end, Some(vm.ctx.int_type())) - ] - ); - let value = get_value(&s); - let sub = get_value(&sub); - let (start, end) = match get_slice(start, end, value.len()) { - Ok((start, end)) => (start, end), - 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)) -} - -fn str_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(s, Some(vm.ctx.str_type())), (sub, Some(vm.ctx.str_type()))], - optional = [ - (start, Some(vm.ctx.int_type())), - (end, Some(vm.ctx.int_type())) - ] - ); - let value = get_value(&s); - let sub = get_value(&sub); - let (start, end) = match get_slice(start, end, value.len()) { - Ok((start, end)) => (start, end), - Err(e) => return Err(vm.new_index_error(e)), - }; - let ind: usize = match value[start..=end].find(&sub) { - Some(num) => num, - None => { - return Err(vm.new_value_error("substring not found".to_string())); - } - }; - Ok(vm.ctx.new_int(ind)) -} - -fn str_find(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(s, Some(vm.ctx.str_type())), (sub, Some(vm.ctx.str_type()))], - optional = [ - (start, Some(vm.ctx.int_type())), - (end, Some(vm.ctx.int_type())) - ] - ); - let value = get_value(&s); - let sub = get_value(&sub); - let (start, end) = match get_slice(start, end, value.len()) { - Ok((start, end)) => (start, end), - Err(e) => return Err(vm.new_index_error(e)), - }; - let ind: i128 = match value[start..=end].find(&sub) { - Some(num) => num as i128, - None => -1 as i128, - }; - Ok(vm.ctx.new_int(ind)) -} - -fn str_replace(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (s, Some(vm.ctx.str_type())), - (old, Some(vm.ctx.str_type())), - (rep, Some(vm.ctx.str_type())) - ], - optional = [(n, Some(vm.ctx.int_type()))] - ); - let s = get_value(&s); - let old_str = get_value(&old); - let rep_str = get_value(&rep); - let num_rep: usize = match n { - Some(num) => objint::get_value(&num).to_usize().unwrap(), - None => 1, - }; - let new_str = s.replacen(&old_str, &rep_str, num_rep); - Ok(vm.ctx.new_str(new_str)) -} - -fn str_expandtabs(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(s, Some(vm.ctx.str_type()))], - optional = [(size, Some(vm.ctx.int_type()))] - ); - let value = get_value(&s); - let tab_stop = match size { - Some(num) => objint::get_value(&num).to_usize().unwrap(), - None => 8 as usize, - }; - let mut expanded_str = String::new(); - let mut tab_size = tab_stop; - let mut col_count = 0 as usize; - for ch in value.chars() { - // 0x0009 is tab - if ch == 0x0009 as char { - let num_spaces = tab_size - col_count; - col_count += num_spaces; - let expand = " ".repeat(num_spaces); - expanded_str.push_str(&expand); - } else { - expanded_str.push(ch); - col_count += 1; - } - if col_count >= tab_size { - tab_size += tab_stop; - } - } - Ok(vm.ctx.new_str(expanded_str)) -} - -fn str_rjust(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(s, Some(vm.ctx.str_type())), (num, Some(vm.ctx.int_type()))], - optional = [(rep, Some(vm.ctx.str_type()))] - ); - let value = get_value(&s); - let num = objint::get_value(&num).to_usize().unwrap(); - let rep = match rep { - Some(st) => { - let rep_str = get_value(&st); - if rep_str.len() == 1 { - rep_str - } else { - return Err(vm.new_type_error( - "The fill character must be exactly one character long".to_string(), - )); - } - } - None => " ".to_string(), - }; - let new_str = format!("{}{}", rep.repeat(num), value); - Ok(vm.ctx.new_str(new_str)) -} - -fn str_ljust(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(s, Some(vm.ctx.str_type())), (num, Some(vm.ctx.int_type()))], - optional = [(rep, Some(vm.ctx.str_type()))] - ); - let value = get_value(&s); - let num = objint::get_value(&num).to_usize().unwrap(); - let rep = match rep { - Some(st) => { - let rep_str = get_value(&st); - if rep_str.len() == 1 { - rep_str - } else { - return Err(vm.new_type_error( - "The fill character must be exactly one character long".to_string(), - )); - } - } - None => " ".to_string(), - }; - let new_str = format!("{}{}", value, rep.repeat(num)); - Ok(vm.ctx.new_str(new_str)) -} - -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 is_titled = if value.is_empty() { - false - } else { - value.split(' ').all(|word| word == make_title(word)) - }; - - Ok(vm.ctx.new_bool(is_titled)) -} - -fn str_center(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(s, Some(vm.ctx.str_type())), (len, Some(vm.ctx.int_type()))], - optional = [(chars, Some(vm.ctx.str_type()))] - ); - let value = get_value(&s); - let len = objint::get_value(&len).to_usize().unwrap(); - let rep_char = match chars { - Some(c) => get_value(&c), - None => " ".to_string(), - }; - let left_buff: usize = (len - value.len()) / 2; - let right_buff = len - value.len() - left_buff; - let new_str = format!( - "{}{}{}", - rep_char.repeat(left_buff), - value, - rep_char.repeat(right_buff) - ); - Ok(vm.ctx.new_str(new_str)) -} - -fn str_rindex(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(s, Some(vm.ctx.str_type())), (sub, Some(vm.ctx.str_type()))], - optional = [ - (start, Some(vm.ctx.int_type())), - (end, Some(vm.ctx.int_type())) - ] - ); - let value = get_value(&s); - let sub = get_value(&sub); - let (start, end) = match get_slice(start, end, value.len()) { - Ok((start, end)) => (start, end), - Err(e) => return Err(vm.new_index_error(e)), - }; - 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())); - } - }; - Ok(vm.ctx.new_int(ind)) -} - -fn str_rfind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(s, Some(vm.ctx.str_type())), (sub, Some(vm.ctx.str_type()))], - optional = [ - (start, Some(vm.ctx.int_type())), - (end, Some(vm.ctx.int_type())) - ] - ); - let value = get_value(&s); - let sub = get_value(&sub); - let (start, end) = match get_slice(start, end, value.len()) { - Ok((start, end)) => (start, end), - Err(e) => return Err(vm.new_index_error(e)), - }; - let ind = match value[start..=end].rfind(&sub) { - Some(num) => num as i128, - None => -1 as i128, - }; - Ok(vm.ctx.new_int(ind)) -} - // TODO: should with following format // class str(object='') // class str(object=b'', encoding='utf-8', errors='strict') @@ -975,23 +882,31 @@ pub fn subscript(vm: &mut VirtualMachine, value: &str, b: PyObjectRef) -> PyResu } // help get optional string indices -fn get_slice( - start: Option<&PyObjectRef>, - end: Option<&PyObjectRef>, +fn adjust_indices( + start: OptionalArg, + end: OptionalArg, len: usize, -) -> Result<(usize, usize), String> { - let start_idx = match start { - Some(int) => objint::get_value(&int).to_usize().unwrap(), - None => 0 as usize, - }; - let end_idx = match end { - Some(int) => objint::get_value(&int).to_usize().unwrap(), - None => len - 1, - }; - if start_idx >= usize::min_value() && start_idx < end_idx && end_idx < len { - Ok((start_idx, end_idx)) +) -> Option<(usize, usize)> { + let mut start = start.into_option().unwrap_or(0); + let mut end = end.into_option().unwrap_or(len as isize); + if end > len as isize { + end = len as isize; + } else if end < 0 { + end += len as isize; + if end < 0 { + end = 0; + } + } + if start < 0 { + start += len as isize; + if start < 0 { + start = 0; + } + } + if start > end { + None } else { - Err("provided index is not valid".to_string()) + Some((start as usize, end as usize)) } } From e220f0a571bf2649384411941a69378d8ae1ea89 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Thu, 7 Mar 2019 20:42:10 +0000 Subject: [PATCH 191/380] Add PyClass and remove old Class payload --- vm/src/obj/objdict.rs | 8 +++--- vm/src/obj/objobject.rs | 8 +++--- vm/src/obj/objtype.rs | 47 ++++++++++++++++++++++----------- vm/src/pyobject.rs | 57 ++++++++++++++++++++--------------------- vm/src/vm.rs | 1 - 5 files changed, 70 insertions(+), 51 deletions(-) diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 0a7416ccaa..df85e75791 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -340,9 +340,11 @@ pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: // this is not ideal let ptr = PyObjectRef::into_raw(dict_type.clone()) as *mut PyObject; unsafe { - (*ptr).payload = PyObjectPayload::Class { - name: String::from("dict"), - mro: vec![object_type], + (*ptr).payload = PyObjectPayload::AnyRustValue { + value: Box::new(objtype::PyClass { + name: String::from("dict"), + mro: vec![object_type], + }), }; (*ptr).dict = Some(RefCell::new(HashMap::new())); (*ptr).typ = Some(type_type.clone()); diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 00acdb7ef1..030a3dc702 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -24,9 +24,11 @@ pub fn create_object(type_type: PyObjectRef, object_type: PyObjectRef, _dict_typ // this is not ideal let ptr = PyObjectRef::into_raw(object_type.clone()) as *mut PyObject; unsafe { - (*ptr).payload = PyObjectPayload::Class { - name: String::from("object"), - mro: vec![], + (*ptr).payload = PyObjectPayload::AnyRustValue { + value: Box::new(objtype::PyClass { + name: String::from("object"), + mro: vec![], + }), }; (*ptr).dict = Some(RefCell::new(HashMap::new())); (*ptr).typ = Some(type_type.clone()); diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 1ced9d2a3b..cee984f517 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -1,13 +1,27 @@ use super::objdict; use super::objstr; +use crate::function::PyRef; use crate::pyobject::{ AttributeProtocol, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObject, PyObjectPayload, - PyObjectRef, PyResult, TypeProtocol, + PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use std::cell::RefCell; use std::collections::HashMap; +#[derive(Clone, Debug)] +pub struct PyClass { + pub name: String, + pub mro: Vec, +} +pub type PyClassRef = PyRef; + +impl PyObjectPayload2 for PyClass { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.type_type() + } +} + /* * The magical type type */ @@ -16,9 +30,11 @@ pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, _dict_type: // this is not ideal let ptr = PyObjectRef::into_raw(type_type.clone()) as *mut PyObject; unsafe { - (*ptr).payload = PyObjectPayload::Class { - name: String::from("type"), - mro: vec![object_type], + (*ptr).payload = PyObjectPayload::AnyRustValue { + value: Box::new(PyClass { + name: String::from("type"), + mro: vec![object_type], + }), }; (*ptr).dict = Some(RefCell::new(PyAttributes::new())); (*ptr).typ = Some(type_type); @@ -85,13 +101,12 @@ fn type_mro(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn _mro(cls: PyObjectRef) -> Option> { - match cls.payload { - PyObjectPayload::Class { ref mro, .. } => { - let mut mro = mro.clone(); - mro.insert(0, cls.clone()); - Some(mro) - } - _ => None, + if let Some(PyClass { ref mro, .. }) = cls.payload::() { + let mut mro = mro.clone(); + mro.insert(0, cls.clone()); + Some(mro) + } else { + None } } @@ -132,7 +147,7 @@ fn type_subclass_check(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn get_type_name(typ: &PyObjectRef) -> String { - if let PyObjectPayload::Class { name, .. } = &typ.payload { + if let Some(PyClass { name, .. }) = &typ.payload::() { name.clone() } else { panic!("Cannot get type_name of non-type type {:?}", typ); @@ -319,9 +334,11 @@ pub fn new( let mros = bases.into_iter().map(|x| _mro(x).unwrap()).collect(); let mro = linearise_mro(mros).unwrap(); Ok(PyObject { - payload: PyObjectPayload::Class { - name: String::from(name), - mro, + payload: PyObjectPayload::AnyRustValue { + value: Box::new(PyClass { + name: String::from(name), + mro, + }), }, dict: Some(RefCell::new(dict)), typ: Some(typ), diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index a5f8a5725e..cf41cf0803 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -41,7 +41,7 @@ use crate::obj::objslice; use crate::obj::objstr; use crate::obj::objsuper; use crate::obj::objtuple; -use crate::obj::objtype; +use crate::obj::objtype::{self, PyClass}; use crate::obj::objzip; use crate::vm::VirtualMachine; @@ -81,18 +81,19 @@ pub type PyAttributes = HashMap; impl fmt::Display for PyObject { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::TypeProtocol; + if let Some(PyClass { ref name, .. }) = self.payload::() { + 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 + if type_name == "type" { + return write!(f, "type object '{}'", name); + } else { + return write!(f, "'{}' object", type_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 - if type_name == "type" { - write!(f, "type object '{}'", name) - } else { - write!(f, "'{}' object", type_name) - } - } _ => write!(f, "'{}' object", objtype::get_type_name(&self.typ())), } } @@ -808,19 +809,20 @@ fn class_has_item(class: &PyObjectRef, attr_name: &str) -> bool { impl AttributeProtocol for PyObjectRef { fn get_attr(&self, attr_name: &str) -> Option { - match self.payload { - PyObjectPayload::Module { ref scope, .. } => scope.locals.get_item(attr_name), - PyObjectPayload::Class { ref mro, .. } => { - if let Some(item) = class_get_item(self, attr_name) { + if let Some(PyClass { ref mro, .. }) = self.payload::() { + if let Some(item) = class_get_item(self, attr_name) { + return Some(item); + } + for class in mro { + if let Some(item) = class_get_item(class, attr_name) { return Some(item); } - for class in mro { - if let Some(item) = class_get_item(class, attr_name) { - return Some(item); - } - } - None } + return None; + } + + match self.payload { + PyObjectPayload::Module { ref scope, .. } => scope.locals.get_item(attr_name), _ => { if let Some(ref dict) = self.dict { dict.borrow().get(attr_name).cloned() @@ -832,11 +834,13 @@ impl AttributeProtocol for PyObjectRef { } fn has_attr(&self, attr_name: &str) -> bool { + if let Some(PyClass { ref mro, .. }) = self.payload::() { + return class_has_item(self, attr_name) + || mro.iter().any(|d| class_has_item(d, attr_name)); + } + match self.payload { PyObjectPayload::Module { ref scope, .. } => scope.locals.contains_key(attr_name), - PyObjectPayload::Class { ref mro, .. } => { - class_has_item(self, attr_name) || mro.iter().any(|d| class_has_item(d, attr_name)) - } _ => { if let Some(ref dict) = self.dict { dict.borrow().contains_key(attr_name) @@ -1527,10 +1531,6 @@ pub enum PyObjectPayload { name: String, scope: ScopeRef, }, - Class { - name: String, - mro: Vec, - }, WeakRef { referent: PyObjectWeakRef, }, @@ -1570,7 +1570,6 @@ impl fmt::Debug for PyObjectPayload { ref object, } => write!(f, "bound-method: {:?} of {:?}", function, object), PyObjectPayload::Module { .. } => write!(f, "module"), - PyObjectPayload::Class { ref name, .. } => write!(f, "class {:?}", name), PyObjectPayload::RustFunction { .. } => write!(f, "rust function"), PyObjectPayload::Frame { .. } => write!(f, "frame"), PyObjectPayload::AnyRustValue { .. } => write!(f, "some rust value"), diff --git a/vm/src/vm.rs b/vm/src/vm.rs index aa96d4cfd1..bdc77969b1 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -298,7 +298,6 @@ impl VirtualMachine { ref scope, ref defaults, } => self.invoke_python_function(code, scope, defaults, args), - PyObjectPayload::Class { .. } => self.call_method(&func_ref, "__call__", args), PyObjectPayload::BoundMethod { ref function, ref object, From c563a6d4980710708d8913b663be34a0ee52d1cb Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Thu, 7 Mar 2019 20:48:14 -0600 Subject: [PATCH 192/380] Convert Python errors to JS errors with row --- wasm/lib/src/browser_module.rs | 8 ++--- wasm/lib/src/convert.rs | 57 ++++++++++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index ea21d30fda..490d6ef37f 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -221,8 +221,7 @@ fn promise_then(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } }; - ret.map(|val| convert::py_to_js(vm, val)) - .map_err(|err| convert::py_to_js(vm, err)) + convert::pyresult_to_jsresult(vm, ret) }); let ret_promise = future_to_promise(ret_future); @@ -254,9 +253,8 @@ fn promise_catch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .upgrade() .expect("that the vm is valid when the promise resolves"); let err = convert::js_to_py(vm, err); - vm.invoke(on_reject, PyFuncArgs::new(vec![err], vec![])) - .map(|val| convert::py_to_js(vm, val)) - .map_err(|err| convert::py_to_js(vm, err)) + let res = vm.invoke(on_reject, PyFuncArgs::new(vec![err], vec![])); + convert::pyresult_to_jsresult(vm, res) } }); diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index cb37efe5a8..086b531cfd 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -1,14 +1,59 @@ use crate::browser_module; use crate::vm_class::{AccessibleVM, WASMVirtualMachine}; use js_sys::{Array, ArrayBuffer, Object, Promise, Reflect, Uint8Array}; -use rustpython_vm::obj::{objbytes, objtype}; -use rustpython_vm::pyobject::{self, DictProtocol, PyFuncArgs, PyObjectRef, PyResult}; +use num_traits::cast::ToPrimitive; +use rustpython_vm::obj::{objbytes, objint, objsequence, objtype}; +use rustpython_vm::pyobject::{ + self, AttributeProtocol, DictProtocol, PyFuncArgs, PyObjectRef, PyResult, +}; use rustpython_vm::VirtualMachine; 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_err_to_js_err(vm: &mut VirtualMachine, py_err: &PyObjectRef) -> JsValue { + macro_rules! map_exceptions { + ($py_exc:ident, $msg:expr, { $($py_exc_ty:expr => $js_err_new:expr),*$(,)? }) => { + $(if objtype::isinstance($py_exc, $py_exc_ty) { + JsValue::from($js_err_new($msg)) + } else)* { + JsValue::from(js_sys::Error::new($msg)) + } + }; + } + let msg = match py_err + .get_attr("msg") + .and_then(|msg| vm.to_pystr(&msg).ok()) + { + Some(msg) => msg, + None => return js_sys::Error::new("error getting error").into(), + }; + let js_err = map_exceptions!(py_err,& msg, { + // TypeError is sort of a catch-all for "this value isn't what I thought it was like" + &vm.ctx.exceptions.type_error => js_sys::TypeError::new, + &vm.ctx.exceptions.value_error => js_sys::TypeError::new, + &vm.ctx.exceptions.index_error => js_sys::TypeError::new, + &vm.ctx.exceptions.key_error => js_sys::TypeError::new, + &vm.ctx.exceptions.attribute_error => js_sys::TypeError::new, + &vm.ctx.exceptions.name_error => js_sys::ReferenceError::new, + &vm.ctx.exceptions.syntax_error => js_sys::SyntaxError::new, + }); + if let Some(tb) = py_err.get_attr("__traceback__") { + if objtype::isinstance(&tb, &vm.ctx.list_type()) { + let elements = objsequence::get_elements(&tb).to_vec(); + if let Some(top) = elements.get(0) { + if objtype::isinstance(&top, &vm.ctx.tuple_type()) { + let element = objsequence::get_elements(&top); + + if let Some(lineno) = objint::to_int(vm, &element[1], 10) + .ok() + .and_then(|lineno| lineno.to_u32()) + { + Reflect::set(&js_err, &"row".into(), &lineno.into()); + } + } + } + } + } + js_err } pub fn js_py_typeerror(vm: &mut VirtualMachine, js_err: JsValue) -> PyObjectRef { @@ -110,7 +155,7 @@ pub fn object_entries(obj: &Object) -> impl Iterator Result { result .map(|value| py_to_js(vm, value)) - .map_err(|err| py_str_err(vm, &err).into()) + .map_err(|err| py_err_to_js_err(vm, &err).into()) } pub fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef { From 4b1cd723558227c7d053e91263a75a6c51715f90 Mon Sep 17 00:00:00 2001 From: ben Date: Fri, 8 Mar 2019 17:04:32 +1300 Subject: [PATCH 193/380] Add todo for using PyClassRef when that lands --- vm/src/obj/objobject.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 6b01fd662d..bf659b2c72 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -195,6 +195,7 @@ fn object_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { Ok(vm.ctx.none()) } +// TODO Use PyClassRef for owner to enforce type fn object_class(_obj: PyObjectRef, owner: PyObjectRef, _vm: &mut VirtualMachine) -> PyObjectRef { owner } From 231ab682b91722abe3f793ca977f77c88129fe46 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Thu, 7 Mar 2019 22:18:23 -0600 Subject: [PATCH 194/380] Convert CodeObject payload to AnyRustValue --- vm/src/compile.rs | 8 +++++++- vm/src/obj/objcode.rs | 29 ++++++++++++++++++++++++++--- vm/src/pyobject.rs | 11 ++++++----- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/vm/src/compile.rs b/vm/src/compile.rs index a5e01ab2f8..e30b006764 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -7,6 +7,7 @@ use crate::bytecode::{self, CallType, CodeObject, Instruction}; use crate::error::CompileError; +use crate::obj::objcode; use crate::pyobject::{PyObject, PyObjectPayload, PyObjectRef}; use num_complex::Complex64; use rustpython_parser::{ast, parser}; @@ -48,7 +49,12 @@ pub fn compile( let code = compiler.pop_code_object(); trace!("Compilation completed: {:?}", code); - Ok(PyObject::new(PyObjectPayload::Code { code }, code_type)) + Ok(PyObject::new( + PyObjectPayload::AnyRustValue { + value: Box::new(objcode::PyCode::new(code)), + }, + code_type, + )) } pub enum Mode { diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index 53ea55f4c3..b6a1850899 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -4,9 +4,32 @@ use crate::bytecode; use crate::pyobject::{ - IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + IdProtocol, PyContext, PyFuncArgs, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; +use std::fmt; + +pub struct PyCode { + code: bytecode::CodeObject, +} + +impl PyCode { + pub fn new(code: bytecode::CodeObject) -> PyCode { + PyCode { code } + } +} + +impl fmt::Debug for PyCode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "code: {:?}", self.code) + } +} + +impl PyObjectPayload2 for PyCode { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.code_type() + } +} pub fn init(context: &PyContext) { let code_type = &context.code_type; @@ -29,8 +52,8 @@ pub fn init(context: &PyContext) { } pub fn get_value(obj: &PyObjectRef) -> bytecode::CodeObject { - if let PyObjectPayload::Code { code } = &obj.payload { - code.clone() + if let Some(code) = obj.payload::() { + code.code.clone() } else { panic!("Inner error getting code {:?}", obj) } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 8e1e45c8c3..d2407360d3 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -636,7 +636,12 @@ impl PyContext { } pub fn new_code_object(&self, code: bytecode::CodeObject) -> PyObjectRef { - PyObject::new(PyObjectPayload::Code { code }, self.code_type()) + PyObject::new( + PyObjectPayload::AnyRustValue { + value: Box::new(objcode::PyCode::new(code)), + }, + self.code_type(), + ) } pub fn new_function( @@ -1497,9 +1502,6 @@ pub enum PyObjectPayload { MemoryView { obj: PyObjectRef, }, - Code { - code: bytecode::CodeObject, - }, Frame { frame: Frame, }, @@ -1550,7 +1552,6 @@ impl fmt::Debug for PyObjectPayload { 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"), PyObjectPayload::Generator { .. } => write!(f, "generator"), PyObjectPayload::BoundMethod { From 87a58786af481d0d544f5a2a668569036bcb6d10 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Thu, 7 Mar 2019 22:20:10 -0600 Subject: [PATCH 195/380] Have AnyRustValue actually format with its own Debug impl --- vm/src/pyobject.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 8e1e45c8c3..120724af21 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1562,7 +1562,7 @@ impl fmt::Debug for PyObjectPayload { PyObjectPayload::Instance { .. } => write!(f, "instance"), PyObjectPayload::RustFunction { .. } => write!(f, "rust function"), PyObjectPayload::Frame { .. } => write!(f, "frame"), - PyObjectPayload::AnyRustValue { .. } => write!(f, "some rust value"), + PyObjectPayload::AnyRustValue { value } => value.fmt(f), } } } From 1bcbb28715455171aea8fd8079764ffcaf979134 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 7 Mar 2019 19:20:01 -0800 Subject: [PATCH 196/380] Convert list and tuple to Any payload --- vm/src/obj/objbool.rs | 35 ++++++++++--------- vm/src/obj/objiter.rs | 21 +++++------ vm/src/obj/objlist.rs | 28 ++++++++++++--- vm/src/obj/objsequence.rs | 73 ++++++++++++++++++++++++--------------- vm/src/obj/objtuple.rs | 33 +++++++++++++++--- vm/src/pyobject.rs | 16 ++++----- vm/src/vm.rs | 8 +++-- 7 files changed, 137 insertions(+), 77 deletions(-) diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index 09e0759c76..f71c2ea181 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -1,14 +1,14 @@ use num_traits::Zero; -use crate::pyobject::{ - IntoPyObject, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, -}; +use crate::pyobject::{IntoPyObject, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; use super::objdict::PyDict; use super::objfloat::PyFloat; use super::objint::PyInt; +use super::objlist::PyList; use super::objstr::PyString; +use super::objtuple::PyTuple; use super::objtype; impl IntoPyObject for bool { @@ -30,21 +30,22 @@ pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { if let Some(i) = obj.payload::() { return Ok(!i.value.is_zero()); } - let result = match obj.payload { - PyObjectPayload::Sequence { ref elements } => !elements.borrow().is_empty(), - _ => { - if let Ok(f) = vm.get_method(obj.clone(), "__bool__") { - let bool_res = vm.invoke(f, PyFuncArgs::default())?; - match bool_res.payload::() { - Some(i) => !i.value.is_zero(), - None => return Err(vm.new_type_error(String::from("TypeError"))), - } - } else { - true - } + if let Some(list) = obj.payload::() { + return Ok(!list.elements.borrow().is_empty()); + } + if let Some(tuple) = obj.payload::() { + return Ok(!tuple.elements.borrow().is_empty()); + } + + Ok(if let Ok(f) = vm.get_method(obj.clone(), "__bool__") { + let bool_res = vm.invoke(f, PyFuncArgs::default())?; + match bool_res.payload::() { + Some(i) => !i.value.is_zero(), + None => return Err(vm.new_type_error(String::from("TypeError"))), } - }; - Ok(result) + } else { + true + }) } pub fn init(context: &PyContext) { diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 049ea8be39..827ff1cd06 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -11,6 +11,7 @@ use super::objbool; use super::objbytearray::PyByteArray; use super::objbytes::PyBytes; use super::objrange::PyRange; +use super::objsequence; use super::objtype; /* @@ -156,19 +157,13 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Err(new_stop_iteration(vm)) } } else { - match iterated_obj_ref.payload { - PyObjectPayload::Sequence { ref elements } => { - if position.get() < elements.borrow().len() { - let obj_ref = elements.borrow()[position.get()].clone(); - position.set(position.get() + 1); - Ok(obj_ref) - } else { - Err(new_stop_iteration(vm)) - } - } - _ => { - panic!("NOT IMPL"); - } + let elements = objsequence::get_elements(iterated_obj_ref); + if position.get() < elements.len() { + let obj_ref = elements[position.get()].clone(); + position.set(position.get() + 1); + Ok(obj_ref) + } else { + Err(new_stop_iteration(vm)) } } } else { diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 5f2643e785..0a2ece3651 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -9,12 +9,32 @@ use super::objsequence::{ use super::objstr; use super::objtype; use crate::pyobject::{ - IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, - TypeProtocol, + IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, + PyResult, TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; use num_traits::ToPrimitive; +#[derive(Debug, Default)] +pub struct PyList { + // TODO: shouldn't be public + pub elements: RefCell>, +} + +impl From> for PyList { + fn from(elements: Vec) -> Self { + PyList { + elements: RefCell::new(elements), + } + } +} + +impl PyObjectPayload2 for PyList { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.list_type() + } +} + // set_item: fn set_item( vm: &mut VirtualMachine, @@ -57,8 +77,8 @@ fn list_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; Ok(PyObject::new( - PyObjectPayload::Sequence { - elements: RefCell::new(elements), + PyObjectPayload::AnyRustValue { + value: Box::new(PyList::from(elements)), }, cls.clone(), )) diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index 5f875a5b07..f18f23e4d8 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -2,13 +2,17 @@ use std::cell::RefCell; use std::marker::Sized; use std::ops::{Deref, DerefMut, Range}; -use super::objbool; -use super::objint::{self, PyInt}; -use crate::pyobject::{IdProtocol, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol}; -use crate::vm::VirtualMachine; use num_bigint::BigInt; use num_traits::{One, Signed, ToPrimitive, Zero}; +use crate::pyobject::{IdProtocol, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol}; +use crate::vm::VirtualMachine; + +use super::objbool; +use super::objint::{self, PyInt}; +use super::objlist::PyList; +use super::objtuple::PyTuple; + pub trait PySliceableSequence { fn do_slice(&self, range: Range) -> Self; fn do_slice_reverse(&self, range: Range) -> Self; @@ -156,16 +160,27 @@ pub fn get_item( } }; } + match &subscript.payload { - PyObjectPayload::Slice { .. } => Ok(PyObject::new( - match &sequence.payload { - PyObjectPayload::Sequence { .. } => PyObjectPayload::Sequence { - elements: RefCell::new(elements.to_vec().get_slice_items(vm, &subscript)?), - }, - ref payload => panic!("sequence get_item called for non-sequence: {:?}", payload), - }, - sequence.typ(), - )), + PyObjectPayload::Slice { .. } => { + let payload = if sequence.payload::().is_some() { + PyObjectPayload::AnyRustValue { + value: Box::new(PyList::from( + elements.to_vec().get_slice_items(vm, &subscript)?, + )), + } + } else if sequence.payload::().is_some() { + PyObjectPayload::AnyRustValue { + value: Box::new(PyTuple::from( + elements.to_vec().get_slice_items(vm, &subscript)?, + )), + } + } else { + panic!("sequence get_item called for non-sequence") + }; + + Ok(PyObject::new(payload, sequence.typ())) + } _ => Err(vm.new_type_error(format!( "TypeError: indexing type {:?} with index {:?} is not supported (yet?)", sequence, subscript @@ -304,27 +319,31 @@ 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"); + if let Some(list) = obj.payload::() { + return &list.elements; } + if let Some(tuple) = obj.payload::() { + return &tuple.elements; + } + 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() - } else { - panic!("Cannot extract elements from non-sequence"); + if let Some(list) = obj.payload::() { + return list.elements.borrow(); + } + if let Some(tuple) = obj.payload::() { + return tuple.elements.borrow(); } + panic!("Cannot extract elements from non-sequence"); } pub fn get_mut_elements<'a>(obj: &'a PyObjectRef) -> impl DerefMut> + 'a { - if let PyObjectPayload::Sequence { ref elements } = obj.payload { - elements.borrow_mut() - } else { - panic!("Cannot extract list elements from non-sequence"); - // TODO: raise proper error? - // Err(vm.new_type_error("list.append is called with no list".to_string())) + if let Some(list) = obj.payload::() { + return list.elements.borrow_mut(); + } + if let Some(tuple) = obj.payload::() { + return tuple.elements.borrow_mut(); } + panic!("Cannot extract elements from non-sequence"); } diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 7747d2fc92..a9f140b53c 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -1,6 +1,12 @@ use std::cell::{Cell, RefCell}; use std::hash::{Hash, Hasher}; +use crate::pyobject::{ + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, + TypeProtocol, +}; +use crate::vm::{ReprGuard, VirtualMachine}; + use super::objbool; use super::objint; use super::objsequence::{ @@ -8,8 +14,27 @@ use super::objsequence::{ }; use super::objstr; use super::objtype; -use crate::pyobject::{PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyResult, TypeProtocol}; -use crate::vm::{ReprGuard, VirtualMachine}; + +#[derive(Debug, Default)] +pub struct PyTuple { + // TODO: shouldn't be public + // TODO: tuples are immutable, remove this RefCell + pub elements: RefCell>, +} + +impl From> for PyTuple { + fn from(elements: Vec) -> Self { + PyTuple { + elements: RefCell::new(elements), + } + } +} + +impl PyObjectPayload2 for PyTuple { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.tuple_type() + } +} fn tuple_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( @@ -185,8 +210,8 @@ fn tuple_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; Ok(PyObject::new( - PyObjectPayload::Sequence { - elements: RefCell::new(elements), + PyObjectPayload::AnyRustValue { + value: Box::new(PyTuple::from(elements)), }, cls.clone(), )) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 8e1e45c8c3..2ce836c3ee 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -28,7 +28,7 @@ use crate::obj::objfunction; use crate::obj::objgenerator; use crate::obj::objint::{self, PyInt}; use crate::obj::objiter; -use crate::obj::objlist; +use crate::obj::objlist::{self, PyList}; use crate::obj::objmap; use crate::obj::objmemory; use crate::obj::objmodule; @@ -40,7 +40,7 @@ use crate::obj::objset::{self, PySet}; use crate::obj::objslice; use crate::obj::objstr; use crate::obj::objsuper; -use crate::obj::objtuple; +use crate::obj::objtuple::{self, PyTuple}; use crate::obj::objtype; use crate::obj::objzip; use crate::vm::VirtualMachine; @@ -549,8 +549,8 @@ impl PyContext { pub fn new_tuple(&self, elements: Vec) -> PyObjectRef { PyObject::new( - PyObjectPayload::Sequence { - elements: RefCell::new(elements), + PyObjectPayload::AnyRustValue { + value: Box::new(PyTuple::from(elements)), }, self.tuple_type(), ) @@ -558,8 +558,8 @@ impl PyContext { pub fn new_list(&self, elements: Vec) -> PyObjectRef { PyObject::new( - PyObjectPayload::Sequence { - elements: RefCell::new(elements), + PyObjectPayload::AnyRustValue { + value: Box::new(PyList::from(elements)), }, self.list_type(), ) @@ -1467,9 +1467,6 @@ into_py_native_func_tuple!((a, A), (b, B), (c, C), (d, D), (e, E)); /// of rust data for a particular python object. Determine the python type /// by using for example the `.typ()` method on a python object. pub enum PyObjectPayload { - Sequence { - elements: RefCell>, - }, Iterator { position: Cell, iterated_obj: PyObjectRef, @@ -1542,7 +1539,6 @@ impl fmt::Debug for PyObjectPayload { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { PyObjectPayload::MemoryView { ref obj } => write!(f, "bytes/bytearray {:?}", obj), - PyObjectPayload::Sequence { .. } => write!(f, "list or tuple"), PyObjectPayload::WeakRef { .. } => write!(f, "weakref"), PyObjectPayload::Iterator { .. } => write!(f, "iterator"), PyObjectPayload::EnumerateIterator { .. } => write!(f, "enumerate"), diff --git a/vm/src/vm.rs b/vm/src/vm.rs index e3d06d5fae..f67c931d30 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -20,8 +20,10 @@ use crate::obj::objcode; use crate::obj::objframe; use crate::obj::objgenerator; use crate::obj::objiter; +use crate::obj::objlist::PyList; use crate::obj::objsequence; use crate::obj::objstr; +use crate::obj::objtuple::PyTuple; use crate::obj::objtype; use crate::pyobject::{ AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, @@ -446,8 +448,10 @@ impl VirtualMachine { if nargs < nexpected_args { let available_defaults = if defaults.is(&self.get_none()) { vec![] - } else if let PyObjectPayload::Sequence { ref elements } = defaults.payload { - elements.borrow().clone() + } else if let Some(list) = defaults.payload::() { + list.elements.borrow().clone() + } else if let Some(tuple) = defaults.payload::() { + tuple.elements.borrow().clone() } else { panic!("function defaults not tuple or None"); }; From cdba098e70b5f98f5815b01c54146f050e373442 Mon Sep 17 00:00:00 2001 From: Tzu-Yin Hong Date: Fri, 8 Mar 2019 14:41:08 +0800 Subject: [PATCH 197/380] Implement dict.get --- tests/snippets/builtin_dict.py | 4 ++++ vm/src/obj/objdict.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/tests/snippets/builtin_dict.py b/tests/snippets/builtin_dict.py index a57541fa58..3252d7b41f 100644 --- a/tests/snippets/builtin_dict.py +++ b/tests/snippets/builtin_dict.py @@ -8,3 +8,7 @@ d = {} d['a'] = d assert repr(d) == "{'a': {...}}" + +assert {'a': 123}.get('a') == 123 +assert {'a': 123}.get('b') == None +assert {'a': 123}.get('b', 456) == 456 diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 4fd99ae7ea..c86156e950 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -336,6 +336,32 @@ fn dict_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } +fn dict_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [ + (dict, Some(vm.ctx.dict_type())), + (key, Some(vm.ctx.str_type())) + ], + optional = [ + (default, None) + ] + ); + + // What we are looking for: + let key = objstr::get_value(&key); + + let elements = get_elements(dict); + if elements.contains_key(&key) { + Ok(elements[&key].1.clone()) + } else if let Some(value) = default { + Ok(value.clone()) + } else { + Ok(vm.get_none()) + } +} + pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: PyObjectRef) { // this is not ideal let ptr = PyObjectRef::into_raw(dict_type.clone()) as *mut PyObject; @@ -379,4 +405,5 @@ pub fn init(context: &PyContext) { context.set_attr(&dict_type, "values", context.new_rustfunc(dict_values)); context.set_attr(&dict_type, "items", context.new_rustfunc(dict_items)); context.set_attr(&dict_type, "keys", context.new_rustfunc(dict_iter)); + context.set_attr(&dict_type, "get", context.new_rustfunc(dict_get)); } From 3f665ce673bda13aec424d1f9af541dce2a356c3 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Fri, 8 Mar 2019 08:43:07 +0000 Subject: [PATCH 198/380] Replace PyObjectPayload::Module with PyModule. --- vm/src/obj/objmodule.rs | 48 ++++++++++++++----------- vm/src/pyobject.rs | 80 ++++++++++++++++++----------------------- vm/src/vm.rs | 10 ++---- 3 files changed, 65 insertions(+), 73 deletions(-) diff --git a/vm/src/obj/objmodule.rs b/vm/src/obj/objmodule.rs index ef6cf2d6df..bbd109cce5 100644 --- a/vm/src/obj/objmodule.rs +++ b/vm/src/obj/objmodule.rs @@ -1,30 +1,36 @@ use crate::frame::ScopeRef; -use crate::pyobject::{ - DictProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, -}; +use crate::function::PyRef; +use crate::pyobject::{DictProtocol, PyContext, PyObjectPayload2, PyObjectRef, PyResult}; use crate::vm::VirtualMachine; -fn module_dir(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(obj, Some(vm.ctx.module_type()))]); - let scope = get_scope(obj); - let keys = scope - .locals - .get_key_value_pairs() - .iter() - .map(|(k, _v)| k.clone()) - .collect(); - Ok(vm.ctx.new_list(keys)) +#[derive(Clone, Debug)] +pub struct PyModule { + pub name: String, + pub scope: ScopeRef, } +pub type PyModuleRef = PyRef; -pub fn init(context: &PyContext) { - let module_type = &context.module_type; - context.set_attr(&module_type, "__dir__", context.new_rustfunc(module_dir)); +impl PyObjectPayload2 for PyModule { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.module_type() + } } -fn get_scope(obj: &PyObjectRef) -> &ScopeRef { - if let PyObjectPayload::Module { ref scope, .. } = &obj.payload { - &scope - } else { - panic!("Can't get scope from non-module.") +impl PyModuleRef { + fn dir(self, vm: &mut VirtualMachine) -> PyResult { + let keys = self + .scope + .locals + .get_key_value_pairs() + .iter() + .map(|(k, _v)| k.clone()) + .collect(); + Ok(vm.ctx.new_list(keys)) } } + +pub fn init(context: &PyContext) { + extend_class!(&context, &context.module_type, { + "__dir__" => context.new_rustfunc(PyModuleRef::dir) + }); +} diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index bf38802d09..3f79786c04 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -31,7 +31,7 @@ use crate::obj::objiter; use crate::obj::objlist::{self, PyList}; use crate::obj::objmap; use crate::obj::objmemory; -use crate::obj::objmodule; +use crate::obj::objmodule::{self, PyModule}; use crate::obj::objnone; use crate::obj::objobject; use crate::obj::objproperty; @@ -92,10 +92,10 @@ impl fmt::Display for PyObject { } } - match &self.payload { - PyObjectPayload::Module { name, .. } => write!(f, "module '{}'", name), - _ => write!(f, "'{}' object", objtype::get_type_name(&self.typ())), + if let Some(PyModule { ref name, .. }) = self.payload::() { + return write!(f, "module '{}'", name); } + write!(f, "'{}' object", objtype::get_type_name(&self.typ())) } } @@ -600,9 +600,11 @@ impl PyContext { pub fn new_module(&self, name: &str, scope: ScopeRef) -> PyObjectRef { PyObject::new( - PyObjectPayload::Module { - name: name.to_string(), - scope, + PyObjectPayload::AnyRustValue { + value: Box::new(PyModule { + name: name.to_string(), + scope, + }), }, self.module_type.clone(), ) @@ -730,7 +732,7 @@ impl PyContext { } pub fn set_attr(&self, obj: &PyObjectRef, attr_name: &str, value: PyObjectRef) { - if let PyObjectPayload::Module { ref scope, .. } = obj.payload { + if let Some(PyModule { ref scope, .. }) = obj.payload::() { scope.locals.set_item(self, attr_name, value) } else if let Some(ref dict) = obj.dict { dict.borrow_mut().insert(attr_name.to_string(), value); @@ -850,15 +852,14 @@ impl AttributeProtocol for PyObjectRef { return None; } - match self.payload { - PyObjectPayload::Module { ref scope, .. } => scope.locals.get_item(attr_name), - _ => { - if let Some(ref dict) = self.dict { - dict.borrow().get(attr_name).cloned() - } else { - None - } - } + if let Some(PyModule { ref scope, .. }) = self.payload::() { + return scope.locals.get_item(attr_name); + } + + if let Some(ref dict) = self.dict { + dict.borrow().get(attr_name).cloned() + } else { + None } } @@ -868,15 +869,14 @@ impl AttributeProtocol for PyObjectRef { || mro.iter().any(|d| class_has_item(d, attr_name)); } - match self.payload { - PyObjectPayload::Module { ref scope, .. } => scope.locals.contains_key(attr_name), - _ => { - if let Some(ref dict) = self.dict { - dict.borrow().contains_key(attr_name) - } else { - false - } - } + if let Some(PyModule { ref scope, .. }) = self.payload::() { + return scope.locals.contains_key(attr_name); + } + + if let Some(ref dict) = self.dict { + dict.borrow().contains_key(attr_name) + } else { + false } } } @@ -900,22 +900,20 @@ impl DictProtocol for PyObjectRef { fn get_item(&self, k: &str) -> Option { if let Some(dict) = self.payload::() { objdict::content_get_key_str(&dict.entries.borrow(), k) + } else if let Some(PyModule { ref scope, .. }) = self.payload::() { + scope.locals.get_item(k) } else { - match self.payload { - PyObjectPayload::Module { ref scope, .. } => scope.locals.get_item(k), - ref k => panic!("TODO {:?}", k), - } + panic!("TODO {:?}", k) } } fn get_key_value_pairs(&self) -> Vec<(PyObjectRef, PyObjectRef)> { if let Some(_) = self.payload::() { objdict::get_key_value_pairs(self) + } else if let Some(PyModule { ref scope, .. }) = self.payload::() { + scope.locals.get_key_value_pairs() } else { - match self.payload { - PyObjectPayload::Module { ref scope, .. } => scope.locals.get_key_value_pairs(), - _ => panic!("TODO"), - } + panic!("TODO") } } @@ -924,13 +922,10 @@ impl DictProtocol for PyObjectRef { if let Some(dict) = self.payload::() { let key = ctx.new_str(key.to_string()); objdict::set_item_in_content(&mut dict.entries.borrow_mut(), &key, &v); + } else if let Some(PyModule { ref scope, .. }) = self.payload::() { + scope.locals.set_item(ctx, key, v); } else { - match &self.payload { - PyObjectPayload::Module { scope, .. } => { - scope.locals.set_item(ctx, key, v); - } - ref k => panic!("TODO {:?}", k), - }; + panic!("TODO {:?}", self); } } } @@ -1542,10 +1537,6 @@ pub enum PyObjectPayload { function: PyObjectRef, object: PyObjectRef, }, - Module { - name: String, - scope: ScopeRef, - }, WeakRef { referent: PyObjectWeakRef, }, @@ -1582,7 +1573,6 @@ impl fmt::Debug for PyObjectPayload { ref function, ref object, } => write!(f, "bound-method: {:?} of {:?}", function, object), - PyObjectPayload::Module { .. } => write!(f, "module"), PyObjectPayload::RustFunction { .. } => write!(f, "rust function"), PyObjectPayload::Frame { .. } => write!(f, "frame"), PyObjectPayload::AnyRustValue { value } => value.fmt(f), diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 953e016e24..bf5ce74332 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -21,6 +21,7 @@ use crate::obj::objframe; use crate::obj::objgenerator; use crate::obj::objiter; use crate::obj::objlist::PyList; +use crate::obj::objmodule::PyModule; use crate::obj::objsequence; use crate::obj::objstr; use crate::obj::objtuple::PyTuple; @@ -212,13 +213,8 @@ impl VirtualMachine { } pub fn get_builtin_scope(&self) -> ScopeRef { - let a2 = &*self.builtins; - match a2.payload { - PyObjectPayload::Module { ref scope, .. } => scope.clone(), - _ => { - panic!("OMG"); - } - } + let PyModule { ref scope, .. } = self.builtins.payload::().unwrap(); + scope.clone() } // Container of the virtual machine state: From 40c10ab70a7341a9d7c551176f3cff5fa00bfb62 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Fri, 8 Mar 2019 16:16:59 +0200 Subject: [PATCH 199/380] Handle unknown address family --- tests/snippets/stdlib_socket.py | 4 ++++ vm/src/builtins.rs | 1 + vm/src/stdlib/socket.rs | 13 +++++++------ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/snippets/stdlib_socket.py b/tests/snippets/stdlib_socket.py index f984515ea6..7fb936e3c0 100644 --- a/tests/snippets/stdlib_socket.py +++ b/tests/snippets/stdlib_socket.py @@ -73,3 +73,7 @@ assert recv_b == MESSAGE_B sock1.close() sock3.close() + +### Errors +with assertRaises(OSError): + socket.socket(100, socket.SOCK_STREAM) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 8f4347734e..ec79c55710 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -802,6 +802,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { "StopIteration" => ctx.exceptions.stop_iteration.clone(), "ZeroDivisionError" => ctx.exceptions.zero_division_error.clone(), "KeyError" => ctx.exceptions.key_error.clone(), + "OSError" => ctx.exceptions.os_error.clone(), }); #[cfg(not(target_arch = "wasm32"))] diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index 4afe7a372f..93a13c1003 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -24,12 +24,12 @@ enum AddressFamily { } impl AddressFamily { - fn from_i32(value: i32) -> AddressFamily { + fn from_i32(vm: &mut VirtualMachine, value: i32) -> Result { match value { - 1 => AddressFamily::Unix, - 2 => AddressFamily::Inet, - 3 => AddressFamily::Inet6, - _ => panic!("Unknown value: {}", value), + 1 => Ok(AddressFamily::Unix), + 2 => Ok(AddressFamily::Inet), + 3 => Ok(AddressFamily::Inet6), + _ => Err(vm.new_os_error(format!("Unknown address family value: {}", value))), } } } @@ -146,7 +146,8 @@ fn socket_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ] ); - let address_family = AddressFamily::from_i32(objint::get_value(family_int).to_i32().unwrap()); + let address_family = + AddressFamily::from_i32(vm, objint::get_value(family_int).to_i32().unwrap())?; let kind = SocketKind::from_i32(objint::get_value(kind_int).to_i32().unwrap()); let socket = RefCell::new(Socket::new(address_family, kind)); From 3fae8ccfbcc57735407e051786545f7f44429852 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Fri, 8 Mar 2019 16:19:26 +0200 Subject: [PATCH 200/380] Handle unknown socket kind --- tests/snippets/stdlib_socket.py | 3 +++ vm/src/stdlib/socket.rs | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/snippets/stdlib_socket.py b/tests/snippets/stdlib_socket.py index 7fb936e3c0..6428f52570 100644 --- a/tests/snippets/stdlib_socket.py +++ b/tests/snippets/stdlib_socket.py @@ -77,3 +77,6 @@ ### Errors with assertRaises(OSError): socket.socket(100, socket.SOCK_STREAM) + +with assertRaises(OSError): + socket.socket(socket.AF_INET, 1000) diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index 93a13c1003..6c2fae51a3 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -41,11 +41,11 @@ enum SocketKind { } impl SocketKind { - fn from_i32(value: i32) -> SocketKind { + fn from_i32(vm: &mut VirtualMachine, value: i32) -> Result { match value { - 1 => SocketKind::Stream, - 2 => SocketKind::Dgram, - _ => panic!("Unknown value: {}", value), + 1 => Ok(SocketKind::Stream), + 2 => Ok(SocketKind::Dgram), + _ => Err(vm.new_os_error(format!("Unknown socket kind value: {}", value))), } } } @@ -148,7 +148,7 @@ fn socket_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let address_family = AddressFamily::from_i32(vm, objint::get_value(family_int).to_i32().unwrap())?; - let kind = SocketKind::from_i32(objint::get_value(kind_int).to_i32().unwrap()); + let kind = SocketKind::from_i32(vm, objint::get_value(kind_int).to_i32().unwrap())?; let socket = RefCell::new(Socket::new(address_family, kind)); From de0e76baf8890d42a97e7b2bb1e6fb9db1e62df8 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Fri, 8 Mar 2019 16:33:22 +0200 Subject: [PATCH 201/380] socket connect and bind throws OSError with indicative error message --- tests/snippets/stdlib_socket.py | 12 ++++++++++++ vm/src/stdlib/socket.rs | 33 +++++++++++++-------------------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/tests/snippets/stdlib_socket.py b/tests/snippets/stdlib_socket.py index 6428f52570..d0e2bd6356 100644 --- a/tests/snippets/stdlib_socket.py +++ b/tests/snippets/stdlib_socket.py @@ -29,9 +29,17 @@ with assertRaises(TypeError): s.connect(("127.0.0.1", 8888, 8888)) +with assertRaises(OSError): + # Lets hope nobody is listening on port 1 + s.connect(("127.0.0.1", 1)) + with assertRaises(TypeError): s.bind(("127.0.0.1", 8888, 8888)) +with assertRaises(OSError): + # Lets hope nobody run this test on machine with ip 1.2.3.4 + s.bind(("1.2.3.4", 8888)) + with assertRaises(TypeError): s.bind((888, 8888)) @@ -74,6 +82,10 @@ sock1.close() sock3.close() +s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +with assertRaises(OSError): + s.bind(("1.2.3.4", 888)) + ### Errors with assertRaises(OSError): socket.socket(100, socket.SOCK_STREAM) diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index 6c2fae51a3..60c1ccf5c8 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -172,21 +172,18 @@ fn socket_connect(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut socket = get_socket(zelf); match socket.socket_kind { - SocketKind::Stream => { - if let Ok(stream) = TcpStream::connect(address_string) { + SocketKind::Stream => match TcpStream::connect(address_string) { + Ok(stream) => { socket.con = Some(Connection::TcpStream(stream)); Ok(vm.get_none()) - } else { - // TODO: Socket error - Err(vm.new_type_error("socket failed".to_string())) } - } + Err(s) => Err(vm.new_os_error(s.to_string())), + }, SocketKind::Dgram => { if let Some(Connection::UdpSocket(con)) = &socket.con { match con.connect(address_string) { Ok(_) => Ok(vm.get_none()), - // TODO: Socket error - Err(_) => Err(vm.new_type_error("socket failed".to_string())), + Err(s) => Err(vm.new_os_error(s.to_string())), } } else { Err(vm.new_type_error("".to_string())) @@ -207,24 +204,20 @@ fn socket_bind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut socket = get_socket(zelf); match socket.socket_kind { - SocketKind::Stream => { - if let Ok(stream) = TcpListener::bind(address_string) { + SocketKind::Stream => match TcpListener::bind(address_string) { + Ok(stream) => { socket.con = Some(Connection::TcpListener(stream)); Ok(vm.get_none()) - } else { - // TODO: Socket error - Err(vm.new_type_error("socket failed".to_string())) } - } - SocketKind::Dgram => { - if let Ok(dgram) = UdpSocket::bind(address_string) { + Err(s) => Err(vm.new_os_error(s.to_string())), + }, + SocketKind::Dgram => match UdpSocket::bind(address_string) { + Ok(dgram) => { socket.con = Some(Connection::UdpSocket(dgram)); Ok(vm.get_none()) - } else { - // TODO: Socket error - Err(vm.new_type_error("socket failed".to_string())) } - } + Err(s) => Err(vm.new_os_error(s.to_string())), + }, } } From 76c43457b5859d084805dfbb66489c7b1cd4d0f3 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Fri, 8 Mar 2019 17:02:49 +0200 Subject: [PATCH 202/380] Add more OSError exceptions to socket --- tests/snippets/stdlib_socket.py | 12 ++++++++++ vm/src/stdlib/socket.rs | 42 ++++++++++++++++++++------------- 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/tests/snippets/stdlib_socket.py b/tests/snippets/stdlib_socket.py index d0e2bd6356..01000ccd9a 100644 --- a/tests/snippets/stdlib_socket.py +++ b/tests/snippets/stdlib_socket.py @@ -43,6 +43,13 @@ with assertRaises(TypeError): s.bind((888, 8888)) +s.bind(("127.0.0.1", 0)) +with assertRaises(OSError): + s.recv(100) + +with assertRaises(OSError): + s.send(MESSAGE_A) + s.close() # UDP @@ -86,6 +93,11 @@ with assertRaises(OSError): s.bind(("1.2.3.4", 888)) +s.bind(("127.0.0.1", 0)) +with assertRaises(OSError): + s.sendto(MESSAGE_A, ("1.2.3.4", 888)) + +s.close() ### Errors with assertRaises(OSError): socket.socket(100, socket.SOCK_STREAM) diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index 60c1ccf5c8..99bd19aba4 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -266,7 +266,7 @@ fn socket_accept(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let (tcp_stream, addr) = match ret { Ok((socket, addr)) => (socket, addr), - _ => return Err(vm.new_type_error("".to_string())), + Err(s) => return Err(vm.new_os_error(s.to_string())), }; let socket = RefCell::new(Socket { @@ -297,7 +297,10 @@ fn socket_recv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut buffer = vec![0u8; objint::get_value(bufsize).to_usize().unwrap()]; match socket.con { - Some(ref mut v) => v.read_exact(&mut buffer).unwrap(), + Some(ref mut v) => match v.read_exact(&mut buffer) { + Ok(_) => (), + Err(s) => return Err(vm.new_os_error(s.to_string())), + }, None => return Err(vm.new_type_error("".to_string())), }; Ok(vm.ctx.new_bytes(buffer)) @@ -320,7 +323,7 @@ fn socket_recvfrom(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let addr = match ret { Ok((_size, addr)) => addr, - _ => return Err(vm.new_type_error("".to_string())), + Err(s) => return Err(vm.new_os_error(s.to_string())), }; let addr_tuple = get_addr_tuple(vm, addr)?; @@ -337,7 +340,10 @@ fn socket_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut socket = get_socket(zelf); match socket.con { - Some(ref mut v) => v.write(&objbytes::get_value(&bytes)).unwrap(), + Some(ref mut v) => match v.write(&objbytes::get_value(&bytes)) { + Ok(_) => (), + Err(s) => return Err(vm.new_os_error(s.to_string())), + }, None => return Err(vm.new_type_error("".to_string())), }; Ok(vm.get_none()) @@ -360,22 +366,24 @@ fn socket_sendto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match socket.socket_kind { SocketKind::Dgram => { match socket.con { - Some(ref mut v) => { - if let Ok(_) = v.send_to(&objbytes::get_value(&bytes), address_string) { - Ok(vm.get_none()) - } else { - Err(vm.new_type_error("socket failed".to_string())) - } - } + Some(ref mut v) => match v.send_to(&objbytes::get_value(&bytes), address_string) { + Ok(_) => Ok(vm.get_none()), + Err(s) => Err(vm.new_os_error(s.to_string())), + }, None => { // Doing implicit bind - if let Ok(dgram) = UdpSocket::bind("0.0.0.0:0") { - if let Ok(_) = dgram.send_to(&objbytes::get_value(&bytes), address_string) { - socket.con = Some(Connection::UdpSocket(dgram)); - return Ok(vm.get_none()); + match UdpSocket::bind("0.0.0.0:0") { + Ok(dgram) => { + match dgram.send_to(&objbytes::get_value(&bytes), address_string) { + Ok(_) => { + socket.con = Some(Connection::UdpSocket(dgram)); + Ok(vm.get_none()) + } + Err(s) => Err(vm.new_os_error(s.to_string())), + } } + Err(s) => Err(vm.new_os_error(s.to_string())), } - Err(vm.new_type_error("socket failed".to_string())) } } } @@ -402,7 +410,7 @@ fn socket_getsockname(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match addr { Ok(addr) => get_addr_tuple(vm, addr), - _ => Err(vm.new_type_error("".to_string())), + Err(s) => Err(vm.new_os_error(s.to_string())), } } From 502f8098b4ac48880720bbc2133ad04a34816f95 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Fri, 8 Mar 2019 18:51:20 +0100 Subject: [PATCH 203/380] Change returns into return. --- vm/src/compile.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 72e2186580..442aa3169c 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -617,7 +617,7 @@ impl Compiler { // key: self.emit(Instruction::LoadConst { value: bytecode::Constant::String { - value: "returns".to_string(), + value: "return".to_string(), }, }); // value: From 10531191bb32b8d24ff439102c26c0279cb4194b Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Fri, 8 Mar 2019 19:42:38 +0100 Subject: [PATCH 204/380] Convert most tuple/list methods to new args style --- vm/src/frame.rs | 19 +- vm/src/function.rs | 7 + vm/src/obj/objfloat.rs | 4 +- vm/src/obj/objlist.rs | 689 ++++++++++++++------------------------ vm/src/obj/objsequence.rs | 6 +- vm/src/obj/objtuple.rs | 428 ++++++++++------------- 6 files changed, 444 insertions(+), 709 deletions(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 4fd7ae687b..4bbf523419 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -21,7 +21,7 @@ use crate::obj::objstr; use crate::obj::objtype; use crate::pyobject::{ DictProtocol, IdProtocol, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, - TypeProtocol, + TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -138,14 +138,7 @@ impl Frame { vm.ctx.new_int(lineno.get_row()), vm.ctx.new_str(run_obj_name.clone()), ]); - objlist::list_append( - vm, - PyFuncArgs { - args: vec![traceback, pos], - kwargs: vec![], - }, - ) - .unwrap(); + objlist::PyListRef::try_from_object(vm, traceback)?.append(pos, vm); // exception.__trace match self.unwind_exception(vm, exception) { None => {} @@ -312,13 +305,7 @@ impl Frame { bytecode::Instruction::ListAppend { i } => { let list_obj = self.nth_value(*i); let item = self.pop_value(); - objlist::list_append( - vm, - PyFuncArgs { - args: vec![list_obj.clone(), item], - kwargs: vec![], - }, - )?; + objlist::PyListRef::try_from_object(vm, list_obj)?.append(item, vm); Ok(None) } bytecode::Instruction::SetAdd { i } => { diff --git a/vm/src/function.rs b/vm/src/function.rs index 993167ffc9..5cffd8c987 100644 --- a/vm/src/function.rs +++ b/vm/src/function.rs @@ -44,6 +44,13 @@ where _payload: PhantomData, } } + + pub fn as_object(&self) -> &PyObjectRef { + &self.obj + } + pub fn into_object(self) -> PyObjectRef { + self.obj + } } impl Deref for PyRef diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 64c26973e9..f544adcc36 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -152,7 +152,7 @@ impl PyFloatRef { } } - fn new_str(cls: PyObjectRef, arg: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn new_float(cls: PyObjectRef, arg: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { let value = if objtype::isinstance(&arg, &vm.ctx.float_type()) { get_value(&arg) } else if objtype::isinstance(&arg, &vm.ctx.int_type()) { @@ -373,7 +373,7 @@ pub fn init(context: &PyContext) { context.set_attr(&float_type, "__radd__", context.new_rustfunc(PyFloatRef::add)); context.set_attr(&float_type, "__divmod__", context.new_rustfunc(PyFloatRef::divmod)); context.set_attr(&float_type, "__floordiv__", context.new_rustfunc(PyFloatRef::floordiv)); - context.set_attr(&float_type, "__new__", context.new_rustfunc(PyFloatRef::new_str)); + context.set_attr(&float_type, "__new__", context.new_rustfunc(PyFloatRef::new_float)); context.set_attr(&float_type, "__mod__", context.new_rustfunc(PyFloatRef::mod_)); context.set_attr(&float_type, "__neg__", context.new_rustfunc(PyFloatRef::neg)); context.set_attr(&float_type, "__pow__", context.new_rustfunc(PyFloatRef::pow)); diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 0a2ece3651..d71c13976f 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -3,14 +3,15 @@ use std::cell::{Cell, RefCell}; use super::objbool; use super::objint; use super::objsequence::{ - get_elements, get_elements_cell, 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, seq_equal, seq_ge, seq_gt, seq_le, seq_lt, seq_mul, + PySliceableSequence, }; use super::objstr; use super::objtype; +use crate::function::PyRef; use crate::pyobject::{ - IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, - PyResult, TypeProtocol, + IdProtocol, OptionalArg, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, + PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; use num_traits::ToPrimitive; @@ -35,323 +36,279 @@ impl PyObjectPayload2 for PyList { } } -// set_item: -fn set_item( - vm: &mut VirtualMachine, - l: &mut Vec, - idx: PyObjectRef, - obj: PyObjectRef, -) -> PyResult { - if objtype::isinstance(&idx, &vm.ctx.int_type()) { - let value = objint::get_value(&idx).to_i32().unwrap(); - 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?)", - l, idx - ) - } -} +pub type PyListRef = PyRef; -fn list_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(cls, None)], - optional = [(iterable, None)] - ); +impl PyListRef { + pub fn append(self, x: PyObjectRef, _vm: &mut VirtualMachine) { + self.elements.borrow_mut().push(x); + } - if !objtype::issubclass(cls, &vm.ctx.list_type()) { - return Err(vm.new_type_error(format!("{:?} is not a subtype of list", cls))); + fn extend(self, x: PyObjectRef, vm: &mut VirtualMachine) -> PyResult<()> { + let mut new_elements = vm.extract_elements(&x)?; + self.elements.borrow_mut().append(&mut new_elements); + Ok(()) } - let elements = if let Some(iterable) = iterable { - vm.extract_elements(iterable)? - } else { - vec![] - }; + fn insert(self, position: isize, element: PyObjectRef, _vm: &mut VirtualMachine) { + let mut vec = self.elements.borrow_mut(); + let vec_len = vec.len().to_isize().unwrap(); + // This unbounded position can be < 0 or > vec.len() + let unbounded_position = if position < 0 { + vec_len + position + } else { + 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(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyList::from(elements)), - }, - cls.clone(), - )) -} + fn add(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&other, &vm.ctx.list_type()) { + let e1 = self.elements.borrow(); + let e2 = get_elements(&other); + 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 {}", self.as_object(), other))) + } + } -fn list_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.list_type())), (other, None)] - ); + fn iadd(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&other, &vm.ctx.list_type()) { + self.elements + .borrow_mut() + .extend_from_slice(&get_elements(&other)); + Ok(self.into_object()) + } else { + Ok(vm.ctx.not_implemented()) + } + } - if zelf.is(&other) { - return Ok(vm.ctx.new_bool(true)); + fn clear(self, _vm: &mut VirtualMachine) { + self.elements.borrow_mut().clear(); } - let result = if objtype::isinstance(other, &vm.ctx.list_type()) { - let zelf = get_elements(zelf); - let other = get_elements(other); - seq_equal(vm, &zelf, &other)? - } else { - false - }; - Ok(vm.ctx.new_bool(result)) -} + fn copy(self, vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.new_list(self.elements.borrow().clone()) + } -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, other))); - }; + fn len(self, _vm: &mut VirtualMachine) -> usize { + self.elements.borrow().len() + } - Ok(vm.ctx.new_bool(result)) -} + fn reverse(self, _vm: &mut VirtualMachine) { + self.elements.borrow_mut().reverse(); + } -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, other))); - }; + fn getitem(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + get_item( + vm, + self.as_object(), + &self.elements.borrow(), + needle.clone(), + ) + } - Ok(vm.ctx.new_bool(result)) -} + fn iter(self, vm: &mut VirtualMachine) -> PyObjectRef { + PyObject::new( + PyObjectPayload::Iterator { + position: Cell::new(0), + iterated_obj: self.into_object(), + }, + vm.ctx.iter_type(), + ) + } -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, other))); - }; + fn setitem(self, key: PyObjectRef, value: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + let mut elements = self.elements.borrow_mut(); - Ok(vm.ctx.new_bool(result)) -} + if objtype::isinstance(&key, &vm.ctx.int_type()) { + let idx = objint::get_value(&key).to_i32().unwrap(); + if let Some(pos_index) = elements.get_pos(idx) { + elements[pos_index] = value; + 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?)", + elements, key + ) + } + } -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, other))); - }; + fn repr(self, vm: &mut VirtualMachine) -> PyResult { + let s = if let Some(_guard) = ReprGuard::enter(self.as_object()) { + let mut str_parts = vec![]; + for elem in self.elements.borrow().iter() { + let s = vm.to_repr(elem)?; + str_parts.push(objstr::get_value(&s)); + } + format!("[{}]", str_parts.join(", ")) + } else { + "[...]".to_string() + }; + Ok(s) + } - Ok(vm.ctx.new_bool(result)) -} + fn mul(self, counter: isize, vm: &mut VirtualMachine) -> PyObjectRef { + let new_elements = seq_mul(&self.elements.borrow(), counter); + vm.ctx.new_list(new_elements) + } -fn list_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(o, Some(vm.ctx.list_type())), (o2, None)] - ); - - 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()).cloned().collect(); - Ok(vm.ctx.new_list(elements)) - } else { - Err(vm.new_type_error(format!("Cannot add {} and {}", o, o2))) + fn count(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + let mut count: usize = 0; + for element in self.elements.borrow().iter() { + if needle.is(&element) { + count += 1; + } else { + let py_equal = vm._eq(element.clone(), needle.clone())?; + if objbool::boolval(vm, py_equal)? { + count += 1; + } + } + } + Ok(count) } -} -fn list_iadd(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.list_type())), (other, None)] - ); + fn contains(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + for element in self.elements.borrow().iter() { + if needle.is(&element) { + return Ok(true); + } + let py_equal = vm._eq(element.clone(), needle.clone())?; + if objbool::boolval(vm, py_equal)? { + return Ok(true); + } + } - if objtype::isinstance(other, &vm.ctx.list_type()) { - get_mut_elements(zelf).extend_from_slice(&get_elements(other)); - Ok(zelf.clone()) - } else { - Ok(vm.ctx.not_implemented()) + Ok(false) } -} -fn list_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(o, Some(vm.ctx.list_type()))]); + fn index(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + for (index, element) in self.elements.borrow().iter().enumerate() { + if needle.is(&element) { + return Ok(index); + } + let py_equal = vm._eq(needle.clone(), element.clone())?; + if objbool::boolval(vm, py_equal)? { + return Ok(index); + } + } + let needle_str = objstr::get_value(&vm.to_str(&needle).unwrap()); + Err(vm.new_value_error(format!("'{}' is not in list", needle_str))) + } - 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)); + fn pop(self, vm: &mut VirtualMachine) -> PyResult { + let mut elements = self.elements.borrow_mut(); + if let Some(result) = elements.pop() { + Ok(result) + } else { + Err(vm.new_index_error("pop from empty list".to_string())) } - format!("[{}]", str_parts.join(", ")) - } else { - "[...]".to_string() - }; + } - Ok(vm.new_str(s)) -} + fn remove(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult<()> { + let mut ri: Option = None; + for (index, element) in self.elements.borrow().iter().enumerate() { + if needle.is(&element) { + ri = Some(index); + break; + } + let py_equal = vm._eq(needle.clone(), element.clone())?; + if objbool::get_value(&py_equal) { + ri = Some(index); + break; + } + } -pub fn list_append(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - trace!("list.append called with: {:?}", args); - arg_check!( - vm, - args, - required = [(list, Some(vm.ctx.list_type())), (x, None)] - ); - let mut elements = get_mut_elements(list); - elements.push(x.clone()); - Ok(vm.get_none()) -} + if let Some(index) = ri { + self.elements.borrow_mut().remove(index); + Ok(()) + } else { + let needle_str = objstr::get_value(&vm.to_str(&needle)?); + Err(vm.new_value_error(format!("'{}' is not in list", needle_str))) + } + } -fn list_clear(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - trace!("list.clear called with: {:?}", args); - arg_check!(vm, args, required = [(list, Some(vm.ctx.list_type()))]); - let mut elements = get_mut_elements(list); - elements.clear(); - Ok(vm.get_none()) -} + fn eq(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if self.as_object().is(&other) { + return Ok(true); + } -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())) -} + if objtype::isinstance(&other, &vm.ctx.list_type()) { + let zelf = self.elements.borrow(); + let other = get_elements(&other); + Ok(seq_equal(vm, &zelf, &other)?) + } else { + Ok(false) + } + } -fn list_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.list_type())), (value, None)] - ); - let elements = get_elements(zelf); - let mut count: usize = 0; - for element in elements.iter() { - if value.is(&element) { - count += 1; + fn lt(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&other, &vm.ctx.list_type()) { + let zelf = self.elements.borrow(); + let other = get_elements(&other); + Ok(seq_lt(vm, &zelf, &other)?) } else { - let is_eq = vm._eq(element.clone(), value.clone())?; - if objbool::boolval(vm, is_eq)? { - count += 1; - } + Err(vm.new_type_error(format!("Cannot compare {} and {} using '<'", self, other))) } } - Ok(vm.context().new_int(count)) -} -pub fn list_extend(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(list, Some(vm.ctx.list_type())), (x, None)] - ); - let mut new_elements = vm.extract_elements(x)?; - let mut elements = get_mut_elements(list); - elements.append(&mut new_elements); - Ok(vm.get_none()) -} + fn gt(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&other, &vm.ctx.list_type()) { + let zelf = self.elements.borrow(); + let other = get_elements(&other); + Ok(seq_gt(vm, &zelf, &other)?) + } else { + Err(vm.new_type_error(format!("Cannot compare {} and {} using '>'", self, other))) + } + } -fn list_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - trace!("list.index called with: {:?}", args); - arg_check!( - vm, - args, - required = [(list, Some(vm.ctx.list_type())), (needle, None)] - ); - for (index, element) in get_elements(list).iter().enumerate() { - if needle.is(&element) { - return Ok(vm.context().new_int(index)); + fn ge(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&other, &vm.ctx.list_type()) { + let zelf = self.elements.borrow(); + let other = get_elements(&other); + Ok(seq_ge(vm, &zelf, &other)?) + } else { + Err(vm.new_type_error(format!("Cannot compare {} and {} using '>='", self, other))) } - let py_equal = vm._eq(needle.clone(), element.clone())?; - if objbool::get_value(&py_equal) { - return Ok(vm.context().new_int(index)); + } + + fn le(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&other, &vm.ctx.list_type()) { + let zelf = self.elements.borrow(); + let other = get_elements(&other); + Ok(seq_le(vm, &zelf, &other)?) + } else { + Err(vm.new_type_error(format!("Cannot compare {} and {} using '<='", self, other))) } } - let needle_str = objstr::get_value(&vm.to_str(needle).unwrap()); - 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 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 isize".to_string()) - ); - } - }; - let mut vec = get_mut_elements(list); - 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 +fn list_new( + cls: PyRef, + iterable: OptionalArg, + vm: &mut VirtualMachine, +) -> PyResult { + if !objtype::issubclass(cls.as_object(), &vm.ctx.list_type()) { + return Err(vm.new_type_error(format!("{} is not a subtype of list", cls))); + } + + let elements = if let OptionalArg::Present(iterable) = iterable { + vm.extract_elements(&iterable)? } else { - int_position + vec![] }; - // 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()) -} - -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())) -} -fn list_reverse(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - trace!("list.reverse called with: {:?}", args); - arg_check!(vm, args, required = [(list, Some(vm.ctx.list_type()))]); - let mut elements = get_mut_elements(list); - elements.reverse(); - Ok(vm.get_none()) + Ok(PyObject::new( + PyObjectPayload::AnyRustValue { + value: Box::new(PyList::from(elements)), + }, + cls.into_object(), + )) } fn quicksort( @@ -443,121 +400,7 @@ fn list_sort(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.get_none()) } -fn list_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - trace!("list.contains called with: {:?}", args); - arg_check!( - vm, - args, - required = [(list, Some(vm.ctx.list_type())), (needle, None)] - ); - for element in get_elements(list).iter() { - if needle.is(&element) { - return Ok(vm.new_bool(true)); - } - match vm._eq(needle.clone(), element.clone()) { - Ok(value) => { - if objbool::get_value(&value) { - return Ok(vm.new_bool(true)); - } - } - Err(_) => return Err(vm.new_type_error("".to_string())), - } - } - - Ok(vm.new_bool(false)) -} - -fn list_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - trace!("list.getitem called with: {:?}", args); - arg_check!( - vm, - args, - required = [(list, Some(vm.ctx.list_type())), (needle, None)] - ); - get_item(vm, list, &get_elements(list), needle.clone()) -} - -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( - PyObjectPayload::Iterator { - position: Cell::new(0), - iterated_obj: list.clone(), - }, - vm.ctx.iter_type(), - ); - - // We are all good here: - Ok(iter_obj) -} - -fn list_setitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(list, Some(vm.ctx.list_type())), (key, None), (value, None)] - ); - let mut elements = get_mut_elements(list); - set_item(vm, &mut elements, key.clone(), value.clone()) -} - -fn list_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (list, Some(vm.ctx.list_type())), - (product, Some(vm.ctx.int_type())) - ] - ); - - let new_elements = seq_mul(&get_elements(list), product); - - 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())) - } -} - -fn list_remove(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(list, Some(vm.ctx.list_type())), (needle, None)] - ); - - let mut ri: Option = None; - for (index, element) in get_elements(list).iter().enumerate() { - if needle.is(&element) { - ri = Some(index); - break; - } - let py_equal = vm._eq(needle.clone(), element.clone())?; - if objbool::get_value(&py_equal) { - ri = Some(index); - break; - } - } - - if let Some(index) = ri { - let mut elements = get_mut_elements(list); - elements.remove(index); - Ok(vm.get_none()) - } else { - let needle_str = objstr::get_value(&vm.to_str(needle)?); - Err(vm.new_value_error(format!("'{}' is not in list", needle_str))) - } -} - +#[rustfmt::skip] // to avoid line splitting pub fn init(context: &PyContext) { let list_type = &context.list_type; @@ -565,43 +408,31 @@ pub fn init(context: &PyContext) { 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, "__iadd__", context.new_rustfunc(list_iadd)); - context.set_attr( - &list_type, - "__contains__", - 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__", - context.new_rustfunc(list_getitem), - ); - context.set_attr(&list_type, "__iter__", context.new_rustfunc(list_iter)); - context.set_attr( - &list_type, - "__setitem__", - context.new_rustfunc(list_setitem), - ); - context.set_attr(&list_type, "__mul__", context.new_rustfunc(list_mul)); - context.set_attr(&list_type, "__len__", context.new_rustfunc(list_len)); + context.set_attr(&list_type, "__add__", context.new_rustfunc(PyListRef::add)); + context.set_attr(&list_type, "__iadd__", context.new_rustfunc(PyListRef::iadd)); + context.set_attr(&list_type, "__contains__", context.new_rustfunc(PyListRef::contains)); + context.set_attr(&list_type, "__eq__", context.new_rustfunc(PyListRef::eq)); + context.set_attr(&list_type, "__lt__", context.new_rustfunc(PyListRef::lt)); + context.set_attr(&list_type, "__gt__", context.new_rustfunc(PyListRef::gt)); + context.set_attr(&list_type, "__le__", context.new_rustfunc(PyListRef::le)); + context.set_attr(&list_type, "__ge__", context.new_rustfunc(PyListRef::ge)); + context.set_attr(&list_type, "__getitem__", context.new_rustfunc(PyListRef::getitem)); + context.set_attr(&list_type, "__iter__", context.new_rustfunc(PyListRef::iter)); + context.set_attr(&list_type, "__setitem__", context.new_rustfunc(PyListRef::setitem)); + context.set_attr(&list_type, "__mul__", context.new_rustfunc(PyListRef::mul)); + context.set_attr(&list_type, "__len__", context.new_rustfunc(PyListRef::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, "__repr__", context.new_rustfunc(PyListRef::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, "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)); - 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, "append", context.new_rustfunc(PyListRef::append)); + context.set_attr(&list_type, "clear", context.new_rustfunc(PyListRef::clear)); + context.set_attr(&list_type, "copy", context.new_rustfunc(PyListRef::copy)); + context.set_attr(&list_type, "count", context.new_rustfunc(PyListRef::count)); + context.set_attr(&list_type, "extend", context.new_rustfunc(PyListRef::extend)); + context.set_attr(&list_type, "index", context.new_rustfunc(PyListRef::index)); + context.set_attr(&list_type, "insert", context.new_rustfunc(PyListRef::insert)); + context.set_attr(&list_type, "reverse", context.new_rustfunc(PyListRef::reverse)); context.set_attr(&list_type, "sort", context.new_rustfunc(list_sort)); - context.set_attr(&list_type, "pop", context.new_rustfunc(list_pop)); - context.set_attr(&list_type, "remove", context.new_rustfunc(list_remove)); + context.set_attr(&list_type, "pop", context.new_rustfunc(PyListRef::pop)); + context.set_attr(&list_type, "remove", context.new_rustfunc(PyListRef::remove)); } diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index f18f23e4d8..ad68206210 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -9,7 +9,7 @@ use crate::pyobject::{IdProtocol, PyObject, PyObjectPayload, PyObjectRef, PyResu use crate::vm::VirtualMachine; use super::objbool; -use super::objint::{self, PyInt}; +use super::objint::PyInt; use super::objlist::PyList; use super::objtuple::PyTuple; @@ -304,9 +304,7 @@ pub fn seq_le( Ok(seq_lt(vm, zelf, other)? || seq_equal(vm, zelf, other)?) } -pub fn seq_mul(elements: &[PyObjectRef], product: &PyObjectRef) -> Vec { - let counter = objint::get_value(&product).to_isize().unwrap(); - +pub fn seq_mul(elements: &[PyObjectRef], counter: isize) -> Vec { let current_len = elements.len(); let new_len = counter.max(0) as usize * current_len; let mut new_elements = Vec::with_capacity(new_len); diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index a9f140b53c..e2e80d8c81 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -1,9 +1,10 @@ use std::cell::{Cell, RefCell}; use std::hash::{Hash, Hasher}; +use crate::function::PyRef; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, - TypeProtocol, + IdProtocol, OptionalArg, PyContext, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, + PyResult, }; use crate::vm::{ReprGuard, VirtualMachine}; @@ -36,175 +37,179 @@ impl PyObjectPayload2 for PyTuple { } } -fn tuple_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] - ); +pub type PyTupleRef = PyRef; - 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, other))); - }; - - 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, other))); - }; - - 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, other))); - }; +impl PyTupleRef { + fn lt(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&other, &vm.ctx.tuple_type()) { + let zelf = self.elements.borrow(); + let other = get_elements(&other); + Ok(seq_lt(vm, &zelf, &other)?) + } else { + Err(vm.new_type_error(format!("Cannot compare {} and {} using '<'", self, other))) + } + } - Ok(vm.ctx.new_bool(result)) -} + fn gt(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&other, &vm.ctx.tuple_type()) { + let zelf = self.elements.borrow(); + let other = get_elements(&other); + Ok(seq_gt(vm, &zelf, &other)?) + } else { + Err(vm.new_type_error(format!("Cannot compare {} and {} using '>'", self, other))) + } + } -fn tuple_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] - ); + fn ge(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&other, &vm.ctx.tuple_type()) { + let zelf = self.elements.borrow(); + let other = get_elements(&other); + Ok(seq_ge(vm, &zelf, &other)?) + } else { + Err(vm.new_type_error(format!("Cannot compare {} and {} using '>='", self, other))) + } + } - 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, other))); - }; + fn le(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&other, &vm.ctx.tuple_type()) { + let zelf = self.elements.borrow(); + let other = get_elements(&other); + Ok(seq_le(vm, &zelf, &other)?) + } else { + Err(vm.new_type_error(format!("Cannot compare {} and {} using '<='", self, other))) + } + } - Ok(vm.ctx.new_bool(result)) -} + fn add(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&other, &vm.ctx.tuple_type()) { + let e1 = self.elements.borrow(); + let e2 = get_elements(&other); + let elements = e1.iter().chain(e2.iter()).cloned().collect(); + Ok(vm.ctx.new_tuple(elements)) + } else { + Err(vm.new_type_error(format!("Cannot add {} and {}", self, other))) + } + } -fn tuple_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] - ); + fn count(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + let mut count: usize = 0; + for element in self.elements.borrow().iter() { + if element.is(&needle) { + count += 1; + } else { + let is_eq = vm._eq(element.clone(), needle.clone())?; + if objbool::boolval(vm, is_eq)? { + count += 1; + } + } + } + Ok(count) + } - 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()).cloned().collect(); - Ok(vm.ctx.new_tuple(elements)) - } else { - Err(vm.new_type_error(format!("Cannot add {} and {}", zelf, other))) + fn eq(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&other, &vm.ctx.tuple_type()) { + let zelf = &self.elements.borrow(); + let other = get_elements(&other); + seq_equal(vm, &zelf, &other) + } else { + Ok(false) + } } -} -fn tuple_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.tuple_type())), (value, None)] - ); - let elements = get_elements(zelf); - let mut count: usize = 0; - for element in elements.iter() { - let is_eq = vm._eq(element.clone(), value.clone())?; - if objbool::boolval(vm, is_eq)? { - count += 1; + fn hash(self, vm: &mut VirtualMachine) -> PyResult { + let mut hasher = std::collections::hash_map::DefaultHasher::new(); + for element in self.elements.borrow().iter() { + let element_hash = objint::get_value(&vm.call_method(element, "__hash__", vec![])?); + element_hash.hash(&mut hasher); } + Ok(hasher.finish()) } - Ok(vm.context().new_int(count)) -} -fn tuple_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] - ); + fn iter(self, vm: &mut VirtualMachine) -> PyObjectRef { + PyObject::new( + PyObjectPayload::Iterator { + position: Cell::new(0), + iterated_obj: self.into_object(), + }, + vm.ctx.iter_type(), + ) + } - let result = if objtype::isinstance(other, &vm.ctx.tuple_type()) { - let zelf = get_elements(zelf); - let other = get_elements(other); - seq_equal(vm, &zelf, &other)? - } else { - false - }; - Ok(vm.ctx.new_bool(result)) -} + fn len(self, _vm: &mut VirtualMachine) -> usize { + self.elements.borrow().len() + } + + fn repr(self, vm: &mut VirtualMachine) -> PyResult { + let s = if let Some(_guard) = ReprGuard::enter(self.as_object()) { + let mut str_parts = vec![]; + for elem in self.elements.borrow().iter() { + let s = vm.to_repr(elem)?; + str_parts.push(objstr::get_value(&s)); + } -fn tuple_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(zelf, Some(vm.ctx.tuple_type()))]); - let elements = get_elements(zelf); - let mut hasher = std::collections::hash_map::DefaultHasher::new(); - for element in elements.iter() { - let element_hash = objint::get_value(&vm.call_method(element, "__hash__", vec![])?); - element_hash.hash(&mut hasher); + if str_parts.len() == 1 { + format!("({},)", str_parts[0]) + } else { + format!("({})", str_parts.join(", ")) + } + } else { + "(...)".to_string() + }; + Ok(s) } - let hash = hasher.finish(); - Ok(vm.ctx.new_int(hash)) -} -fn tuple_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(tuple, Some(vm.ctx.tuple_type()))]); + fn mul(self, counter: isize, vm: &mut VirtualMachine) -> PyObjectRef { + let new_elements = seq_mul(&self.elements.borrow(), counter); + vm.ctx.new_tuple(new_elements) + } - let iter_obj = PyObject::new( - PyObjectPayload::Iterator { - position: Cell::new(0), - iterated_obj: tuple.clone(), - }, - vm.ctx.iter_type(), - ); + fn getitem(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + get_item( + vm, + self.as_object(), + &self.elements.borrow(), + needle.clone(), + ) + } - Ok(iter_obj) -} + fn index(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + for (index, element) in self.elements.borrow().iter().enumerate() { + if element.is(&needle) { + return Ok(index); + } + let is_eq = vm._eq(needle.clone(), element.clone())?; + if objbool::boolval(vm, is_eq)? { + return Ok(index); + } + } + Err(vm.new_value_error("tuple.index(x): x not in tuple".to_string())) + } -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())) + fn contains(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + for element in self.elements.borrow().iter() { + if element.is(&needle) { + return Ok(true); + } + let is_eq = vm._eq(needle.clone(), element.clone())?; + if objbool::boolval(vm, is_eq)? { + return Ok(true); + } + } + Ok(false) + } } -fn tuple_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(cls, None)], - optional = [(iterable, None)] - ); - - if !objtype::issubclass(cls, &vm.ctx.tuple_type()) { +fn tuple_new( + cls: PyRef, + iterable: OptionalArg, + vm: &mut VirtualMachine, +) -> PyResult { + if !objtype::issubclass(cls.as_object(), &vm.ctx.tuple_type()) { return Err(vm.new_type_error(format!("{} is not a subtype of tuple", cls))); } - let elements = if let Some(iterable) = iterable { - vm.extract_elements(iterable)? + let elements = if let OptionalArg::Present(iterable) = iterable { + vm.extract_elements(&iterable)? } else { vec![] }; @@ -213,125 +218,32 @@ fn tuple_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { PyObjectPayload::AnyRustValue { value: Box::new(PyTuple::from(elements)), }, - cls.clone(), + cls.into_object(), )) } -fn tuple_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(zelf, Some(vm.ctx.tuple_type()))]); - - 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)); - } - - if str_parts.len() == 1 { - format!("({},)", str_parts[0]) - } else { - format!("({})", str_parts.join(", ")) - } - } else { - "(...)".to_string() - }; - 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, - args, - required = [(tuple, Some(vm.ctx.tuple_type())), (needle, None)] - ); - 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._eq(needle.clone(), 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, - args, - required = [(tuple, Some(vm.ctx.tuple_type())), (needle, None)] - ); - for element in get_elements(tuple).iter() { - match vm._eq(needle.clone(), element.clone()) { - Ok(value) => { - if objbool::get_value(&value) { - return Ok(vm.new_bool(true)); - } - } - Err(_) => return Err(vm.new_type_error("".to_string())), - } - } - - Ok(vm.new_bool(false)) -} - +#[rustfmt::skip] // to avoid line splitting 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( - &tuple_type, - "__contains__", - context.new_rustfunc(tuple_contains), - ); - context.set_attr( - &tuple_type, - "__getitem__", - context.new_rustfunc(tuple_getitem), - ); - context.set_attr(&tuple_type, "__hash__", context.new_rustfunc(tuple_hash)); - 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, "__add__", context.new_rustfunc(PyTupleRef::add)); + context.set_attr(&tuple_type, "__eq__", context.new_rustfunc(PyTupleRef::eq)); + context.set_attr(&tuple_type,"__contains__",context.new_rustfunc(PyTupleRef::contains)); + context.set_attr(&tuple_type,"__getitem__",context.new_rustfunc(PyTupleRef::getitem)); + context.set_attr(&tuple_type, "__hash__", context.new_rustfunc(PyTupleRef::hash)); + context.set_attr(&tuple_type, "__iter__", context.new_rustfunc(PyTupleRef::iter)); + context.set_attr(&tuple_type, "__len__", context.new_rustfunc(PyTupleRef::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)); - 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)); - context.set_attr( - &tuple_type, - "__doc__", - context.new_str(tuple_doc.to_string()), - ); - context.set_attr(&tuple_type, "index", context.new_rustfunc(tuple_index)); + context.set_attr(&tuple_type, "__mul__", context.new_rustfunc(PyTupleRef::mul)); + context.set_attr(&tuple_type, "__repr__", context.new_rustfunc(PyTupleRef::repr)); + context.set_attr(&tuple_type, "count", context.new_rustfunc(PyTupleRef::count)); + context.set_attr(&tuple_type, "__lt__", context.new_rustfunc(PyTupleRef::lt)); + context.set_attr(&tuple_type, "__le__", context.new_rustfunc(PyTupleRef::le)); + context.set_attr(&tuple_type, "__gt__", context.new_rustfunc(PyTupleRef::gt)); + context.set_attr(&tuple_type, "__ge__", context.new_rustfunc(PyTupleRef::ge)); + context.set_attr(&tuple_type,"__doc__",context.new_str(tuple_doc.to_string())); + context.set_attr(&tuple_type, "index", context.new_rustfunc(PyTupleRef::index)); } From 5abc674737c6fb154a7cdb3623ef37d4c1af5240 Mon Sep 17 00:00:00 2001 From: ben Date: Sat, 9 Mar 2019 12:11:07 +1300 Subject: [PATCH 205/380] Change property implementation to have rust payload and to use new function style. --- vm/src/obj/objproperty.rs | 196 +++++++++++++++++++++++++++++--------- vm/src/pyobject.rs | 17 ++++ vm/src/vm.rs | 5 + 3 files changed, 172 insertions(+), 46 deletions(-) diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index 0a952375d6..e1e6bfa3c6 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -2,11 +2,151 @@ */ -use crate::pyobject::{PyContext, PyFuncArgs, PyResult, TypeProtocol}; -use crate::vm::VirtualMachine; +use crate::pyobject::{PyContext, PyFuncArgs, PyObject, PyObjectRef, PyObjectPayload, PyObjectPayload2, PyResult, TypeProtocol}; +use crate::pyobject::IntoPyNativeFunc; +use crate::obj::objnone::PyNone; +use crate::VirtualMachine; +use crate::function::PyRef; +use std::marker::PhantomData; + +/// Read-only property, doesn't have __set__ or __delete__ +#[derive(Debug)] +pub struct PyReadOnlyProperty { + getter: PyObjectRef +} + +impl PyObjectPayload2 for PyReadOnlyProperty { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.readonly_property_type() + } +} + +pub type PyReadOnlyPropertyRef = PyRef; + +impl PyReadOnlyPropertyRef { + fn get(self, obj: PyObjectRef, _owner: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + vm.invoke(self.getter.clone(), obj) + } +} + +/// Fully fledged property +#[derive(Debug)] +pub struct PyProperty { + getter: Option, + setter: Option, + deleter: Option +} + +impl PyObjectPayload2 for PyProperty { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.property_type() + } +} + +pub type PyPropertyRef = PyRef; + +impl PyPropertyRef { + fn get(self, obj: PyObjectRef, _owner: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if let Some(getter) = self.getter.as_ref() { + vm.invoke(getter.clone(), obj) + } else { + Err(vm.new_attribute_error("unreadable attribute".to_string())) + } + } + + fn set(self, obj: PyObjectRef, value: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if let Some(setter) = self.setter.as_ref() { + vm.invoke(setter.clone(), vec![obj, value]) + } else { + Err(vm.new_attribute_error("can't set attribute".to_string())) + } + } + + fn delete(self, obj: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if let Some(deleter) = self.deleter.as_ref() { + vm.invoke(deleter.clone(), obj) + } else { + Err(vm.new_attribute_error("can't delete attribute".to_string())) + } + } +} + +pub struct PropertyBuilder<'a, I, T> { + vm: &'a mut VirtualMachine, + getter: Option, + setter: Option, + instance: PhantomData, + _return: PhantomData +} + + +impl<'a, I, T> PropertyBuilder<'a, I, T> { + pub(crate) fn new(vm: &'a mut VirtualMachine) -> Self { + Self { + vm, + getter: None, + setter: None, + instance: PhantomData, + _return: PhantomData + } + } + + pub fn add_getter>(self, func: F) -> Self { + let func = self.vm.ctx.new_rustfunc(func); + Self { + vm: self.vm, + getter: Some(func), + setter: self.setter, + instance: PhantomData, + _return: PhantomData + } + } + + pub fn add_setter>(self, func: F) -> Self { + let func = self.vm.ctx.new_rustfunc(func); + Self { + vm: self.vm, + getter: self.setter, + setter: Some(func), + instance: PhantomData, + _return: PhantomData + } + } + + pub fn create(self) -> PyObjectRef { + if self.setter.is_some() { + let payload = PyProperty { + getter: self.getter.clone(), + setter: self.setter.clone(), + deleter: None + }; + + PyObject::new( + PyObjectPayload::AnyRustValue { + value: Box::new(payload) + }, + self.vm.ctx.property_type(), + ) + } else { + let payload = PyReadOnlyProperty { + getter: self.getter.expect("One of add_getter/add_setter must be called when constructing a property") + }; + + PyObject::new( + PyObjectPayload::AnyRustValue { + value: Box::new(payload) + }, + self.vm.ctx.readonly_property_type(), + ) + } + } +} + pub fn init(context: &PyContext) { - let property_type = &context.property_type; + extend_class!(context, &context.readonly_property_type, { + "__get__" => context.new_rustfunc(PyReadOnlyPropertyRef::get), + }); let property_doc = "Property attribute.\n\n \ @@ -36,50 +176,14 @@ pub fn init(context: &PyContext) { def x(self):\n \ del self._x"; - context.set_attr( - &property_type, - "__get__", - context.new_rustfunc(property_get), - ); - context.set_attr( - &property_type, - "__new__", - context.new_rustfunc(property_new), - ); - context.set_attr( - &property_type, - "__doc__", - context.new_str(property_doc.to_string()), - ); - // TODO: how to handle __set__ ? -} + extend_class!(context, &context.property_type, { + "__new__" => context.new_rustfunc(property_new), + "__doc__" => context.new_str(property_doc.to_string()), -// `property` methods. -fn property_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - trace!("property.__get__ {:?}", args.args); - arg_check!( - vm, - args, - required = [ - (cls, Some(vm.ctx.property_type())), - (inst, None), - (_owner, None) - ] - ); - - match vm.ctx.get_attr(&cls, "fget") { - Some(getter) => { - let py_method = vm.ctx.new_bound_method(getter, inst.clone()); - vm.invoke(py_method, PyFuncArgs::default()) - } - None => { - let attribute_error = vm.context().exceptions.attribute_error.clone(); - Err(vm.new_exception( - attribute_error, - String::from("Attribute Error: property must have 'fget' attribute"), - )) - } - } + "__get__" => context.new_rustfunc(PyPropertyRef::get), + "__set__" => context.new_rustfunc(PyPropertyRef::set), + "__delete__" => context.new_rustfunc(PyPropertyRef::delete), + }); } fn property_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index afdb1fb475..fc14ac52fd 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -147,6 +147,7 @@ pub struct PyContext { pub function_type: PyObjectRef, pub builtin_function_or_method_type: PyObjectRef, pub property_type: PyObjectRef, + pub readonly_property_type: PyObjectRef, pub module_type: PyObjectRef, pub bound_method_type: PyObjectRef, pub member_descriptor_type: PyObjectRef, @@ -197,6 +198,7 @@ impl PyContext { &dict_type, ); let property_type = create_type("property", &type_type, &object_type, &dict_type); + let readonly_property_type = create_type("readonly_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); let bound_method_type = create_type("method", &type_type, &object_type, &dict_type); @@ -297,6 +299,7 @@ impl PyContext { builtin_function_or_method_type, super_type, property_type, + readonly_property_type, generator_type, module_type, bound_method_type, @@ -448,6 +451,10 @@ impl PyContext { self.property_type.clone() } + pub fn readonly_property_type(&self) -> PyObjectRef { + self.readonly_property_type.clone() + } + pub fn classmethod_type(&self) -> PyObjectRef { self.classmethod_type.clone() } @@ -962,6 +969,16 @@ impl From> for PyFuncArgs { } } +impl From for PyFuncArgs { + fn from(arg: PyObjectRef) -> Self { + PyFuncArgs { + args: vec![arg], + kwargs: vec![], + } + } +} + + impl PyFuncArgs { pub fn new(mut args: Vec, kwarg_names: Vec) -> PyFuncArgs { let mut kwargs = vec![]; diff --git a/vm/src/vm.rs b/vm/src/vm.rs index e3d06d5fae..652c05df14 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -132,6 +132,11 @@ impl VirtualMachine { self.invoke(exc_type, args).unwrap() } + pub fn new_attribute_error(&mut self, msg: String) -> PyObjectRef { + let type_error = self.ctx.exceptions.arithmetic_error.clone(); + self.new_exception(type_error, msg) + } + pub fn new_type_error(&mut self, msg: String) -> PyObjectRef { let type_error = self.ctx.exceptions.type_error.clone(); self.new_exception(type_error, msg) From 2edfe4c7be09484f1b7a3c64747daab92ffb92e0 Mon Sep 17 00:00:00 2001 From: ben Date: Sat, 9 Mar 2019 13:00:54 +1300 Subject: [PATCH 206/380] Migrate usage of member_descriptor and data_descriptor to new_property/PropertyBuilder --- vm/src/obj/objcode.rs | 5 ++-- vm/src/obj/objfunction.rs | 50 +-------------------------------------- vm/src/obj/objobject.rs | 12 ++++++---- vm/src/obj/objproperty.rs | 38 +++++++++++++---------------- vm/src/obj/objtype.rs | 5 ++-- vm/src/pyobject.rs | 45 ++--------------------------------- 6 files changed, 31 insertions(+), 124 deletions(-) diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index b6a1850899..ec3e48ec0f 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -47,7 +47,7 @@ pub fn init(context: &PyContext) { ("co_kwonlyargcount", code_co_kwonlyargcount), ("co_name", code_co_name), ] { - context.set_attr(code_type, name, context.new_member_descriptor(f)) + context.set_attr(code_type, name, context.new_property(f)) } } @@ -83,8 +83,7 @@ fn member_code_obj(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult PyResult { } } -fn member_get(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { - match args.shift().get_attr("function") { - Some(function) => vm.invoke(function, args), - None => { - let attribute_error = vm.context().exceptions.attribute_error.clone(); - Err(vm.new_exception(attribute_error, String::from("Attribute Error"))) - } - } -} - -fn data_get(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { - match args.shift().get_attr("fget") { - Some(function) => vm.invoke(function, args), - None => { - let attribute_error = vm.context().exceptions.attribute_error.clone(); - Err(vm.new_exception(attribute_error, String::from("Attribute Error"))) - } - } -} - -fn data_set(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { - match args.shift().get_attr("fset") { - Some(function) => vm.invoke(function, args), - None => { - let attribute_error = vm.context().exceptions.attribute_error.clone(); - Err(vm.new_exception(attribute_error, String::from("Attribute Error"))) - } - } -} - // Classmethod type methods: fn classmethod_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("classmethod.__get__ {:?}", args.args); diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index e2bf4463e2..9b161f9d26 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -8,6 +8,7 @@ use crate::pyobject::{ use crate::vm::VirtualMachine; use std::cell::RefCell; use std::collections::HashMap; +use crate::obj::objproperty::PropertyBuilder; #[derive(Clone, Debug)] pub struct PyInstance; @@ -171,7 +172,9 @@ pub fn init(context: &PyContext) { context.set_attr( &object, "__class__", - context.new_data_descriptor(object_class, object_class_setter), + PropertyBuilder::new(context) + .add_getter(object_class) + .add_setter(object_class_setter).create(), ); context.set_attr(&object, "__eq__", context.new_rustfunc(object_eq)); context.set_attr(&object, "__ne__", context.new_rustfunc(object_ne)); @@ -183,7 +186,7 @@ pub fn init(context: &PyContext) { context.set_attr( &object, "__dict__", - context.new_member_descriptor(object_dict), + context.new_property(object_dict), ); context.set_attr(&object, "__dir__", context.new_rustfunc(object_dir)); context.set_attr(&object, "__hash__", context.new_rustfunc(object_hash)); @@ -202,9 +205,8 @@ fn object_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { Ok(vm.ctx.none()) } -// TODO Use PyClassRef for owner to enforce type -fn object_class(_obj: PyObjectRef, owner: PyObjectRef, _vm: &mut VirtualMachine) -> PyObjectRef { - owner +fn object_class(obj: PyObjectRef, _vm: &mut VirtualMachine) -> PyObjectRef { + obj.typ() } fn object_class_setter( diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index e1e6bfa3c6..ac3c21d5f1 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -4,10 +4,10 @@ use crate::pyobject::{PyContext, PyFuncArgs, PyObject, PyObjectRef, PyObjectPayload, PyObjectPayload2, PyResult, TypeProtocol}; use crate::pyobject::IntoPyNativeFunc; -use crate::obj::objnone::PyNone; use crate::VirtualMachine; use crate::function::PyRef; use std::marker::PhantomData; +use crate::obj::objtype::PyClassRef; /// Read-only property, doesn't have __set__ or __delete__ #[derive(Debug)] @@ -24,7 +24,7 @@ impl PyObjectPayload2 for PyReadOnlyProperty { pub type PyReadOnlyPropertyRef = PyRef; impl PyReadOnlyPropertyRef { - fn get(self, obj: PyObjectRef, _owner: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn get(self, obj: PyObjectRef, _owner: PyClassRef, vm: &mut VirtualMachine) -> PyResult { vm.invoke(self.getter.clone(), obj) } } @@ -46,7 +46,7 @@ impl PyObjectPayload2 for PyProperty { pub type PyPropertyRef = PyRef; impl PyPropertyRef { - fn get(self, obj: PyObjectRef, _owner: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn get(self, obj: PyObjectRef, _owner: PyClassRef, vm: &mut VirtualMachine) -> PyResult { if let Some(getter) = self.getter.as_ref() { vm.invoke(getter.clone(), obj) } else { @@ -71,44 +71,40 @@ impl PyPropertyRef { } } -pub struct PropertyBuilder<'a, I, T> { - vm: &'a mut VirtualMachine, +pub struct PropertyBuilder<'a, T> { + ctx: &'a PyContext, getter: Option, setter: Option, - instance: PhantomData, _return: PhantomData } -impl<'a, I, T> PropertyBuilder<'a, I, T> { - pub(crate) fn new(vm: &'a mut VirtualMachine) -> Self { +impl<'a, T> PropertyBuilder<'a, T> { + pub fn new(ctx: &'a PyContext) -> Self { Self { - vm, + ctx, getter: None, setter: None, - instance: PhantomData, _return: PhantomData } } - pub fn add_getter>(self, func: F) -> Self { - let func = self.vm.ctx.new_rustfunc(func); + pub fn add_getter>(self, func: F) -> Self { + let func = self.ctx.new_rustfunc(func); Self { - vm: self.vm, + ctx: self.ctx, getter: Some(func), setter: self.setter, - instance: PhantomData, _return: PhantomData } } - pub fn add_setter>(self, func: F) -> Self { - let func = self.vm.ctx.new_rustfunc(func); + pub fn add_setter>(self, func: F) -> Self { + let func = self.ctx.new_rustfunc(func); Self { - vm: self.vm, - getter: self.setter, + ctx: self.ctx, + getter: self.getter, setter: Some(func), - instance: PhantomData, _return: PhantomData } } @@ -125,7 +121,7 @@ impl<'a, I, T> PropertyBuilder<'a, I, T> { PyObjectPayload::AnyRustValue { value: Box::new(payload) }, - self.vm.ctx.property_type(), + self.ctx.property_type(), ) } else { let payload = PyReadOnlyProperty { @@ -136,7 +132,7 @@ impl<'a, I, T> PropertyBuilder<'a, I, T> { PyObjectPayload::AnyRustValue { value: Box::new(payload) }, - self.vm.ctx.readonly_property_type(), + self.ctx.readonly_property_type(), ) } } diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index b82e39465a..55d4443d38 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -53,7 +53,7 @@ pub fn init(context: &PyContext) { context.set_attr( &type_type, "__mro__", - context.new_member_descriptor(type_mro), + context.new_property(type_mro), ); context.set_attr(&type_type, "__repr__", context.new_rustfunc(type_repr)); context.set_attr( @@ -85,8 +85,7 @@ fn type_mro(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm, args, required = [ - (cls, Some(vm.ctx.type_type())), - (_typ, Some(vm.ctx.type_type())) + (cls, Some(vm.ctx.type_type())) ] ); match _mro(cls.clone()) { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 07afc8f4fa..c32c71667b 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -151,8 +151,6 @@ pub struct PyContext { pub readonly_property_type: PyObjectRef, pub module_type: PyObjectRef, pub bound_method_type: PyObjectRef, - pub member_descriptor_type: PyObjectRef, - pub data_descriptor_type: PyObjectRef, pub object: PyObjectRef, pub exceptions: exceptions::ExceptionZoo, } @@ -198,10 +196,6 @@ impl PyContext { let super_type = create_type("super", &type_type, &object_type, &dict_type); let generator_type = create_type("generator", &type_type, &object_type, &dict_type); let bound_method_type = create_type("method", &type_type, &object_type, &dict_type); - let member_descriptor_type = - create_type("member_descriptor", &type_type, &object_type, &dict_type); - let data_descriptor_type = - create_type("data_descriptor", &type_type, &object_type, &dict_type); let str_type = create_type("str", &type_type, &object_type, &dict_type); let list_type = create_type("list", &type_type, &object_type, &dict_type); let set_type = create_type("set", &type_type, &object_type, &dict_type); @@ -299,8 +293,6 @@ impl PyContext { generator_type, module_type, bound_method_type, - member_descriptor_type, - data_descriptor_type, type_type, exceptions, }; @@ -466,12 +458,6 @@ impl PyContext { pub fn bound_method_type(&self) -> PyObjectRef { self.bound_method_type.clone() } - pub fn member_descriptor_type(&self) -> PyObjectRef { - self.member_descriptor_type.clone() - } - pub fn data_descriptor_type(&self) -> PyObjectRef { - self.data_descriptor_type.clone() - } pub fn type_type(&self) -> PyObjectRef { self.type_type.clone() @@ -640,10 +626,7 @@ impl PyContext { where F: IntoPyNativeFunc, { - let fget = self.new_rustfunc(f); - let py_obj = self.new_instance(self.property_type(), None); - self.set_attr(&py_obj, "fget", fget); - py_obj + PropertyBuilder::new(self).add_getter(f).create() } pub fn new_code_object(&self, code: bytecode::CodeObject) -> PyObjectRef { @@ -678,31 +661,6 @@ impl PyContext { ) } - pub fn new_member_descriptor PyResult>( - &self, - function: F, - ) -> PyObjectRef { - 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_data_descriptor< - G: IntoPyNativeFunc<(I, PyObjectRef), T>, - S: IntoPyNativeFunc<(I, T), PyResult>, - T, - I, - >( - &self, - getter: G, - setter: S, - ) -> PyObjectRef { - let mut dict = PyAttributes::new(); - dict.insert("fget".to_string(), self.new_rustfunc(getter)); - dict.insert("fset".to_string(), self.new_rustfunc(setter)); - self.new_instance(self.data_descriptor_type(), Some(dict)) - } - pub fn new_instance(&self, class: PyObjectRef, dict: Option) -> PyObjectRef { let dict = if let Some(dict) = dict { dict @@ -1292,6 +1250,7 @@ pub enum OptionalArg { } use self::OptionalArg::*; +use crate::obj::objproperty::PropertyBuilder; impl OptionalArg { pub fn into_option(self) -> Option { From 0ec034df51adb15c4808f658f2221fabf6780a76 Mon Sep 17 00:00:00 2001 From: ben Date: Sat, 9 Mar 2019 14:07:42 +1300 Subject: [PATCH 207/380] Change property.__new__ to use new style function and construct PyProperty --- vm/src/function.rs | 20 ++++++++++++ vm/src/obj/objcode.rs | 8 +---- vm/src/obj/objfunction.rs | 1 - vm/src/obj/objobject.rs | 11 +++---- vm/src/obj/objproperty.rs | 67 +++++++++++++++++++++++---------------- vm/src/obj/objtype.rs | 14 ++------ vm/src/pyobject.rs | 4 +-- 7 files changed, 69 insertions(+), 56 deletions(-) diff --git a/vm/src/function.rs b/vm/src/function.rs index 993167ffc9..1589008514 100644 --- a/vm/src/function.rs +++ b/vm/src/function.rs @@ -3,6 +3,7 @@ use std::marker::PhantomData; use std::ops::Deref; use crate::obj::objtype; +use crate::obj::objtype::PyClassRef; use crate::pyobject::{ IntoPyObject, PyContext, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, TryFromObject, TypeProtocol, @@ -44,6 +45,25 @@ where _payload: PhantomData, } } + + pub fn new_with_type(vm: &mut VirtualMachine, payload: T, cls: PyClassRef) -> PyResult { + let required_type = T::required_type(&vm.ctx); + if objtype::issubclass(&cls.obj, &required_type) { + Ok(PyRef { + obj: PyObject::new( + PyObjectPayload::AnyRustValue { + value: Box::new(payload), + }, + cls.obj, + ), + _payload: PhantomData, + }) + } else { + let subtype = vm.to_pystr(&cls.obj)?; + let basetype = vm.to_pystr(&required_type)?; + Err(vm.new_type_error(format!("{} is not a subtype of {}", subtype, basetype))) + } + } } impl Deref for PyRef diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index ec3e48ec0f..ccd2114aed 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -79,13 +79,7 @@ fn code_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn member_code_obj(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (zelf, Some(vm.ctx.code_type())) - ] - ); + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.code_type()))]); Ok(get_value(zelf)) } diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index 12bba360ad..0c87826f25 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -20,7 +20,6 @@ pub fn init(context: &PyContext) { context.new_rustfunc(bind_method), ); - let classmethod_type = &context.classmethod_type; context.set_attr( &classmethod_type, diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 9b161f9d26..0d9d21f1ea 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -1,6 +1,7 @@ use super::objstr; use super::objtype; use crate::function::PyRef; +use crate::obj::objproperty::PropertyBuilder; use crate::pyobject::{ AttributeProtocol, DictProtocol, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, @@ -8,7 +9,6 @@ use crate::pyobject::{ use crate::vm::VirtualMachine; use std::cell::RefCell; use std::collections::HashMap; -use crate::obj::objproperty::PropertyBuilder; #[derive(Clone, Debug)] pub struct PyInstance; @@ -174,7 +174,8 @@ pub fn init(context: &PyContext) { "__class__", PropertyBuilder::new(context) .add_getter(object_class) - .add_setter(object_class_setter).create(), + .add_setter(object_class_setter) + .create(), ); context.set_attr(&object, "__eq__", context.new_rustfunc(object_eq)); context.set_attr(&object, "__ne__", context.new_rustfunc(object_ne)); @@ -183,11 +184,7 @@ pub fn init(context: &PyContext) { 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, - "__dict__", - context.new_property(object_dict), - ); + context.set_attr(&object, "__dict__", context.new_property(object_dict)); context.set_attr(&object, "__dir__", context.new_rustfunc(object_dir)); context.set_attr(&object, "__hash__", context.new_rustfunc(object_hash)); context.set_attr(&object, "__str__", context.new_rustfunc(object_str)); diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index ac3c21d5f1..b94692177e 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -2,17 +2,20 @@ */ -use crate::pyobject::{PyContext, PyFuncArgs, PyObject, PyObjectRef, PyObjectPayload, PyObjectPayload2, PyResult, TypeProtocol}; +use crate::function::PyRef; +use crate::obj::objstr::PyStringRef; +use crate::obj::objtype::PyClassRef; use crate::pyobject::IntoPyNativeFunc; +use crate::pyobject::{ + OptionalArg, PyContext, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, +}; use crate::VirtualMachine; -use crate::function::PyRef; use std::marker::PhantomData; -use crate::obj::objtype::PyClassRef; /// Read-only property, doesn't have __set__ or __delete__ #[derive(Debug)] pub struct PyReadOnlyProperty { - getter: PyObjectRef + getter: PyObjectRef, } impl PyObjectPayload2 for PyReadOnlyProperty { @@ -34,7 +37,7 @@ impl PyReadOnlyPropertyRef { pub struct PyProperty { getter: Option, setter: Option, - deleter: Option + deleter: Option, } impl PyObjectPayload2 for PyProperty { @@ -46,6 +49,25 @@ impl PyObjectPayload2 for PyProperty { pub type PyPropertyRef = PyRef; impl PyPropertyRef { + fn new_property( + cls: PyClassRef, + fget: OptionalArg, + fset: OptionalArg, + fdel: OptionalArg, + _doc: OptionalArg, + vm: &mut VirtualMachine, + ) -> PyResult { + Self::new_with_type( + vm, + PyProperty { + getter: fget.into_option(), + setter: fset.into_option(), + deleter: fdel.into_option(), + }, + cls, + ) + } + fn get(self, obj: PyObjectRef, _owner: PyClassRef, vm: &mut VirtualMachine) -> PyResult { if let Some(getter) = self.getter.as_ref() { vm.invoke(getter.clone(), obj) @@ -75,37 +97,36 @@ pub struct PropertyBuilder<'a, T> { ctx: &'a PyContext, getter: Option, setter: Option, - _return: PhantomData + _return: PhantomData, } - impl<'a, T> PropertyBuilder<'a, T> { pub fn new(ctx: &'a PyContext) -> Self { Self { ctx, getter: None, setter: None, - _return: PhantomData + _return: PhantomData, } } - pub fn add_getter>(self, func: F) -> Self { + pub fn add_getter>(self, func: F) -> Self { let func = self.ctx.new_rustfunc(func); Self { ctx: self.ctx, getter: Some(func), setter: self.setter, - _return: PhantomData + _return: PhantomData, } } - pub fn add_setter>(self, func: F) -> Self { + pub fn add_setter>(self, func: F) -> Self { let func = self.ctx.new_rustfunc(func); Self { ctx: self.ctx, getter: self.getter, setter: Some(func), - _return: PhantomData + _return: PhantomData, } } @@ -114,23 +135,25 @@ impl<'a, T> PropertyBuilder<'a, T> { let payload = PyProperty { getter: self.getter.clone(), setter: self.setter.clone(), - deleter: None + deleter: None, }; PyObject::new( PyObjectPayload::AnyRustValue { - value: Box::new(payload) + value: Box::new(payload), }, self.ctx.property_type(), ) } else { let payload = PyReadOnlyProperty { - getter: self.getter.expect("One of add_getter/add_setter must be called when constructing a property") + getter: self.getter.expect( + "One of add_getter/add_setter must be called when constructing a property", + ), }; PyObject::new( PyObjectPayload::AnyRustValue { - value: Box::new(payload) + value: Box::new(payload), }, self.ctx.readonly_property_type(), ) @@ -138,7 +161,6 @@ impl<'a, T> PropertyBuilder<'a, T> { } } - pub fn init(context: &PyContext) { extend_class!(context, &context.readonly_property_type, { "__get__" => context.new_rustfunc(PyReadOnlyPropertyRef::get), @@ -173,7 +195,7 @@ pub fn init(context: &PyContext) { del self._x"; extend_class!(context, &context.property_type, { - "__new__" => context.new_rustfunc(property_new), + "__new__" => context.new_rustfunc(PyPropertyRef::new_property), "__doc__" => context.new_str(property_doc.to_string()), "__get__" => context.new_rustfunc(PyPropertyRef::get), @@ -181,12 +203,3 @@ pub fn init(context: &PyContext) { "__delete__" => context.new_rustfunc(PyPropertyRef::delete), }); } - -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 = 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 55d4443d38..be4591dfcd 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -50,11 +50,7 @@ pub fn init(context: &PyContext) { 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( - &type_type, - "__mro__", - context.new_property(type_mro), - ); + context.set_attr(&type_type, "__mro__", context.new_property(type_mro)); context.set_attr(&type_type, "__repr__", context.new_rustfunc(type_repr)); context.set_attr( &type_type, @@ -81,13 +77,7 @@ pub fn init(context: &PyContext) { } fn type_mro(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (cls, Some(vm.ctx.type_type())) - ] - ); + arg_check!(vm, args, required = [(cls, Some(vm.ctx.type_type()))]); match _mro(cls.clone()) { Some(mro) => Ok(vm.context().new_tuple(mro)), None => Err(vm.new_type_error("Only classes have an MRO.".to_string())), diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index c32c71667b..c42f047ffb 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -192,7 +192,8 @@ impl PyContext { &dict_type, ); let property_type = create_type("property", &type_type, &object_type, &dict_type); - let readonly_property_type = create_type("readonly_property", &type_type, &object_type, &dict_type); + let readonly_property_type = + create_type("readonly_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); let bound_method_type = create_type("method", &type_type, &object_type, &dict_type); @@ -948,7 +949,6 @@ impl From for PyFuncArgs { } } - impl PyFuncArgs { pub fn new(mut args: Vec, kwarg_names: Vec) -> PyFuncArgs { let mut kwargs = vec![]; From 303e19d7139c58586a330b72921d556f0b986833 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Fri, 8 Mar 2019 18:29:41 -0800 Subject: [PATCH 208/380] map --- vm/src/obj/objmap.rs | 32 +++++++++++++++++++++++++------- vm/src/pyobject.rs | 5 ----- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/vm/src/obj/objmap.rs b/vm/src/obj/objmap.rs index 5ecf171234..c21cbb98ff 100644 --- a/vm/src/obj/objmap.rs +++ b/vm/src/obj/objmap.rs @@ -1,6 +1,22 @@ +use crate::pyobject::{ + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, + TypeProtocol, +}; +use crate::vm::VirtualMachine; + use super::objiter; -use crate::pyobject::{PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyResult, TypeProtocol}; -use crate::vm::VirtualMachine; // Required for arg_check! to use isinstance + +#[derive(Debug)] +pub struct PyMap { + mapper: PyObjectRef, + iterators: Vec, +} + +impl PyObjectPayload2 for PyMap { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.map_type() + } +} fn map_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { no_kwargs!(vm, args); @@ -15,9 +31,11 @@ fn map_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .map(|iterable| objiter::get_iter(vm, iterable)) .collect::, _>>()?; Ok(PyObject::new( - PyObjectPayload::MapIterator { - mapper: function.clone(), - iterators, + PyObjectPayload::AnyRustValue { + value: Box::new(PyMap { + mapper: function.clone(), + iterators, + }), }, cls.clone(), )) @@ -27,10 +45,10 @@ fn map_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn map_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(map, Some(vm.ctx.map_type()))]); - if let PyObjectPayload::MapIterator { + if let Some(PyMap { ref mapper, ref iterators, - } = map.payload + }) = map.payload::() { let next_objs = iterators .iter() diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index bf38802d09..f0d5427b87 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1512,10 +1512,6 @@ pub enum PyObjectPayload { predicate: PyObjectRef, iterator: PyObjectRef, }, - MapIterator { - mapper: PyObjectRef, - iterators: Vec, - }, ZipIterator { iterators: Vec, }, @@ -1573,7 +1569,6 @@ impl fmt::Debug for PyObjectPayload { 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::Function { .. } => write!(f, "function"), From 9d8bdd7012a00aae3047e260e3577f7a71128671 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Fri, 8 Mar 2019 18:29:51 -0800 Subject: [PATCH 209/380] zip --- vm/src/obj/objmap.rs | 2 +- vm/src/obj/objzip.rs | 25 +++++++++++++++++++++---- vm/src/pyobject.rs | 4 ---- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/vm/src/obj/objmap.rs b/vm/src/obj/objmap.rs index c21cbb98ff..4cbb8d3ee9 100644 --- a/vm/src/obj/objmap.rs +++ b/vm/src/obj/objmap.rs @@ -48,7 +48,7 @@ fn map_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if let Some(PyMap { ref mapper, ref iterators, - }) = map.payload::() + }) = map.payload() { let next_objs = iterators .iter() diff --git a/vm/src/obj/objzip.rs b/vm/src/obj/objzip.rs index d1cb833db4..70cded6b20 100644 --- a/vm/src/obj/objzip.rs +++ b/vm/src/obj/objzip.rs @@ -1,6 +1,21 @@ +use crate::pyobject::{ + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, + TypeProtocol, +}; +use crate::vm::VirtualMachine; + use super::objiter; -use crate::pyobject::{PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyResult, TypeProtocol}; -use crate::vm::VirtualMachine; // Required for arg_check! to use isinstance + +#[derive(Debug)] +pub struct PyZip { + iterators: Vec, +} + +impl PyObjectPayload2 for PyZip { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.zip_type() + } +} fn zip_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { no_kwargs!(vm, args); @@ -11,7 +26,9 @@ fn zip_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .map(|iterable| objiter::get_iter(vm, iterable)) .collect::, _>>()?; Ok(PyObject::new( - PyObjectPayload::ZipIterator { iterators }, + PyObjectPayload::AnyRustValue { + value: Box::new(PyZip { iterators }), + }, cls.clone(), )) } @@ -19,7 +36,7 @@ fn zip_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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 iterators } = zip.payload { + if let Some(PyZip { ref iterators }) = zip.payload() { if iterators.is_empty() { Err(objiter::new_stop_iteration(vm)) } else { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index f0d5427b87..b5c356fb27 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1512,9 +1512,6 @@ pub enum PyObjectPayload { predicate: PyObjectRef, iterator: PyObjectRef, }, - ZipIterator { - iterators: Vec, - }, Slice { start: Option, stop: Option, @@ -1569,7 +1566,6 @@ impl fmt::Debug for PyObjectPayload { PyObjectPayload::Iterator { .. } => write!(f, "iterator"), PyObjectPayload::EnumerateIterator { .. } => write!(f, "enumerate"), PyObjectPayload::FilterIterator { .. } => write!(f, "filter"), - PyObjectPayload::ZipIterator { .. } => write!(f, "zip"), PyObjectPayload::Slice { .. } => write!(f, "slice"), PyObjectPayload::Function { .. } => write!(f, "function"), PyObjectPayload::Generator { .. } => write!(f, "generator"), From 9544a47f464cd57c910d6a754b396b6c86a82f7b Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Fri, 8 Mar 2019 18:29:57 -0800 Subject: [PATCH 210/380] filter --- vm/src/obj/objfilter.rs | 32 ++++++++++++++++++++++++-------- vm/src/pyobject.rs | 5 ----- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/vm/src/obj/objfilter.rs b/vm/src/obj/objfilter.rs index b4338192f9..94703be575 100644 --- a/vm/src/obj/objfilter.rs +++ b/vm/src/obj/objfilter.rs @@ -1,10 +1,24 @@ -use super::objbool; -use super::objiter; use crate::pyobject::{ - IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyResult, TypeProtocol, + IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, + PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; // Required for arg_check! to use isinstance +use super::objbool; +use super::objiter; + +#[derive(Debug)] +pub struct PyFilter { + predicate: PyObjectRef, + iterator: PyObjectRef, +} + +impl PyObjectPayload2 for PyFilter { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.filter_type() + } +} + fn filter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -13,9 +27,11 @@ fn filter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let iterator = objiter::get_iter(vm, iterable)?; Ok(PyObject::new( - PyObjectPayload::FilterIterator { - predicate: function.clone(), - iterator, + PyObjectPayload::AnyRustValue { + value: Box::new(PyFilter { + predicate: function.clone(), + iterator, + }), }, cls.clone(), )) @@ -24,10 +40,10 @@ fn filter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn filter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(filter, Some(vm.ctx.filter_type()))]); - if let PyObjectPayload::FilterIterator { + if let Some(PyFilter { ref predicate, ref iterator, - } = filter.payload + }) = filter.payload() { loop { let next_obj = objiter::call_next(vm, iterator)?; diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index b5c356fb27..4a4107d118 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1508,10 +1508,6 @@ pub enum PyObjectPayload { counter: RefCell, iterator: PyObjectRef, }, - FilterIterator { - predicate: PyObjectRef, - iterator: PyObjectRef, - }, Slice { start: Option, stop: Option, @@ -1565,7 +1561,6 @@ impl fmt::Debug for PyObjectPayload { PyObjectPayload::WeakRef { .. } => write!(f, "weakref"), PyObjectPayload::Iterator { .. } => write!(f, "iterator"), PyObjectPayload::EnumerateIterator { .. } => write!(f, "enumerate"), - PyObjectPayload::FilterIterator { .. } => write!(f, "filter"), PyObjectPayload::Slice { .. } => write!(f, "slice"), PyObjectPayload::Function { .. } => write!(f, "function"), PyObjectPayload::Generator { .. } => write!(f, "generator"), From a2951f8a8b19a2d70198de85cc2d3d035dde5939 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Fri, 8 Mar 2019 18:29:58 -0800 Subject: [PATCH 211/380] enumerate --- vm/src/obj/objenumerate.rs | 29 +++++++++++++++++++++++------ vm/src/pyobject.rs | 5 ----- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/vm/src/obj/objenumerate.rs b/vm/src/obj/objenumerate.rs index 88097bfe5e..aa573ad6f0 100644 --- a/vm/src/obj/objenumerate.rs +++ b/vm/src/obj/objenumerate.rs @@ -3,11 +3,26 @@ use std::ops::AddAssign; use super::objint; use super::objiter; -use crate::pyobject::{PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyResult, TypeProtocol}; +use crate::pyobject::{ + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, + TypeProtocol, +}; use crate::vm::VirtualMachine; use num_bigint::BigInt; use num_traits::Zero; +#[derive(Debug)] +pub struct PyEnumerate { + counter: RefCell, + iterator: PyObjectRef, +} + +impl PyObjectPayload2 for PyEnumerate { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.enumerate_type() + } +} + fn enumerate_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -22,9 +37,11 @@ fn enumerate_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; let iterator = objiter::get_iter(vm, iterable)?; Ok(PyObject::new( - PyObjectPayload::EnumerateIterator { - counter: RefCell::new(counter), - iterator, + PyObjectPayload::AnyRustValue { + value: Box::new(PyEnumerate { + counter: RefCell::new(counter), + iterator, + }), }, cls.clone(), )) @@ -37,10 +54,10 @@ fn enumerate_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(enumerate, Some(vm.ctx.enumerate_type()))] ); - if let PyObjectPayload::EnumerateIterator { + if let Some(PyEnumerate { ref counter, ref iterator, - } = enumerate.payload + }) = enumerate.payload() { let next_obj = objiter::call_next(vm, iterator)?; let result = vm diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 4a4107d118..bdce7260a1 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1504,10 +1504,6 @@ pub enum PyObjectPayload { position: Cell, iterated_obj: PyObjectRef, }, - EnumerateIterator { - counter: RefCell, - iterator: PyObjectRef, - }, Slice { start: Option, stop: Option, @@ -1560,7 +1556,6 @@ impl fmt::Debug for PyObjectPayload { PyObjectPayload::MemoryView { ref obj } => write!(f, "bytes/bytearray {:?}", obj), PyObjectPayload::WeakRef { .. } => write!(f, "weakref"), PyObjectPayload::Iterator { .. } => write!(f, "iterator"), - PyObjectPayload::EnumerateIterator { .. } => write!(f, "enumerate"), PyObjectPayload::Slice { .. } => write!(f, "slice"), PyObjectPayload::Function { .. } => write!(f, "function"), PyObjectPayload::Generator { .. } => write!(f, "generator"), From 8df0e46c322fc76b4095f3393fc90a646f744e0a Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sat, 9 Mar 2019 09:29:33 +0200 Subject: [PATCH 212/380] Try to fix windows CI errors --- tests/snippets/stdlib_socket.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/snippets/stdlib_socket.py b/tests/snippets/stdlib_socket.py index 01000ccd9a..79efaa3639 100644 --- a/tests/snippets/stdlib_socket.py +++ b/tests/snippets/stdlib_socket.py @@ -43,6 +43,8 @@ with assertRaises(TypeError): s.bind((888, 8888)) +s.close() +s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(("127.0.0.1", 0)) with assertRaises(OSError): s.recv(100) @@ -93,10 +95,6 @@ with assertRaises(OSError): s.bind(("1.2.3.4", 888)) -s.bind(("127.0.0.1", 0)) -with assertRaises(OSError): - s.sendto(MESSAGE_A, ("1.2.3.4", 888)) - s.close() ### Errors with assertRaises(OSError): From 3f492e5a3f5df31ec2c280e2950db4b1bf5be01e Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Sat, 9 Mar 2019 11:48:35 +0100 Subject: [PATCH 213/380] Add __annotations__ attribute to functions. --- tests/snippets/type_hints.py | 7 ++++++- vm/src/frame.rs | 9 ++------- vm/src/pyobject.rs | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/snippets/type_hints.py b/tests/snippets/type_hints.py index 0a36e36d6f..b6c25e59f1 100644 --- a/tests/snippets/type_hints.py +++ b/tests/snippets/type_hints.py @@ -1,7 +1,12 @@ # See also: https://github.com/RustPython/RustPython/issues/587 -def curry(foo: int, bla=2) -> float: +def curry(foo: int, bla: int =2) -> float: return foo * 3.1415926 * bla assert curry(2) > 10 + +print(curry.__annotations__) +assert curry.__annotations__['foo'] is int +assert curry.__annotations__['return'] is float +assert curry.__annotations__['bla'] is int diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 8748175a71..f96ade968d 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -453,7 +453,7 @@ impl Frame { let _qualified_name = self.pop_value(); let code_obj = self.pop_value(); - let _annotations = if flags.contains(bytecode::FunctionOpArg::HAS_ANNOTATIONS) { + let annotations = if flags.contains(bytecode::FunctionOpArg::HAS_ANNOTATIONS) { self.pop_value() } else { vm.new_dict() @@ -470,13 +470,8 @@ impl Frame { let scope = self.scope.clone(); let obj = vm.ctx.new_function(code_obj, scope, defaults); - let annotation_repr = vm.to_pystr(&_annotations)?; + vm.ctx.set_attr(&obj, "__annotations__", annotations); - warn!( - "Type annotation must be stored in attribute! {:?}", - annotation_repr - ); - // TODO: use annotations with set_attr here! self.push_value(obj); Ok(None) } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index c123f8bd1c..14f7b6e7b1 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1566,7 +1566,7 @@ impl PyObject { PyObject { payload, typ: Some(typ), - dict: None, + dict: Some(RefCell::new(PyAttributes::new())), } .into_ref() } From 6ba5b091e9bbe9f474fe7bfa3a3e959c28273c53 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Sat, 9 Mar 2019 12:38:14 +0100 Subject: [PATCH 214/380] Use py_module at more places. --- vm/src/stdlib/ast.rs | 6 +++--- vm/src/stdlib/io.rs | 4 ++-- vm/src/stdlib/os.rs | 22 +++++++++++----------- vm/src/stdlib/weakref.rs | 13 +++++++------ wasm/lib/src/browser_module.rs | 10 ++++------ 5 files changed, 27 insertions(+), 28 deletions(-) diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index 7d1f49a024..d3bdd3656b 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -624,8 +624,8 @@ fn ast_parse(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn mk_module(ctx: &PyContext) -> PyObjectRef { py_module!(ctx, "ast", { "parse" => ctx.new_rustfunc(ast_parse), - "Module" => ctx.new_class("_ast.Module", ctx.object()), - "FunctionDef" =>ctx.new_class("_ast.FunctionDef", ctx.object()), - "Call" => ctx.new_class("_ast.Call", ctx.object()) + "Module" => py_class!(ctx, "_ast.Module", ctx.object(), {}), + "FunctionDef" => py_class!(ctx, "_ast.FunctionDef", ctx.object(), {}), + "Call" => py_class!(ctx, "_ast.Call", ctx.object(), {}) }) } diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 12b85e6604..f35d5eb041 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -347,10 +347,10 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn mk_module(ctx: &PyContext) -> PyObjectRef { //IOBase the abstract base class of the IO Module - let io_base = ctx.new_class("IOBase", ctx.object()); + let io_base = py_class!(ctx, "IOBase", ctx.object(), {}); // IOBase Subclasses - let raw_io_base = ctx.new_class("RawIOBase", ctx.object()); + let raw_io_base = py_class!(ctx, "RawIOBase", ctx.object(), {}); let buffered_io_base = py_class!(ctx, "BufferedIOBase", io_base.clone(), { "__init__" => ctx.new_rustfunc(buffered_io_base_init) diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index ef45d2d84a..5379fefc4d 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -119,17 +119,17 @@ fn os_error(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn mk_module(ctx: &PyContext) -> PyObjectRef { - 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)); - - 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)); + let py_mod = py_module!(ctx, "os", { + "open" => ctx.new_rustfunc(os_open), + "close" => ctx.new_rustfunc(os_close), + "error" => ctx.new_rustfunc(os_error), + "O_RDONLY" => ctx.new_int(0), + "O_WRONLY" => ctx.new_int(1), + "O_RDWR" => ctx.new_int(2), + "O_NONBLOCK" => ctx.new_int(4), + "O_APPEND" => ctx.new_int(8), + "O_CREAT" => ctx.new_int(512) + }); if cfg!(windows) { ctx.set_attr(&py_mod, "name", ctx.new_str("nt".to_string())); diff --git a/vm/src/stdlib/weakref.rs b/vm/src/stdlib/weakref.rs index 5b67f1664e..ba6015ec21 100644 --- a/vm/src/stdlib/weakref.rs +++ b/vm/src/stdlib/weakref.rs @@ -13,13 +13,14 @@ use crate::VirtualMachine; use std::rc::Rc; pub fn mk_module(ctx: &PyContext) -> PyObjectRef { - let py_mod = ctx.new_module("_weakref", ctx.new_scope(None)); + let py_ref_class = py_class!(ctx, "ref", ctx.object(), { + "__new__" => ctx.new_rustfunc(ref_new), + "__call__" => ctx.new_rustfunc(ref_call) + }); - let py_ref_class = ctx.new_class("ref", ctx.object()); - ctx.set_attr(&py_ref_class, "__new__", ctx.new_rustfunc(ref_new)); - ctx.set_attr(&py_ref_class, "__call__", ctx.new_rustfunc(ref_call)); - ctx.set_attr(&py_mod, "ref", py_ref_class); - py_mod + py_module!(ctx, "_weakref", { + "ref" => py_ref_class + }) } fn ref_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index ea21d30fda..9a80be0c9a 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -313,12 +313,10 @@ fn browser_prompt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { const BROWSER_NAME: &str = "browser"; pub fn mk_module(ctx: &PyContext) -> PyObjectRef { - let promise = { - let promise = ctx.new_class("Promise", ctx.object()); - ctx.set_attr(&promise, "then", ctx.new_rustfunc(promise_then)); - ctx.set_attr(&promise, "catch", ctx.new_rustfunc(promise_catch)); - promise - }; + let promise = py_class!(ctx, "Promise", ctx.object(), { + "then" => ctx.new_rustfunc(promise_then), + "catch" => ctx.new_rustfunc(promise_catch) + }); py_module!(ctx, BROWSER_NAME, { "fetch" => ctx.new_rustfunc(browser_fetch), From dd56d1d5a215e3828a397e3723745fc504d9bc33 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Fri, 8 Mar 2019 18:52:11 -0800 Subject: [PATCH 215/380] function --- vm/src/obj/objfunction.rs | 34 ++++++++++++++++++++++++++++++---- vm/src/pyobject.rs | 14 +++----------- vm/src/vm.rs | 20 ++++++++++++-------- 3 files changed, 45 insertions(+), 23 deletions(-) diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index 26fee403fa..46e59663d1 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -1,8 +1,34 @@ +use crate::frame::ScopeRef; use crate::pyobject::{ - AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyResult, TypeProtocol, + AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectPayload2, PyObjectRef, PyResult, + TypeProtocol, }; use crate::vm::VirtualMachine; +#[derive(Debug)] +pub struct PyFunction { + // TODO: these shouldn't be public + pub code: PyObjectRef, + pub scope: ScopeRef, + pub defaults: PyObjectRef, +} + +impl PyFunction { + pub fn new(code: PyObjectRef, scope: ScopeRef, defaults: PyObjectRef) -> Self { + PyFunction { + code, + scope, + defaults, + } + } +} + +impl PyObjectPayload2 for PyFunction { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.function_type() + } +} + pub fn init(context: &PyContext) { let function_type = &context.function_type; context.set_attr(&function_type, "__get__", context.new_rustfunc(bind_method)); @@ -79,9 +105,9 @@ fn bind_method(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn function_code(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - match args.args[0].payload { - PyObjectPayload::Function { ref code, .. } => Ok(code.clone()), - _ => Err(vm.new_type_error("no code".to_string())), + match args.args[0].payload() { + Some(PyFunction { ref code, .. }) => Ok(code.clone()), + None => Err(vm.new_type_error("no code".to_string())), } } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 14f7b6e7b1..e5c3f4e5c4 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -24,7 +24,7 @@ use crate::obj::objenumerate; use crate::obj::objfilter; use crate::obj::objfloat::{self, PyFloat}; use crate::obj::objframe; -use crate::obj::objfunction; +use crate::obj::objfunction::{self, PyFunction}; use crate::obj::objgenerator; use crate::obj::objint::{self, PyInt}; use crate::obj::objiter; @@ -657,10 +657,8 @@ impl PyContext { defaults: PyObjectRef, ) -> PyObjectRef { PyObject::new( - PyObjectPayload::Function { - code: code_obj, - scope, - defaults, + PyObjectPayload::AnyRustValue { + value: Box::new(PyFunction::new(code_obj, scope, defaults)), }, self.function_type(), ) @@ -1510,11 +1508,6 @@ pub enum PyObjectPayload { Frame { frame: Frame, }, - Function { - code: PyObjectRef, - scope: ScopeRef, - defaults: PyObjectRef, - }, Generator { frame: PyObjectRef, }, @@ -1548,7 +1541,6 @@ impl fmt::Debug for PyObjectPayload { PyObjectPayload::WeakRef { .. } => write!(f, "weakref"), PyObjectPayload::Iterator { .. } => write!(f, "iterator"), PyObjectPayload::Slice { .. } => write!(f, "slice"), - PyObjectPayload::Function { .. } => write!(f, "function"), PyObjectPayload::Generator { .. } => write!(f, "generator"), PyObjectPayload::BoundMethod { ref function, diff --git a/vm/src/vm.rs b/vm/src/vm.rs index bf5ce74332..63f252bc08 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -18,6 +18,7 @@ use crate::frame::{Scope, ScopeRef}; use crate::obj::objbool; use crate::obj::objcode; use crate::obj::objframe; +use crate::obj::objfunction::PyFunction; use crate::obj::objgenerator; use crate::obj::objiter; use crate::obj::objlist::PyList; @@ -289,13 +290,16 @@ impl VirtualMachine { { let args = args.into(); trace!("Invoke: {:?} {:?}", func_ref, args); + if let Some(PyFunction { + ref code, + ref scope, + ref defaults, + }) = func_ref.payload() + { + return self.invoke_python_function(code, scope, defaults, args); + } match func_ref.payload { PyObjectPayload::RustFunction { ref function } => function(self, args), - PyObjectPayload::Function { - ref code, - ref scope, - ref defaults, - } => self.invoke_python_function(code, scope, defaults, args), PyObjectPayload::BoundMethod { ref function, ref object, @@ -331,11 +335,11 @@ impl VirtualMachine { } pub fn invoke_with_locals(&mut self, function: PyObjectRef, locals: PyObjectRef) -> PyResult { - if let PyObjectPayload::Function { + if let Some(PyFunction { code, scope, - defaults: _defaults, - } = &function.payload + defaults: _, + }) = &function.payload() { let scope = Rc::new(Scope { locals, From 5a74121c76a02e146bf5d6daf28f1f6dbe7752f3 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Fri, 8 Mar 2019 19:11:01 -0800 Subject: [PATCH 216/380] method --- vm/src/obj/objfunction.rs | 19 +++++++++++++++++++ vm/src/pyobject.rs | 14 ++++---------- vm/src/vm.rs | 13 ++++++++----- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index 46e59663d1..432266225b 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -29,6 +29,25 @@ impl PyObjectPayload2 for PyFunction { } } +#[derive(Debug)] +pub struct PyMethod { + // TODO: these shouldn't be public + pub object: PyObjectRef, + pub function: PyObjectRef, +} + +impl PyMethod { + pub fn new(object: PyObjectRef, function: PyObjectRef) -> Self { + PyMethod { object, function } + } +} + +impl PyObjectPayload2 for PyMethod { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.bound_method_type() + } +} + pub fn init(context: &PyContext) { let function_type = &context.function_type; context.set_attr(&function_type, "__get__", context.new_rustfunc(bind_method)); diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index e5c3f4e5c4..cd7712c452 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -24,7 +24,7 @@ use crate::obj::objenumerate; use crate::obj::objfilter; use crate::obj::objfloat::{self, PyFloat}; use crate::obj::objframe; -use crate::obj::objfunction::{self, PyFunction}; +use crate::obj::objfunction::{self, PyFunction, PyMethod}; use crate::obj::objgenerator; use crate::obj::objint::{self, PyInt}; use crate::obj::objiter; @@ -666,7 +666,9 @@ impl PyContext { pub fn new_bound_method(&self, function: PyObjectRef, object: PyObjectRef) -> PyObjectRef { PyObject::new( - PyObjectPayload::BoundMethod { function, object }, + PyObjectPayload::AnyRustValue { + value: Box::new(PyMethod::new(object, function)), + }, self.bound_method_type(), ) } @@ -1511,10 +1513,6 @@ pub enum PyObjectPayload { Generator { frame: PyObjectRef, }, - BoundMethod { - function: PyObjectRef, - object: PyObjectRef, - }, WeakRef { referent: PyObjectWeakRef, }, @@ -1542,10 +1540,6 @@ impl fmt::Debug for PyObjectPayload { PyObjectPayload::Iterator { .. } => write!(f, "iterator"), PyObjectPayload::Slice { .. } => write!(f, "slice"), PyObjectPayload::Generator { .. } => write!(f, "generator"), - PyObjectPayload::BoundMethod { - ref function, - ref object, - } => write!(f, "bound-method: {:?} of {:?}", function, object), PyObjectPayload::RustFunction { .. } => write!(f, "rust function"), PyObjectPayload::Frame { .. } => write!(f, "frame"), PyObjectPayload::AnyRustValue { value } => value.fmt(f), diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 63f252bc08..72af935dbf 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -18,7 +18,7 @@ use crate::frame::{Scope, ScopeRef}; use crate::obj::objbool; use crate::obj::objcode; use crate::obj::objframe; -use crate::obj::objfunction::PyFunction; +use crate::obj::objfunction::{PyFunction, PyMethod}; use crate::obj::objgenerator; use crate::obj::objiter; use crate::obj::objlist::PyList; @@ -298,12 +298,15 @@ impl VirtualMachine { { return self.invoke_python_function(code, scope, defaults, args); } + if let Some(PyMethod { + ref function, + ref object, + }) = func_ref.payload() + { + return self.invoke(function.clone(), args.insert(object.clone())); + } match func_ref.payload { PyObjectPayload::RustFunction { ref function } => function(self, args), - PyObjectPayload::BoundMethod { - ref function, - ref object, - } => self.invoke(function.clone(), args.insert(object.clone())), ref payload => { // TODO: is it safe to just invoke __call__ otherwise? trace!("invoke __call__ for: {:?}", payload); From 976c9e8b587cc2a292836e3c0de57a8212e3e94d Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sat, 9 Mar 2019 18:41:02 +0200 Subject: [PATCH 217/380] Add interactive shell to WASM demo --- wasm/demo/package.json | 3 ++- wasm/demo/src/index.ejs | 3 +++ wasm/demo/src/index.js | 1 + wasm/demo/src/main.js | 43 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 1 deletion(-) diff --git a/wasm/demo/package.json b/wasm/demo/package.json index 321bef0902..2184c6db84 100644 --- a/wasm/demo/package.json +++ b/wasm/demo/package.json @@ -4,7 +4,8 @@ "description": "Bindings to the RustPython library for WebAssembly", "main": "index.js", "dependencies": { - "codemirror": "^5.42.0" + "codemirror": "^5.42.0", + "xterm": "^3.8.0" }, "devDependencies": { "@wasm-tool/wasm-pack-plugin": "0.2.0", diff --git a/wasm/demo/src/index.ejs b/wasm/demo/src/index.ejs index 0cb2def1d5..a2a02d214b 100644 --- a/wasm/demo/src/index.ejs +++ b/wasm/demo/src/index.ejs @@ -30,6 +30,9 @@

Standard Output

+

Interactive shell

+
+

Here's some info regarding the rp.pyEval() function

  • diff --git a/wasm/demo/src/index.js b/wasm/demo/src/index.js index bf5257df2a..9b58afe398 100644 --- a/wasm/demo/src/index.js +++ b/wasm/demo/src/index.js @@ -1,5 +1,6 @@ import './style.css'; import 'codemirror/lib/codemirror.css'; +import 'xterm/dist/xterm.css'; // A dependency graph that contains any wasm must all be imported // asynchronously. This `index.js` file does the single async import, so diff --git a/wasm/demo/src/main.js b/wasm/demo/src/main.js index 0735f8d227..8b6c608c62 100644 --- a/wasm/demo/src/main.js +++ b/wasm/demo/src/main.js @@ -2,6 +2,7 @@ import * as rp from '../../lib/pkg'; import CodeMirror from 'codemirror'; import 'codemirror/mode/python/python'; import 'codemirror/addon/comment/comment'; +import { Terminal } from 'xterm'; // so people can play around with it window.rp = rp; @@ -72,3 +73,45 @@ 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(); + +const term = new Terminal(); +term.open(document.getElementById('terminal')); +term.write(">>>> "); + +function remove_non_ascii(str) { + if ((str===null) || (str==='')) + return false; + else + str = str.toString(); + + return str.replace(/[^\x20-\x7E]/g, ''); +} + +function print_to_console(data) { + term.write(remove_non_ascii(data) + "\r\n"); +} + +var input = ""; +term.on("data", (data) => { + const code = data.charCodeAt(0); + if (code == 13) { // CR + term.write("\r\n"); + try { + rp.pyEval(input, { + stdout: print_to_console + }); + } catch (err) { + if (err instanceof WebAssembly.RuntimeError) { + err = window.__RUSTPYTHON_ERROR || err; + } + print_to_console(err); + } + term.write(">>>> "); + input = ""; + } else if (code < 32 || code == 127) { // Control + return; + } else { // Visible + term.write(data); + input += data; + } +}); From c1180fc564011dda46b095f7c643deeafc822ee6 Mon Sep 17 00:00:00 2001 From: Joey Date: Sat, 9 Mar 2019 08:45:46 -0800 Subject: [PATCH 218/380] builtin_function_or_method --- vm/src/frame.rs | 7 +++++-- vm/src/obj/mod.rs | 1 + vm/src/obj/objbuiltinfunc.rs | 26 ++++++++++++++++++++++++++ vm/src/pyobject.rs | 9 +++------ vm/src/vm.rs | 18 +++++++++--------- 5 files changed, 44 insertions(+), 17 deletions(-) create mode 100644 vm/src/obj/objbuiltinfunc.rs diff --git a/vm/src/frame.rs b/vm/src/frame.rs index db913fe1b8..8e91194a05 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -11,6 +11,7 @@ use crate::builtins; use crate::bytecode; use crate::import::{import, import_module}; use crate::obj::objbool; +use crate::obj::objbuiltinfunc::PyBuiltinFunction; use crate::obj::objcode; use crate::obj::objdict; use crate::obj::objdict::PyDict; @@ -592,8 +593,10 @@ impl Frame { } bytecode::Instruction::LoadBuildClass => { let rustfunc = PyObject::new( - PyObjectPayload::RustFunction { - function: Box::new(builtins::builtin_build_class_), + PyObjectPayload::AnyRustValue { + value: Box::new(PyBuiltinFunction::new(Box::new( + builtins::builtin_build_class_, + ))), }, vm.ctx.type_type(), ); diff --git a/vm/src/obj/mod.rs b/vm/src/obj/mod.rs index 6cbb5fa66f..3d068df648 100644 --- a/vm/src/obj/mod.rs +++ b/vm/src/obj/mod.rs @@ -1,6 +1,7 @@ //! This package contains the python basic/builtin types pub mod objbool; +pub mod objbuiltinfunc; pub mod objbytearray; pub mod objbytes; pub mod objcode; diff --git a/vm/src/obj/objbuiltinfunc.rs b/vm/src/obj/objbuiltinfunc.rs new file mode 100644 index 0000000000..8e3b2c3422 --- /dev/null +++ b/vm/src/obj/objbuiltinfunc.rs @@ -0,0 +1,26 @@ +use std::fmt; + +use crate::pyobject::{PyContext, PyNativeFunc, PyObjectPayload2, PyObjectRef}; + +pub struct PyBuiltinFunction { + // TODO: shouldn't be public + pub value: PyNativeFunc, +} + +impl PyObjectPayload2 for PyBuiltinFunction { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.builtin_function_or_method_type() + } +} + +impl fmt::Debug for PyBuiltinFunction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "builtin function") + } +} + +impl PyBuiltinFunction { + pub fn new(value: PyNativeFunc) -> Self { + Self { value } + } +} diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index cd7712c452..740e27ace3 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -14,6 +14,7 @@ use crate::bytecode; use crate::exceptions; use crate::frame::{Frame, Scope, ScopeRef}; use crate::obj::objbool; +use crate::obj::objbuiltinfunc::PyBuiltinFunction; use crate::obj::objbytearray; use crate::obj::objbytes; use crate::obj::objcode; @@ -615,8 +616,8 @@ impl PyContext { F: IntoPyNativeFunc, { PyObject::new( - PyObjectPayload::RustFunction { - function: f.into_func(), + PyObjectPayload::AnyRustValue { + value: Box::new(PyBuiltinFunction::new(f.into_func())), }, self.builtin_function_or_method_type(), ) @@ -1516,9 +1517,6 @@ pub enum PyObjectPayload { WeakRef { referent: PyObjectWeakRef, }, - RustFunction { - function: PyNativeFunc, - }, AnyRustValue { value: Box, }, @@ -1540,7 +1538,6 @@ impl fmt::Debug for PyObjectPayload { PyObjectPayload::Iterator { .. } => write!(f, "iterator"), PyObjectPayload::Slice { .. } => write!(f, "slice"), PyObjectPayload::Generator { .. } => write!(f, "generator"), - PyObjectPayload::RustFunction { .. } => write!(f, "rust function"), PyObjectPayload::Frame { .. } => write!(f, "frame"), PyObjectPayload::AnyRustValue { value } => value.fmt(f), } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 72af935dbf..32a0104d23 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -16,6 +16,7 @@ use crate::bytecode; use crate::frame::ExecutionResult; use crate::frame::{Scope, ScopeRef}; use crate::obj::objbool; +use crate::obj::objbuiltinfunc::PyBuiltinFunction; use crate::obj::objcode; use crate::obj::objframe; use crate::obj::objfunction::{PyFunction, PyMethod}; @@ -28,8 +29,8 @@ use crate::obj::objstr; use crate::obj::objtuple::PyTuple; use crate::obj::objtype; use crate::pyobject::{ - AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, - PyObjectRef, PyResult, TypeProtocol, + AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, + TypeProtocol, }; use crate::stdlib; use crate::sysmodule; @@ -305,14 +306,13 @@ impl VirtualMachine { { return self.invoke(function.clone(), args.insert(object.clone())); } - match func_ref.payload { - PyObjectPayload::RustFunction { ref function } => function(self, args), - ref payload => { - // TODO: is it safe to just invoke __call__ otherwise? - trace!("invoke __call__ for: {:?}", payload); - self.call_method(&func_ref, "__call__", args) - } + if let Some(PyBuiltinFunction { ref value }) = func_ref.payload() { + return value(self, args); } + + // TODO: is it safe to just invoke __call__ otherwise? + trace!("invoke __call__ for: {:?}", func_ref.payload); + self.call_method(&func_ref, "__call__", args) } fn invoke_python_function( From 53e4591911947bd63530ffaa5947ebc3f7269b12 Mon Sep 17 00:00:00 2001 From: Joey Date: Sat, 9 Mar 2019 09:21:21 -0800 Subject: [PATCH 219/380] frame --- vm/src/frame.rs | 10 ++++++++-- vm/src/obj/objframe.rs | 10 ++-------- vm/src/obj/objgenerator.rs | 4 ++-- vm/src/pyobject.rs | 8 ++------ 4 files changed, 14 insertions(+), 18 deletions(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 8e91194a05..391ab6fe3d 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -21,8 +21,8 @@ use crate::obj::objlist; use crate::obj::objstr; use crate::obj::objtype; use crate::pyobject::{ - DictProtocol, IdProtocol, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, - TryFromObject, TypeProtocol, + DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, + PyObjectRef, PyResult, TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -76,6 +76,12 @@ pub struct Frame { pub lasti: RefCell, // index of last instruction ran } +impl PyObjectPayload2 for Frame { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.frame_type() + } +} + // Running a frame can result in one of the below: pub enum ExecutionResult { Return(PyObjectRef), diff --git a/vm/src/obj/objframe.rs b/vm/src/obj/objframe.rs index 37acd33385..459b5a0c3d 100644 --- a/vm/src/obj/objframe.rs +++ b/vm/src/obj/objframe.rs @@ -3,9 +3,7 @@ */ use crate::frame::Frame; -use crate::pyobject::{ - PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, -}; +use crate::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; pub fn init(context: &PyContext) { @@ -39,9 +37,5 @@ fn frame_fcode(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn get_value(obj: &PyObjectRef) -> &Frame { - if let PyObjectPayload::Frame { frame } = &obj.payload { - frame - } else { - panic!("Inner error getting int {:?}", obj); - } + &obj.payload::().unwrap() } diff --git a/vm/src/obj/objgenerator.rs b/vm/src/obj/objgenerator.rs index 05a7a60ae6..9192b07aae 100644 --- a/vm/src/obj/objgenerator.rs +++ b/vm/src/obj/objgenerator.rs @@ -2,7 +2,7 @@ * The mythical generator. */ -use crate::frame::ExecutionResult; +use crate::frame::{ExecutionResult, Frame}; use crate::pyobject::{ PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; @@ -56,7 +56,7 @@ fn generator_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn send(vm: &mut VirtualMachine, gen: &PyObjectRef, value: &PyObjectRef) -> PyResult { if let PyObjectPayload::Generator { ref frame } = gen.payload { - if let PyObjectPayload::Frame { ref frame } = frame.payload { + if let Some(frame) = frame.payload::() { frame.push_value(value.clone()); } else { panic!("Generator frame isn't a frame."); diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 740e27ace3..c1dadb92d2 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -625,8 +625,8 @@ impl PyContext { pub fn new_frame(&self, code: PyObjectRef, scope: ScopeRef) -> PyObjectRef { PyObject::new( - PyObjectPayload::Frame { - frame: Frame::new(code, scope), + PyObjectPayload::AnyRustValue { + value: Box::new(Frame::new(code, scope)), }, self.frame_type(), ) @@ -1508,9 +1508,6 @@ pub enum PyObjectPayload { MemoryView { obj: PyObjectRef, }, - Frame { - frame: Frame, - }, Generator { frame: PyObjectRef, }, @@ -1538,7 +1535,6 @@ impl fmt::Debug for PyObjectPayload { PyObjectPayload::Iterator { .. } => write!(f, "iterator"), PyObjectPayload::Slice { .. } => write!(f, "slice"), PyObjectPayload::Generator { .. } => write!(f, "generator"), - PyObjectPayload::Frame { .. } => write!(f, "frame"), PyObjectPayload::AnyRustValue { value } => value.fmt(f), } } From 6eea40799be84c0624cec7ba3fe073bad6aae65c Mon Sep 17 00:00:00 2001 From: Joey Date: Sat, 9 Mar 2019 09:27:58 -0800 Subject: [PATCH 220/380] generator --- vm/src/obj/objgenerator.rs | 20 +++++++++++++++++--- vm/src/pyobject.rs | 4 ---- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/vm/src/obj/objgenerator.rs b/vm/src/obj/objgenerator.rs index 9192b07aae..1babd5c9dc 100644 --- a/vm/src/obj/objgenerator.rs +++ b/vm/src/obj/objgenerator.rs @@ -4,10 +4,22 @@ use crate::frame::{ExecutionResult, Frame}; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, + TypeProtocol, }; use crate::vm::VirtualMachine; +#[derive(Debug)] +pub struct PyGenerator { + frame: PyObjectRef, +} + +impl PyObjectPayload2 for PyGenerator { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.generator_type() + } +} + pub fn init(context: &PyContext) { let generator_type = &context.generator_type; context.set_attr( @@ -29,7 +41,9 @@ pub fn init(context: &PyContext) { pub fn new_generator(vm: &mut VirtualMachine, frame: PyObjectRef) -> PyResult { Ok(PyObject::new( - PyObjectPayload::Generator { frame }, + PyObjectPayload::AnyRustValue { + value: Box::new(PyGenerator { frame }), + }, vm.ctx.generator_type.clone(), )) } @@ -55,7 +69,7 @@ fn generator_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn send(vm: &mut VirtualMachine, gen: &PyObjectRef, value: &PyObjectRef) -> PyResult { - if let PyObjectPayload::Generator { ref frame } = gen.payload { + if let Some(PyGenerator { ref frame }) = gen.payload() { if let Some(frame) = frame.payload::() { frame.push_value(value.clone()); } else { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index c1dadb92d2..c9c7ed02ac 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1508,9 +1508,6 @@ pub enum PyObjectPayload { MemoryView { obj: PyObjectRef, }, - Generator { - frame: PyObjectRef, - }, WeakRef { referent: PyObjectWeakRef, }, @@ -1534,7 +1531,6 @@ impl fmt::Debug for PyObjectPayload { PyObjectPayload::WeakRef { .. } => write!(f, "weakref"), PyObjectPayload::Iterator { .. } => write!(f, "iterator"), PyObjectPayload::Slice { .. } => write!(f, "slice"), - PyObjectPayload::Generator { .. } => write!(f, "generator"), PyObjectPayload::AnyRustValue { value } => value.fmt(f), } } From ec3ace527b538fd3c6372e8c55bb31a0333f98f2 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sat, 9 Mar 2019 20:57:36 +0200 Subject: [PATCH 221/380] Change prompt to >>>>> --- wasm/demo/src/main.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/wasm/demo/src/main.js b/wasm/demo/src/main.js index 8b6c608c62..3ef876ec0f 100644 --- a/wasm/demo/src/main.js +++ b/wasm/demo/src/main.js @@ -74,9 +74,11 @@ snippets.addEventListener('change', updateSnippet); // option selected for the `select`, but the textarea won't be updated) updateSnippet(); +const prompt = ">>>>> "; + const term = new Terminal(); term.open(document.getElementById('terminal')); -term.write(">>>> "); +term.write(prompt); function remove_non_ascii(str) { if ((str===null) || (str==='')) @@ -106,7 +108,7 @@ term.on("data", (data) => { } print_to_console(err); } - term.write(">>>> "); + term.write(prompt); input = ""; } else if (code < 32 || code == 127) { // Control return; From 03d431ecacb4690128bf3a0701e0072e221d6418 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sat, 9 Mar 2019 21:17:13 +0200 Subject: [PATCH 222/380] Support backspace in interactive shell --- wasm/demo/src/main.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wasm/demo/src/main.js b/wasm/demo/src/main.js index 3ef876ec0f..e2ef78fc63 100644 --- a/wasm/demo/src/main.js +++ b/wasm/demo/src/main.js @@ -110,6 +110,9 @@ term.on("data", (data) => { } term.write(prompt); input = ""; + } else if (code == 127) { + term.write("\b \b"); + input = input.slice(0, -1); } else if (code < 32 || code == 127) { // Control return; } else { // Visible From 669aa70aabaf8e13f1e8b8d76aab3041c3f1eeff Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sat, 9 Mar 2019 21:23:59 +0200 Subject: [PATCH 223/380] Support ':' in interactive shell --- wasm/demo/src/main.js | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/wasm/demo/src/main.js b/wasm/demo/src/main.js index e2ef78fc63..2fecd945b7 100644 --- a/wasm/demo/src/main.js +++ b/wasm/demo/src/main.js @@ -97,19 +97,24 @@ var input = ""; term.on("data", (data) => { const code = data.charCodeAt(0); if (code == 13) { // CR - term.write("\r\n"); - try { - rp.pyEval(input, { - stdout: print_to_console - }); - } catch (err) { - if (err instanceof WebAssembly.RuntimeError) { - err = window.__RUSTPYTHON_ERROR || err; + if (input[input.length - 1] == ':') { + input += data + term.write("\r\n....."); + } else { + term.write("\r\n"); + try { + rp.pyEval(input, { + stdout: print_to_console + }); + } catch (err) { + if (err instanceof WebAssembly.RuntimeError) { + err = window.__RUSTPYTHON_ERROR || err; + } + print_to_console(err); } - print_to_console(err); + term.write(prompt); + input = ""; } - term.write(prompt); - input = ""; } else if (code == 127) { term.write("\b \b"); input = input.slice(0, -1); From 3c3c1f2b6f6fd8e1938b63153554f90cfa611ac9 Mon Sep 17 00:00:00 2001 From: ben Date: Sun, 10 Mar 2019 09:01:46 +1300 Subject: [PATCH 224/380] Fixed new_attribute_error and added more tests for property --- tests/snippets/class.py | 16 ---------------- tests/snippets/property.py | 32 ++++++++++++++++++++++++++++++++ vm/src/vm.rs | 2 +- 3 files changed, 33 insertions(+), 17 deletions(-) create mode 100644 tests/snippets/property.py diff --git a/tests/snippets/class.py b/tests/snippets/class.py index 5bba7be96b..ba2d423ea1 100644 --- a/tests/snippets/class.py +++ b/tests/snippets/class.py @@ -16,22 +16,6 @@ def square(self): assert foo.square() == 25 -class Fubar: - def __init__(self): - self.x = 100 - - @property - def foo(self): - value = self.x - self.x += 1 - return value - - -f = Fubar() -assert f.foo == 100 -assert f.foo == 101 - - class Bar: """ W00t """ def __init__(self, x): diff --git a/tests/snippets/property.py b/tests/snippets/property.py new file mode 100644 index 0000000000..a55870e3b8 --- /dev/null +++ b/tests/snippets/property.py @@ -0,0 +1,32 @@ +from testutils import assertRaises + + +class Fubar: + def __init__(self): + self.x = 100 + + @property + def foo(self): + value = self.x + self.x += 1 + return value + + +f = Fubar() +assert f.foo == 100 +assert f.foo == 101 + + +null_property = property() +assert type(null_property) is property + +p = property(lambda x: x[0]) +assert p.__get__((2,), tuple) == 2 +# TODO owner parameter is optional +# assert p.__get__((2,)) == 2 + +with assertRaises(AttributeError): + null_property.__get__((), tuple) + +with assertRaises(TypeError): + property.__new__(object) diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 7f50a4636e..5dabf19cb8 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -135,7 +135,7 @@ impl VirtualMachine { } pub fn new_attribute_error(&mut self, msg: String) -> PyObjectRef { - let type_error = self.ctx.exceptions.arithmetic_error.clone(); + let type_error = self.ctx.exceptions.attribute_error.clone(); self.new_exception(type_error, msg) } From 157d18d7a155dfaa740426a68f43b0face68c203 Mon Sep 17 00:00:00 2001 From: Joey Date: Sat, 9 Mar 2019 10:15:18 -0800 Subject: [PATCH 225/380] Convert iterator --- vm/src/obj/objbytes.rs | 12 +++++++----- vm/src/obj/objdict.rs | 28 +++++++++++++++++----------- vm/src/obj/objiter.rs | 6 +++--- vm/src/obj/objlist.rs | 12 +++++++----- vm/src/obj/objrange.rs | 30 +++++++++++++++++------------- vm/src/obj/objset.rs | 12 +++++++----- vm/src/obj/objtuple.rs | 12 +++++++----- vm/src/pyobject.rs | 30 ++++++++++++++---------------- 8 files changed, 79 insertions(+), 63 deletions(-) diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index d285b86e9a..2aa0006ddf 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -5,8 +5,8 @@ use std::ops::Deref; use super::objint; use super::objtype; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, - TypeProtocol, + PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectPayload, PyObjectPayload2, + PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use num_traits::ToPrimitive; @@ -209,9 +209,11 @@ 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: Cell::new(0), - iterated_obj: obj.clone(), + PyObjectPayload::AnyRustValue { + value: Box::new(PyIteratorValue { + position: Cell::new(0), + iterated_obj: obj.clone(), + }), }, vm.ctx.iter_type(), ); diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index df85e75791..fd1aafa2af 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -6,8 +6,8 @@ use super::objiter; use super::objstr; use super::objtype; use crate::pyobject::{ - PyAttributes, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, - PyResult, TypeProtocol, + PyAttributes, PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectPayload, + PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; @@ -249,9 +249,11 @@ fn dict_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let key_list = vm.ctx.new_list(keys); let iter_obj = PyObject::new( - PyObjectPayload::Iterator { - position: Cell::new(0), - iterated_obj: key_list, + PyObjectPayload::AnyRustValue { + value: Box::new(PyIteratorValue { + position: Cell::new(0), + iterated_obj: key_list, + }), }, vm.ctx.iter_type(), ); @@ -269,9 +271,11 @@ fn dict_values(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let values_list = vm.ctx.new_list(values); let iter_obj = PyObject::new( - PyObjectPayload::Iterator { - position: Cell::new(0), - iterated_obj: values_list, + PyObjectPayload::AnyRustValue { + value: Box::new(PyIteratorValue { + position: Cell::new(0), + iterated_obj: values_list, + }), }, vm.ctx.iter_type(), ); @@ -289,9 +293,11 @@ fn dict_items(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let items_list = vm.ctx.new_list(items); let iter_obj = PyObject::new( - PyObjectPayload::Iterator { - position: Cell::new(0), - iterated_obj: items_list, + PyObjectPayload::AnyRustValue { + value: Box::new(PyIteratorValue { + position: Cell::new(0), + iterated_obj: items_list, + }), }, vm.ctx.iter_type(), ); diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 827ff1cd06..153750feef 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -3,7 +3,7 @@ */ use crate::pyobject::{ - PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyIteratorValue, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -128,10 +128,10 @@ fn iter_new(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 { + if let Some(PyIteratorValue { ref position, iterated_obj: ref iterated_obj_ref, - } = iter.payload + }) = iter.payload() { if let Some(range) = iterated_obj_ref.payload::() { if let Some(int) = range.get(position.get()) { diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index d71c13976f..8ad69bff31 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -10,8 +10,8 @@ use super::objstr; use super::objtype; use crate::function::PyRef; use crate::pyobject::{ - IdProtocol, OptionalArg, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, - PyObjectRef, PyResult, TypeProtocol, + IdProtocol, OptionalArg, PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectPayload, + PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; use num_traits::ToPrimitive; @@ -112,9 +112,11 @@ impl PyListRef { fn iter(self, vm: &mut VirtualMachine) -> PyObjectRef { PyObject::new( - PyObjectPayload::Iterator { - position: Cell::new(0), - iterated_obj: self.into_object(), + PyObjectPayload::AnyRustValue { + value: Box::new(PyIteratorValue { + position: Cell::new(0), + iterated_obj: self.into_object(), + }), }, vm.ctx.iter_type(), ) diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 55f736da56..b526cbbb5d 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -6,8 +6,8 @@ use num_integer::Integer; use num_traits::{One, Signed, ToPrimitive, Zero}; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, - TypeProtocol, + PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectPayload, PyObjectPayload2, + PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -240,9 +240,11 @@ 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: Cell::new(0), - iterated_obj: range.clone(), + PyObjectPayload::AnyRustValue { + value: Box::new(PyIteratorValue { + position: Cell::new(0), + iterated_obj: range.clone(), + }), }, vm.ctx.iter_type(), )) @@ -254,14 +256,16 @@ fn range_reversed(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let range = get_value(zelf).reversed(); Ok(PyObject::new( - PyObjectPayload::Iterator { - position: Cell::new(0), - iterated_obj: PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(range), - }, - vm.ctx.range_type(), - ), + PyObjectPayload::AnyRustValue { + value: Box::new(PyIteratorValue { + position: Cell::new(0), + iterated_obj: PyObject::new( + PyObjectPayload::AnyRustValue { + value: Box::new(range), + }, + vm.ctx.range_type(), + ), + }), }, vm.ctx.iter_type(), )) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index f194e20f23..76eca5ec2a 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -13,8 +13,8 @@ use super::objiter; use super::objstr; use super::objtype; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, - TypeProtocol, + PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectPayload, PyObjectPayload2, + PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; @@ -566,9 +566,11 @@ fn set_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let items = get_elements(zelf).values().cloned().collect(); let set_list = vm.ctx.new_list(items); let iter_obj = PyObject::new( - PyObjectPayload::Iterator { - position: Cell::new(0), - iterated_obj: set_list, + PyObjectPayload::AnyRustValue { + value: Box::new(PyIteratorValue { + position: Cell::new(0), + iterated_obj: set_list, + }), }, vm.ctx.iter_type(), ); diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index e2e80d8c81..e583055c30 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -3,8 +3,8 @@ use std::hash::{Hash, Hasher}; use crate::function::PyRef; use crate::pyobject::{ - IdProtocol, OptionalArg, PyContext, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, - PyResult, + IdProtocol, OptionalArg, PyContext, PyIteratorValue, PyObject, PyObjectPayload, + PyObjectPayload2, PyObjectRef, PyResult, }; use crate::vm::{ReprGuard, VirtualMachine}; @@ -127,9 +127,11 @@ impl PyTupleRef { fn iter(self, vm: &mut VirtualMachine) -> PyObjectRef { PyObject::new( - PyObjectPayload::Iterator { - position: Cell::new(0), - iterated_obj: self.into_object(), + PyObjectPayload::AnyRustValue { + value: Box::new(PyIteratorValue { + position: Cell::new(0), + iterated_obj: self.into_object(), + }), }, vm.ctx.iter_type(), ) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index c9c7ed02ac..ab2d116016 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -100,17 +100,6 @@ impl fmt::Display for PyObject { } } -/* - // Idea: implement the iterator trait upon PyObjectRef -impl Iterator for (VirtualMachine, PyObjectRef) { - type Item = char; - - fn next(&mut self) -> Option { - // call method ("_next__") - } -} -*/ - #[derive(Debug)] pub struct PyContext { pub bytes_type: PyObjectRef, @@ -1496,10 +1485,6 @@ into_py_native_func_tuple!((a, A), (b, B), (c, C), (d, D), (e, E)); /// of rust data for a particular python object. Determine the python type /// by using for example the `.typ()` method on a python object. pub enum PyObjectPayload { - Iterator { - position: Cell, - iterated_obj: PyObjectRef, - }, Slice { start: Option, stop: Option, @@ -1524,12 +1509,25 @@ impl Default for PyObjectPayload { } } +// TODO: This is a workaround and shouldn't exist. +// Each iterable type should have its own distinct iterator type. +#[derive(Debug)] +pub struct PyIteratorValue { + pub position: Cell, + pub iterated_obj: PyObjectRef, +} + +impl PyObjectPayload2 for PyIteratorValue { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.iter_type() + } +} + impl fmt::Debug for PyObjectPayload { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { PyObjectPayload::MemoryView { ref obj } => write!(f, "bytes/bytearray {:?}", obj), PyObjectPayload::WeakRef { .. } => write!(f, "weakref"), - PyObjectPayload::Iterator { .. } => write!(f, "iterator"), PyObjectPayload::Slice { .. } => write!(f, "slice"), PyObjectPayload::AnyRustValue { value } => value.fmt(f), } From 776dd805919a5e9a9e552facf263ac220d81b065 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sat, 9 Mar 2019 14:58:33 -0800 Subject: [PATCH 226/380] memoryview --- vm/src/obj/objmemory.rs | 22 +++++++++++++++++++--- vm/src/pyobject.rs | 4 ---- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/vm/src/obj/objmemory.rs b/vm/src/obj/objmemory.rs index 8119f9fee9..988abbed9c 100644 --- a/vm/src/obj/objmemory.rs +++ b/vm/src/obj/objmemory.rs @@ -1,12 +1,28 @@ -use crate::pyobject::{PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyResult, TypeProtocol}; +use crate::pyobject::{ + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, + TypeProtocol, +}; use crate::vm::VirtualMachine; +#[derive(Debug)] +pub struct PyMemoryView { + obj: PyObjectRef, +} + +impl PyObjectPayload2 for PyMemoryView { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.memoryview_type() + } +} + 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( - PyObjectPayload::MemoryView { - obj: bytes_object.clone(), + PyObjectPayload::AnyRustValue { + value: Box::new(PyMemoryView { + obj: bytes_object.clone(), + }), }, cls.clone(), )) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index ab2d116016..5f8d2ea0db 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1490,9 +1490,6 @@ pub enum PyObjectPayload { stop: Option, step: Option, }, - MemoryView { - obj: PyObjectRef, - }, WeakRef { referent: PyObjectWeakRef, }, @@ -1526,7 +1523,6 @@ impl PyObjectPayload2 for PyIteratorValue { impl fmt::Debug for PyObjectPayload { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - PyObjectPayload::MemoryView { ref obj } => write!(f, "bytes/bytearray {:?}", obj), PyObjectPayload::WeakRef { .. } => write!(f, "weakref"), PyObjectPayload::Slice { .. } => write!(f, "slice"), PyObjectPayload::AnyRustValue { value } => value.fmt(f), From e1d728e81fc984494be38b22982c32d816d81372 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sat, 9 Mar 2019 15:10:44 -0800 Subject: [PATCH 227/380] slice --- vm/src/frame.rs | 5 ++- vm/src/obj/objrange.rs | 78 +++++++++++++++++++-------------------- vm/src/obj/objsequence.rs | 50 ++++++++++++------------- vm/src/obj/objslice.rs | 33 +++++++++++++---- vm/src/obj/objstr.rs | 43 ++++++++++----------- vm/src/pyobject.rs | 14 +------ 6 files changed, 115 insertions(+), 108 deletions(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 391ab6fe3d..bb8e7088e0 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -18,6 +18,7 @@ use crate::obj::objdict::PyDict; use crate::obj::objint::PyInt; use crate::obj::objiter; use crate::obj::objlist; +use crate::obj::objslice::PySlice; use crate::obj::objstr; use crate::obj::objtype; use crate::pyobject::{ @@ -303,7 +304,9 @@ impl Frame { let step = if out.len() == 3 { out[2].take() } else { None }; let obj = PyObject::new( - PyObjectPayload::Slice { start, stop, step }, + PyObjectPayload::AnyRustValue { + value: Box::new(PySlice { start, stop, step }), + }, vm.ctx.slice_type(), ); self.push_value(obj); diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index b526cbbb5d..86db5d9146 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -12,6 +12,7 @@ use crate::pyobject::{ use crate::vm::VirtualMachine; use super::objint::{self, PyInt}; +use super::objslice::PySlice; use super::objtype; #[derive(Debug, Clone)] @@ -298,52 +299,51 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; } - match subscript.payload { - PyObjectPayload::Slice { - ref start, - ref stop, - ref step, - } => { - let new_start = if let Some(int) = start { - if let Some(i) = range.get(int) { - i - } else { - range.start.clone() - } + if let Some(PySlice { + ref start, + ref stop, + ref step, + }) = subscript.payload() + { + let new_start = if let Some(int) = start { + if let Some(i) = range.get(int) { + i } else { range.start.clone() - }; - - let new_end = if let Some(int) = stop { - if let Some(i) = range.get(int) { - i - } else { - range.end - } + } + } else { + range.start.clone() + }; + + let new_end = if let Some(int) = stop { + if let Some(i) = range.get(int) { + i } else { range.end - }; + } + } else { + range.end + }; - let new_step = if let Some(int) = step { - int * range.step - } else { - range.step - }; - - Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyRange { - start: new_start, - end: new_end, - step: new_step, - }), - }, - vm.ctx.range_type(), - )) - } + let new_step = if let Some(int) = step { + int * range.step + } else { + range.step + }; - _ => Err(vm.new_type_error("range indices must be integer or slice".to_string())), + return Ok(PyObject::new( + PyObjectPayload::AnyRustValue { + value: Box::new(PyRange { + 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())) } fn range_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index ad68206210..5d7ddaf329 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -11,6 +11,7 @@ use crate::vm::VirtualMachine; use super::objbool; use super::objint::PyInt; use super::objlist::PyList; +use super::objslice::PySlice; use super::objtuple::PyTuple; pub trait PySliceableSequence { @@ -69,8 +70,8 @@ pub trait PySliceableSequence { Self: Sized, { // TODO: we could potentially avoid this copy and use slice - match &slice.payload { - PyObjectPayload::Slice { start, stop, step } => { + match slice.payload() { + Some(PySlice { start, stop, step }) => { 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())) @@ -161,31 +162,30 @@ pub fn get_item( }; } - match &subscript.payload { - PyObjectPayload::Slice { .. } => { - let payload = if sequence.payload::().is_some() { - PyObjectPayload::AnyRustValue { - value: Box::new(PyList::from( - elements.to_vec().get_slice_items(vm, &subscript)?, - )), - } - } else if sequence.payload::().is_some() { - PyObjectPayload::AnyRustValue { - value: Box::new(PyTuple::from( - elements.to_vec().get_slice_items(vm, &subscript)?, - )), - } - } else { - panic!("sequence get_item called for non-sequence") - }; + if subscript.payload::().is_some() { + let payload = if sequence.payload::().is_some() { + PyObjectPayload::AnyRustValue { + value: Box::new(PyList::from( + elements.to_vec().get_slice_items(vm, &subscript)?, + )), + } + } else if sequence.payload::().is_some() { + PyObjectPayload::AnyRustValue { + value: Box::new(PyTuple::from( + elements.to_vec().get_slice_items(vm, &subscript)?, + )), + } + } else { + panic!("sequence get_item called for non-sequence") + }; - Ok(PyObject::new(payload, sequence.typ())) - } - _ => Err(vm.new_type_error(format!( - "TypeError: indexing type {:?} with index {:?} is not supported (yet?)", - sequence, subscript - ))), + return Ok(PyObject::new(payload, sequence.typ())); } + + Err(vm.new_type_error(format!( + "TypeError: indexing type {:?} with index {:?} is not supported (yet?)", + sequence, subscript + ))) } pub fn seq_equal( diff --git a/vm/src/obj/objslice.rs b/vm/src/obj/objslice.rs index 0a81847e2c..45b92b5af6 100644 --- a/vm/src/obj/objslice.rs +++ b/vm/src/obj/objslice.rs @@ -1,10 +1,25 @@ use super::objint; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, + TypeProtocol, }; use crate::vm::VirtualMachine; use num_bigint::BigInt; +#[derive(Debug)] +pub struct PySlice { + // TODO: should be private + pub start: Option, + pub stop: Option, + pub step: Option, +} + +impl PyObjectPayload2 for PySlice { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.slice_type() + } +} + fn slice_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { no_kwargs!(vm, args); let (cls, start, stop, step): ( @@ -40,10 +55,12 @@ fn slice_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } }?; 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)), + PyObjectPayload::AnyRustValue { + value: Box::new(PySlice { + 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(), )) @@ -59,7 +76,7 @@ fn get_property_value(vm: &mut VirtualMachine, value: &Option) -> PyResu 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.payload { + if let Some(PySlice { start, .. }) = &slice.payload() { get_property_value(vm, start) } else { panic!("Slice has incorrect payload."); @@ -68,7 +85,7 @@ fn slice_start(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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.payload { + if let Some(PySlice { stop, .. }) = &slice.payload() { get_property_value(vm, stop) } else { panic!("Slice has incorrect payload."); @@ -77,7 +94,7 @@ fn slice_stop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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.payload { + if let Some(PySlice { step, .. }) = &slice.payload() { get_property_value(vm, step) } else { panic!("Slice has incorrect payload."); diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index ba42740091..03417ca147 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -1,22 +1,22 @@ -use super::objint; -use super::objsequence::PySliceableSequence; -use super::objtype; +use std::hash::{Hash, Hasher}; +use std::ops::Range; +use std::str::FromStr; + +use num_traits::ToPrimitive; +use unicode_segmentation::UnicodeSegmentation; + use crate::format::{FormatParseError, FormatPart, FormatString}; use crate::function::PyRef; use crate::pyobject::{ - IntoPyObject, OptionalArg, PyContext, PyFuncArgs, PyIterable, PyObjectPayload, - PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, + IntoPyObject, OptionalArg, PyContext, PyFuncArgs, PyIterable, PyObjectPayload2, PyObjectRef, + PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; -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; -use self::unicode_segmentation::UnicodeSegmentation; +use super::objint; +use super::objsequence::PySliceableSequence; +use super::objslice::PySlice; +use super::objtype; #[derive(Clone, Debug)] pub struct PyString { @@ -867,17 +867,14 @@ pub fn subscript(vm: &mut VirtualMachine, value: &str, b: PyObjectRef) -> PyResu Err(vm.new_index_error("cannot fit 'int' into an index-sized integer".to_string())) } } + } else if b.payload::().is_some() { + let string = value.to_string().get_slice_items(vm, &b)?; + Ok(vm.new_str(string)) } else { - match b.payload { - PyObjectPayload::Slice { .. } => { - 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?)", - value, b - ), - } + panic!( + "TypeError: indexing type {:?} with index {:?} is not supported (yet?)", + value, b + ) } } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 5f8d2ea0db..43895ccc1a 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1485,17 +1485,8 @@ into_py_native_func_tuple!((a, A), (b, B), (c, C), (d, D), (e, E)); /// of rust data for a particular python object. Determine the python type /// by using for example the `.typ()` method on a python object. pub enum PyObjectPayload { - Slice { - start: Option, - stop: Option, - step: Option, - }, - WeakRef { - referent: PyObjectWeakRef, - }, - AnyRustValue { - value: Box, - }, + WeakRef { referent: PyObjectWeakRef }, + AnyRustValue { value: Box }, } impl Default for PyObjectPayload { @@ -1524,7 +1515,6 @@ impl fmt::Debug for PyObjectPayload { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { PyObjectPayload::WeakRef { .. } => write!(f, "weakref"), - PyObjectPayload::Slice { .. } => write!(f, "slice"), PyObjectPayload::AnyRustValue { value } => value.fmt(f), } } From cd3ca16693f945609fbb77eca49a997d60279b5e Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sat, 9 Mar 2019 16:45:19 -0800 Subject: [PATCH 228/380] Allow functions that don't take any args --- vm/src/pyobject.rs | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 14f7b6e7b1..2986c628c2 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1379,6 +1379,20 @@ where } } +// For functions that accept no arguments. Implemented explicitly instead of via +// macro below to avoid unused warnings. +impl FromArgs for () { + fn from_args( + _vm: &mut VirtualMachine, + _args: &mut iter::Peekable, + ) -> Result + where + I: Iterator, + { + Ok(()) + } +} + // A tuple of types that each implement `FromArgs` represents a sequence of // arguments that can be bound and passed to a built-in function. // @@ -1465,25 +1479,26 @@ impl IntoPyNativeFunc for PyNativeFunc { // // Note that this could be done without a macro - it is simply to avoid repetition. macro_rules! into_py_native_func_tuple { - ($(($n:tt, $T:ident)),+) => { - impl IntoPyNativeFunc<($($T,)+), R> for F + ($(($n:tt, $T:ident)),*) => { + impl IntoPyNativeFunc<($($T,)*), R> for F where - F: Fn($($T,)+ &mut VirtualMachine) -> R + 'static, - $($T: FromArgs,)+ - ($($T,)+): FromArgs, + F: Fn($($T,)* &mut VirtualMachine) -> R + 'static, + $($T: FromArgs,)* + ($($T,)*): FromArgs, R: IntoPyObject, { fn into_func(self) -> PyNativeFunc { Box::new(move |vm, args| { - let ($($n,)+) = args.bind::<($($T,)+)>(vm)?; + let ($($n,)*) = args.bind::<($($T,)*)>(vm)?; - (self)($($n,)+ vm).into_pyobject(&vm.ctx) + (self)($($n,)* vm).into_pyobject(&vm.ctx) }) } } }; } +into_py_native_func_tuple!(); into_py_native_func_tuple!((a, A)); into_py_native_func_tuple!((a, A), (b, B)); into_py_native_func_tuple!((a, A), (b, B), (c, C)); From 8f6257b37f071d670e4d3750afd1d7b63ddf5149 Mon Sep 17 00:00:00 2001 From: Roel Bruggink Date: Sun, 10 Mar 2019 03:11:28 +0100 Subject: [PATCH 229/380] Make open a context manager --- tests/snippets/builtin_open.py | 4 ++++ vm/src/stdlib/io.rs | 25 ++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/tests/snippets/builtin_open.py b/tests/snippets/builtin_open.py index 55eafcd6ce..41bcf2b800 100644 --- a/tests/snippets/builtin_open.py +++ b/tests/snippets/builtin_open.py @@ -4,3 +4,7 @@ assert 'RustPython' in fd.read() assert_raises(FileNotFoundError, lambda: open('DoesNotExist')) + +# Use open as a context manager +with open('README.md') as fp: + fp.read() diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index f35d5eb041..9d5600498a 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -60,6 +60,26 @@ fn bytes_io_getvalue(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.get_none()) } +fn io_base_cm_enter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(instance, None)]); + Ok(instance.clone()) +} + +fn io_base_cm_exit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + // The context manager protocol requires these, but we don't use them + required = [ + (_instance, None), + (_exception_type, None), + (_exception_value, None), + (_traceback, None) + ] + ); + Ok(vm.get_none()) +} + fn buffered_io_base_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(buffered, None), (raw, None)]); vm.ctx.set_attr(&buffered, "raw", raw.clone()); @@ -347,7 +367,10 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn mk_module(ctx: &PyContext) -> PyObjectRef { //IOBase the abstract base class of the IO Module - let io_base = py_class!(ctx, "IOBase", ctx.object(), {}); + let io_base = py_class!(ctx, "IOBase", ctx.object(), { + "__enter__" => ctx.new_rustfunc(io_base_cm_enter), + "__exit__" => ctx.new_rustfunc(io_base_cm_exit) + }); // IOBase Subclasses let raw_io_base = py_class!(ctx, "RawIOBase", ctx.object(), {}); From f6765cf16e2e04a370d258c1a60bfa06ef5417af Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sun, 10 Mar 2019 00:34:21 -0800 Subject: [PATCH 230/380] less explicit returns --- vm/src/obj/objrange.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 86db5d9146..f249a1f486 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -292,14 +292,12 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let range = get_value(zelf); if let Some(i) = subscript.payload::() { - return if let Some(int) = range.get(i.value.clone()) { + if let Some(int) = range.get(i.value.clone()) { Ok(vm.ctx.new_int(int)) } else { Err(vm.new_index_error("range object index out of range".to_string())) - }; - } - - if let Some(PySlice { + } + } else if let Some(PySlice { ref start, ref stop, ref step, @@ -331,7 +329,7 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { range.step }; - return Ok(PyObject::new( + Ok(PyObject::new( PyObjectPayload::AnyRustValue { value: Box::new(PyRange { start: new_start, @@ -340,10 +338,10 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }), }, vm.ctx.range_type(), - )); + )) + } else { + Err(vm.new_type_error("range indices must be integer or slice".to_string())) } - - Err(vm.new_type_error("range indices must be integer or slice".to_string())) } fn range_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From 0e23e706c903be3528e07da11a4399a1a47216c5 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Sat, 9 Mar 2019 06:44:29 +0000 Subject: [PATCH 231/380] Restructure scope to distinguish between locals and globals. --- src/main.rs | 10 ++- vm/src/builtins.rs | 16 ++--- vm/src/eval.rs | 6 +- vm/src/frame.rs | 136 +++++++++++++++++++++++++++++---------- vm/src/import.rs | 12 ++-- vm/src/macros.rs | 2 +- vm/src/obj/objdict.rs | 4 +- vm/src/obj/objframe.rs | 2 +- vm/src/obj/objmodule.rs | 8 +-- vm/src/pyobject.rs | 47 +++++++------- vm/src/stdlib/os.rs | 2 +- vm/src/stdlib/weakref.rs | 2 +- vm/src/vm.rs | 26 +++----- wasm/lib/src/vm_class.rs | 17 ++--- 14 files changed, 172 insertions(+), 118 deletions(-) diff --git a/src/main.rs b/src/main.rs index 1dae97a524..e7951b437c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,7 @@ use rustpython_parser::error::ParseError; use rustpython_vm::{ compile, error::CompileError, - frame::ScopeRef, + frame::Scope, import, obj::objstr, print_exception, @@ -82,8 +82,7 @@ fn _run_string(vm: &mut VirtualMachine, source: &str, source_path: String) -> Py vm.new_exception(syntax_error, err.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 + let vars = Scope::new(None, vm.ctx.new_dict()); // Keep track of local variables vm.run_code_obj(code_obj, vars) } @@ -121,7 +120,7 @@ fn run_script(vm: &mut VirtualMachine, script_file: &str) -> PyResult { } } -fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: ScopeRef) -> Result<(), CompileError> { +fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: Scope) -> Result<(), CompileError> { match compile::compile( source, &compile::Mode::Single, @@ -165,8 +164,7 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult { "Welcome to the magnificent Rust Python {} interpreter", crate_version!() ); - let builtins = vm.get_builtin_scope(); - let vars = vm.context().new_scope(Some(builtins)); // Keep track of local variables + let vars = Scope::new(None, vm.ctx.new_dict()); // Read a single line: let mut input = String::new(); diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 8f4347734e..38e4975ff8 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -14,7 +14,7 @@ use crate::obj::objiter; use crate::obj::objstr; use crate::obj::objtype; -use crate::frame::{Scope, ScopeRef}; +use crate::frame::Scope; use crate::pyobject::{ AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol, }; @@ -245,7 +245,7 @@ fn make_scope( vm: &mut VirtualMachine, globals: Option<&PyObjectRef>, locals: Option<&PyObjectRef>, -) -> PyResult { +) -> PyResult { let dict_type = vm.ctx.dict_type(); let globals = match globals { Some(arg) => { @@ -269,16 +269,16 @@ fn make_scope( }; let current_scope = vm.current_scope(); - let parent = match globals { - Some(dict) => Some(Scope::new(dict.clone(), Some(vm.get_builtin_scope()))), - None => current_scope.parent.clone(), + let globals = match globals { + Some(dict) => dict.clone(), + None => current_scope.globals.clone(), }; let locals = match locals { - Some(dict) => dict.clone(), - None => current_scope.locals.clone(), + Some(dict) => Some(dict.clone()), + None => current_scope.get_only_locals(), }; - Ok(Scope::new(locals, parent)) + Ok(Scope::new(locals, globals)) } fn builtin_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/eval.rs b/vm/src/eval.rs index 98e9c54717..4026f728eb 100644 --- a/vm/src/eval.rs +++ b/vm/src/eval.rs @@ -3,11 +3,11 @@ extern crate rustpython_parser; use std::error::Error; use crate::compile; -use crate::frame::ScopeRef; +use crate::frame::Scope; use crate::pyobject::PyResult; use crate::vm::VirtualMachine; -pub fn eval(vm: &mut VirtualMachine, source: &str, scope: ScopeRef, source_path: &str) -> PyResult { +pub fn eval(vm: &mut VirtualMachine, source: &str, scope: Scope, source_path: &str) -> PyResult { match compile::compile( source, &compile::Mode::Eval, @@ -34,7 +34,7 @@ mod tests { fn test_print_42() { let source = String::from("print('Hello world')\n"); let mut vm = VirtualMachine::new(); - let vars = vm.context().new_scope(None); + let vars = Scope::new(None, vm.ctx.new_dict()); let _result = eval(&mut vm, &source, vars, ""); // TODO: check result? diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 8748175a71..1b79e2457d 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -20,8 +20,8 @@ use crate::obj::objlist; use crate::obj::objstr; use crate::obj::objtype; use crate::pyobject::{ - DictProtocol, IdProtocol, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, - TypeProtocol, + DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, + PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -30,16 +30,97 @@ use crate::vm::VirtualMachine; * When a name is looked up, it is check in its scope. */ #[derive(Debug)] +pub struct Locals { + dict: PyObjectRef, + parent: Option>, +} + +#[derive(Debug, Clone)] pub struct Scope { - pub locals: PyObjectRef, // Variables + pub locals: Option>, // Variables + pub globals: PyObjectRef, // TODO: pub locals: RefCell, // Variables - pub parent: Option>, // Parent scope + // pub parent: Option>, // Parent scope } -pub type ScopeRef = Rc; impl Scope { - pub fn new(locals: PyObjectRef, parent: Option) -> ScopeRef { - Rc::new(Scope { locals, parent }) + pub fn new(locals: Option, globals: PyObjectRef) -> Scope { + let locals = match locals { + Some(dict) => Some(Rc::new(Locals { + dict: dict, + parent: None, + })), + None => None, + }; + Scope { locals, globals } + } + + pub fn get_locals(&self) -> PyObjectRef { + match self.locals { + Some(ref locals) => locals.dict.clone(), + None => self.globals.clone(), + } + } + + pub fn get_only_locals(&self) -> Option { + match self.locals { + Some(ref locals) => Some(locals.dict.clone()), + None => None, + } + } + + pub fn child_scope_with_locals(&self, locals: PyObjectRef) -> Scope { + Scope { + locals: Some(Rc::new(Locals { + dict: locals, + parent: self.locals.clone(), + })), + globals: self.globals.clone(), + } + } + + pub fn child_scope(&self, ctx: &PyContext) -> Scope { + self.child_scope_with_locals(ctx.new_dict()) + } +} + +// TODO: Merge with existing Attribute protocol. +pub trait AttributeProtocol2 { + fn get_attr(&self, vm: &VirtualMachine, name: &str) -> Option; + fn set_attr(&self, vm: &VirtualMachine, name: &str, value: PyObjectRef); + fn del_attr(&self, vm: &VirtualMachine, name: &str); +} + +impl AttributeProtocol2 for Scope { + fn get_attr(&self, vm: &VirtualMachine, name: &str) -> Option { + // Lookup name in scope and put it onto the stack! + let mut locals = self.locals.clone(); + loop { + match locals { + Some(new_locals) => { + if let Some(value) = new_locals.dict.get_item(name) { + return Some(value); + } else { + locals = new_locals.parent.clone() + } + } + None => break, + } + } + + if let Some(value) = self.globals.get_item(name) { + return Some(value); + } + + vm.builtins.get_item(name) + } + + fn set_attr(&self, vm: &VirtualMachine, key: &str, value: PyObjectRef) { + self.get_locals().set_item(&vm.ctx, key, value) + } + + fn del_attr(&self, _vm: &VirtualMachine, key: &str) { + self.get_locals().del_item(key) } } @@ -71,7 +152,7 @@ pub struct Frame { // We need 1 stack per frame stack: RefCell>, // The main data frame of the stack machine blocks: RefCell>, // Block frames, for controlling loops and exceptions - pub scope: ScopeRef, // Variables + pub scope: Scope, // Variables pub lasti: RefCell, // index of last instruction ran } @@ -85,7 +166,7 @@ pub enum ExecutionResult { pub type FrameResult = Result, PyObjectRef>; impl Frame { - pub fn new(code: PyObjectRef, scope: ScopeRef) -> Frame { + pub fn new(code: PyObjectRef, scope: Scope) -> Frame { //populate the globals and locals //TODO: This is wrong, check https://github.com/nedbat/byterun/blob/31e6c4a8212c35b5157919abff43a7daa0f377c6/byterun/pyvm2.py#L95 /* @@ -741,9 +822,7 @@ impl Frame { let obj = import_module(vm, current_path, module)?; for (k, v) in obj.get_key_value_pairs().iter() { - self.scope - .locals - .set_item(&vm.ctx, &objstr::get_value(k), v.clone()); + self.scope.set_attr(&vm, &objstr::get_value(k), v.clone()); } Ok(None) } @@ -865,35 +944,26 @@ impl Frame { fn store_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult { let obj = self.pop_value(); - self.scope.locals.set_item(&vm.ctx, name, obj); + self.scope.set_attr(&vm, name, obj); Ok(None) } fn delete_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult { - let name = vm.ctx.new_str(name.to_string()); - vm.call_method(&self.scope.locals, "__delitem__", vec![name])?; + self.scope.del_attr(vm, name); Ok(None) } fn load_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult { - // Lookup name in scope and put it onto the stack! - let mut scope = self.scope.clone(); - loop { - if scope.locals.contains_key(name) { - let obj = scope.locals.get_item(name).unwrap(); - self.push_value(obj); - return Ok(None); + match self.scope.get_attr(&vm, name) { + Some(value) => { + self.push_value(value); + Ok(None) } - match &scope.parent { - Some(parent_scope) => { - scope = parent_scope.clone(); - } - None => { - let name_error_type = vm.ctx.exceptions.name_error.clone(); - let msg = format!("name '{}' is not defined", name); - let name_error = vm.new_exception(name_error_type, msg); - return Err(name_error); - } + None => { + let name_error_type = vm.ctx.exceptions.name_error.clone(); + let msg = format!("name '{}' is not defined", name); + let name_error = vm.new_exception(name_error_type, msg); + Err(name_error) } } } @@ -1144,7 +1214,7 @@ impl fmt::Debug for Frame { .map(|elem| format!("\n > {:?}", elem)) .collect::>() .join(""); - let local_str = match self.scope.locals.payload::() { + let local_str = match self.scope.get_locals().payload::() { Some(dict) => objdict::get_key_value_pairs_from_content(&dict.entries.borrow()) .iter() .map(|elem| format!("\n {:?} = {:?}", elem.0, elem.1)) diff --git a/vm/src/import.rs b/vm/src/import.rs index 3fbcf0f2fd..3031ed38b7 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -6,6 +6,7 @@ use std::error::Error; use std::path::PathBuf; use crate::compile; +use crate::frame::Scope; use crate::obj::{objsequence, objstr}; use crate::pyobject::{AttributeProtocol, DictProtocol, PyResult}; use crate::util; @@ -41,13 +42,10 @@ fn import_uncached_module( })?; // trace!("Code object: {:?}", code_obj); - let builtins = vm.get_builtin_scope(); - let scope = vm.ctx.new_scope(Some(builtins)); - scope - .locals - .set_item(&vm.ctx, "__name__", vm.new_str(module.to_string())); - vm.run_code_obj(code_obj, scope.clone())?; - Ok(vm.ctx.new_module(module, scope)) + let attrs = vm.ctx.new_dict(); + attrs.set_item(&vm.ctx, "__name__", vm.new_str(module.to_string())); + vm.run_code_obj(code_obj, Scope::new(None, attrs.clone()))?; + Ok(vm.ctx.new_module(module, attrs)) } pub fn import_module( diff --git a/vm/src/macros.rs b/vm/src/macros.rs index 861e1db42a..788914b519 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -116,7 +116,7 @@ macro_rules! no_kwargs { macro_rules! py_module { ( $ctx:expr, $module_name:expr, { $($name:expr => $value:expr),* $(,)* }) => { { - let py_mod = $ctx.new_module($module_name, $ctx.new_scope(None)); + let py_mod = $ctx.new_module($module_name, $ctx.new_dict()); $( $ctx.set_attr(&py_mod, $name, $value); )* diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index df85e75791..4ef653896b 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -1,3 +1,4 @@ +use crate::function::PyRef; use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::ops::{Deref, DerefMut}; @@ -18,6 +19,7 @@ pub struct PyDict { // TODO: should be private pub entries: RefCell, } +pub type PyDictRef = PyRef; impl PyObjectPayload2 for PyDict { fn required_type(ctx: &PyContext) -> PyObjectRef { @@ -29,7 +31,7 @@ pub fn get_elements<'a>(obj: &'a PyObjectRef) -> impl Deref().unwrap().entries.borrow() } -fn get_mut_elements<'a>(obj: &'a PyObjectRef) -> impl DerefMut + 'a { +pub fn get_mut_elements<'a>(obj: &'a PyObjectRef) -> impl DerefMut + 'a { obj.payload::().unwrap().entries.borrow_mut() } diff --git a/vm/src/obj/objframe.rs b/vm/src/obj/objframe.rs index 37acd33385..2064c9e6b8 100644 --- a/vm/src/obj/objframe.rs +++ b/vm/src/obj/objframe.rs @@ -30,7 +30,7 @@ fn frame_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn frame_flocals(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(frame, Some(vm.ctx.frame_type()))]); let frame = get_value(frame); - Ok(frame.scope.locals.clone()) + Ok(frame.scope.get_locals().clone()) } fn frame_fcode(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objmodule.rs b/vm/src/obj/objmodule.rs index bbd109cce5..4f7a641b99 100644 --- a/vm/src/obj/objmodule.rs +++ b/vm/src/obj/objmodule.rs @@ -1,4 +1,3 @@ -use crate::frame::ScopeRef; use crate::function::PyRef; use crate::pyobject::{DictProtocol, PyContext, PyObjectPayload2, PyObjectRef, PyResult}; use crate::vm::VirtualMachine; @@ -6,7 +5,7 @@ use crate::vm::VirtualMachine; #[derive(Clone, Debug)] pub struct PyModule { pub name: String, - pub scope: ScopeRef, + pub dict: PyObjectRef, } pub type PyModuleRef = PyRef; @@ -17,10 +16,9 @@ impl PyObjectPayload2 for PyModule { } impl PyModuleRef { - fn dir(self, vm: &mut VirtualMachine) -> PyResult { + fn dir(self: PyModuleRef, vm: &mut VirtualMachine) -> PyResult { let keys = self - .scope - .locals + .dict .get_key_value_pairs() .iter() .map(|(k, _v)| k.clone()) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index c123f8bd1c..a2cde58e23 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -12,7 +12,7 @@ use num_traits::{One, Zero}; use crate::bytecode; use crate::exceptions; -use crate::frame::{Frame, Scope, ScopeRef}; +use crate::frame::{Frame, Scope}; use crate::obj::objbool; use crate::obj::objbytearray; use crate::obj::objbytes; @@ -593,17 +593,12 @@ impl PyContext { objtype::new(self.type_type(), name, vec![base], PyAttributes::new()).unwrap() } - pub fn new_scope(&self, parent: Option) -> ScopeRef { - let locals = self.new_dict(); - Rc::new(Scope { locals, parent }) - } - - pub fn new_module(&self, name: &str, scope: ScopeRef) -> PyObjectRef { + pub fn new_module(&self, name: &str, dict: PyObjectRef) -> PyObjectRef { PyObject::new( PyObjectPayload::AnyRustValue { value: Box::new(PyModule { name: name.to_string(), - scope, + dict, }), }, self.module_type.clone(), @@ -622,7 +617,7 @@ impl PyContext { ) } - pub fn new_frame(&self, code: PyObjectRef, scope: ScopeRef) -> PyObjectRef { + pub fn new_frame(&self, code: PyObjectRef, scope: Scope) -> PyObjectRef { PyObject::new( PyObjectPayload::Frame { frame: Frame::new(code, scope), @@ -653,7 +648,7 @@ impl PyContext { pub fn new_function( &self, code_obj: PyObjectRef, - scope: ScopeRef, + scope: Scope, defaults: PyObjectRef, ) -> PyObjectRef { PyObject::new( @@ -732,8 +727,8 @@ impl PyContext { } pub fn set_attr(&self, obj: &PyObjectRef, attr_name: &str, value: PyObjectRef) { - if let Some(PyModule { ref scope, .. }) = obj.payload::() { - scope.locals.set_item(self, attr_name, value) + if let Some(PyModule { ref dict, .. }) = obj.payload::() { + dict.set_item(self, attr_name, value) } else if let Some(ref dict) = obj.dict { dict.borrow_mut().insert(attr_name.to_string(), value); } else { @@ -852,8 +847,8 @@ impl AttributeProtocol for PyObjectRef { return None; } - if let Some(PyModule { ref scope, .. }) = self.payload::() { - return scope.locals.get_item(attr_name); + if let Some(PyModule { ref dict, .. }) = self.payload::() { + return dict.get_item(attr_name); } if let Some(ref dict) = self.dict { @@ -869,8 +864,8 @@ impl AttributeProtocol for PyObjectRef { || mro.iter().any(|d| class_has_item(d, attr_name)); } - if let Some(PyModule { ref scope, .. }) = self.payload::() { - return scope.locals.contains_key(attr_name); + if let Some(PyModule { ref dict, .. }) = self.payload::() { + return dict.contains_key(attr_name); } if let Some(ref dict) = self.dict { @@ -886,6 +881,7 @@ pub trait DictProtocol { 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); + fn del_item(&self, key: &str); } impl DictProtocol for PyObjectRef { @@ -900,8 +896,8 @@ impl DictProtocol for PyObjectRef { fn get_item(&self, k: &str) -> Option { if let Some(dict) = self.payload::() { objdict::content_get_key_str(&dict.entries.borrow(), k) - } else if let Some(PyModule { ref scope, .. }) = self.payload::() { - scope.locals.get_item(k) + } else if let Some(PyModule { ref dict, .. }) = self.payload::() { + dict.get_item(k) } else { panic!("TODO {:?}", k) } @@ -910,8 +906,8 @@ impl DictProtocol for PyObjectRef { fn get_key_value_pairs(&self) -> Vec<(PyObjectRef, PyObjectRef)> { if let Some(_) = self.payload::() { objdict::get_key_value_pairs(self) - } else if let Some(PyModule { ref scope, .. }) = self.payload::() { - scope.locals.get_key_value_pairs() + } else if let Some(PyModule { ref dict, .. }) = self.payload::() { + dict.get_key_value_pairs() } else { panic!("TODO") } @@ -922,12 +918,17 @@ impl DictProtocol for PyObjectRef { if let Some(dict) = self.payload::() { let key = ctx.new_str(key.to_string()); objdict::set_item_in_content(&mut dict.entries.borrow_mut(), &key, &v); - } else if let Some(PyModule { ref scope, .. }) = self.payload::() { - scope.locals.set_item(ctx, key, v); + } else if let Some(PyModule { ref dict, .. }) = self.payload::() { + dict.set_item(ctx, key, v); } else { panic!("TODO {:?}", self); } } + + fn del_item(&self, key: &str) { + let mut elements = objdict::get_mut_elements(self); + elements.remove(key).unwrap(); + } } pub trait BufferProtocol { @@ -1512,7 +1513,7 @@ pub enum PyObjectPayload { }, Function { code: PyObjectRef, - scope: ScopeRef, + scope: Scope, defaults: PyObjectRef, }, Generator { diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index ef45d2d84a..187c2e414a 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -119,7 +119,7 @@ fn os_error(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn mk_module(ctx: &PyContext) -> PyObjectRef { - let py_mod = ctx.new_module(&"os".to_string(), ctx.new_scope(None)); + let py_mod = ctx.new_module(&"os".to_string(), ctx.new_dict()); 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)); diff --git a/vm/src/stdlib/weakref.rs b/vm/src/stdlib/weakref.rs index 5b67f1664e..5eae53b100 100644 --- a/vm/src/stdlib/weakref.rs +++ b/vm/src/stdlib/weakref.rs @@ -13,7 +13,7 @@ use crate::VirtualMachine; use std::rc::Rc; pub fn mk_module(ctx: &PyContext) -> PyObjectRef { - let py_mod = ctx.new_module("_weakref", ctx.new_scope(None)); + let py_mod = ctx.new_module("_weakref", ctx.new_dict()); 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/vm.rs b/vm/src/vm.rs index bf5ce74332..caf88df186 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -14,14 +14,13 @@ use std::sync::{Mutex, MutexGuard}; use crate::builtins; use crate::bytecode; use crate::frame::ExecutionResult; -use crate::frame::{Scope, ScopeRef}; +use crate::frame::Scope; use crate::obj::objbool; use crate::obj::objcode; use crate::obj::objframe; use crate::obj::objgenerator; use crate::obj::objiter; use crate::obj::objlist::PyList; -use crate::obj::objmodule::PyModule; use crate::obj::objsequence; use crate::obj::objstr; use crate::obj::objtuple::PyTuple; @@ -73,7 +72,7 @@ impl VirtualMachine { } } - pub fn run_code_obj(&mut self, code: PyObjectRef, scope: ScopeRef) -> PyResult { + pub fn run_code_obj(&mut self, code: PyObjectRef, scope: Scope) -> PyResult { let frame = self.ctx.new_frame(code, scope); self.run_frame_full(frame) } @@ -93,7 +92,7 @@ impl VirtualMachine { result } - pub fn current_scope(&self) -> &ScopeRef { + pub fn current_scope(&self) -> &Scope { let current_frame = &self.frames[self.frames.len() - 1]; let frame = objframe::get_value(current_frame); &frame.scope @@ -204,19 +203,13 @@ impl VirtualMachine { } pub fn get_locals(&self) -> PyObjectRef { - let scope = self.current_scope(); - scope.locals.clone() + self.current_scope().get_locals().clone() } pub fn context(&self) -> &PyContext { &self.ctx } - pub fn get_builtin_scope(&self) -> ScopeRef { - let PyModule { ref scope, .. } = self.builtins.payload::().unwrap(); - scope.clone() - } - // Container of the virtual machine state: pub fn to_str(&mut self, obj: &PyObjectRef) -> PyResult { self.call_method(&obj, "__str__", vec![]) @@ -311,13 +304,13 @@ impl VirtualMachine { fn invoke_python_function( &mut self, code: &PyObjectRef, - scope: &ScopeRef, + scope: &Scope, defaults: &PyObjectRef, args: PyFuncArgs, ) -> PyResult { let code_object = objcode::get_value(code); - let scope = self.ctx.new_scope(Some(scope.clone())); - self.fill_locals_from_args(&code_object, &scope.locals, args, defaults)?; + let scope = scope.child_scope(&self.ctx); + self.fill_locals_from_args(&code_object, &scope.get_locals(), args, defaults)?; // Construct frame: let frame = self.ctx.new_frame(code.clone(), scope); @@ -337,10 +330,7 @@ impl VirtualMachine { defaults: _defaults, } = &function.payload { - let scope = Rc::new(Scope { - locals, - parent: Some(scope.clone()), - }); + let scope = scope.child_scope_with_locals(locals); let frame = self.ctx.new_frame(code.clone(), scope); return self.run_frame_full(frame); } diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index 38c27892f1..d515dc4561 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -4,8 +4,8 @@ use crate::wasm_builtins; use js_sys::{Object, SyntaxError, TypeError}; use rustpython_vm::{ compile, - frame::ScopeRef, - pyobject::{DictProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult}, + frame::{AttributeProtocol2, Scope}, + pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult}, VirtualMachine, }; use std::cell::RefCell; @@ -19,7 +19,7 @@ impl HeldRcInner for T {} pub(crate) struct StoredVirtualMachine { pub vm: VirtualMachine, - pub scope: ScopeRef, + pub scope: Scope, /// you can put a Rc in here, keep it as a Weak, and it'll be held only for /// as long as the StoredVM is alive held_rcs: Vec>, @@ -28,8 +28,7 @@ pub(crate) struct StoredVirtualMachine { impl StoredVirtualMachine { fn new(id: String, inject_browser_module: bool) -> StoredVirtualMachine { let mut vm = VirtualMachine::new(); - let builtin = vm.get_builtin_scope(); - let scope = vm.context().new_scope(Some(builtin)); + let scope = Scope::new(None, vm.ctx.new_dict()); if inject_browser_module { setup_browser_module(&mut vm); } @@ -259,7 +258,7 @@ impl WASMVirtualMachine { .. }| { let value = convert::js_to_py(vm, value); - scope.locals.set_item(&vm.ctx, &name, value); + scope.set_attr(&vm, &name, value); }, ) } @@ -299,9 +298,7 @@ impl WASMVirtualMachine { ) .into()); }; - scope - .locals - .set_item(&vm.ctx, "print", vm.ctx.new_rustfunc(print_fn)); + scope.set_attr(&vm, "print", vm.ctx.new_rustfunc(print_fn)); Ok(()) }, )? @@ -320,7 +317,7 @@ impl WASMVirtualMachine { let mod_name = name.clone(); let stdlib_init_fn = move |ctx: &PyContext| { - let py_mod = ctx.new_module(&name, ctx.new_scope(None)); + let py_mod = ctx.new_module(&name, ctx.new_dict()); for (key, value) in module_items.clone() { ctx.set_attr(&py_mod, &key, value); } From 6ad528c15c5c051d603bc211614156c832380de4 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Sat, 9 Mar 2019 07:00:40 +0000 Subject: [PATCH 232/380] Reintroduce new_scope for the common case of an empty scope. --- src/main.rs | 4 ++-- vm/src/eval.rs | 2 +- vm/src/pyobject.rs | 4 ++++ wasm/lib/src/vm_class.rs | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index e7951b437c..4be5c9ece6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -82,7 +82,7 @@ fn _run_string(vm: &mut VirtualMachine, source: &str, source_path: String) -> Py vm.new_exception(syntax_error, err.to_string()) })?; // trace!("Code object: {:?}", code_obj.borrow()); - let vars = Scope::new(None, vm.ctx.new_dict()); // Keep track of local variables + let vars = vm.ctx.new_scope(); // Keep track of local variables vm.run_code_obj(code_obj, vars) } @@ -164,7 +164,7 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult { "Welcome to the magnificent Rust Python {} interpreter", crate_version!() ); - let vars = Scope::new(None, vm.ctx.new_dict()); + let vars = vm.ctx.new_scope(); // Read a single line: let mut input = String::new(); diff --git a/vm/src/eval.rs b/vm/src/eval.rs index 4026f728eb..7f44cde89b 100644 --- a/vm/src/eval.rs +++ b/vm/src/eval.rs @@ -34,7 +34,7 @@ mod tests { fn test_print_42() { let source = String::from("print('Hello world')\n"); let mut vm = VirtualMachine::new(); - let vars = Scope::new(None, vm.ctx.new_dict()); + let vars = vm.ctx.new_scope(); let _result = eval(&mut vm, &source, vars, ""); // TODO: check result? diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index a2cde58e23..a723ac7a56 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -593,6 +593,10 @@ impl PyContext { objtype::new(self.type_type(), name, vec![base], PyAttributes::new()).unwrap() } + pub fn new_scope(&self) -> Scope { + Scope::new(None, self.new_dict()) + } + pub fn new_module(&self, name: &str, dict: PyObjectRef) -> PyObjectRef { PyObject::new( PyObjectPayload::AnyRustValue { diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index d515dc4561..2b76214fca 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -28,7 +28,7 @@ pub(crate) struct StoredVirtualMachine { impl StoredVirtualMachine { fn new(id: String, inject_browser_module: bool) -> StoredVirtualMachine { let mut vm = VirtualMachine::new(); - let scope = Scope::new(None, vm.ctx.new_dict()); + let scope = vm.ctx.new_scope(); if inject_browser_module { setup_browser_module(&mut vm); } From ddf4f55af77465b36b2c66618b9e543e7b22bd5d Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Sat, 9 Mar 2019 07:10:20 +0000 Subject: [PATCH 233/380] Enable tests that now pass thanks to new scope scheme. --- tests/snippets/test_exec.py | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/tests/snippets/test_exec.py b/tests/snippets/test_exec.py index 37ba33ff13..d6fae63f48 100644 --- a/tests/snippets/test_exec.py +++ b/tests/snippets/test_exec.py @@ -13,26 +13,21 @@ exec("assert max(1, 5, square(5)) == 25", None) -# -# These doesn't work yet: -# # Local environment shouldn't replace global environment: -# -# exec("assert max(1, 5, square(5)) == 25", None, {}) -# +exec("assert max(1, 5, square(5)) == 25", None, {}) + # Closures aren't available if local scope is replaced: -# -# def g(): -# seven = "seven" -# def f(): -# try: -# exec("seven", None, {}) -# except NameError: -# pass -# else: -# raise NameError("seven shouldn't be in scope") -# f() -# g() +def g(): + seven = "seven" + def f(): + try: + exec("seven", None, {}) + except NameError: + pass + else: + raise NameError("seven shouldn't be in scope") + f() +g() try: exec("", 1) From 372f09aaa3cf9848afd04a1ebc54378412414866 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Sat, 9 Mar 2019 08:29:03 +0000 Subject: [PATCH 234/380] Abstract rc linked list so we can just use a loop. --- vm/src/frame.rs | 86 +++++++++++++++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 31 deletions(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 1b79e2457d..c821abdaac 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -30,51 +30,84 @@ use crate::vm::VirtualMachine; * When a name is looked up, it is check in its scope. */ #[derive(Debug)] -pub struct Locals { - dict: PyObjectRef, - parent: Option>, +struct RcListNode { + elem: T, + next: Option>>, +} + +#[derive(Debug, Clone)] +struct RcList { + head: Option>>, +} + +struct Iter<'a, T: 'a> { + next: Option<&'a RcListNode>, +} + +impl RcList { + pub fn new() -> Self { + RcList { head: None } + } + + pub fn insert(self, elem: T) -> Self { + RcList { + head: Some(Rc::new(RcListNode { + elem, + next: self.head, + })), + } + } + + pub fn iter(&self) -> Iter { + Iter { + next: self.head.as_ref().map(|node| &**node), + } + } +} + +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + self.next.map(|node| { + self.next = node.next.as_ref().map(|node| &**node); + &node.elem + }) + } } #[derive(Debug, Clone)] pub struct Scope { - pub locals: Option>, // Variables + locals: RcList, pub globals: PyObjectRef, - // TODO: pub locals: RefCell, // Variables - // pub parent: Option>, // Parent scope } impl Scope { pub fn new(locals: Option, globals: PyObjectRef) -> Scope { let locals = match locals { - Some(dict) => Some(Rc::new(Locals { - dict: dict, - parent: None, - })), - None => None, + Some(dict) => RcList::new().insert(dict), + None => RcList::new(), }; Scope { locals, globals } } pub fn get_locals(&self) -> PyObjectRef { - match self.locals { - Some(ref locals) => locals.dict.clone(), + match self.locals.iter().next() { + Some(dict) => dict.clone(), None => self.globals.clone(), } } pub fn get_only_locals(&self) -> Option { - match self.locals { - Some(ref locals) => Some(locals.dict.clone()), + match self.locals.iter().next() { + Some(dict) => Some(dict.clone()), None => None, } } pub fn child_scope_with_locals(&self, locals: PyObjectRef) -> Scope { Scope { - locals: Some(Rc::new(Locals { - dict: locals, - parent: self.locals.clone(), - })), + locals: self.locals.clone().insert(locals), globals: self.globals.clone(), } } @@ -93,18 +126,9 @@ pub trait AttributeProtocol2 { impl AttributeProtocol2 for Scope { fn get_attr(&self, vm: &VirtualMachine, name: &str) -> Option { - // Lookup name in scope and put it onto the stack! - let mut locals = self.locals.clone(); - loop { - match locals { - Some(new_locals) => { - if let Some(value) = new_locals.dict.get_item(name) { - return Some(value); - } else { - locals = new_locals.parent.clone() - } - } - None => break, + for dict in self.locals.iter() { + if let Some(value) = dict.get_item(name) { + return Some(value); } } From 15aaa6d3f653c1894d819ae741f6f10bca756fc1 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Sat, 9 Mar 2019 20:39:29 +0000 Subject: [PATCH 235/380] Add builtin_globals. --- tests/snippets/test_exec.py | 9 +++++++++ vm/src/builtins.rs | 5 ++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/snippets/test_exec.py b/tests/snippets/test_exec.py index d6fae63f48..61932c24cb 100644 --- a/tests/snippets/test_exec.py +++ b/tests/snippets/test_exec.py @@ -35,3 +35,12 @@ def f(): pass else: raise TypeError("exec should fail unless globals is a dict or None") + +g = globals() +g['x'] = 2 +exec('x += 2') +assert x == 4 +assert g['x'] == x + +exec("del x") +assert 'x' not in g diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 38e4975ff8..05c9f2a3c4 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -303,7 +303,9 @@ fn builtin_getattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm.get_attribute(obj.clone(), attr.clone()) } -// builtin_globals +fn builtin_globals(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { + Ok(vm.current_scope().globals.clone()) +} fn builtin_hasattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( @@ -743,6 +745,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { "filter" => ctx.filter_type(), "format" => ctx.new_rustfunc(builtin_format), "getattr" => ctx.new_rustfunc(builtin_getattr), + "globals" => ctx.new_rustfunc(builtin_globals), "hasattr" => ctx.new_rustfunc(builtin_hasattr), "hash" => ctx.new_rustfunc(builtin_hash), "hex" => ctx.new_rustfunc(builtin_hex), From 4a82c8fad5bf7eedc835542c97eff93d39fd22a3 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Sun, 10 Mar 2019 12:48:33 +0000 Subject: [PATCH 236/380] Rename AttributeProtocol2 -> NameProtocol with method name changes. --- vm/src/frame.rs | 25 ++++++++++++------------- wasm/lib/src/vm_class.rs | 6 +++--- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index c821abdaac..4809a7da0d 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -117,15 +117,14 @@ impl Scope { } } -// TODO: Merge with existing Attribute protocol. -pub trait AttributeProtocol2 { - fn get_attr(&self, vm: &VirtualMachine, name: &str) -> Option; - fn set_attr(&self, vm: &VirtualMachine, name: &str, value: PyObjectRef); - fn del_attr(&self, vm: &VirtualMachine, name: &str); +pub trait NameProtocol { + fn load_name(&self, vm: &VirtualMachine, name: &str) -> Option; + fn store_name(&self, vm: &VirtualMachine, name: &str, value: PyObjectRef); + fn delete_name(&self, vm: &VirtualMachine, name: &str); } -impl AttributeProtocol2 for Scope { - fn get_attr(&self, vm: &VirtualMachine, name: &str) -> Option { +impl NameProtocol for Scope { + fn load_name(&self, vm: &VirtualMachine, name: &str) -> Option { for dict in self.locals.iter() { if let Some(value) = dict.get_item(name) { return Some(value); @@ -139,11 +138,11 @@ impl AttributeProtocol2 for Scope { vm.builtins.get_item(name) } - fn set_attr(&self, vm: &VirtualMachine, key: &str, value: PyObjectRef) { + fn store_name(&self, vm: &VirtualMachine, key: &str, value: PyObjectRef) { self.get_locals().set_item(&vm.ctx, key, value) } - fn del_attr(&self, _vm: &VirtualMachine, key: &str) { + fn delete_name(&self, _vm: &VirtualMachine, key: &str) { self.get_locals().del_item(key) } } @@ -846,7 +845,7 @@ impl Frame { let obj = import_module(vm, current_path, module)?; for (k, v) in obj.get_key_value_pairs().iter() { - self.scope.set_attr(&vm, &objstr::get_value(k), v.clone()); + self.scope.store_name(&vm, &objstr::get_value(k), v.clone()); } Ok(None) } @@ -968,17 +967,17 @@ impl Frame { fn store_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult { let obj = self.pop_value(); - self.scope.set_attr(&vm, name, obj); + self.scope.store_name(&vm, name, obj); Ok(None) } fn delete_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult { - self.scope.del_attr(vm, name); + self.scope.delete_name(vm, name); Ok(None) } fn load_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult { - match self.scope.get_attr(&vm, name) { + match self.scope.load_name(&vm, name) { Some(value) => { self.push_value(value); Ok(None) diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index 2b76214fca..ed09052279 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -4,7 +4,7 @@ use crate::wasm_builtins; use js_sys::{Object, SyntaxError, TypeError}; use rustpython_vm::{ compile, - frame::{AttributeProtocol2, Scope}, + frame::{NameProtocol, Scope}, pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult}, VirtualMachine, }; @@ -258,7 +258,7 @@ impl WASMVirtualMachine { .. }| { let value = convert::js_to_py(vm, value); - scope.set_attr(&vm, &name, value); + scope.store_name(&vm, &name, value); }, ) } @@ -298,7 +298,7 @@ impl WASMVirtualMachine { ) .into()); }; - scope.set_attr(&vm, "print", vm.ctx.new_rustfunc(print_fn)); + scope.store_name(&vm, "print", vm.ctx.new_rustfunc(print_fn)); Ok(()) }, )? From e38a82e540df22f9c16a183f08d1e1035d9aa579 Mon Sep 17 00:00:00 2001 From: Chylli Date: Sun, 10 Mar 2019 22:39:22 +0800 Subject: [PATCH 237/380] add __doc__ to str --- vm/src/obj/objstr.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 03417ca147..09ef07576d 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -612,6 +612,16 @@ impl IntoPyObject for String { #[rustfmt::skip] // to avoid line splitting pub fn init(context: &PyContext) { let str_type = &context.str_type; + let str_doc = "str(object='') -> str\n\ + str(bytes_or_buffer[, encoding[, errors]]) -> str\n\ + \n\ + Create a new string object from the given object. If encoding or\n\ + errors is specified, then the object must expose a data buffer\n\ + that will be decoded using the given encoding and error handler.\n\ + Otherwise, returns the result of object.__str__() (if defined)\n\ + or repr(object).\n\ + encoding defaults to sys.getdefaultencoding().\n\ + errors defaults to 'strict'."; context.set_attr(&str_type, "__add__", context.new_rustfunc(PyStringRef::add)); context.set_attr(&str_type, "__eq__", context.new_rustfunc(PyStringRef::eq)); context.set_attr(&str_type, "__contains__", context.new_rustfunc(PyStringRef::contains)); @@ -666,6 +676,7 @@ pub fn init(context: &PyContext) { context.set_attr(&str_type, "center", context.new_rustfunc(PyStringRef::center)); context.set_attr(&str_type, "expandtabs", context.new_rustfunc(PyStringRef::expandtabs)); context.set_attr(&str_type, "isidentifier", context.new_rustfunc(PyStringRef::isidentifier)); + context.set_attr(&str_type, "__doc__", context.new_str(str_doc.to_string())); } pub fn get_value(obj: &PyObjectRef) -> String { From 264d218b709448c405fa255cba4cc69c9a9578fb Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sun, 10 Mar 2019 18:36:29 +0200 Subject: [PATCH 238/380] Use seperate VM for interactive shell --- wasm/demo/src/main.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/wasm/demo/src/main.js b/wasm/demo/src/main.js index 2fecd945b7..02f12c4bd8 100644 --- a/wasm/demo/src/main.js +++ b/wasm/demo/src/main.js @@ -93,6 +93,9 @@ function print_to_console(data) { term.write(remove_non_ascii(data) + "\r\n"); } +const terminalVM = rp.vmStore.init("term_vm"); +terminalVM.setStdout(print_to_console); + var input = ""; term.on("data", (data) => { const code = data.charCodeAt(0); @@ -103,9 +106,7 @@ term.on("data", (data) => { } else { term.write("\r\n"); try { - rp.pyEval(input, { - stdout: print_to_console - }); + terminalVM.exec(input); } catch (err) { if (err instanceof WebAssembly.RuntimeError) { err = window.__RUSTPYTHON_ERROR || err; From 625e181fd8972c9fefb568952857e46498df9de5 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sun, 10 Mar 2019 18:38:17 +0200 Subject: [PATCH 239/380] Use snakeCase --- wasm/demo/src/main.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/wasm/demo/src/main.js b/wasm/demo/src/main.js index 02f12c4bd8..185588921a 100644 --- a/wasm/demo/src/main.js +++ b/wasm/demo/src/main.js @@ -80,7 +80,7 @@ const term = new Terminal(); term.open(document.getElementById('terminal')); term.write(prompt); -function remove_non_ascii(str) { +function removeNonAscii(str) { if ((str===null) || (str==='')) return false; else @@ -89,12 +89,12 @@ function remove_non_ascii(str) { return str.replace(/[^\x20-\x7E]/g, ''); } -function print_to_console(data) { - term.write(remove_non_ascii(data) + "\r\n"); +function printToConsole(data) { + term.write(removeNonAscii(data) + "\r\n"); } const terminalVM = rp.vmStore.init("term_vm"); -terminalVM.setStdout(print_to_console); +terminalVM.setStdout(printToConsole); var input = ""; term.on("data", (data) => { @@ -111,7 +111,7 @@ term.on("data", (data) => { if (err instanceof WebAssembly.RuntimeError) { err = window.__RUSTPYTHON_ERROR || err; } - print_to_console(err); + printToConsole(err); } term.write(prompt); input = ""; From f27cb5b8ed46cb2b97c70ac6f5ea1c3606f243df Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sun, 10 Mar 2019 18:40:03 +0200 Subject: [PATCH 240/380] Don't delete prompt --- wasm/demo/src/main.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/wasm/demo/src/main.js b/wasm/demo/src/main.js index 185588921a..2f6de563d1 100644 --- a/wasm/demo/src/main.js +++ b/wasm/demo/src/main.js @@ -117,8 +117,10 @@ term.on("data", (data) => { input = ""; } } else if (code == 127) { - term.write("\b \b"); - input = input.slice(0, -1); + if (input.length > 0) { + term.write("\b \b"); + input = input.slice(0, -1); + } } else if (code < 32 || code == 127) { // Control return; } else { // Visible From 0ff5155af958ad657947cb608cb27910fba51d5c Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Sun, 10 Mar 2019 17:10:51 +0000 Subject: [PATCH 241/380] Rename mk_module to make_module. --- vm/src/stdlib/ast.rs | 2 +- vm/src/stdlib/dis.rs | 2 +- vm/src/stdlib/io.rs | 2 +- vm/src/stdlib/json.rs | 2 +- vm/src/stdlib/keyword.rs | 2 +- vm/src/stdlib/math.rs | 2 +- vm/src/stdlib/mod.rs | 34 +++++++++++++++++----------------- vm/src/stdlib/os.rs | 2 +- vm/src/stdlib/platform.rs | 2 +- vm/src/stdlib/pystruct.rs | 2 +- vm/src/stdlib/random.rs | 2 +- vm/src/stdlib/re.rs | 2 +- vm/src/stdlib/socket.rs | 2 +- vm/src/stdlib/string.rs | 2 +- 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 | 2 +- vm/src/vm.rs | 2 +- wasm/lib/src/browser_module.rs | 4 ++-- 21 files changed, 38 insertions(+), 38 deletions(-) diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index d3bdd3656b..3875157b8d 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -621,7 +621,7 @@ fn ast_parse(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(ast_node) } -pub fn mk_module(ctx: &PyContext) -> PyObjectRef { +pub fn make_module(ctx: &PyContext) -> PyObjectRef { py_module!(ctx, "ast", { "parse" => ctx.new_rustfunc(ast_parse), "Module" => py_class!(ctx, "_ast.Module", ctx.object(), {}), diff --git a/vm/src/stdlib/dis.rs b/vm/src/stdlib/dis.rs index d726b71106..96d727a3f0 100644 --- a/vm/src/stdlib/dis.rs +++ b/vm/src/stdlib/dis.rs @@ -22,7 +22,7 @@ fn dis_disassemble(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.get_none()) } -pub fn mk_module(ctx: &PyContext) -> PyObjectRef { +pub fn make_module(ctx: &PyContext) -> PyObjectRef { py_module!(ctx, "dis", { "dis" => ctx.new_rustfunc(dis_dis), "disassemble" => ctx.new_rustfunc(dis_disassemble) diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 9d5600498a..02171df344 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -365,7 +365,7 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -pub fn mk_module(ctx: &PyContext) -> PyObjectRef { +pub fn make_module(ctx: &PyContext) -> PyObjectRef { //IOBase the abstract base class of the IO Module let io_base = py_class!(ctx, "IOBase", ctx.object(), { "__enter__" => ctx.new_rustfunc(io_base_cm_enter), diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index 0d0bc23dc5..a23028773f 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -228,7 +228,7 @@ fn json_loads(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }) } -pub fn mk_module(ctx: &PyContext) -> PyObjectRef { +pub fn make_module(ctx: &PyContext) -> PyObjectRef { // TODO: Make this a proper type with a constructor let json_decode_error = create_type( "JSONDecodeError", diff --git a/vm/src/stdlib/keyword.rs b/vm/src/stdlib/keyword.rs index 85d6fa49e6..362d68ece3 100644 --- a/vm/src/stdlib/keyword.rs +++ b/vm/src/stdlib/keyword.rs @@ -17,7 +17,7 @@ fn keyword_iskeyword(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(value) } -pub fn mk_module(ctx: &PyContext) -> PyObjectRef { +pub fn make_module(ctx: &PyContext) -> PyObjectRef { let keyword_kwlist = ctx.new_list( lexer::get_keywords() .keys() diff --git a/vm/src/stdlib/math.rs b/vm/src/stdlib/math.rs index 8caa59e6de..19e5899a2a 100644 --- a/vm/src/stdlib/math.rs +++ b/vm/src/stdlib/math.rs @@ -171,7 +171,7 @@ fn math_lgamma(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -pub fn mk_module(ctx: &PyContext) -> PyObjectRef { +pub fn make_module(ctx: &PyContext) -> PyObjectRef { py_module!(ctx, "math", { // Number theory functions: "fabs" => ctx.new_rustfunc(math_fabs), diff --git a/vm/src/stdlib/mod.rs b/vm/src/stdlib/mod.rs index 2a22093751..c3bb543f52 100644 --- a/vm/src/stdlib/mod.rs +++ b/vm/src/stdlib/mod.rs @@ -28,28 +28,28 @@ pub fn get_module_inits() -> HashMap { let mut modules = HashMap::new(); modules.insert( "ast".to_string(), - Box::new(ast::mk_module) as StdlibInitFunc, + Box::new(ast::make_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)); - modules.insert("math".to_string(), Box::new(math::mk_module)); - modules.insert("platform".to_string(), Box::new(platform::mk_module)); - modules.insert("re".to_string(), Box::new(re::mk_module)); - modules.insert("random".to_string(), Box::new(random::mk_module)); - modules.insert("string".to_string(), Box::new(string::mk_module)); - modules.insert("struct".to_string(), Box::new(pystruct::mk_module)); - modules.insert("time".to_string(), Box::new(time_module::mk_module)); - modules.insert("tokenize".to_string(), Box::new(tokenize::mk_module)); - modules.insert("types".to_string(), Box::new(types::mk_module)); - modules.insert("_weakref".to_string(), Box::new(weakref::mk_module)); + modules.insert("dis".to_string(), Box::new(dis::make_module)); + modules.insert("json".to_string(), Box::new(json::make_module)); + modules.insert("keyword".to_string(), Box::new(keyword::make_module)); + modules.insert("math".to_string(), Box::new(math::make_module)); + modules.insert("platform".to_string(), Box::new(platform::make_module)); + modules.insert("re".to_string(), Box::new(re::make_module)); + modules.insert("random".to_string(), Box::new(random::make_module)); + modules.insert("string".to_string(), Box::new(string::make_module)); + modules.insert("struct".to_string(), Box::new(pystruct::make_module)); + modules.insert("time".to_string(), Box::new(time_module::make_module)); + modules.insert("tokenize".to_string(), Box::new(tokenize::make_module)); + modules.insert("types".to_string(), Box::new(types::make_module)); + modules.insert("_weakref".to_string(), Box::new(weakref::make_module)); // disable some modules on WASM #[cfg(not(target_arch = "wasm32"))] { - modules.insert("io".to_string(), Box::new(io::mk_module)); - modules.insert("os".to_string(), Box::new(os::mk_module)); - modules.insert("socket".to_string(), Box::new(socket::mk_module)); + modules.insert("io".to_string(), Box::new(io::make_module)); + modules.insert("os".to_string(), Box::new(os::make_module)); + modules.insert("socket".to_string(), Box::new(socket::make_module)); } modules diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 5379fefc4d..7745ca2053 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -118,7 +118,7 @@ fn os_error(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Err(vm.new_os_error(msg)) } -pub fn mk_module(ctx: &PyContext) -> PyObjectRef { +pub fn make_module(ctx: &PyContext) -> PyObjectRef { let py_mod = py_module!(ctx, "os", { "open" => ctx.new_rustfunc(os_open), "close" => ctx.new_rustfunc(os_close), diff --git a/vm/src/stdlib/platform.rs b/vm/src/stdlib/platform.rs index e1681a4dd5..dd0bc79bcb 100644 --- a/vm/src/stdlib/platform.rs +++ b/vm/src/stdlib/platform.rs @@ -3,7 +3,7 @@ extern crate rustc_version_runtime; use crate::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult}; use crate::VirtualMachine; -pub fn mk_module(ctx: &PyContext) -> PyObjectRef { +pub fn make_module(ctx: &PyContext) -> PyObjectRef { py_module!(ctx, "platform", { "python_compiler" => ctx.new_rustfunc(platform_python_compiler), "python_implementation" => ctx.new_rustfunc(platform_python_implementation), diff --git a/vm/src/stdlib/pystruct.rs b/vm/src/stdlib/pystruct.rs index c96bba736e..8052492b2b 100644 --- a/vm/src/stdlib/pystruct.rs +++ b/vm/src/stdlib/pystruct.rs @@ -296,7 +296,7 @@ fn struct_unpack(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_tuple(items)) } -pub fn mk_module(ctx: &PyContext) -> PyObjectRef { +pub fn make_module(ctx: &PyContext) -> PyObjectRef { py_module!(ctx, "struct", { "pack" => ctx.new_rustfunc(struct_pack), "unpack" => ctx.new_rustfunc(struct_unpack) diff --git a/vm/src/stdlib/random.rs b/vm/src/stdlib/random.rs index 85cd52a587..31b0162837 100644 --- a/vm/src/stdlib/random.rs +++ b/vm/src/stdlib/random.rs @@ -7,7 +7,7 @@ use crate::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol use crate::stdlib::random::rand::distributions::{Distribution, Normal}; use crate::VirtualMachine; -pub fn mk_module(ctx: &PyContext) -> PyObjectRef { +pub fn make_module(ctx: &PyContext) -> PyObjectRef { py_module!(ctx, "random", { "guass" => ctx.new_rustfunc(random_gauss), "normalvariate" => ctx.new_rustfunc(random_normalvariate), diff --git a/vm/src/stdlib/re.rs b/vm/src/stdlib/re.rs index b257bd8183..0e65db597f 100644 --- a/vm/src/stdlib/re.rs +++ b/vm/src/stdlib/re.rs @@ -17,7 +17,7 @@ use crate::pyobject::{ use crate::VirtualMachine; /// Create the python `re` module with all its members. -pub fn mk_module(ctx: &PyContext) -> PyObjectRef { +pub fn make_module(ctx: &PyContext) -> PyObjectRef { let match_type = py_class!(ctx, "Match", ctx.object(), { "start" => ctx.new_rustfunc(match_start), "end" => ctx.new_rustfunc(match_end) diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index 99bd19aba4..2f9a73cdaf 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -421,7 +421,7 @@ fn get_addr_tuple(vm: &mut VirtualMachine, addr: SocketAddr) -> PyResult { Ok(vm.ctx.new_tuple(vec![ip, port])) } -pub fn mk_module(ctx: &PyContext) -> PyObjectRef { +pub fn make_module(ctx: &PyContext) -> PyObjectRef { let socket = py_class!(ctx, "socket", ctx.object(), { "__new__" => ctx.new_rustfunc(socket_new), "connect" => ctx.new_rustfunc(socket_connect), diff --git a/vm/src/stdlib/string.rs b/vm/src/stdlib/string.rs index 5b3f2a7282..157dae035f 100644 --- a/vm/src/stdlib/string.rs +++ b/vm/src/stdlib/string.rs @@ -5,7 +5,7 @@ use crate::pyobject::{PyContext, PyObjectRef}; -pub fn mk_module(ctx: &PyContext) -> PyObjectRef { +pub fn make_module(ctx: &PyContext) -> PyObjectRef { let ascii_lowercase = "abcdefghijklmnopqrstuvwxyz".to_string(); let ascii_uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".to_string(); let ascii_letters = format!("{}{}", ascii_lowercase, ascii_uppercase); diff --git a/vm/src/stdlib/time_module.rs b/vm/src/stdlib/time_module.rs index b9a360ce81..05c9b640c9 100644 --- a/vm/src/stdlib/time_module.rs +++ b/vm/src/stdlib/time_module.rs @@ -30,7 +30,7 @@ fn time_time(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(value) } -pub fn mk_module(ctx: &PyContext) -> PyObjectRef { +pub fn make_module(ctx: &PyContext) -> PyObjectRef { py_module!(ctx, "time", { "sleep" => ctx.new_rustfunc(time_sleep), "time" => ctx.new_rustfunc(time_time) diff --git a/vm/src/stdlib/tokenize.rs b/vm/src/stdlib/tokenize.rs index 8207e9efc5..1da20655df 100644 --- a/vm/src/stdlib/tokenize.rs +++ b/vm/src/stdlib/tokenize.rs @@ -25,7 +25,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 { +pub fn make_module(ctx: &PyContext) -> PyObjectRef { py_module!(ctx, "tokenize", { "tokenize" => ctx.new_rustfunc(tokenize_tokenize) }) diff --git a/vm/src/stdlib/types.rs b/vm/src/stdlib/types.rs index 959b141f55..d875cbb95d 100644 --- a/vm/src/stdlib/types.rs +++ b/vm/src/stdlib/types.rs @@ -30,7 +30,7 @@ fn types_new_class(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { objtype::new(vm.ctx.type_type(), &name, bases, PyAttributes::new()) } -pub fn mk_module(ctx: &PyContext) -> PyObjectRef { +pub fn make_module(ctx: &PyContext) -> PyObjectRef { py_module!(ctx, "types", { "new_class" => ctx.new_rustfunc(types_new_class), "FunctionType" => ctx.function_type(), diff --git a/vm/src/stdlib/weakref.rs b/vm/src/stdlib/weakref.rs index ba6015ec21..2b1d86e1af 100644 --- a/vm/src/stdlib/weakref.rs +++ b/vm/src/stdlib/weakref.rs @@ -12,7 +12,7 @@ use crate::pyobject::{ use crate::VirtualMachine; use std::rc::Rc; -pub fn mk_module(ctx: &PyContext) -> PyObjectRef { +pub fn make_module(ctx: &PyContext) -> PyObjectRef { let py_ref_class = py_class!(ctx, "ref", ctx.object(), { "__new__" => ctx.new_rustfunc(ref_new), "__call__" => ctx.new_rustfunc(ref_call) diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index d794792b8f..cc82792ced 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -54,7 +54,7 @@ fn sys_getsizeof(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_int(size)) } -pub fn mk_module(ctx: &PyContext) -> PyObjectRef { +pub fn make_module(ctx: &PyContext) -> PyObjectRef { let path_list = match env::var_os("PYTHONPATH") { Some(paths) => env::split_paths(&paths) .map(|path| { diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 0f95bf66c6..80a393cd58 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -57,7 +57,7 @@ impl VirtualMachine { // Hard-core modules: let builtins = builtins::make_module(&ctx); - let sysmod = sysmodule::mk_module(&ctx); + let sysmod = sysmodule::make_module(&ctx); // Add builtins as builtins module: let modules = sysmod.get_attr("modules").unwrap(); diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index d8014ae520..ad151bc534 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -310,7 +310,7 @@ fn browser_prompt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { const BROWSER_NAME: &str = "browser"; -pub fn mk_module(ctx: &PyContext) -> PyObjectRef { +pub fn make_module(ctx: &PyContext) -> PyObjectRef { let promise = py_class!(ctx, "Promise", ctx.object(), { "then" => ctx.new_rustfunc(promise_then), "catch" => ctx.new_rustfunc(promise_catch) @@ -329,5 +329,5 @@ 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)); + .insert(BROWSER_NAME.to_string(), Box::new(make_module)); } From 7c5be7d6b7d97e96daf4fb26e451db76c7d4a6ec Mon Sep 17 00:00:00 2001 From: ben Date: Sat, 9 Mar 2019 17:17:59 +1300 Subject: [PATCH 242/380] Add proper weakref type --- vm/src/obj/mod.rs | 1 + vm/src/obj/objweakref.rs | 50 ++++++++++++++++++++++++++++++++++++++++ vm/src/pyobject.rs | 17 +++++++++----- vm/src/stdlib/weakref.rs | 45 ++---------------------------------- 4 files changed, 64 insertions(+), 49 deletions(-) create mode 100644 vm/src/obj/objweakref.rs diff --git a/vm/src/obj/mod.rs b/vm/src/obj/mod.rs index 3d068df648..f32683fd25 100644 --- a/vm/src/obj/mod.rs +++ b/vm/src/obj/mod.rs @@ -31,4 +31,5 @@ pub mod objstr; pub mod objsuper; pub mod objtuple; pub mod objtype; +pub mod objweakref; pub mod objzip; diff --git a/vm/src/obj/objweakref.rs b/vm/src/obj/objweakref.rs new file mode 100644 index 0000000000..8a4651de73 --- /dev/null +++ b/vm/src/obj/objweakref.rs @@ -0,0 +1,50 @@ +use crate::function::PyRef; +use crate::obj::objtype::PyClassRef; +use crate::pyobject::PyObjectPayload2; +use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult}; +use crate::vm::VirtualMachine; + +use std::rc::{Rc, Weak}; + +#[derive(Debug)] +pub struct PyWeak { + referent: Weak, +} + +impl PyWeak { + pub fn downgrade(obj: PyObjectRef) -> PyWeak { + PyWeak { + referent: Rc::downgrade(&obj), + } + } + + pub fn upgrade(&self) -> Option { + self.referent.upgrade() + } +} + +impl PyObjectPayload2 for PyWeak { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.weakref_type() + } +} + +pub type PyWeakRef = PyRef; + +impl PyWeakRef { + // TODO callbacks + fn create(cls: PyClassRef, referent: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + Self::new_with_type(vm, PyWeak::downgrade(referent), cls) + } + + fn call(self, vm: &mut VirtualMachine) -> PyObjectRef { + self.referent.upgrade().unwrap_or_else(|| vm.get_none()) + } +} + +pub fn init(context: &PyContext) { + extend_class!(context, &context.weakref_type, { + "__new__" => context.new_rustfunc(PyWeakRef::create), + "__call__" => context.new_rustfunc(PyWeakRef::call) + }); +} diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 994a7ca298..3a99b2bccc 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use std::fmt; use std::iter; use std::ops::RangeInclusive; -use std::rc::{Rc, Weak}; +use std::rc::Rc; use num_bigint::BigInt; use num_bigint::ToBigInt; @@ -43,6 +43,7 @@ use crate::obj::objstr; use crate::obj::objsuper; use crate::obj::objtuple::{self, PyTuple}; use crate::obj::objtype::{self, PyClass}; +use crate::obj::objweakref; use crate::obj::objzip; use crate::vm::VirtualMachine; @@ -67,9 +68,6 @@ Basically reference counting, but then done by rust. /// to the python object by 1. pub type PyObjectRef = Rc; -/// Same as PyObjectRef, except for being a weak reference. -pub type PyObjectWeakRef = Weak; - /// Use this type for function which return a python object or and exception. /// Both the python object and the python exception are `PyObjectRef` types /// since exceptions are also python objects. @@ -141,6 +139,7 @@ pub struct PyContext { pub readonly_property_type: PyObjectRef, pub module_type: PyObjectRef, pub bound_method_type: PyObjectRef, + pub weakref_type: PyObjectRef, pub object: PyObjectRef, pub exceptions: exceptions::ExceptionZoo, } @@ -185,6 +184,7 @@ impl PyContext { let readonly_property_type = create_type("readonly_property", &type_type, &object_type, &dict_type); let super_type = create_type("super", &type_type, &object_type, &dict_type); + let weakref_type = create_type("ref", &type_type, &object_type, &dict_type); let generator_type = create_type("generator", &type_type, &object_type, &dict_type); let bound_method_type = create_type("method", &type_type, &object_type, &dict_type); let str_type = create_type("str", &type_type, &object_type, &dict_type); @@ -284,6 +284,7 @@ impl PyContext { generator_type, module_type, bound_method_type, + weakref_type, type_type, exceptions, }; @@ -316,6 +317,7 @@ impl PyContext { objbool::init(&context); objcode::init(&context); objframe::init(&context); + objweakref::init(&context); objnone::init(&context); objmodule::init(&context); exceptions::init(&context); @@ -450,6 +452,10 @@ impl PyContext { self.bound_method_type.clone() } + pub fn weakref_type(&self) -> PyObjectRef { + self.weakref_type.clone() + } + pub fn type_type(&self) -> PyObjectRef { self.type_type.clone() } @@ -465,6 +471,7 @@ impl PyContext { pub fn not_implemented(&self) -> PyObjectRef { self.not_implemented.clone() } + pub fn object(&self) -> PyObjectRef { self.object.clone() } @@ -1481,7 +1488,6 @@ into_py_native_func_tuple!((a, A), (b, B), (c, C), (d, D), (e, E)); /// of rust data for a particular python object. Determine the python type /// by using for example the `.typ()` method on a python object. pub enum PyObjectPayload { - WeakRef { referent: PyObjectWeakRef }, AnyRustValue { value: Box }, } @@ -1510,7 +1516,6 @@ impl PyObjectPayload2 for PyIteratorValue { impl fmt::Debug for PyObjectPayload { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - PyObjectPayload::WeakRef { .. } => write!(f, "weakref"), PyObjectPayload::AnyRustValue { value } => value.fmt(f), } } diff --git a/vm/src/stdlib/weakref.rs b/vm/src/stdlib/weakref.rs index ba6015ec21..b32caa61d1 100644 --- a/vm/src/stdlib/weakref.rs +++ b/vm/src/stdlib/weakref.rs @@ -5,51 +5,10 @@ //! - [rust weak struct](https://doc.rust-lang.org/std/rc/struct.Weak.html) //! -use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyObjectWeakRef, PyResult, - TypeProtocol, -}; -use crate::VirtualMachine; -use std::rc::Rc; +use super::super::pyobject::{PyContext, PyObjectRef}; pub fn mk_module(ctx: &PyContext) -> PyObjectRef { - let py_ref_class = py_class!(ctx, "ref", ctx.object(), { - "__new__" => ctx.new_rustfunc(ref_new), - "__call__" => ctx.new_rustfunc(ref_call) - }); - py_module!(ctx, "_weakref", { - "ref" => py_ref_class + "ref" => ctx.weakref_type() }) } - -fn ref_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - // TODO: check first argument for subclass of `ref`. - arg_check!(vm, args, required = [(cls, None), (referent, None)]); - let referent = Rc::downgrade(referent); - Ok(PyObject::new( - PyObjectPayload::WeakRef { referent }, - cls.clone(), - )) -} - -/// Dereference the weakref, and check if we still refer something. -fn ref_call(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - // TODO: check first argument for subclass of `ref`. - arg_check!(vm, args, required = [(cls, None)]); - let referent = get_value(cls); - let py_obj = if let Some(obj) = referent.upgrade() { - obj - } else { - vm.get_none() - }; - Ok(py_obj) -} - -fn get_value(obj: &PyObjectRef) -> PyObjectWeakRef { - if let PyObjectPayload::WeakRef { referent } = &obj.payload { - referent.clone() - } else { - panic!("Inner error getting weak ref {:?}", obj); - } -} From 6024e3ea0c70f237e698d293f93ea9b51f83aa96 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sun, 10 Mar 2019 12:43:25 -0700 Subject: [PATCH 243/380] Fix irrefutable pattern errors --- vm/src/pyobject.rs | 7 ++----- vm/src/stdlib/re.rs | 15 +++++++-------- vm/src/stdlib/socket.rs | 7 +++---- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 3a99b2bccc..23c269324a 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1537,11 +1537,8 @@ impl PyObject { } pub fn payload(&self) -> Option<&T> { - if let PyObjectPayload::AnyRustValue { ref value } = self.payload { - value.downcast_ref() - } else { - None - } + let PyObjectPayload::AnyRustValue { ref value } = self.payload; + value.downcast_ref() } } diff --git a/vm/src/stdlib/re.rs b/vm/src/stdlib/re.rs index b257bd8183..0828a13d14 100644 --- a/vm/src/stdlib/re.rs +++ b/vm/src/stdlib/re.rs @@ -191,20 +191,19 @@ fn match_end(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { /// Retrieve inner rust regex from python object: fn get_regex<'a>(obj: &'a PyObjectRef) -> &'a Regex { - if let PyObjectPayload::AnyRustValue { ref value } = obj.payload { - if let Some(regex) = value.downcast_ref::() { - return regex; - } + // TODO: Regex shouldn't be stored in payload directly, create newtype wrapper + let PyObjectPayload::AnyRustValue { ref value } = obj.payload; + if let Some(regex) = value.downcast_ref::() { + return regex; } panic!("Inner error getting regex {:?}", obj); } /// Retrieve inner rust match from python object: fn get_match<'a>(obj: &'a PyObjectRef) -> &'a PyMatch { - if let PyObjectPayload::AnyRustValue { ref value } = obj.payload { - if let Some(value) = value.downcast_ref::() { - return value; - } + let PyObjectPayload::AnyRustValue { ref value } = obj.payload; + if let Some(value) = value.downcast_ref::() { + return value; } panic!("Inner error getting match {:?}", obj); } diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index 99bd19aba4..d6e6283af9 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -127,10 +127,9 @@ impl Socket { } fn get_socket<'a>(obj: &'a PyObjectRef) -> impl DerefMut + 'a { - if let PyObjectPayload::AnyRustValue { ref value } = obj.payload { - if let Some(socket) = value.downcast_ref::>() { - return socket.borrow_mut(); - } + let PyObjectPayload::AnyRustValue { ref value } = obj.payload; + if let Some(socket) = value.downcast_ref::>() { + return socket.borrow_mut(); } panic!("Inner error getting socket {:?}", obj); } From 45bc8c8f7a983d3dad5371b7823e34fc676bf2de Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sun, 10 Mar 2019 12:17:50 -0700 Subject: [PATCH 244/380] Move PyRef to pyobject module --- vm/src/function.rs | 118 -------------------------------------- vm/src/lib.rs | 1 - vm/src/obj/objdict.rs | 10 ++-- vm/src/obj/objfloat.rs | 5 +- vm/src/obj/objint.rs | 3 +- vm/src/obj/objlist.rs | 3 +- vm/src/obj/objmodule.rs | 3 +- vm/src/obj/objnone.rs | 4 +- vm/src/obj/objobject.rs | 4 +- vm/src/obj/objproperty.rs | 8 +-- vm/src/obj/objstr.rs | 3 +- vm/src/obj/objtuple.rs | 3 +- vm/src/obj/objtype.rs | 14 +++-- vm/src/obj/objweakref.rs | 3 +- vm/src/pyobject.rs | 108 +++++++++++++++++++++++++++++++++- 15 files changed, 135 insertions(+), 155 deletions(-) delete mode 100644 vm/src/function.rs diff --git a/vm/src/function.rs b/vm/src/function.rs deleted file mode 100644 index c978be7085..0000000000 --- a/vm/src/function.rs +++ /dev/null @@ -1,118 +0,0 @@ -use std::fmt; -use std::marker::PhantomData; -use std::ops::Deref; - -use crate::obj::objtype; -use crate::obj::objtype::PyClassRef; -use crate::pyobject::{ - IntoPyObject, PyContext, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, - TryFromObject, TypeProtocol, -}; -use crate::vm::VirtualMachine; - -// TODO: Move PyFuncArgs, FromArgs, etc. here - -// TODO: `PyRef` probably actually belongs in the pyobject module. - -/// A reference to the payload of a built-in object. -/// -/// Note that a `PyRef` can only deref to a shared / immutable reference. -/// It is the payload type's responsibility to handle (possibly concurrent) -/// mutability with locks or concurrent data structures if required. -/// -/// A `PyRef` can be directly returned from a built-in function to handle -/// situations (such as when implementing in-place methods such as `__iadd__`) -/// where a reference to the same object must be returned. -#[derive(Clone)] -pub struct PyRef { - // invariant: this obj must always have payload of type T - obj: PyObjectRef, - _payload: PhantomData, -} - -impl PyRef -where - T: PyObjectPayload2, -{ - pub fn new(ctx: &PyContext, payload: T) -> Self { - PyRef { - obj: PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(payload), - }, - T::required_type(ctx), - ), - _payload: PhantomData, - } - } - - pub fn new_with_type(vm: &mut VirtualMachine, payload: T, cls: PyClassRef) -> PyResult { - let required_type = T::required_type(&vm.ctx); - if objtype::issubclass(&cls.obj, &required_type) { - Ok(PyRef { - obj: PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(payload), - }, - cls.obj, - ), - _payload: PhantomData, - }) - } else { - let subtype = vm.to_pystr(&cls.obj)?; - let basetype = vm.to_pystr(&required_type)?; - Err(vm.new_type_error(format!("{} is not a subtype of {}", subtype, basetype))) - } - } - - pub fn as_object(&self) -> &PyObjectRef { - &self.obj - } - pub fn into_object(self) -> PyObjectRef { - self.obj - } -} - -impl Deref for PyRef -where - T: PyObjectPayload2, -{ - type Target = T; - - fn deref(&self) -> &T { - self.obj.payload().expect("unexpected payload for type") - } -} - -impl TryFromObject for PyRef -where - T: PyObjectPayload2, -{ - fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { - if objtype::isinstance(&obj, &T::required_type(&vm.ctx)) { - Ok(PyRef { - obj, - _payload: PhantomData, - }) - } else { - let expected_type = vm.to_pystr(&T::required_type(&vm.ctx))?; - let actual_type = vm.to_pystr(&obj.typ())?; - Err(vm.new_type_error(format!( - "Expected type {}, not {}", - expected_type, actual_type, - ))) - } - } -} - -impl IntoPyObject for PyRef { - fn into_pyobject(self, _ctx: &PyContext) -> PyResult { - Ok(self.obj) - } -} - -impl fmt::Display for PyRef { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.obj.fmt(f) - } -} diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 78696f9f3f..b32b91cb7d 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -41,7 +41,6 @@ pub mod eval; mod exceptions; pub mod format; pub mod frame; -pub mod function; pub mod import; pub mod obj; pub mod pyobject; diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index fe27b5b28f..84391cdd87 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -1,17 +1,17 @@ -use crate::function::PyRef; use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::ops::{Deref, DerefMut}; -use super::objiter; -use super::objstr; -use super::objtype; use crate::pyobject::{ PyAttributes, PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectPayload, - PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, + PyObjectPayload2, PyObjectRef, PyRef, PyResult, TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; +use super::objiter; +use super::objstr; +use super::objtype; + pub type DictContentType = HashMap; #[derive(Default, Debug)] diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index f544adcc36..6f1790d682 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -2,10 +2,9 @@ use super::objbytes; use super::objint; use super::objstr; use super::objtype; -use crate::function::PyRef; use crate::pyobject::{ - IntoPyObject, PyContext, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, - TypeProtocol, + IntoPyObject, PyContext, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyRef, + PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use num_bigint::ToBigInt; diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 8e5f3f59c6..5b751faa0f 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -5,10 +5,9 @@ use num_integer::Integer; use num_traits::{Pow, Signed, ToPrimitive, Zero}; use crate::format::FormatSpec; -use crate::function::PyRef; use crate::pyobject::{ FromPyObjectRef, IntoPyObject, PyContext, PyFuncArgs, PyObject, PyObjectPayload, - PyObjectPayload2, PyObjectRef, PyResult, TryFromObject, TypeProtocol, + PyObjectPayload2, PyObjectRef, PyRef, PyResult, TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 8ad69bff31..ecabb009aa 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -8,10 +8,9 @@ use super::objsequence::{ }; use super::objstr; use super::objtype; -use crate::function::PyRef; use crate::pyobject::{ IdProtocol, OptionalArg, PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectPayload, - PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, + PyObjectPayload2, PyObjectRef, PyRef, PyResult, TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; use num_traits::ToPrimitive; diff --git a/vm/src/obj/objmodule.rs b/vm/src/obj/objmodule.rs index 4f7a641b99..92f55566d2 100644 --- a/vm/src/obj/objmodule.rs +++ b/vm/src/obj/objmodule.rs @@ -1,5 +1,4 @@ -use crate::function::PyRef; -use crate::pyobject::{DictProtocol, PyContext, PyObjectPayload2, PyObjectRef, PyResult}; +use crate::pyobject::{DictProtocol, PyContext, PyObjectPayload2, PyObjectRef, PyRef, PyResult}; use crate::vm::VirtualMachine; #[derive(Clone, Debug)] diff --git a/vm/src/obj/objnone.rs b/vm/src/obj/objnone.rs index 80980f5283..6cde6ef063 100644 --- a/vm/src/obj/objnone.rs +++ b/vm/src/obj/objnone.rs @@ -1,6 +1,6 @@ -use crate::function::PyRef; use crate::pyobject::{ - IntoPyObject, PyContext, PyFuncArgs, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, + IntoPyObject, PyContext, PyFuncArgs, PyObjectPayload2, PyObjectRef, PyRef, PyResult, + TypeProtocol, }; use crate::vm::VirtualMachine; diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 0d9d21f1ea..54fcd1b6eb 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -1,10 +1,9 @@ use super::objstr; use super::objtype; -use crate::function::PyRef; use crate::obj::objproperty::PropertyBuilder; use crate::pyobject::{ AttributeProtocol, DictProtocol, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObject, - PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + PyObjectPayload, PyObjectRef, PyRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use std::cell::RefCell; @@ -12,6 +11,7 @@ use std::collections::HashMap; #[derive(Clone, Debug)] pub struct PyInstance; + pub type PyInstanceRef = PyRef; pub fn new_instance(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index b94692177e..9aaea31cdc 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -2,15 +2,15 @@ */ -use crate::function::PyRef; +use std::marker::PhantomData; + use crate::obj::objstr::PyStringRef; use crate::obj::objtype::PyClassRef; -use crate::pyobject::IntoPyNativeFunc; use crate::pyobject::{ - OptionalArg, PyContext, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, + IntoPyNativeFunc, OptionalArg, PyContext, PyObject, PyObjectPayload, PyObjectPayload2, + PyObjectRef, PyRef, PyResult, }; use crate::VirtualMachine; -use std::marker::PhantomData; /// Read-only property, doesn't have __set__ or __delete__ #[derive(Debug)] diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 09ef07576d..d9c39e3371 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -6,10 +6,9 @@ use num_traits::ToPrimitive; use unicode_segmentation::UnicodeSegmentation; use crate::format::{FormatParseError, FormatPart, FormatString}; -use crate::function::PyRef; use crate::pyobject::{ IntoPyObject, OptionalArg, PyContext, PyFuncArgs, PyIterable, PyObjectPayload2, PyObjectRef, - PyResult, TypeProtocol, + PyRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index e583055c30..95c1b12f98 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -1,10 +1,9 @@ use std::cell::{Cell, RefCell}; use std::hash::{Hash, Hasher}; -use crate::function::PyRef; use crate::pyobject::{ IdProtocol, OptionalArg, PyContext, PyIteratorValue, PyObject, PyObjectPayload, - PyObjectPayload2, PyObjectRef, PyResult, + PyObjectPayload2, PyObjectRef, PyRef, PyResult, }; use crate::vm::{ReprGuard, VirtualMachine}; diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index be4591dfcd..44bb9bc628 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -1,19 +1,21 @@ -use super::objdict; -use super::objstr; -use crate::function::PyRef; +use std::cell::RefCell; +use std::collections::HashMap; + use crate::pyobject::{ AttributeProtocol, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObject, PyObjectPayload, - PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, + PyObjectPayload2, PyObjectRef, PyRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; -use std::cell::RefCell; -use std::collections::HashMap; + +use super::objdict; +use super::objstr; #[derive(Clone, Debug)] pub struct PyClass { pub name: String, pub mro: Vec, } + pub type PyClassRef = PyRef; impl PyObjectPayload2 for PyClass { diff --git a/vm/src/obj/objweakref.rs b/vm/src/obj/objweakref.rs index 8a4651de73..45cb4108a8 100644 --- a/vm/src/obj/objweakref.rs +++ b/vm/src/obj/objweakref.rs @@ -1,7 +1,6 @@ -use crate::function::PyRef; use crate::obj::objtype::PyClassRef; use crate::pyobject::PyObjectPayload2; -use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult}; +use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyRef, PyResult}; use crate::vm::VirtualMachine; use std::rc::{Rc, Weak}; diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 23c269324a..c7c99723c6 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -2,7 +2,8 @@ use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::fmt; use std::iter; -use std::ops::RangeInclusive; +use std::marker::PhantomData; +use std::ops::{Deref, RangeInclusive}; use std::rc::Rc; use num_bigint::BigInt; @@ -42,7 +43,7 @@ use crate::obj::objslice; use crate::obj::objstr; use crate::obj::objsuper; use crate::obj::objtuple::{self, PyTuple}; -use crate::obj::objtype::{self, PyClass}; +use crate::obj::objtype::{self, PyClass, PyClassRef}; use crate::obj::objweakref; use crate::obj::objzip; use crate::vm::VirtualMachine; @@ -741,6 +742,109 @@ pub struct PyObject { pub dict: Option>, // __dict__ member } +/// A reference to a Python object. +/// +/// Note that a `PyRef` can only deref to a shared / immutable reference. +/// It is the payload type's responsibility to handle (possibly concurrent) +/// mutability with locks or concurrent data structures if required. +/// +/// A `PyRef` can be directly returned from a built-in function to handle +/// situations (such as when implementing in-place methods such as `__iadd__`) +/// where a reference to the same object must be returned. +#[derive(Clone)] +pub struct PyRef { + // invariant: this obj must always have payload of type T + obj: PyObjectRef, + _payload: PhantomData, +} + +impl PyRef +where + T: PyObjectPayload2, +{ + pub fn new(ctx: &PyContext, payload: T) -> Self { + PyRef { + obj: PyObject::new( + PyObjectPayload::AnyRustValue { + value: Box::new(payload), + }, + T::required_type(ctx), + ), + _payload: PhantomData, + } + } + + pub fn new_with_type(vm: &mut VirtualMachine, payload: T, cls: PyClassRef) -> PyResult { + let required_type = T::required_type(&vm.ctx); + if objtype::issubclass(&cls.obj, &required_type) { + Ok(PyRef { + obj: PyObject::new( + PyObjectPayload::AnyRustValue { + value: Box::new(payload), + }, + cls.obj, + ), + _payload: PhantomData, + }) + } else { + let subtype = vm.to_pystr(&cls.obj)?; + let basetype = vm.to_pystr(&required_type)?; + Err(vm.new_type_error(format!("{} is not a subtype of {}", subtype, basetype))) + } + } + + pub fn as_object(&self) -> &PyObjectRef { + &self.obj + } + pub fn into_object(self) -> PyObjectRef { + self.obj + } +} + +impl Deref for PyRef +where + T: PyObjectPayload2, +{ + type Target = T; + + fn deref(&self) -> &T { + self.obj.payload().expect("unexpected payload for type") + } +} + +impl TryFromObject for PyRef +where + T: PyObjectPayload2, +{ + fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { + if objtype::isinstance(&obj, &T::required_type(&vm.ctx)) { + Ok(PyRef { + obj, + _payload: PhantomData, + }) + } else { + let expected_type = vm.to_pystr(&T::required_type(&vm.ctx))?; + let actual_type = vm.to_pystr(&obj.typ())?; + Err(vm.new_type_error(format!( + "Expected type {}, not {}", + expected_type, actual_type, + ))) + } + } +} + +impl IntoPyObject for PyRef { + fn into_pyobject(self, _ctx: &PyContext) -> PyResult { + Ok(self.obj) + } +} + +impl fmt::Display for PyRef { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.obj.fmt(f) + } +} + pub trait IdProtocol { fn get_id(&self) -> usize; fn is(&self, other: &PyObjectRef) -> bool; From e2e13af7ea2af733754d7aca8c1debc367d0272f Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sun, 10 Mar 2019 13:45:38 -0700 Subject: [PATCH 245/380] Remove PyObjectPayload --- vm/src/compile.rs | 6 +- vm/src/frame.rs | 20 ++-- vm/src/obj/objbytearray.rs | 7 +- vm/src/obj/objbytes.rs | 21 ++-- vm/src/obj/objcomplex.rs | 10 +- vm/src/obj/objdict.rs | 44 ++++---- vm/src/obj/objenumerate.rs | 13 +-- vm/src/obj/objfilter.rs | 14 ++- vm/src/obj/objfloat.rs | 10 +- vm/src/obj/objgenerator.rs | 7 +- vm/src/obj/objint.rs | 11 +- vm/src/obj/objlist.rs | 18 ++-- vm/src/obj/objmap.rs | 13 +-- vm/src/obj/objmemory.rs | 11 +- vm/src/obj/objobject.rs | 12 +-- vm/src/obj/objproperty.rs | 18 +--- vm/src/obj/objrange.rs | 45 +++------ vm/src/obj/objsequence.rs | 32 +++--- vm/src/obj/objset.rs | 54 ++++------ vm/src/obj/objslice.rs | 15 ++- vm/src/obj/objtuple.rs | 18 ++-- vm/src/obj/objtype.rs | 24 ++--- vm/src/obj/objzip.rs | 10 +- vm/src/pyobject.rs | 200 ++++++++----------------------------- vm/src/stdlib/re.rs | 24 +---- vm/src/stdlib/socket.rs | 21 +--- 26 files changed, 210 insertions(+), 468 deletions(-) diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 21ce3a99fa..5a3fe6b1da 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -8,7 +8,7 @@ use crate::bytecode::{self, CallType, CodeObject, Instruction}; use crate::error::CompileError; use crate::obj::objcode; -use crate::pyobject::{PyObject, PyObjectPayload, PyObjectRef}; +use crate::pyobject::{PyObject, PyObjectRef}; use num_complex::Complex64; use rustpython_parser::{ast, parser}; @@ -50,9 +50,7 @@ pub fn compile( let code = compiler.pop_code_object(); trace!("Compilation completed: {:?}", code); Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(objcode::PyCode::new(code)), - }, + Box::new(objcode::PyCode::new(code)), code_type, )) } diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 0d87886370..2e100f7914 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -22,8 +22,8 @@ use crate::obj::objslice::PySlice; use crate::obj::objstr; use crate::obj::objtype; use crate::pyobject::{ - DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, - PyObjectRef, PyResult, TryFromObject, TypeProtocol, + DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload2, PyObjectRef, + PyResult, TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -407,12 +407,8 @@ impl Frame { let stop = out[1].take(); let step = if out.len() == 3 { out[2].take() } else { None }; - let obj = PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PySlice { start, stop, step }), - }, - vm.ctx.slice_type(), - ); + let obj = + PyObject::new(Box::new(PySlice { start, stop, step }), vm.ctx.slice_type()); self.push_value(obj); Ok(None) } @@ -706,11 +702,9 @@ impl Frame { } bytecode::Instruction::LoadBuildClass => { let rustfunc = PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyBuiltinFunction::new(Box::new( - builtins::builtin_build_class_, - ))), - }, + Box::new(PyBuiltinFunction::new(Box::new( + builtins::builtin_build_class_, + ))), vm.ctx.type_type(), ); self.push_value(rustfunc); diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index 77b1c14ebc..d2a098edfc 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -5,8 +5,7 @@ use std::fmt::Write; use std::ops::{Deref, DerefMut}; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, - TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, }; use super::objint; @@ -174,9 +173,7 @@ fn bytearray_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vec![] }; Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyByteArray::new(value)), - }, + Box::new(PyByteArray::new(value)), cls.clone(), )) } diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 2aa0006ddf..31fe95e14c 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -5,8 +5,8 @@ use std::ops::Deref; use super::objint; use super::objtype; use crate::pyobject::{ - PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectPayload, PyObjectPayload2, - PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectPayload2, PyObjectRef, PyResult, + TypeProtocol, }; use crate::vm::VirtualMachine; use num_traits::ToPrimitive; @@ -95,12 +95,7 @@ fn bytes_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vec![] }; - Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyBytes::new(value)), - }, - cls.clone(), - )) + Ok(PyObject::new(Box::new(PyBytes::new(value)), cls.clone())) } fn bytes_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -209,12 +204,10 @@ 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::AnyRustValue { - value: Box::new(PyIteratorValue { - position: Cell::new(0), - iterated_obj: obj.clone(), - }), - }, + Box::new(PyIteratorValue { + position: Cell::new(0), + iterated_obj: obj.clone(), + }), vm.ctx.iter_type(), ); diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index 66205559c9..afafe48ed0 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -2,8 +2,7 @@ use super::objfloat; use super::objint; use super::objtype; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, - TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use num_complex::Complex64; @@ -90,12 +89,7 @@ fn complex_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let value = Complex64::new(real, imag); - Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyComplex { value }), - }, - cls.clone(), - )) + Ok(PyObject::new(Box::new(PyComplex { value }), cls.clone())) } fn complex_real(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 84391cdd87..7e3cef048c 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -3,8 +3,8 @@ use std::collections::HashMap; use std::ops::{Deref, DerefMut}; use crate::pyobject::{ - PyAttributes, PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectPayload, - PyObjectPayload2, PyObjectRef, PyRef, PyResult, TypeProtocol, + PyAttributes, PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectPayload2, PyObjectRef, + PyRef, PyResult, TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; @@ -251,12 +251,10 @@ fn dict_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let key_list = vm.ctx.new_list(keys); let iter_obj = PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyIteratorValue { - position: Cell::new(0), - iterated_obj: key_list, - }), - }, + Box::new(PyIteratorValue { + position: Cell::new(0), + iterated_obj: key_list, + }), vm.ctx.iter_type(), ); @@ -273,12 +271,10 @@ fn dict_values(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let values_list = vm.ctx.new_list(values); let iter_obj = PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyIteratorValue { - position: Cell::new(0), - iterated_obj: values_list, - }), - }, + Box::new(PyIteratorValue { + position: Cell::new(0), + iterated_obj: values_list, + }), vm.ctx.iter_type(), ); @@ -295,12 +291,10 @@ fn dict_items(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let items_list = vm.ctx.new_list(items); let iter_obj = PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyIteratorValue { - position: Cell::new(0), - iterated_obj: items_list, - }), - }, + Box::new(PyIteratorValue { + position: Cell::new(0), + iterated_obj: items_list, + }), vm.ctx.iter_type(), ); @@ -348,12 +342,10 @@ pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: // this is not ideal let ptr = PyObjectRef::into_raw(dict_type.clone()) as *mut PyObject; unsafe { - (*ptr).payload = PyObjectPayload::AnyRustValue { - value: Box::new(objtype::PyClass { - name: String::from("dict"), - mro: vec![object_type], - }), - }; + (*ptr).payload = Box::new(objtype::PyClass { + name: String::from("dict"), + mro: vec![object_type], + }); (*ptr).dict = Some(RefCell::new(HashMap::new())); (*ptr).typ = Some(type_type.clone()); } diff --git a/vm/src/obj/objenumerate.rs b/vm/src/obj/objenumerate.rs index aa573ad6f0..d02fc20867 100644 --- a/vm/src/obj/objenumerate.rs +++ b/vm/src/obj/objenumerate.rs @@ -4,8 +4,7 @@ use std::ops::AddAssign; use super::objint; use super::objiter; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, - TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use num_bigint::BigInt; @@ -37,12 +36,10 @@ fn enumerate_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; let iterator = objiter::get_iter(vm, iterable)?; Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyEnumerate { - counter: RefCell::new(counter), - iterator, - }), - }, + Box::new(PyEnumerate { + counter: RefCell::new(counter), + iterator, + }), cls.clone(), )) } diff --git a/vm/src/obj/objfilter.rs b/vm/src/obj/objfilter.rs index 94703be575..009c7da90c 100644 --- a/vm/src/obj/objfilter.rs +++ b/vm/src/obj/objfilter.rs @@ -1,6 +1,6 @@ use crate::pyobject::{ - IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, - PyResult, TypeProtocol, + IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload2, PyObjectRef, PyResult, + TypeProtocol, }; use crate::vm::VirtualMachine; // Required for arg_check! to use isinstance @@ -27,12 +27,10 @@ fn filter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let iterator = objiter::get_iter(vm, iterable)?; Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyFilter { - predicate: function.clone(), - iterator, - }), - }, + Box::new(PyFilter { + predicate: function.clone(), + iterator, + }), cls.clone(), )) } diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 6f1790d682..efbd4fed40 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -3,8 +3,7 @@ use super::objint; use super::objstr; use super::objtype; use crate::pyobject::{ - IntoPyObject, PyContext, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyRef, - PyResult, TypeProtocol, + IntoPyObject, PyContext, PyObject, PyObjectPayload2, PyObjectRef, PyRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use num_bigint::ToBigInt; @@ -189,12 +188,7 @@ impl PyFloatRef { let type_name = objtype::get_type_name(&arg.typ()); return Err(vm.new_type_error(format!("can't convert {} to float", type_name))); }; - Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyFloat { value }), - }, - cls.clone(), - )) + Ok(PyObject::new(Box::new(PyFloat { value }), cls.clone())) } fn mod_(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { diff --git a/vm/src/obj/objgenerator.rs b/vm/src/obj/objgenerator.rs index 1babd5c9dc..4e2f40a125 100644 --- a/vm/src/obj/objgenerator.rs +++ b/vm/src/obj/objgenerator.rs @@ -4,8 +4,7 @@ use crate::frame::{ExecutionResult, Frame}; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, - TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -41,9 +40,7 @@ pub fn init(context: &PyContext) { pub fn new_generator(vm: &mut VirtualMachine, frame: PyObjectRef) -> PyResult { Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyGenerator { frame }), - }, + Box::new(PyGenerator { frame }), vm.ctx.generator_type.clone(), )) } diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 5b751faa0f..4352e97c61 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -6,8 +6,8 @@ use num_traits::{Pow, Signed, ToPrimitive, Zero}; use crate::format::FormatSpec; use crate::pyobject::{ - FromPyObjectRef, IntoPyObject, PyContext, PyFuncArgs, PyObject, PyObjectPayload, - PyObjectPayload2, PyObjectRef, PyRef, PyResult, TryFromObject, TypeProtocol, + FromPyObjectRef, IntoPyObject, PyContext, PyFuncArgs, PyObject, PyObjectPayload2, PyObjectRef, + PyRef, PyResult, TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -105,12 +105,7 @@ fn int_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Some(val) => to_int(vm, val, base)?, None => Zero::zero(), }; - Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyInt::new(val)), - }, - cls.clone(), - )) + Ok(PyObject::new(Box::new(PyInt::new(val)), cls.clone())) } // Casting function: diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index ecabb009aa..29d8dc386a 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -9,8 +9,8 @@ use super::objsequence::{ use super::objstr; use super::objtype; use crate::pyobject::{ - IdProtocol, OptionalArg, PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectPayload, - PyObjectPayload2, PyObjectRef, PyRef, PyResult, TypeProtocol, + IdProtocol, OptionalArg, PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectPayload2, + PyObjectRef, PyRef, PyResult, TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; use num_traits::ToPrimitive; @@ -111,12 +111,10 @@ impl PyListRef { fn iter(self, vm: &mut VirtualMachine) -> PyObjectRef { PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyIteratorValue { - position: Cell::new(0), - iterated_obj: self.into_object(), - }), - }, + Box::new(PyIteratorValue { + position: Cell::new(0), + iterated_obj: self.into_object(), + }), vm.ctx.iter_type(), ) } @@ -305,9 +303,7 @@ fn list_new( }; Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyList::from(elements)), - }, + Box::new(PyList::from(elements)), cls.into_object(), )) } diff --git a/vm/src/obj/objmap.rs b/vm/src/obj/objmap.rs index 4cbb8d3ee9..eb748bc01f 100644 --- a/vm/src/obj/objmap.rs +++ b/vm/src/obj/objmap.rs @@ -1,6 +1,5 @@ use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, - TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -31,12 +30,10 @@ fn map_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .map(|iterable| objiter::get_iter(vm, iterable)) .collect::, _>>()?; Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyMap { - mapper: function.clone(), - iterators, - }), - }, + Box::new(PyMap { + mapper: function.clone(), + iterators, + }), cls.clone(), )) } diff --git a/vm/src/obj/objmemory.rs b/vm/src/obj/objmemory.rs index 988abbed9c..c5669532c7 100644 --- a/vm/src/obj/objmemory.rs +++ b/vm/src/obj/objmemory.rs @@ -1,6 +1,5 @@ use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, - TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -19,11 +18,9 @@ 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( - PyObjectPayload::AnyRustValue { - value: Box::new(PyMemoryView { - obj: bytes_object.clone(), - }), - }, + Box::new(PyMemoryView { + obj: bytes_object.clone(), + }), cls.clone(), )) } diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 54fcd1b6eb..7a560399c5 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -3,7 +3,7 @@ use super::objtype; use crate::obj::objproperty::PropertyBuilder; use crate::pyobject::{ AttributeProtocol, DictProtocol, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObject, - PyObjectPayload, PyObjectRef, PyRef, PyResult, TypeProtocol, + PyObjectRef, PyRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use std::cell::RefCell; @@ -25,12 +25,10 @@ pub fn create_object(type_type: PyObjectRef, object_type: PyObjectRef, _dict_typ // this is not ideal let ptr = PyObjectRef::into_raw(object_type.clone()) as *mut PyObject; unsafe { - (*ptr).payload = PyObjectPayload::AnyRustValue { - value: Box::new(objtype::PyClass { - name: String::from("object"), - mro: vec![], - }), - }; + (*ptr).payload = Box::new(objtype::PyClass { + name: String::from("object"), + mro: vec![], + }); (*ptr).dict = Some(RefCell::new(HashMap::new())); (*ptr).typ = Some(type_type.clone()); } diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index 9aaea31cdc..03c012ad50 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -7,8 +7,8 @@ use std::marker::PhantomData; use crate::obj::objstr::PyStringRef; use crate::obj::objtype::PyClassRef; use crate::pyobject::{ - IntoPyNativeFunc, OptionalArg, PyContext, PyObject, PyObjectPayload, PyObjectPayload2, - PyObjectRef, PyRef, PyResult, + IntoPyNativeFunc, OptionalArg, PyContext, PyObject, PyObjectPayload2, PyObjectRef, PyRef, + PyResult, }; use crate::VirtualMachine; @@ -138,12 +138,7 @@ impl<'a, T> PropertyBuilder<'a, T> { deleter: None, }; - PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(payload), - }, - self.ctx.property_type(), - ) + PyObject::new(Box::new(payload), self.ctx.property_type()) } else { let payload = PyReadOnlyProperty { getter: self.getter.expect( @@ -151,12 +146,7 @@ impl<'a, T> PropertyBuilder<'a, T> { ), }; - PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(payload), - }, - self.ctx.readonly_property_type(), - ) + PyObject::new(Box::new(payload), self.ctx.readonly_property_type()) } } } diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index f249a1f486..3100d2351d 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -6,8 +6,8 @@ use num_integer::Integer; use num_traits::{One, Signed, ToPrimitive, Zero}; use crate::pyobject::{ - PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectPayload, PyObjectPayload2, - PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectPayload2, PyObjectRef, PyResult, + TypeProtocol, }; use crate::vm::VirtualMachine; @@ -229,9 +229,7 @@ fn range_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Err(vm.new_value_error("range with 0 step size".to_string())) } else { Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyRange { start, end, step }), - }, + Box::new(PyRange { start, end, step }), cls.clone(), )) } @@ -241,12 +239,10 @@ fn range_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(range, Some(vm.ctx.range_type()))]); Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyIteratorValue { - position: Cell::new(0), - iterated_obj: range.clone(), - }), - }, + Box::new(PyIteratorValue { + position: Cell::new(0), + iterated_obj: range.clone(), + }), vm.ctx.iter_type(), )) } @@ -257,17 +253,10 @@ fn range_reversed(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let range = get_value(zelf).reversed(); Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyIteratorValue { - position: Cell::new(0), - iterated_obj: PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(range), - }, - vm.ctx.range_type(), - ), - }), - }, + Box::new(PyIteratorValue { + position: Cell::new(0), + iterated_obj: PyObject::new(Box::new(range), vm.ctx.range_type()), + }), vm.ctx.iter_type(), )) } @@ -330,13 +319,11 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyRange { - start: new_start, - end: new_end, - step: new_step, - }), - }, + Box::new(PyRange { + start: new_start, + end: new_end, + step: new_step, + }), vm.ctx.range_type(), )) } else { diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index 5d7ddaf329..d9961b3acb 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -5,7 +5,7 @@ use std::ops::{Deref, DerefMut, Range}; use num_bigint::BigInt; use num_traits::{One, Signed, ToPrimitive, Zero}; -use crate::pyobject::{IdProtocol, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol}; +use crate::pyobject::{IdProtocol, PyObject, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; use super::objbool; @@ -163,29 +163,29 @@ pub fn get_item( } if subscript.payload::().is_some() { - let payload = if sequence.payload::().is_some() { - PyObjectPayload::AnyRustValue { - value: Box::new(PyList::from( + if sequence.payload::().is_some() { + Ok(PyObject::new( + Box::new(PyList::from( elements.to_vec().get_slice_items(vm, &subscript)?, )), - } + sequence.typ(), + )) } else if sequence.payload::().is_some() { - PyObjectPayload::AnyRustValue { - value: Box::new(PyTuple::from( + Ok(PyObject::new( + Box::new(PyTuple::from( elements.to_vec().get_slice_items(vm, &subscript)?, )), - } + sequence.typ(), + )) } else { panic!("sequence get_item called for non-sequence") - }; - - return Ok(PyObject::new(payload, sequence.typ())); + } + } else { + Err(vm.new_type_error(format!( + "TypeError: indexing type {:?} with index {:?} is not supported (yet?)", + sequence, subscript + ))) } - - Err(vm.new_type_error(format!( - "TypeError: indexing type {:?} with index {:?} is not supported (yet?)", - sequence, subscript - ))) } pub fn seq_equal( diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 76eca5ec2a..3f5c306a88 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -13,8 +13,8 @@ use super::objiter; use super::objstr; use super::objtype; use crate::pyobject::{ - PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectPayload, PyObjectPayload2, - PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectPayload2, PyObjectRef, PyResult, + TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; @@ -169,11 +169,9 @@ fn set_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PySet { - elements: RefCell::new(elements), - }), - }, + Box::new(PySet { + elements: RefCell::new(elements), + }), cls.clone(), )) } @@ -190,11 +188,9 @@ fn set_copy(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.set_type()))]); let elements = get_elements(s); Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PySet { - elements: RefCell::new(elements), - }), - }, + Box::new(PySet { + elements: RefCell::new(elements), + }), vm.ctx.set_type(), )) } @@ -346,11 +342,9 @@ fn set_union(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { elements.extend(get_elements(other).clone()); Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PySet { - elements: RefCell::new(elements), - }), - }, + Box::new(PySet { + elements: RefCell::new(elements), + }), vm.ctx.set_type(), )) } @@ -390,11 +384,9 @@ fn set_symmetric_difference(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResu } Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PySet { - elements: RefCell::new(elements), - }), - }, + Box::new(PySet { + elements: RefCell::new(elements), + }), vm.ctx.set_type(), )) } @@ -432,11 +424,9 @@ fn set_combine_inner( } Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PySet { - elements: RefCell::new(elements), - }), - }, + Box::new(PySet { + elements: RefCell::new(elements), + }), vm.ctx.set_type(), )) } @@ -566,12 +556,10 @@ fn set_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let items = get_elements(zelf).values().cloned().collect(); let set_list = vm.ctx.new_list(items); let iter_obj = PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyIteratorValue { - position: Cell::new(0), - iterated_obj: set_list, - }), - }, + Box::new(PyIteratorValue { + position: Cell::new(0), + iterated_obj: set_list, + }), vm.ctx.iter_type(), ); diff --git a/vm/src/obj/objslice.rs b/vm/src/obj/objslice.rs index 45b92b5af6..f146bb062e 100644 --- a/vm/src/obj/objslice.rs +++ b/vm/src/obj/objslice.rs @@ -1,7 +1,6 @@ use super::objint; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, - TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use num_bigint::BigInt; @@ -55,13 +54,11 @@ fn slice_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } }?; Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PySlice { - start: start.map(|x| objint::get_value(x)), - stop: stop.map(|x| objint::get_value(x)), - step: step.map(|x| objint::get_value(x)), - }), - }, + Box::new(PySlice { + 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(), )) } diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 95c1b12f98..9d120d2715 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -2,8 +2,8 @@ use std::cell::{Cell, RefCell}; use std::hash::{Hash, Hasher}; use crate::pyobject::{ - IdProtocol, OptionalArg, PyContext, PyIteratorValue, PyObject, PyObjectPayload, - PyObjectPayload2, PyObjectRef, PyRef, PyResult, + IdProtocol, OptionalArg, PyContext, PyIteratorValue, PyObject, PyObjectPayload2, PyObjectRef, + PyRef, PyResult, }; use crate::vm::{ReprGuard, VirtualMachine}; @@ -126,12 +126,10 @@ impl PyTupleRef { fn iter(self, vm: &mut VirtualMachine) -> PyObjectRef { PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyIteratorValue { - position: Cell::new(0), - iterated_obj: self.into_object(), - }), - }, + Box::new(PyIteratorValue { + position: Cell::new(0), + iterated_obj: self.into_object(), + }), vm.ctx.iter_type(), ) } @@ -216,9 +214,7 @@ fn tuple_new( }; Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyTuple::from(elements)), - }, + Box::new(PyTuple::from(elements)), cls.into_object(), )) } diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 44bb9bc628..a9d5e5c4fa 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -2,8 +2,8 @@ use std::cell::RefCell; use std::collections::HashMap; use crate::pyobject::{ - AttributeProtocol, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObject, PyObjectPayload, - PyObjectPayload2, PyObjectRef, PyRef, PyResult, TypeProtocol, + AttributeProtocol, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObject, PyObjectPayload2, + PyObjectRef, PyRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -32,12 +32,10 @@ pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, _dict_type: // this is not ideal let ptr = PyObjectRef::into_raw(type_type.clone()) as *mut PyObject; unsafe { - (*ptr).payload = PyObjectPayload::AnyRustValue { - value: Box::new(PyClass { - name: String::from("type"), - mro: vec![object_type], - }), - }; + (*ptr).payload = Box::new(PyClass { + name: String::from("type"), + mro: vec![object_type], + }); (*ptr).dict = Some(RefCell::new(PyAttributes::new())); (*ptr).typ = Some(type_type); } @@ -320,12 +318,10 @@ pub fn new( let mros = bases.into_iter().map(|x| _mro(x).unwrap()).collect(); let mro = linearise_mro(mros).unwrap(); Ok(PyObject { - payload: PyObjectPayload::AnyRustValue { - value: Box::new(PyClass { - name: String::from(name), - mro, - }), - }, + payload: Box::new(PyClass { + name: String::from(name), + mro, + }), dict: Some(RefCell::new(dict)), typ: Some(typ), } diff --git a/vm/src/obj/objzip.rs b/vm/src/obj/objzip.rs index 70cded6b20..25714fd917 100644 --- a/vm/src/obj/objzip.rs +++ b/vm/src/obj/objzip.rs @@ -1,6 +1,5 @@ use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, - TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -25,12 +24,7 @@ fn zip_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .iter() .map(|iterable| objiter::get_iter(vm, iterable)) .collect::, _>>()?; - Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyZip { iterators }), - }, - cls.clone(), - )) + Ok(PyObject::new(Box::new(PyZip { iterators }), cls.clone())) } fn zip_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index c7c99723c6..c213fcda77 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1,3 +1,4 @@ +use std::any::Any; use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::fmt; @@ -213,38 +214,19 @@ impl PyContext { let exceptions = exceptions::ExceptionZoo::new(&type_type, &object_type, &dict_type); let none = PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(objnone::PyNone), - }, + Box::new(objnone::PyNone), create_type("NoneType", &type_type, &object_type, &dict_type), ); - let ellipsis = PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(()), - }, - ellipsis_type.clone(), - ); + let ellipsis = PyObject::new(Box::new(()), ellipsis_type.clone()); let not_implemented = PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(()), - }, + Box::new(()), create_type("NotImplementedType", &type_type, &object_type, &dict_type), ); - let true_value = PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyInt::new(BigInt::one())), - }, - bool_type.clone(), - ); - let false_value = PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyInt::new(BigInt::zero())), - }, - bool_type.clone(), - ); + let true_value = PyObject::new(Box::new(PyInt::new(BigInt::one())), bool_type.clone()); + let false_value = PyObject::new(Box::new(PyInt::new(BigInt::zero())), bool_type.clone()); let context = PyContext { bool_type, memoryview_type, @@ -482,55 +464,28 @@ impl PyContext { } pub fn new_int(&self, i: T) -> PyObjectRef { - PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyInt::new(i)), - }, - self.int_type(), - ) + PyObject::new(Box::new(PyInt::new(i)), self.int_type()) } pub fn new_float(&self, value: f64) -> PyObjectRef { - PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyFloat::from(value)), - }, - self.float_type(), - ) + PyObject::new(Box::new(PyFloat::from(value)), self.float_type()) } pub fn new_complex(&self, value: Complex64) -> PyObjectRef { - PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyComplex::from(value)), - }, - self.complex_type(), - ) + PyObject::new(Box::new(PyComplex::from(value)), self.complex_type()) } pub fn new_str(&self, s: String) -> PyObjectRef { - PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(objstr::PyString { value: s }), - }, - self.str_type(), - ) + PyObject::new(Box::new(objstr::PyString { value: s }), self.str_type()) } pub fn new_bytes(&self, data: Vec) -> PyObjectRef { - PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(objbytes::PyBytes::new(data)), - }, - self.bytes_type(), - ) + PyObject::new(Box::new(objbytes::PyBytes::new(data)), self.bytes_type()) } pub fn new_bytearray(&self, data: Vec) -> PyObjectRef { PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(objbytearray::PyByteArray::new(data)), - }, + Box::new(objbytearray::PyByteArray::new(data)), self.bytearray_type(), ) } @@ -544,41 +499,21 @@ impl PyContext { } pub fn new_tuple(&self, elements: Vec) -> PyObjectRef { - PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyTuple::from(elements)), - }, - self.tuple_type(), - ) + PyObject::new(Box::new(PyTuple::from(elements)), self.tuple_type()) } pub fn new_list(&self, elements: Vec) -> PyObjectRef { - PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyList::from(elements)), - }, - self.list_type(), - ) + PyObject::new(Box::new(PyList::from(elements)), self.list_type()) } 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. - PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PySet::default()), - }, - self.set_type(), - ) + PyObject::new(Box::new(PySet::default()), self.set_type()) } pub fn new_dict(&self) -> PyObjectRef { - PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyDict::default()), - }, - self.dict_type(), - ) + PyObject::new(Box::new(PyDict::default()), self.dict_type()) } pub fn new_class(&self, name: &str, base: PyObjectRef) -> PyObjectRef { @@ -591,12 +526,10 @@ impl PyContext { pub fn new_module(&self, name: &str, dict: PyObjectRef) -> PyObjectRef { PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyModule { - name: name.to_string(), - dict, - }), - }, + Box::new(PyModule { + name: name.to_string(), + dict, + }), self.module_type.clone(), ) } @@ -606,20 +539,13 @@ impl PyContext { F: IntoPyNativeFunc, { PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyBuiltinFunction::new(f.into_func())), - }, + Box::new(PyBuiltinFunction::new(f.into_func())), self.builtin_function_or_method_type(), ) } pub fn new_frame(&self, code: PyObjectRef, scope: Scope) -> PyObjectRef { - PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(Frame::new(code, scope)), - }, - self.frame_type(), - ) + PyObject::new(Box::new(Frame::new(code, scope)), self.frame_type()) } pub fn new_property(&self, f: F) -> PyObjectRef @@ -630,12 +556,7 @@ impl PyContext { } pub fn new_code_object(&self, code: bytecode::CodeObject) -> PyObjectRef { - PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(objcode::PyCode::new(code)), - }, - self.code_type(), - ) + PyObject::new(Box::new(objcode::PyCode::new(code)), self.code_type()) } pub fn new_function( @@ -645,18 +566,14 @@ impl PyContext { defaults: PyObjectRef, ) -> PyObjectRef { PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyFunction::new(code_obj, scope, defaults)), - }, + Box::new(PyFunction::new(code_obj, scope, defaults)), self.function_type(), ) } pub fn new_bound_method(&self, function: PyObjectRef, object: PyObjectRef) -> PyObjectRef { PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyMethod::new(object, function)), - }, + Box::new(PyMethod::new(object, function)), self.bound_method_type(), ) } @@ -668,11 +585,9 @@ impl PyContext { PyAttributes::new() }; PyObject { - payload: PyObjectPayload::AnyRustValue { - value: Box::new(objobject::PyInstance), - }, typ: Some(class), dict: Some(RefCell::new(dict)), + payload: Box::new(objobject::PyInstance), } .into_ref() } @@ -735,11 +650,20 @@ impl Default for PyContext { /// This is an actual python object. It consists of a `typ` which is the /// python class, and carries some rust payload optionally. This rust /// payload can be a rust float or rust int in case of float and int objects. -#[derive(Default)] pub struct PyObject { - pub payload: PyObjectPayload, pub typ: Option, pub dict: Option>, // __dict__ member + pub payload: Box, +} + +impl Default for PyObject { + fn default() -> Self { + PyObject { + typ: None, + dict: None, + payload: Box::new(()), + } + } } /// A reference to a Python object. @@ -764,12 +688,7 @@ where { pub fn new(ctx: &PyContext, payload: T) -> Self { PyRef { - obj: PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(payload), - }, - T::required_type(ctx), - ), + obj: PyObject::new(Box::new(payload), T::required_type(ctx)), _payload: PhantomData, } } @@ -778,12 +697,7 @@ where let required_type = T::required_type(&vm.ctx); if objtype::issubclass(&cls.obj, &required_type) { Ok(PyRef { - obj: PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(payload), - }, - cls.obj, - ), + obj: PyObject::new(Box::new(payload), cls.obj), _payload: PhantomData, }) } else { @@ -1452,12 +1366,7 @@ where T: PyObjectPayload2 + Sized, { fn into_pyobject(self, ctx: &PyContext) -> PyResult { - Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(self), - }, - T::required_type(ctx), - )) + Ok(PyObject::new(Box::new(self), T::required_type(ctx))) } } @@ -1587,22 +1496,6 @@ into_py_native_func_tuple!((a, A), (b, B), (c, C)); into_py_native_func_tuple!((a, A), (b, B), (c, C), (d, D)); into_py_native_func_tuple!((a, A), (b, B), (c, C), (d, D), (e, E)); -/// Rather than determining the type of a python object, this enum is more -/// a holder for the rust payload of a python object. It is more a carrier -/// of rust data for a particular python object. Determine the python type -/// by using for example the `.typ()` method on a python object. -pub enum PyObjectPayload { - AnyRustValue { value: Box }, -} - -impl Default for PyObjectPayload { - fn default() -> Self { - PyObjectPayload::AnyRustValue { - value: Box::new(()), - } - } -} - // TODO: This is a workaround and shouldn't exist. // Each iterable type should have its own distinct iterator type. #[derive(Debug)] @@ -1617,16 +1510,8 @@ impl PyObjectPayload2 for PyIteratorValue { } } -impl fmt::Debug for PyObjectPayload { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - PyObjectPayload::AnyRustValue { value } => value.fmt(f), - } - } -} - impl PyObject { - pub fn new(payload: PyObjectPayload, typ: PyObjectRef) -> PyObjectRef { + pub fn new(payload: Box, typ: PyObjectRef) -> PyObjectRef { PyObject { payload, typ: Some(typ), @@ -1641,8 +1526,7 @@ impl PyObject { } pub fn payload(&self) -> Option<&T> { - let PyObjectPayload::AnyRustValue { ref value } = self.payload; - value.downcast_ref() + self.payload.downcast_ref() } } diff --git a/vm/src/stdlib/re.rs b/vm/src/stdlib/re.rs index 0828a13d14..bf24d7b51d 100644 --- a/vm/src/stdlib/re.rs +++ b/vm/src/stdlib/re.rs @@ -11,9 +11,7 @@ use regex::{Match, Regex}; use std::path::PathBuf; use crate::obj::objstr; -use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, -}; +use crate::pyobject::{PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, TypeProtocol}; use crate::VirtualMachine; /// Create the python `re` module with all its members. @@ -118,12 +116,7 @@ fn create_match(vm: &mut VirtualMachine, match_value: &Match) -> PyResult { end: match_value.end(), }; - Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(match_value), - }, - match_class.clone(), - )) + Ok(PyObject::new(Box::new(match_value), match_class.clone())) } /// Compile a regular expression into a Pattern object. @@ -141,12 +134,7 @@ fn re_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let module = import::import_module(vm, PathBuf::default(), "re").unwrap(); let pattern_class = vm.ctx.get_attr(&module, "Pattern").unwrap(); - Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(regex), - }, - pattern_class.clone(), - )) + Ok(PyObject::new(Box::new(regex), pattern_class.clone())) } fn pattern_match(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -192,8 +180,7 @@ fn match_end(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { /// Retrieve inner rust regex from python object: fn get_regex<'a>(obj: &'a PyObjectRef) -> &'a Regex { // TODO: Regex shouldn't be stored in payload directly, create newtype wrapper - let PyObjectPayload::AnyRustValue { ref value } = obj.payload; - if let Some(regex) = value.downcast_ref::() { + if let Some(regex) = obj.payload.downcast_ref::() { return regex; } panic!("Inner error getting regex {:?}", obj); @@ -201,8 +188,7 @@ fn get_regex<'a>(obj: &'a PyObjectRef) -> &'a Regex { /// Retrieve inner rust match from python object: fn get_match<'a>(obj: &'a PyObjectRef) -> &'a PyMatch { - let PyObjectPayload::AnyRustValue { ref value } = obj.payload; - if let Some(value) = value.downcast_ref::() { + if let Some(value) = obj.payload.downcast_ref::() { return value; } panic!("Inner error getting match {:?}", obj); diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index d6e6283af9..b4f1c61b31 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -9,9 +9,7 @@ use crate::obj::objbytes; use crate::obj::objint; use crate::obj::objsequence::get_elements; use crate::obj::objstr; -use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, -}; +use crate::pyobject::{PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; use num_traits::ToPrimitive; @@ -127,8 +125,7 @@ impl Socket { } fn get_socket<'a>(obj: &'a PyObjectRef) -> impl DerefMut + 'a { - let PyObjectPayload::AnyRustValue { ref value } = obj.payload; - if let Some(socket) = value.downcast_ref::>() { + if let Some(socket) = obj.payload.downcast_ref::>() { return socket.borrow_mut(); } panic!("Inner error getting socket {:?}", obj); @@ -151,12 +148,7 @@ fn socket_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let socket = RefCell::new(Socket::new(address_family, kind)); - Ok(PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(socket), - }, - cls.clone(), - )) + Ok(PyObject::new(Box::new(socket), cls.clone())) } fn socket_connect(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -274,12 +266,7 @@ fn socket_accept(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { con: Some(Connection::TcpStream(tcp_stream)), }); - let sock_obj = PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(socket), - }, - zelf.typ(), - ); + let sock_obj = PyObject::new(Box::new(socket), zelf.typ()); let addr_tuple = get_addr_tuple(vm, addr)?; From 5971fc3bd487a144cfd1730fd7bd2730e85dafee Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sun, 10 Mar 2019 13:48:19 -0700 Subject: [PATCH 246/380] rename PyObjectPayload2 to PyValue --- vm/src/frame.rs | 6 +++--- vm/src/obj/objbuiltinfunc.rs | 4 ++-- vm/src/obj/objbytearray.rs | 4 ++-- vm/src/obj/objbytes.rs | 5 ++--- vm/src/obj/objcode.rs | 4 ++-- vm/src/obj/objcomplex.rs | 4 ++-- vm/src/obj/objdict.rs | 6 +++--- vm/src/obj/objenumerate.rs | 4 ++-- vm/src/obj/objfilter.rs | 5 ++--- vm/src/obj/objfloat.rs | 4 ++-- vm/src/obj/objfunction.rs | 6 +++--- vm/src/obj/objgenerator.rs | 4 ++-- vm/src/obj/objint.rs | 6 +++--- vm/src/obj/objlist.rs | 6 +++--- vm/src/obj/objmap.rs | 4 ++-- vm/src/obj/objmemory.rs | 4 ++-- vm/src/obj/objmodule.rs | 4 ++-- vm/src/obj/objnone.rs | 5 ++--- vm/src/obj/objproperty.rs | 7 +++---- vm/src/obj/objrange.rs | 5 ++--- vm/src/obj/objset.rs | 5 ++--- vm/src/obj/objslice.rs | 4 ++-- vm/src/obj/objstr.rs | 6 +++--- vm/src/obj/objtuple.rs | 6 +++--- vm/src/obj/objtype.rs | 6 +++--- vm/src/obj/objweakref.rs | 4 ++-- vm/src/obj/objzip.rs | 4 ++-- vm/src/pyobject.rs | 14 +++++++------- 28 files changed, 70 insertions(+), 76 deletions(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 2e100f7914..8342010748 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -22,8 +22,8 @@ use crate::obj::objslice::PySlice; use crate::obj::objstr; use crate::obj::objtype; use crate::pyobject::{ - DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload2, PyObjectRef, - PyResult, TryFromObject, TypeProtocol, + DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, + TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -181,7 +181,7 @@ pub struct Frame { pub lasti: RefCell, // index of last instruction ran } -impl PyObjectPayload2 for Frame { +impl PyValue for Frame { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.frame_type() } diff --git a/vm/src/obj/objbuiltinfunc.rs b/vm/src/obj/objbuiltinfunc.rs index 8e3b2c3422..94a8bc59ba 100644 --- a/vm/src/obj/objbuiltinfunc.rs +++ b/vm/src/obj/objbuiltinfunc.rs @@ -1,13 +1,13 @@ use std::fmt; -use crate::pyobject::{PyContext, PyNativeFunc, PyObjectPayload2, PyObjectRef}; +use crate::pyobject::{PyContext, PyNativeFunc, PyObjectRef, PyValue}; pub struct PyBuiltinFunction { // TODO: shouldn't be public pub value: PyNativeFunc, } -impl PyObjectPayload2 for PyBuiltinFunction { +impl PyValue for PyBuiltinFunction { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.builtin_function_or_method_type() } diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index d2a098edfc..56a925b883 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -5,7 +5,7 @@ use std::fmt::Write; use std::ops::{Deref, DerefMut}; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, }; use super::objint; @@ -28,7 +28,7 @@ impl PyByteArray { } } -impl PyObjectPayload2 for PyByteArray { +impl PyValue for PyByteArray { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.bytearray_type() } diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 31fe95e14c..b24912bafd 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -5,8 +5,7 @@ use std::ops::Deref; use super::objint; use super::objtype; use crate::pyobject::{ - PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectPayload2, PyObjectRef, PyResult, - TypeProtocol, + PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; use num_traits::ToPrimitive; @@ -30,7 +29,7 @@ impl Deref for PyBytes { } } -impl PyObjectPayload2 for PyBytes { +impl PyValue for PyBytes { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.bytes_type() } diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index ccd2114aed..36aaf4eab8 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -4,7 +4,7 @@ use crate::bytecode; use crate::pyobject::{ - IdProtocol, PyContext, PyFuncArgs, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, + IdProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; use std::fmt; @@ -25,7 +25,7 @@ impl fmt::Debug for PyCode { } } -impl PyObjectPayload2 for PyCode { +impl PyValue for PyCode { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.code_type() } diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index afafe48ed0..76f298bdd8 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -2,7 +2,7 @@ use super::objfloat; use super::objint; use super::objtype; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; use num_complex::Complex64; @@ -13,7 +13,7 @@ pub struct PyComplex { value: Complex64, } -impl PyObjectPayload2 for PyComplex { +impl PyValue for PyComplex { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.complex_type() } diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 7e3cef048c..8d8a62c4f0 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -3,8 +3,8 @@ use std::collections::HashMap; use std::ops::{Deref, DerefMut}; use crate::pyobject::{ - PyAttributes, PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectPayload2, PyObjectRef, - PyRef, PyResult, TypeProtocol, + PyAttributes, PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectRef, PyRef, PyResult, + PyValue, TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; @@ -21,7 +21,7 @@ pub struct PyDict { } pub type PyDictRef = PyRef; -impl PyObjectPayload2 for PyDict { +impl PyValue for PyDict { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.dict_type() } diff --git a/vm/src/obj/objenumerate.rs b/vm/src/obj/objenumerate.rs index d02fc20867..c2c6c86189 100644 --- a/vm/src/obj/objenumerate.rs +++ b/vm/src/obj/objenumerate.rs @@ -4,7 +4,7 @@ use std::ops::AddAssign; use super::objint; use super::objiter; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; use num_bigint::BigInt; @@ -16,7 +16,7 @@ pub struct PyEnumerate { iterator: PyObjectRef, } -impl PyObjectPayload2 for PyEnumerate { +impl PyValue for PyEnumerate { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.enumerate_type() } diff --git a/vm/src/obj/objfilter.rs b/vm/src/obj/objfilter.rs index 009c7da90c..f7a08e30fc 100644 --- a/vm/src/obj/objfilter.rs +++ b/vm/src/obj/objfilter.rs @@ -1,6 +1,5 @@ use crate::pyobject::{ - IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload2, PyObjectRef, PyResult, - TypeProtocol, + IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; // Required for arg_check! to use isinstance @@ -13,7 +12,7 @@ pub struct PyFilter { iterator: PyObjectRef, } -impl PyObjectPayload2 for PyFilter { +impl PyValue for PyFilter { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.filter_type() } diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index efbd4fed40..a5416b233f 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -3,7 +3,7 @@ use super::objint; use super::objstr; use super::objtype; use crate::pyobject::{ - IntoPyObject, PyContext, PyObject, PyObjectPayload2, PyObjectRef, PyRef, PyResult, TypeProtocol, + IntoPyObject, PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; use num_bigint::ToBigInt; @@ -15,7 +15,7 @@ pub struct PyFloat { value: f64, } -impl PyObjectPayload2 for PyFloat { +impl PyValue for PyFloat { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.float_type() } diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index 2f278ca076..ec2a460737 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -1,6 +1,6 @@ use crate::frame::Scope; use crate::pyobject::{ - AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectPayload2, PyObjectRef, PyResult, + AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -23,7 +23,7 @@ impl PyFunction { } } -impl PyObjectPayload2 for PyFunction { +impl PyValue for PyFunction { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.function_type() } @@ -42,7 +42,7 @@ impl PyMethod { } } -impl PyObjectPayload2 for PyMethod { +impl PyValue for PyMethod { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.bound_method_type() } diff --git a/vm/src/obj/objgenerator.rs b/vm/src/obj/objgenerator.rs index 4e2f40a125..3ba5037135 100644 --- a/vm/src/obj/objgenerator.rs +++ b/vm/src/obj/objgenerator.rs @@ -4,7 +4,7 @@ use crate::frame::{ExecutionResult, Frame}; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -13,7 +13,7 @@ pub struct PyGenerator { frame: PyObjectRef, } -impl PyObjectPayload2 for PyGenerator { +impl PyValue for PyGenerator { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.generator_type() } diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 4352e97c61..7b77301f3a 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -6,8 +6,8 @@ use num_traits::{Pow, Signed, ToPrimitive, Zero}; use crate::format::FormatSpec; use crate::pyobject::{ - FromPyObjectRef, IntoPyObject, PyContext, PyFuncArgs, PyObject, PyObjectPayload2, PyObjectRef, - PyRef, PyResult, TryFromObject, TypeProtocol, + FromPyObjectRef, IntoPyObject, PyContext, PyFuncArgs, PyObject, PyObjectRef, PyRef, PyResult, + PyValue, TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -31,7 +31,7 @@ impl PyInt { } } -impl PyObjectPayload2 for PyInt { +impl PyValue for PyInt { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.int_type() } diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 29d8dc386a..c2a3d84975 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -9,8 +9,8 @@ use super::objsequence::{ use super::objstr; use super::objtype; use crate::pyobject::{ - IdProtocol, OptionalArg, PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectPayload2, - PyObjectRef, PyRef, PyResult, TypeProtocol, + IdProtocol, OptionalArg, PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectRef, PyRef, + PyResult, PyValue, TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; use num_traits::ToPrimitive; @@ -29,7 +29,7 @@ impl From> for PyList { } } -impl PyObjectPayload2 for PyList { +impl PyValue for PyList { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.list_type() } diff --git a/vm/src/obj/objmap.rs b/vm/src/obj/objmap.rs index eb748bc01f..1a84307e47 100644 --- a/vm/src/obj/objmap.rs +++ b/vm/src/obj/objmap.rs @@ -1,5 +1,5 @@ use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -11,7 +11,7 @@ pub struct PyMap { iterators: Vec, } -impl PyObjectPayload2 for PyMap { +impl PyValue for PyMap { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.map_type() } diff --git a/vm/src/obj/objmemory.rs b/vm/src/obj/objmemory.rs index c5669532c7..13e455d0f4 100644 --- a/vm/src/obj/objmemory.rs +++ b/vm/src/obj/objmemory.rs @@ -1,5 +1,5 @@ use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -8,7 +8,7 @@ pub struct PyMemoryView { obj: PyObjectRef, } -impl PyObjectPayload2 for PyMemoryView { +impl PyValue for PyMemoryView { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.memoryview_type() } diff --git a/vm/src/obj/objmodule.rs b/vm/src/obj/objmodule.rs index 92f55566d2..297ee6dc00 100644 --- a/vm/src/obj/objmodule.rs +++ b/vm/src/obj/objmodule.rs @@ -1,4 +1,4 @@ -use crate::pyobject::{DictProtocol, PyContext, PyObjectPayload2, PyObjectRef, PyRef, PyResult}; +use crate::pyobject::{DictProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; #[derive(Clone, Debug)] @@ -8,7 +8,7 @@ pub struct PyModule { } pub type PyModuleRef = PyRef; -impl PyObjectPayload2 for PyModule { +impl PyValue for PyModule { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.module_type() } diff --git a/vm/src/obj/objnone.rs b/vm/src/obj/objnone.rs index 6cde6ef063..6e3bca6804 100644 --- a/vm/src/obj/objnone.rs +++ b/vm/src/obj/objnone.rs @@ -1,6 +1,5 @@ use crate::pyobject::{ - IntoPyObject, PyContext, PyFuncArgs, PyObjectPayload2, PyObjectRef, PyRef, PyResult, - TypeProtocol, + IntoPyObject, PyContext, PyFuncArgs, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -8,7 +7,7 @@ use crate::vm::VirtualMachine; pub struct PyNone; pub type PyNoneRef = PyRef; -impl PyObjectPayload2 for PyNone { +impl PyValue for PyNone { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.none().typ() } diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index 03c012ad50..166ca9c8ef 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -7,8 +7,7 @@ use std::marker::PhantomData; use crate::obj::objstr::PyStringRef; use crate::obj::objtype::PyClassRef; use crate::pyobject::{ - IntoPyNativeFunc, OptionalArg, PyContext, PyObject, PyObjectPayload2, PyObjectRef, PyRef, - PyResult, + IntoPyNativeFunc, OptionalArg, PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue, }; use crate::VirtualMachine; @@ -18,7 +17,7 @@ pub struct PyReadOnlyProperty { getter: PyObjectRef, } -impl PyObjectPayload2 for PyReadOnlyProperty { +impl PyValue for PyReadOnlyProperty { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.readonly_property_type() } @@ -40,7 +39,7 @@ pub struct PyProperty { deleter: Option, } -impl PyObjectPayload2 for PyProperty { +impl PyValue for PyProperty { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.property_type() } diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 3100d2351d..84448a4b00 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -6,8 +6,7 @@ use num_integer::Integer; use num_traits::{One, Signed, ToPrimitive, Zero}; use crate::pyobject::{ - PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectPayload2, PyObjectRef, PyResult, - TypeProtocol, + PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -24,7 +23,7 @@ pub struct PyRange { pub step: BigInt, } -impl PyObjectPayload2 for PyRange { +impl PyValue for PyRange { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.range_type() } diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 3f5c306a88..83b36d6765 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -13,8 +13,7 @@ use super::objiter; use super::objstr; use super::objtype; use crate::pyobject::{ - PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectPayload2, PyObjectRef, PyResult, - TypeProtocol, + PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; @@ -23,7 +22,7 @@ pub struct PySet { elements: RefCell>, } -impl PyObjectPayload2 for PySet { +impl PyValue for PySet { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.set_type() } diff --git a/vm/src/obj/objslice.rs b/vm/src/obj/objslice.rs index f146bb062e..cb06f9424f 100644 --- a/vm/src/obj/objslice.rs +++ b/vm/src/obj/objslice.rs @@ -1,6 +1,6 @@ use super::objint; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; use num_bigint::BigInt; @@ -13,7 +13,7 @@ pub struct PySlice { pub step: Option, } -impl PyObjectPayload2 for PySlice { +impl PyValue for PySlice { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.slice_type() } diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index d9c39e3371..a0e2e110d7 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -7,8 +7,8 @@ use unicode_segmentation::UnicodeSegmentation; use crate::format::{FormatParseError, FormatPart, FormatString}; use crate::pyobject::{ - IntoPyObject, OptionalArg, PyContext, PyFuncArgs, PyIterable, PyObjectPayload2, PyObjectRef, - PyRef, PyResult, TypeProtocol, + IntoPyObject, OptionalArg, PyContext, PyFuncArgs, PyIterable, PyObjectRef, PyRef, PyResult, + PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -596,7 +596,7 @@ impl PyStringRef { } } -impl PyObjectPayload2 for PyString { +impl PyValue for PyString { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.str_type() } diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 9d120d2715..5772aa3e8b 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -2,8 +2,8 @@ use std::cell::{Cell, RefCell}; use std::hash::{Hash, Hasher}; use crate::pyobject::{ - IdProtocol, OptionalArg, PyContext, PyIteratorValue, PyObject, PyObjectPayload2, PyObjectRef, - PyRef, PyResult, + IdProtocol, OptionalArg, PyContext, PyIteratorValue, PyObject, PyObjectRef, PyRef, PyResult, + PyValue, }; use crate::vm::{ReprGuard, VirtualMachine}; @@ -30,7 +30,7 @@ impl From> for PyTuple { } } -impl PyObjectPayload2 for PyTuple { +impl PyValue for PyTuple { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.tuple_type() } diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index a9d5e5c4fa..3b893f321e 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -2,8 +2,8 @@ use std::cell::RefCell; use std::collections::HashMap; use crate::pyobject::{ - AttributeProtocol, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObject, PyObjectPayload2, - PyObjectRef, PyRef, PyResult, TypeProtocol, + AttributeProtocol, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObject, PyObjectRef, + PyRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -18,7 +18,7 @@ pub struct PyClass { pub type PyClassRef = PyRef; -impl PyObjectPayload2 for PyClass { +impl PyValue for PyClass { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.type_type() } diff --git a/vm/src/obj/objweakref.rs b/vm/src/obj/objweakref.rs index 45cb4108a8..d2e2d08dc4 100644 --- a/vm/src/obj/objweakref.rs +++ b/vm/src/obj/objweakref.rs @@ -1,5 +1,5 @@ use crate::obj::objtype::PyClassRef; -use crate::pyobject::PyObjectPayload2; +use crate::pyobject::PyValue; use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyRef, PyResult}; use crate::vm::VirtualMachine; @@ -22,7 +22,7 @@ impl PyWeak { } } -impl PyObjectPayload2 for PyWeak { +impl PyValue for PyWeak { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.weakref_type() } diff --git a/vm/src/obj/objzip.rs b/vm/src/obj/objzip.rs index 25714fd917..883d95ab1b 100644 --- a/vm/src/obj/objzip.rs +++ b/vm/src/obj/objzip.rs @@ -1,5 +1,5 @@ use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -10,7 +10,7 @@ pub struct PyZip { iterators: Vec, } -impl PyObjectPayload2 for PyZip { +impl PyValue for PyZip { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.zip_type() } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index c213fcda77..5b76ffd5b8 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -684,7 +684,7 @@ pub struct PyRef { impl PyRef where - T: PyObjectPayload2, + T: PyValue, { pub fn new(ctx: &PyContext, payload: T) -> Self { PyRef { @@ -717,7 +717,7 @@ where impl Deref for PyRef where - T: PyObjectPayload2, + T: PyValue, { type Target = T; @@ -728,7 +728,7 @@ where impl TryFromObject for PyRef where - T: PyObjectPayload2, + T: PyValue, { fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { if objtype::isinstance(&obj, &T::required_type(&vm.ctx)) { @@ -1363,7 +1363,7 @@ where // explicitly implementing `IntoPyObject`. impl IntoPyObject for T where - T: PyObjectPayload2 + Sized, + T: PyValue + Sized, { fn into_pyobject(self, ctx: &PyContext) -> PyResult { Ok(PyObject::new(Box::new(self), T::required_type(ctx))) @@ -1504,7 +1504,7 @@ pub struct PyIteratorValue { pub iterated_obj: PyObjectRef, } -impl PyObjectPayload2 for PyIteratorValue { +impl PyValue for PyIteratorValue { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.iter_type() } @@ -1525,14 +1525,14 @@ impl PyObject { Rc::new(self) } - pub fn payload(&self) -> Option<&T> { + pub fn payload(&self) -> Option<&T> { self.payload.downcast_ref() } } // The intention is for this to replace `PyObjectPayload` once everything is // converted to use `PyObjectPayload::AnyRustvalue`. -pub trait PyObjectPayload2: std::any::Any + fmt::Debug { +pub trait PyValue: Any + fmt::Debug { fn required_type(ctx: &PyContext) -> PyObjectRef; } From 4510489bba1c135fc84946bdadf721888d2ebb47 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sun, 10 Mar 2019 15:19:04 -0700 Subject: [PATCH 247/380] Fix PyObjectPayload usage in wasm --- wasm/lib/src/browser_module.rs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index d8014ae520..3d9ed71354 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -4,7 +4,7 @@ use js_sys::Promise; use num_traits::cast::ToPrimitive; use rustpython_vm::obj::{objint, objstr}; use rustpython_vm::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, TypeProtocol, }; use rustpython_vm::{import::import, VirtualMachine}; use std::path::PathBuf; @@ -157,20 +157,13 @@ pub struct PyPromise { impl PyPromise { pub fn new_obj(promise_type: PyObjectRef, value: Promise) -> PyObjectRef { - PyObject::new( - PyObjectPayload::AnyRustValue { - value: Box::new(PyPromise { value }), - }, - promise_type, - ) + PyObject::new(Box::new(PyPromise { value }), promise_type) } } pub fn get_promise_value(obj: &PyObjectRef) -> Promise { - if let PyObjectPayload::AnyRustValue { value } = &obj.payload { - if let Some(promise) = value.downcast_ref::() { - return promise.value.clone(); - } + if let Some(promise) = obj.payload.downcast_ref::() { + return promise.value.clone(); } panic!("Inner error getting promise") } From 8407f7498e44ab98156deab1e80632ba895b0cc1 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Sun, 10 Mar 2019 17:20:32 -0500 Subject: [PATCH 248/380] Change setStdout to take "console" instead of undefined --- wasm/lib/src/vm_class.rs | 30 ++++++++++++++++++++++-------- wasm/lib/src/wasm_builtins.rs | 32 ++++++++++++++------------------ 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index 18de108957..02e48c4dae 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -11,7 +11,7 @@ use rustpython_vm::{ use std::cell::RefCell; use std::collections::HashMap; use std::rc::{Rc, Weak}; -use wasm_bindgen::prelude::*; +use wasm_bindgen::{prelude::*, JsCast}; pub trait HeldRcInner {} @@ -271,11 +271,25 @@ impl WASMVirtualMachine { ref mut scope, .. }| { + fn error() -> JsValue { + TypeError::new( + "Unknown stdout option, please pass a function, a textarea element, or \ + 'console'", + ) + .into() + } let print_fn: Box PyResult> = - if let Some(selector) = stdout.as_string() { + if let Some(s) = stdout.as_string() { + let print = match s.as_str() { + "console" => wasm_builtins::builtin_print_console, + _ => return Err(error()), + }; + Box::new(print) + } else if let Some(element) = stdout.dyn_ref::() { + let element = element.clone(); Box::new( move |vm: &mut VirtualMachine, args: PyFuncArgs| -> PyResult { - wasm_builtins::builtin_print_html(vm, args, &selector) + wasm_builtins::builtin_print_html(vm, args, &element) }, ) } else if stdout.is_function() { @@ -291,12 +305,12 @@ impl WASMVirtualMachine { }, ) } else if stdout.is_undefined() || stdout.is_null() { - Box::new(wasm_builtins::builtin_print_console) + fn noop(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { + Ok(vm.get_none()) + } + Box::new(noop) } else { - return Err(TypeError::new( - "stdout must be null, a function or a css selector", - ) - .into()); + return Err(error()); }; scope.store_name(&vm, "print", vm.ctx.new_rustfunc(print_fn)); Ok(()) diff --git a/wasm/lib/src/wasm_builtins.rs b/wasm/lib/src/wasm_builtins.rs index 7f2b7a6225..a5867ec020 100644 --- a/wasm/lib/src/wasm_builtins.rs +++ b/wasm/lib/src/wasm_builtins.rs @@ -9,33 +9,25 @@ use js_sys::{self, Array}; use rustpython_vm::obj::{objstr, objtype}; use rustpython_vm::pyobject::{IdProtocol, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use rustpython_vm::VirtualMachine; -use wasm_bindgen::{prelude::*, JsCast}; +use wasm_bindgen::prelude::*; use web_sys::{self, console, HtmlTextAreaElement}; pub(crate) fn window() -> web_sys::Window { web_sys::window().expect("Window to be available") } -// The HTML id of the textarea element that act as our STDOUT +// The HTML id of the element element that act as our STDOUT -pub fn print_to_html(text: &str, selector: &str) -> Result<(), JsValue> { - let document = window().document().expect("Document to be available"); - let element = document - .query_selector(selector)? - .ok_or_else(|| js_sys::TypeError::new("Couldn't get element"))?; - let textarea = element - .dyn_ref::() - .ok_or_else(|| js_sys::TypeError::new("Element must be a textarea"))?; +pub fn print_to_html(text: &str, element: &HtmlTextAreaElement) -> Result<(), JsValue> { + let value = element.value(); - let value = textarea.value(); + let scroll_height = element.scroll_height(); + let scrolled_to_bottom = scroll_height - element.scroll_top() == element.client_height(); - let scroll_height = textarea.scroll_height(); - let scrolled_to_bottom = scroll_height - textarea.scroll_top() == textarea.client_height(); - - textarea.set_value(&format!("{}{}", value, text)); + element.set_value(&format!("{}{}", value, text)); if scrolled_to_bottom { - textarea.scroll_with_x_and_y(0.0, scroll_height.into()); + element.scroll_with_x_and_y(0.0, scroll_height.into()); } Ok(()) @@ -93,9 +85,13 @@ pub fn format_print_args(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result PyResult { +pub fn builtin_print_html( + vm: &mut VirtualMachine, + args: PyFuncArgs, + element: &HtmlTextAreaElement, +) -> 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, element).map_err(|err| convert::js_to_py(vm, err))?; Ok(vm.get_none()) } From 29ec84ead905cd0696404d1b80cb7d129d3e148c Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Sun, 10 Mar 2019 17:22:22 -0500 Subject: [PATCH 249/380] Change main.js to print to the console --- wasm/demo/src/main.js | 73 ++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/wasm/demo/src/main.js b/wasm/demo/src/main.js index 2f6de563d1..b35c9aaa29 100644 --- a/wasm/demo/src/main.js +++ b/wasm/demo/src/main.js @@ -36,7 +36,7 @@ function runCodeFromTextarea() { const code = editor.getValue(); try { const result = rp.pyEval(code, { - stdout: '#console' + stdout: consoleElement }); if (result !== null) { consoleElement.value += `\n${result}\n`; @@ -74,57 +74,58 @@ snippets.addEventListener('change', updateSnippet); // option selected for the `select`, but the textarea won't be updated) updateSnippet(); -const prompt = ">>>>> "; +const prompt = '>>>>> '; const term = new Terminal(); term.open(document.getElementById('terminal')); term.write(prompt); function removeNonAscii(str) { - if ((str===null) || (str==='')) - return false; - else - str = str.toString(); + if (str === null || str === '') return false; + else str = str.toString(); return str.replace(/[^\x20-\x7E]/g, ''); } function printToConsole(data) { - term.write(removeNonAscii(data) + "\r\n"); + term.write(removeNonAscii(data) + '\r\n'); } -const terminalVM = rp.vmStore.init("term_vm"); +const terminalVM = rp.vmStore.init('term_vm'); terminalVM.setStdout(printToConsole); -var input = ""; -term.on("data", (data) => { - const code = data.charCodeAt(0); - if (code == 13) { // CR - if (input[input.length - 1] == ':') { - input += data - term.write("\r\n....."); - } else { - term.write("\r\n"); - try { - terminalVM.exec(input); - } catch (err) { - if (err instanceof WebAssembly.RuntimeError) { - err = window.__RUSTPYTHON_ERROR || err; +var input = ''; +term.on('data', data => { + const code = data.charCodeAt(0); + if (code == 13) { + // CR + if (input[input.length - 1] == ':') { + input += data; + term.write('\r\n.....'); + } else { + term.write('\r\n'); + try { + terminalVM.exec(input); + } catch (err) { + if (err instanceof WebAssembly.RuntimeError) { + err = window.__RUSTPYTHON_ERROR || err; + } + printToConsole(err); } - printToConsole(err); + term.write(prompt); + input = ''; } - term.write(prompt); - input = ""; - } - } else if (code == 127) { - if (input.length > 0) { - term.write("\b \b"); - input = input.slice(0, -1); + } else if (code == 127) { + if (input.length > 0) { + term.write('\b \b'); + input = input.slice(0, -1); + } + } else if (code < 32 || code == 127) { + // Control + return; + } else { + // Visible + term.write(data); + input += data; } - } else if (code < 32 || code == 127) { // Control - return; - } else { // Visible - term.write(data); - input += data; - } }); From 40ef62db70dc891035dbcb37410489825315b5b5 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Sun, 10 Mar 2019 17:42:39 -0500 Subject: [PATCH 250/380] Use a generator for the mandelbrot demo snippet --- wasm/demo/snippets/mandelbrot.py | 69 +++++++++++++++++--------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/wasm/demo/snippets/mandelbrot.py b/wasm/demo/snippets/mandelbrot.py index adeeefe13e..7d55137198 100644 --- a/wasm/demo/snippets/mandelbrot.py +++ b/wasm/demo/snippets/mandelbrot.py @@ -3,36 +3,39 @@ w = 50.0 h = 50.0 -# to make up for the lack of `global` -_y = {'y': 0.0} - -def mandel(_time_elapsed=None): - y = _y['y'] - if y >= h: - return - x = 0.0 - while x < w: - Zr, Zi, Tr, Ti = 0.0, 0.0, 0.0, 0.0 - Cr = 2 * x / w - 1.5 - Ci = 2 * y / h - 1.0 - - i = 0 - while i < 50 and Tr + Ti <= 4: - Zi = 2 * Zr * Zi + Ci - Zr = Tr - Ti + Cr - Tr = Zr * Zr - Ti = Zi * Zi - i += 1 - - if Tr + Ti <= 4: - print('*', end='') - else: - print('·', end='') - - x += 1 - - print() - _y['y'] += 1 - request_animation_frame(mandel) - -request_animation_frame(mandel) +def mandel(): + """Print a mandelbrot fractal to the console, yielding after each character + is printed""" + y = 0.0 + while y < h: + x = 0.0 + while x < w: + Zr, Zi, Tr, Ti = 0.0, 0.0, 0.0, 0.0 + Cr = 2 * x / w - 1.5 + Ci = 2 * y / h - 1.0 + + i = 0 + while i < 50 and Tr + Ti <= 4: + Zi = 2 * Zr * Zi + Ci + Zr = Tr - Ti + Cr + Tr = Zr * Zr + Ti = Zi * Zi + i += 1 + + if Tr + Ti <= 4: + print('*', end='') + else: + print('·', end='') + + x += 1 + yield + + print() + y += 1 + yield + +gen = mandel() +def gen_cb(_time=None): + gen.__next__() + request_animation_frame(gen_cb) +gen_cb() From b123e58c557a27286f667fb2aef1d565e8023fa0 Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Mon, 11 Mar 2019 00:31:26 +0100 Subject: [PATCH 251/380] Support index in list.pop() --- tests/snippets/list.py | 16 +++++++++++++++- vm/src/obj/objlist.rs | 14 ++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/tests/snippets/list.py b/tests/snippets/list.py index 9e385abdce..6b9839321d 100644 --- a/tests/snippets/list.py +++ b/tests/snippets/list.py @@ -29,8 +29,22 @@ assert x > y, "list __gt__ failed" -assert [1,2,'a'].pop() == 'a', "list pop failed" +x = [0, 1, 2] +assert x.pop() == 2 +assert x == [0, 1] + +def test_pop(lst, idx, value, new_lst): + assert lst.pop(idx) == value + assert lst == new_lst +test_pop([0, 1, 2], -1, 2, [0, 1]) +test_pop([0, 1, 2], 0, 0, [1, 2]) +test_pop([0, 1, 2], 1, 1, [0, 2]) +test_pop([0, 1, 2], 2, 2, [0, 1]) assert_raises(IndexError, lambda: [].pop()) +assert_raises(IndexError, lambda: [].pop(0)) +assert_raises(IndexError, lambda: [].pop(-1)) +assert_raises(IndexError, lambda: [0].pop(1)) +assert_raises(IndexError, lambda: [0].pop(-2)) recursive = [] recursive.append(recursive) diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 8ad69bff31..477ebcf5e4 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -203,12 +203,18 @@ impl PyListRef { Err(vm.new_value_error(format!("'{}' is not in list", needle_str))) } - fn pop(self, vm: &mut VirtualMachine) -> PyResult { + fn pop(self, i: OptionalArg, vm: &mut VirtualMachine) -> PyResult { + let mut i = i.into_option().unwrap_or(-1); let mut elements = self.elements.borrow_mut(); - if let Some(result) = elements.pop() { - Ok(result) - } else { + if i < 0 { + i += elements.len() as isize; + } + if elements.is_empty() { Err(vm.new_index_error("pop from empty list".to_string())) + } else if i < 0 || i as usize >= elements.len() { + Err(vm.new_index_error("pop index out of range".to_string())) + } else { + Ok(elements.remove(i as usize)) } } From 09ab6d1d7c49dbfd3d57df4bef70d3ce075f62f2 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Sun, 10 Mar 2019 19:12:11 -0500 Subject: [PATCH 252/380] Reformat --- wasm/lib/src/vm_class.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index 02e48c4dae..bdf3eb3254 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -280,11 +280,10 @@ impl WASMVirtualMachine { } let print_fn: Box PyResult> = if let Some(s) = stdout.as_string() { - let print = match s.as_str() { - "console" => wasm_builtins::builtin_print_console, + match s.as_str() { + "console" => Box::new(wasm_builtins::builtin_print_console), _ => return Err(error()), - }; - Box::new(print) + } } else if let Some(element) = stdout.dyn_ref::() { let element = element.clone(); Box::new( From 053ceb1a30c267c88169c34ee099eb35a5a58653 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sun, 10 Mar 2019 20:14:02 -0700 Subject: [PATCH 253/380] Move payload boxing into PyObject::new --- vm/src/compile.rs | 5 +- vm/src/frame.rs | 7 +- vm/src/obj/objbytearray.rs | 5 +- vm/src/obj/objbytes.rs | 6 +- vm/src/obj/objcomplex.rs | 2 +- vm/src/obj/objdict.rs | 12 +-- vm/src/obj/objenumerate.rs | 4 +- vm/src/obj/objfilter.rs | 4 +- vm/src/obj/objfloat.rs | 2 +- vm/src/obj/objgenerator.rs | 2 +- vm/src/obj/objint.rs | 2 +- vm/src/obj/objlist.rs | 9 +-- vm/src/obj/objmap.rs | 4 +- vm/src/obj/objmemory.rs | 4 +- vm/src/obj/objproperty.rs | 4 +- vm/src/obj/objrange.rs | 19 ++--- vm/src/obj/objsequence.rs | 8 +- vm/src/obj/objset.rs | 24 +++--- vm/src/obj/objslice.rs | 4 +- vm/src/obj/objtuple.rs | 9 +-- vm/src/obj/objzip.rs | 2 +- vm/src/pyobject.rs | 83 +++++++++++--------- vm/src/stdlib/re.rs | 23 +++++- vm/src/stdlib/socket.rs | 135 +++++++++++++++++++-------------- wasm/lib/src/browser_module.rs | 12 ++- 25 files changed, 210 insertions(+), 181 deletions(-) diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 5a3fe6b1da..08045eb535 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -49,10 +49,7 @@ pub fn compile( let code = compiler.pop_code_object(); trace!("Compilation completed: {:?}", code); - Ok(PyObject::new( - Box::new(objcode::PyCode::new(code)), - code_type, - )) + Ok(PyObject::new(objcode::PyCode::new(code), code_type)) } pub enum Mode { diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 8342010748..48d3d6229c 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -407,8 +407,7 @@ impl Frame { let stop = out[1].take(); let step = if out.len() == 3 { out[2].take() } else { None }; - let obj = - PyObject::new(Box::new(PySlice { start, stop, step }), vm.ctx.slice_type()); + let obj = PyObject::new(PySlice { start, stop, step }, vm.ctx.slice_type()); self.push_value(obj); Ok(None) } @@ -702,9 +701,7 @@ impl Frame { } bytecode::Instruction::LoadBuildClass => { let rustfunc = PyObject::new( - Box::new(PyBuiltinFunction::new(Box::new( - builtins::builtin_build_class_, - ))), + PyBuiltinFunction::new(Box::new(builtins::builtin_build_class_)), vm.ctx.type_type(), ); self.push_value(rustfunc); diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index 56a925b883..d737efaaea 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -172,10 +172,7 @@ fn bytearray_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } else { vec![] }; - Ok(PyObject::new( - Box::new(PyByteArray::new(value)), - cls.clone(), - )) + Ok(PyObject::new(PyByteArray::new(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 b24912bafd..c78e919bbc 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -94,7 +94,7 @@ fn bytes_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vec![] }; - Ok(PyObject::new(Box::new(PyBytes::new(value)), cls.clone())) + Ok(PyObject::new(PyBytes::new(value), cls.clone())) } fn bytes_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -203,10 +203,10 @@ 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( - Box::new(PyIteratorValue { + PyIteratorValue { position: Cell::new(0), iterated_obj: obj.clone(), - }), + }, vm.ctx.iter_type(), ); diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index 76f298bdd8..75cd455f82 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -89,7 +89,7 @@ fn complex_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let value = Complex64::new(real, imag); - Ok(PyObject::new(Box::new(PyComplex { value }), cls.clone())) + Ok(PyObject::new(PyComplex { value }, cls.clone())) } fn complex_real(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 8d8a62c4f0..c5cc3ec992 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -251,10 +251,10 @@ fn dict_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let key_list = vm.ctx.new_list(keys); let iter_obj = PyObject::new( - Box::new(PyIteratorValue { + PyIteratorValue { position: Cell::new(0), iterated_obj: key_list, - }), + }, vm.ctx.iter_type(), ); @@ -271,10 +271,10 @@ fn dict_values(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let values_list = vm.ctx.new_list(values); let iter_obj = PyObject::new( - Box::new(PyIteratorValue { + PyIteratorValue { position: Cell::new(0), iterated_obj: values_list, - }), + }, vm.ctx.iter_type(), ); @@ -291,10 +291,10 @@ fn dict_items(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let items_list = vm.ctx.new_list(items); let iter_obj = PyObject::new( - Box::new(PyIteratorValue { + PyIteratorValue { position: Cell::new(0), iterated_obj: items_list, - }), + }, vm.ctx.iter_type(), ); diff --git a/vm/src/obj/objenumerate.rs b/vm/src/obj/objenumerate.rs index c2c6c86189..b37f610628 100644 --- a/vm/src/obj/objenumerate.rs +++ b/vm/src/obj/objenumerate.rs @@ -36,10 +36,10 @@ fn enumerate_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; let iterator = objiter::get_iter(vm, iterable)?; Ok(PyObject::new( - Box::new(PyEnumerate { + PyEnumerate { counter: RefCell::new(counter), iterator, - }), + }, cls.clone(), )) } diff --git a/vm/src/obj/objfilter.rs b/vm/src/obj/objfilter.rs index f7a08e30fc..94cd15bb83 100644 --- a/vm/src/obj/objfilter.rs +++ b/vm/src/obj/objfilter.rs @@ -26,10 +26,10 @@ fn filter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let iterator = objiter::get_iter(vm, iterable)?; Ok(PyObject::new( - Box::new(PyFilter { + PyFilter { predicate: function.clone(), iterator, - }), + }, cls.clone(), )) } diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index a5416b233f..ae16b625aa 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -188,7 +188,7 @@ impl PyFloatRef { let type_name = objtype::get_type_name(&arg.typ()); return Err(vm.new_type_error(format!("can't convert {} to float", type_name))); }; - Ok(PyObject::new(Box::new(PyFloat { value }), cls.clone())) + Ok(PyObject::new(PyFloat { value }, cls.clone())) } fn mod_(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { diff --git a/vm/src/obj/objgenerator.rs b/vm/src/obj/objgenerator.rs index 3ba5037135..8e2434c384 100644 --- a/vm/src/obj/objgenerator.rs +++ b/vm/src/obj/objgenerator.rs @@ -40,7 +40,7 @@ pub fn init(context: &PyContext) { pub fn new_generator(vm: &mut VirtualMachine, frame: PyObjectRef) -> PyResult { Ok(PyObject::new( - Box::new(PyGenerator { frame }), + PyGenerator { frame }, vm.ctx.generator_type.clone(), )) } diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 7b77301f3a..a7bc9e2b29 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -105,7 +105,7 @@ fn int_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Some(val) => to_int(vm, val, base)?, None => Zero::zero(), }; - Ok(PyObject::new(Box::new(PyInt::new(val)), cls.clone())) + Ok(PyObject::new(PyInt::new(val), cls.clone())) } // Casting function: diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index c2a3d84975..8bf7dce249 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -111,10 +111,10 @@ impl PyListRef { fn iter(self, vm: &mut VirtualMachine) -> PyObjectRef { PyObject::new( - Box::new(PyIteratorValue { + PyIteratorValue { position: Cell::new(0), iterated_obj: self.into_object(), - }), + }, vm.ctx.iter_type(), ) } @@ -302,10 +302,7 @@ fn list_new( vec![] }; - Ok(PyObject::new( - Box::new(PyList::from(elements)), - cls.into_object(), - )) + Ok(PyObject::new(PyList::from(elements), cls.into_object())) } fn quicksort( diff --git a/vm/src/obj/objmap.rs b/vm/src/obj/objmap.rs index 1a84307e47..c183da0df8 100644 --- a/vm/src/obj/objmap.rs +++ b/vm/src/obj/objmap.rs @@ -30,10 +30,10 @@ fn map_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .map(|iterable| objiter::get_iter(vm, iterable)) .collect::, _>>()?; Ok(PyObject::new( - Box::new(PyMap { + PyMap { mapper: function.clone(), iterators, - }), + }, cls.clone(), )) } diff --git a/vm/src/obj/objmemory.rs b/vm/src/obj/objmemory.rs index 13e455d0f4..0170ee435c 100644 --- a/vm/src/obj/objmemory.rs +++ b/vm/src/obj/objmemory.rs @@ -18,9 +18,9 @@ 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( - Box::new(PyMemoryView { + PyMemoryView { obj: bytes_object.clone(), - }), + }, cls.clone(), )) } diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index 166ca9c8ef..59117244d2 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -137,7 +137,7 @@ impl<'a, T> PropertyBuilder<'a, T> { deleter: None, }; - PyObject::new(Box::new(payload), self.ctx.property_type()) + PyObject::new(payload, self.ctx.property_type()) } else { let payload = PyReadOnlyProperty { getter: self.getter.expect( @@ -145,7 +145,7 @@ impl<'a, T> PropertyBuilder<'a, T> { ), }; - PyObject::new(Box::new(payload), self.ctx.readonly_property_type()) + PyObject::new(payload, self.ctx.readonly_property_type()) } } } diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 84448a4b00..1e0eee53f6 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -227,10 +227,7 @@ fn range_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if step.is_zero() { Err(vm.new_value_error("range with 0 step size".to_string())) } else { - Ok(PyObject::new( - Box::new(PyRange { start, end, step }), - cls.clone(), - )) + Ok(PyObject::new(PyRange { start, end, step }, cls.clone())) } } @@ -238,10 +235,10 @@ fn range_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(range, Some(vm.ctx.range_type()))]); Ok(PyObject::new( - Box::new(PyIteratorValue { + PyIteratorValue { position: Cell::new(0), iterated_obj: range.clone(), - }), + }, vm.ctx.iter_type(), )) } @@ -252,10 +249,10 @@ fn range_reversed(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let range = get_value(zelf).reversed(); Ok(PyObject::new( - Box::new(PyIteratorValue { + PyIteratorValue { position: Cell::new(0), - iterated_obj: PyObject::new(Box::new(range), vm.ctx.range_type()), - }), + iterated_obj: PyObject::new(range, vm.ctx.range_type()), + }, vm.ctx.iter_type(), )) } @@ -318,11 +315,11 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; Ok(PyObject::new( - Box::new(PyRange { + PyRange { start: new_start, end: new_end, step: new_step, - }), + }, vm.ctx.range_type(), )) } else { diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index d9961b3acb..120868d452 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -165,16 +165,12 @@ pub fn get_item( if subscript.payload::().is_some() { if sequence.payload::().is_some() { Ok(PyObject::new( - Box::new(PyList::from( - elements.to_vec().get_slice_items(vm, &subscript)?, - )), + PyList::from(elements.to_vec().get_slice_items(vm, &subscript)?), sequence.typ(), )) } else if sequence.payload::().is_some() { Ok(PyObject::new( - Box::new(PyTuple::from( - elements.to_vec().get_slice_items(vm, &subscript)?, - )), + PyTuple::from(elements.to_vec().get_slice_items(vm, &subscript)?), sequence.typ(), )) } else { diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 83b36d6765..bb8483a0e2 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -168,9 +168,9 @@ fn set_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; Ok(PyObject::new( - Box::new(PySet { + PySet { elements: RefCell::new(elements), - }), + }, cls.clone(), )) } @@ -187,9 +187,9 @@ fn set_copy(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.set_type()))]); let elements = get_elements(s); Ok(PyObject::new( - Box::new(PySet { + PySet { elements: RefCell::new(elements), - }), + }, vm.ctx.set_type(), )) } @@ -341,9 +341,9 @@ fn set_union(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { elements.extend(get_elements(other).clone()); Ok(PyObject::new( - Box::new(PySet { + PySet { elements: RefCell::new(elements), - }), + }, vm.ctx.set_type(), )) } @@ -383,9 +383,9 @@ fn set_symmetric_difference(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResu } Ok(PyObject::new( - Box::new(PySet { + PySet { elements: RefCell::new(elements), - }), + }, vm.ctx.set_type(), )) } @@ -423,9 +423,9 @@ fn set_combine_inner( } Ok(PyObject::new( - Box::new(PySet { + PySet { elements: RefCell::new(elements), - }), + }, vm.ctx.set_type(), )) } @@ -555,10 +555,10 @@ fn set_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let items = get_elements(zelf).values().cloned().collect(); let set_list = vm.ctx.new_list(items); let iter_obj = PyObject::new( - Box::new(PyIteratorValue { + PyIteratorValue { position: Cell::new(0), iterated_obj: set_list, - }), + }, vm.ctx.iter_type(), ); diff --git a/vm/src/obj/objslice.rs b/vm/src/obj/objslice.rs index cb06f9424f..5ab3c7a332 100644 --- a/vm/src/obj/objslice.rs +++ b/vm/src/obj/objslice.rs @@ -54,11 +54,11 @@ fn slice_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } }?; Ok(PyObject::new( - Box::new(PySlice { + PySlice { 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(), )) } diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 5772aa3e8b..2c692469fc 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -126,10 +126,10 @@ impl PyTupleRef { fn iter(self, vm: &mut VirtualMachine) -> PyObjectRef { PyObject::new( - Box::new(PyIteratorValue { + PyIteratorValue { position: Cell::new(0), iterated_obj: self.into_object(), - }), + }, vm.ctx.iter_type(), ) } @@ -213,10 +213,7 @@ fn tuple_new( vec![] }; - Ok(PyObject::new( - Box::new(PyTuple::from(elements)), - cls.into_object(), - )) + Ok(PyObject::new(PyTuple::from(elements), cls.into_object())) } #[rustfmt::skip] // to avoid line splitting diff --git a/vm/src/obj/objzip.rs b/vm/src/obj/objzip.rs index 883d95ab1b..8831564a8f 100644 --- a/vm/src/obj/objzip.rs +++ b/vm/src/obj/objzip.rs @@ -24,7 +24,7 @@ fn zip_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .iter() .map(|iterable| objiter::get_iter(vm, iterable)) .collect::, _>>()?; - Ok(PyObject::new(Box::new(PyZip { iterators }), cls.clone())) + Ok(PyObject::new(PyZip { iterators }, cls.clone())) } fn zip_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 5b76ffd5b8..55ea50a390 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -161,6 +161,24 @@ pub fn create_type( objtype::new(type_type.clone(), name, vec![base.clone()], dict).unwrap() } +#[derive(Debug)] +pub struct PyNotImplemented; + +impl PyValue for PyNotImplemented { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.not_implemented().typ() + } +} + +#[derive(Debug)] +pub struct PyEllipsis; + +impl PyValue for PyEllipsis { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.ellipsis_type.clone() + } +} + // Basic objects: impl PyContext { pub fn new() -> Self { @@ -214,19 +232,19 @@ impl PyContext { let exceptions = exceptions::ExceptionZoo::new(&type_type, &object_type, &dict_type); let none = PyObject::new( - Box::new(objnone::PyNone), + objnone::PyNone, create_type("NoneType", &type_type, &object_type, &dict_type), ); - let ellipsis = PyObject::new(Box::new(()), ellipsis_type.clone()); + let ellipsis = PyObject::new(PyEllipsis, ellipsis_type.clone()); let not_implemented = PyObject::new( - Box::new(()), + PyNotImplemented, create_type("NotImplementedType", &type_type, &object_type, &dict_type), ); - let true_value = PyObject::new(Box::new(PyInt::new(BigInt::one())), bool_type.clone()); - let false_value = PyObject::new(Box::new(PyInt::new(BigInt::zero())), bool_type.clone()); + let true_value = PyObject::new(PyInt::new(BigInt::one()), bool_type.clone()); + let false_value = PyObject::new(PyInt::new(BigInt::zero()), bool_type.clone()); let context = PyContext { bool_type, memoryview_type, @@ -464,30 +482,27 @@ impl PyContext { } pub fn new_int(&self, i: T) -> PyObjectRef { - PyObject::new(Box::new(PyInt::new(i)), self.int_type()) + PyObject::new(PyInt::new(i), self.int_type()) } pub fn new_float(&self, value: f64) -> PyObjectRef { - PyObject::new(Box::new(PyFloat::from(value)), self.float_type()) + PyObject::new(PyFloat::from(value), self.float_type()) } pub fn new_complex(&self, value: Complex64) -> PyObjectRef { - PyObject::new(Box::new(PyComplex::from(value)), self.complex_type()) + PyObject::new(PyComplex::from(value), self.complex_type()) } pub fn new_str(&self, s: String) -> PyObjectRef { - PyObject::new(Box::new(objstr::PyString { value: s }), self.str_type()) + PyObject::new(objstr::PyString { value: s }, self.str_type()) } pub fn new_bytes(&self, data: Vec) -> PyObjectRef { - PyObject::new(Box::new(objbytes::PyBytes::new(data)), self.bytes_type()) + PyObject::new(objbytes::PyBytes::new(data), self.bytes_type()) } pub fn new_bytearray(&self, data: Vec) -> PyObjectRef { - PyObject::new( - Box::new(objbytearray::PyByteArray::new(data)), - self.bytearray_type(), - ) + PyObject::new(objbytearray::PyByteArray::new(data), self.bytearray_type()) } pub fn new_bool(&self, b: bool) -> PyObjectRef { @@ -499,21 +514,21 @@ impl PyContext { } pub fn new_tuple(&self, elements: Vec) -> PyObjectRef { - PyObject::new(Box::new(PyTuple::from(elements)), self.tuple_type()) + PyObject::new(PyTuple::from(elements), self.tuple_type()) } pub fn new_list(&self, elements: Vec) -> PyObjectRef { - PyObject::new(Box::new(PyList::from(elements)), self.list_type()) + PyObject::new(PyList::from(elements), self.list_type()) } 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. - PyObject::new(Box::new(PySet::default()), self.set_type()) + PyObject::new(PySet::default(), self.set_type()) } pub fn new_dict(&self) -> PyObjectRef { - PyObject::new(Box::new(PyDict::default()), self.dict_type()) + PyObject::new(PyDict::default(), self.dict_type()) } pub fn new_class(&self, name: &str, base: PyObjectRef) -> PyObjectRef { @@ -526,10 +541,10 @@ impl PyContext { pub fn new_module(&self, name: &str, dict: PyObjectRef) -> PyObjectRef { PyObject::new( - Box::new(PyModule { + PyModule { name: name.to_string(), dict, - }), + }, self.module_type.clone(), ) } @@ -539,13 +554,13 @@ impl PyContext { F: IntoPyNativeFunc, { PyObject::new( - Box::new(PyBuiltinFunction::new(f.into_func())), + PyBuiltinFunction::new(f.into_func()), self.builtin_function_or_method_type(), ) } pub fn new_frame(&self, code: PyObjectRef, scope: Scope) -> PyObjectRef { - PyObject::new(Box::new(Frame::new(code, scope)), self.frame_type()) + PyObject::new(Frame::new(code, scope), self.frame_type()) } pub fn new_property(&self, f: F) -> PyObjectRef @@ -556,7 +571,7 @@ impl PyContext { } pub fn new_code_object(&self, code: bytecode::CodeObject) -> PyObjectRef { - PyObject::new(Box::new(objcode::PyCode::new(code)), self.code_type()) + PyObject::new(objcode::PyCode::new(code), self.code_type()) } pub fn new_function( @@ -566,16 +581,13 @@ impl PyContext { defaults: PyObjectRef, ) -> PyObjectRef { PyObject::new( - Box::new(PyFunction::new(code_obj, scope, defaults)), + PyFunction::new(code_obj, scope, defaults), self.function_type(), ) } pub fn new_bound_method(&self, function: PyObjectRef, object: PyObjectRef) -> PyObjectRef { - PyObject::new( - Box::new(PyMethod::new(object, function)), - self.bound_method_type(), - ) + PyObject::new(PyMethod::new(object, function), self.bound_method_type()) } pub fn new_instance(&self, class: PyObjectRef, dict: Option) -> PyObjectRef { @@ -682,13 +694,10 @@ pub struct PyRef { _payload: PhantomData, } -impl PyRef -where - T: PyValue, -{ +impl PyRef { pub fn new(ctx: &PyContext, payload: T) -> Self { PyRef { - obj: PyObject::new(Box::new(payload), T::required_type(ctx)), + obj: PyObject::new(payload, T::required_type(ctx)), _payload: PhantomData, } } @@ -697,7 +706,7 @@ where let required_type = T::required_type(&vm.ctx); if objtype::issubclass(&cls.obj, &required_type) { Ok(PyRef { - obj: PyObject::new(Box::new(payload), cls.obj), + obj: PyObject::new(payload, cls.obj), _payload: PhantomData, }) } else { @@ -1366,7 +1375,7 @@ where T: PyValue + Sized, { fn into_pyobject(self, ctx: &PyContext) -> PyResult { - Ok(PyObject::new(Box::new(self), T::required_type(ctx))) + Ok(PyObject::new(self, T::required_type(ctx))) } } @@ -1511,11 +1520,11 @@ impl PyValue for PyIteratorValue { } impl PyObject { - pub fn new(payload: Box, typ: PyObjectRef) -> PyObjectRef { + pub fn new(payload: T, typ: PyObjectRef) -> PyObjectRef { PyObject { - payload, typ: Some(typ), dict: Some(RefCell::new(PyAttributes::new())), + payload: Box::new(payload), } .into_ref() } diff --git a/vm/src/stdlib/re.rs b/vm/src/stdlib/re.rs index bf24d7b51d..d9e1a2e761 100644 --- a/vm/src/stdlib/re.rs +++ b/vm/src/stdlib/re.rs @@ -11,9 +11,18 @@ use regex::{Match, Regex}; use std::path::PathBuf; use crate::obj::objstr; -use crate::pyobject::{PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, TypeProtocol}; +use crate::pyobject::{ + PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, +}; use crate::VirtualMachine; +impl PyValue for Regex { + fn required_type(_ctx: &PyContext) -> PyObjectRef { + // TODO + unimplemented!() + } +} + /// Create the python `re` module with all its members. pub fn mk_module(ctx: &PyContext) -> PyObjectRef { let match_type = py_class!(ctx, "Match", ctx.object(), { @@ -95,11 +104,19 @@ fn make_regex(vm: &mut VirtualMachine, pattern: &PyObjectRef) -> PyResult } /// Inner data for a match object. +#[derive(Debug)] struct PyMatch { start: usize, end: usize, } +impl PyValue for PyMatch { + fn required_type(_ctx: &PyContext) -> PyObjectRef { + // TODO + unimplemented!() + } +} + /// Take a found regular expression and convert it to proper match object. fn create_match(vm: &mut VirtualMachine, match_value: &Match) -> PyResult { // Return match object: @@ -116,7 +133,7 @@ fn create_match(vm: &mut VirtualMachine, match_value: &Match) -> PyResult { end: match_value.end(), }; - Ok(PyObject::new(Box::new(match_value), match_class.clone())) + Ok(PyObject::new(match_value, match_class.clone())) } /// Compile a regular expression into a Pattern object. @@ -134,7 +151,7 @@ fn re_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let module = import::import_module(vm, PathBuf::default(), "re").unwrap(); let pattern_class = vm.ctx.get_attr(&module, "Pattern").unwrap(); - Ok(PyObject::new(Box::new(regex), pattern_class.clone())) + Ok(PyObject::new(regex, pattern_class.clone())) } fn pattern_match(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index b4f1c61b31..df8bd33243 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -3,18 +3,20 @@ use std::io; use std::io::Read; use std::io::Write; use std::net::{SocketAddr, TcpListener, TcpStream, ToSocketAddrs, UdpSocket}; -use std::ops::DerefMut; +use std::ops::Deref; use crate::obj::objbytes; use crate::obj::objint; use crate::obj::objsequence::get_elements; use crate::obj::objstr; -use crate::pyobject::{PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, TypeProtocol}; +use crate::pyobject::{ + PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, +}; use crate::vm::VirtualMachine; use num_traits::ToPrimitive; -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] enum AddressFamily { Unix = 1, Inet = 2, @@ -32,7 +34,7 @@ impl AddressFamily { } } -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] enum SocketKind { Stream = 1, Dgram = 2, @@ -48,6 +50,7 @@ impl SocketKind { } } +#[derive(Debug)] enum Connection { TcpListener(TcpListener), TcpStream(TcpStream), @@ -108,10 +111,18 @@ impl Write for Connection { } } +#[derive(Debug)] pub struct Socket { address_family: AddressFamily, socket_kind: SocketKind, - con: Option, + con: RefCell>, +} + +impl PyValue for Socket { + fn required_type(_ctx: &PyContext) -> PyObjectRef { + // TODO + unimplemented!() + } } impl Socket { @@ -119,16 +130,13 @@ impl Socket { Socket { address_family, socket_kind, - con: None, + con: RefCell::new(None), } } } -fn get_socket<'a>(obj: &'a PyObjectRef) -> impl DerefMut + 'a { - if let Some(socket) = obj.payload.downcast_ref::>() { - return socket.borrow_mut(); - } - panic!("Inner error getting socket {:?}", obj); +fn get_socket<'a>(obj: &'a PyObjectRef) -> impl Deref + 'a { + obj.payload::().unwrap() } fn socket_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -146,9 +154,10 @@ fn socket_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { AddressFamily::from_i32(vm, objint::get_value(family_int).to_i32().unwrap())?; let kind = SocketKind::from_i32(vm, objint::get_value(kind_int).to_i32().unwrap())?; - let socket = RefCell::new(Socket::new(address_family, kind)); - - Ok(PyObject::new(Box::new(socket), cls.clone())) + Ok(PyObject::new( + Socket::new(address_family, kind), + cls.clone(), + )) } fn socket_connect(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -160,18 +169,21 @@ fn socket_connect(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let address_string = get_address_string(vm, address)?; - let mut socket = get_socket(zelf); + let socket = get_socket(zelf); match socket.socket_kind { SocketKind::Stream => match TcpStream::connect(address_string) { Ok(stream) => { - socket.con = Some(Connection::TcpStream(stream)); + socket + .con + .borrow_mut() + .replace(Connection::TcpStream(stream)); Ok(vm.get_none()) } Err(s) => Err(vm.new_os_error(s.to_string())), }, SocketKind::Dgram => { - if let Some(Connection::UdpSocket(con)) = &socket.con { + if let Some(Connection::UdpSocket(con)) = socket.con.borrow().as_ref() { match con.connect(address_string) { Ok(_) => Ok(vm.get_none()), Err(s) => Err(vm.new_os_error(s.to_string())), @@ -192,19 +204,25 @@ fn socket_bind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let address_string = get_address_string(vm, address)?; - let mut socket = get_socket(zelf); + let socket = get_socket(zelf); match socket.socket_kind { SocketKind::Stream => match TcpListener::bind(address_string) { Ok(stream) => { - socket.con = Some(Connection::TcpListener(stream)); + socket + .con + .borrow_mut() + .replace(Connection::TcpListener(stream)); Ok(vm.get_none()) } Err(s) => Err(vm.new_os_error(s.to_string())), }, SocketKind::Dgram => match UdpSocket::bind(address_string) { Ok(dgram) => { - socket.con = Some(Connection::UdpSocket(dgram)); + socket + .con + .borrow_mut() + .replace(Connection::UdpSocket(dgram)); Ok(vm.get_none()) } Err(s) => Err(vm.new_os_error(s.to_string())), @@ -248,10 +266,10 @@ fn socket_listen(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn socket_accept(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, None)]); - let mut socket = get_socket(zelf); + let socket = get_socket(zelf); - let ret = match socket.con { - Some(ref mut v) => v.accept(), + let ret = match socket.con.borrow_mut().as_mut() { + Some(v) => v.accept(), None => return Err(vm.new_type_error("".to_string())), }; @@ -260,13 +278,13 @@ fn socket_accept(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Err(s) => return Err(vm.new_os_error(s.to_string())), }; - let socket = RefCell::new(Socket { + let socket = Socket { address_family: socket.address_family, socket_kind: socket.socket_kind, - con: Some(Connection::TcpStream(tcp_stream)), - }); + con: RefCell::new(Some(Connection::TcpStream(tcp_stream))), + }; - let sock_obj = PyObject::new(Box::new(socket), zelf.typ()); + let sock_obj = PyObject::new(socket, zelf.typ()); let addr_tuple = get_addr_tuple(vm, addr)?; @@ -279,11 +297,11 @@ fn socket_recv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(zelf, None), (bufsize, Some(vm.ctx.int_type()))] ); - let mut socket = get_socket(zelf); + let socket = get_socket(zelf); let mut buffer = vec![0u8; objint::get_value(bufsize).to_usize().unwrap()]; - match socket.con { - Some(ref mut v) => match v.read_exact(&mut buffer) { + match socket.con.borrow_mut().as_mut() { + Some(v) => match v.read_exact(&mut buffer) { Ok(_) => (), Err(s) => return Err(vm.new_os_error(s.to_string())), }, @@ -299,11 +317,11 @@ fn socket_recvfrom(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(zelf, None), (bufsize, Some(vm.ctx.int_type()))] ); - let mut socket = get_socket(zelf); + let socket = get_socket(zelf); let mut buffer = vec![0u8; objint::get_value(bufsize).to_usize().unwrap()]; - let ret = match socket.con { - Some(ref mut v) => v.recv_from(&mut buffer), + let ret = match socket.con.borrow().as_ref() { + Some(v) => v.recv_from(&mut buffer), None => return Err(vm.new_type_error("".to_string())), }; @@ -323,10 +341,10 @@ fn socket_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(zelf, None), (bytes, Some(vm.ctx.bytes_type()))] ); - let mut socket = get_socket(zelf); + let socket = get_socket(zelf); - match socket.con { - Some(ref mut v) => match v.write(&objbytes::get_value(&bytes)) { + match socket.con.borrow_mut().as_mut() { + Some(v) => match v.write(&objbytes::get_value(&bytes)) { Ok(_) => (), Err(s) => return Err(vm.new_os_error(s.to_string())), }, @@ -347,30 +365,29 @@ fn socket_sendto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let address_string = get_address_string(vm, address)?; - let mut socket = get_socket(zelf); + let socket = get_socket(zelf); match socket.socket_kind { SocketKind::Dgram => { - match socket.con { - Some(ref mut v) => match v.send_to(&objbytes::get_value(&bytes), address_string) { + if let Some(v) = socket.con.borrow().as_ref() { + return match v.send_to(&objbytes::get_value(&bytes), address_string) { Ok(_) => Ok(vm.get_none()), Err(s) => Err(vm.new_os_error(s.to_string())), - }, - None => { - // Doing implicit bind - match UdpSocket::bind("0.0.0.0:0") { - Ok(dgram) => { - match dgram.send_to(&objbytes::get_value(&bytes), address_string) { - Ok(_) => { - socket.con = Some(Connection::UdpSocket(dgram)); - Ok(vm.get_none()) - } - Err(s) => Err(vm.new_os_error(s.to_string())), - } - } - Err(s) => Err(vm.new_os_error(s.to_string())), + }; + } + // Doing implicit bind + match UdpSocket::bind("0.0.0.0:0") { + Ok(dgram) => match dgram.send_to(&objbytes::get_value(&bytes), address_string) { + Ok(_) => { + socket + .con + .borrow_mut() + .replace(Connection::UdpSocket(dgram)); + Ok(vm.get_none()) } - } + Err(s) => Err(vm.new_os_error(s.to_string())), + }, + Err(s) => Err(vm.new_os_error(s.to_string())), } } _ => Err(vm.new_not_implemented_error("".to_string())), @@ -380,17 +397,17 @@ fn socket_sendto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn socket_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, None)]); - let mut socket = get_socket(zelf); - socket.con = None; + let socket = get_socket(zelf); + socket.con.borrow_mut().take(); Ok(vm.get_none()) } fn socket_getsockname(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, None)]); - let mut socket = get_socket(zelf); + let socket = get_socket(zelf); - let addr = match socket.con { - Some(ref mut v) => v.local_addr(), + let addr = match socket.con.borrow().as_ref() { + Some(v) => v.local_addr(), None => return Err(vm.new_type_error("".to_string())), }; diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index 3d9ed71354..f6362be0ea 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -4,7 +4,7 @@ use js_sys::Promise; use num_traits::cast::ToPrimitive; use rustpython_vm::obj::{objint, objstr}; use rustpython_vm::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, }; use rustpython_vm::{import::import, VirtualMachine}; use std::path::PathBuf; @@ -151,13 +151,21 @@ fn browser_cancel_animation_frame(vm: &mut VirtualMachine, args: PyFuncArgs) -> Ok(vm.get_none()) } +#[derive(Debug)] pub struct PyPromise { value: Promise, } +impl PyValue for PyPromise { + fn required_type(_ctx: &PyContext) -> PyObjectRef { + // TODO + unimplemented!() + } +} + impl PyPromise { pub fn new_obj(promise_type: PyObjectRef, value: Promise) -> PyObjectRef { - PyObject::new(Box::new(PyPromise { value }), promise_type) + PyObject::new(PyPromise { value }, promise_type) } } From 65c26a1f1b902a47c08cb2f0d91e8914037090bf Mon Sep 17 00:00:00 2001 From: ben Date: Mon, 11 Mar 2019 17:30:10 +1300 Subject: [PATCH 254/380] Use new_attribute_error in more places --- vm/src/obj/objfunction.rs | 20 ++++++-------------- vm/src/obj/objobject.rs | 6 +----- vm/src/obj/objtype.rs | 6 +----- vm/src/vm.rs | 4 ++-- 4 files changed, 10 insertions(+), 26 deletions(-) diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index 2f278ca076..8b9dd70d37 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -129,13 +129,9 @@ fn classmethod_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let py_method = vm.ctx.new_bound_method(function, py_obj); Ok(py_method) } - None => { - let attribute_error = vm.context().exceptions.attribute_error.clone(); - Err(vm.new_exception( - attribute_error, - String::from("Attribute Error: classmethod must have 'function' attribute"), - )) - } + None => Err(vm.new_attribute_error( + "Attribute Error: classmethod must have 'function' attribute".to_string(), + )), } } @@ -162,13 +158,9 @@ fn staticmethod_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); match cls.get_attr("function") { Some(function) => Ok(function), - None => { - let attribute_error = vm.context().exceptions.attribute_error.clone(); - Err(vm.new_exception( - attribute_error, - String::from("Attribute Error: staticmethod must have 'function' attribute"), - )) - } + None => Err(vm.new_attribute_error( + "Attribute Error: staticmethod must have 'function' attribute".to_string(), + )), } } diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 0d9d21f1ea..eb03b33932 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -256,11 +256,7 @@ fn object_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } else if let Some(getter) = cls.get_attr("__getattr__") { vm.invoke(getter, vec![cls, name_str.clone()]) } else { - let attribute_error = vm.context().exceptions.attribute_error.clone(); - Err(vm.new_exception( - attribute_error, - format!("{} has no attribute '{}'", obj, name), - )) + Err(vm.new_attribute_error(format!("{} has no attribute '{}'", obj, name))) } } diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index be4591dfcd..5a975556fc 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -225,11 +225,7 @@ pub fn type_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult } else if let Some(getter) = cls.get_attr("__getattr__") { vm.invoke(getter, vec![mcl, name_str.clone()]) } else { - let attribute_error = vm.context().exceptions.attribute_error.clone(); - Err(vm.new_exception( - attribute_error, - format!("{} has no attribute '{}'", cls, name), - )) + Err(vm.new_attribute_error(format!("{} has no attribute '{}'", cls, name))) } } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 0f95bf66c6..361f776aa2 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -137,8 +137,8 @@ impl VirtualMachine { } 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) + let attribute_error = self.ctx.exceptions.attribute_error.clone(); + self.new_exception(attribute_error, msg) } pub fn new_type_error(&mut self, msg: String) -> PyObjectRef { From 7894627b9d91a697f35630e3871123f84dceeb24 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Mon, 11 Mar 2019 10:22:53 +0000 Subject: [PATCH 255/380] Use extend_class in objtype. --- vm/src/obj/objtype.rs | 42 +++++++++++++----------------------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 5a975556fc..a2042ec860 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -41,39 +41,23 @@ pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, _dict_type: } } -pub fn init(context: &PyContext) { - let type_type = &context.type_type; - +pub fn init(ctx: &PyContext) { 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(&type_type, "__mro__", context.new_property(type_mro)); - context.set_attr(&type_type, "__repr__", context.new_rustfunc(type_repr)); - context.set_attr( - &type_type, - "__prepare__", - context.new_rustfunc(type_prepare), - ); - context.set_attr( - &type_type, - "__getattribute__", - context.new_rustfunc(type_getattribute), - ); - context.set_attr( - &type_type, - "__instancecheck__", - context.new_rustfunc(type_instance_check), - ); - context.set_attr( - &type_type, - "__subclasscheck__", - context.new_rustfunc(type_subclass_check), - ); - context.set_attr(&type_type, "__doc__", context.new_str(type_doc.to_string())); - context.set_attr(&type_type, "__dir__", context.new_rustfunc(type_dir)); + extend_class!(&ctx, &ctx.type_type, { + "__call__" => ctx.new_rustfunc(type_call), + "__new__" => ctx.new_rustfunc(type_new), + "__mro__" => ctx.new_property(type_mro), + "__repr__" => ctx.new_rustfunc(type_repr), + "__prepare__" => ctx.new_rustfunc(type_prepare), + "__getattribute__" => ctx.new_rustfunc(type_getattribute), + "__instancecheck__" => ctx.new_rustfunc(type_instance_check), + "__subclasscheck__" => ctx.new_rustfunc(type_subclass_check), + "__doc__" => ctx.new_str(type_doc.to_string()), + "__dir__" => ctx.new_rustfunc(type_dir), + }); } fn type_mro(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From 4d779bbf98f74200e68638ed56fd0277817fc3df Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Mon, 11 Mar 2019 10:31:22 +0000 Subject: [PATCH 256/380] isinstance/issubclass - avoid expensive construction of full mro. --- vm/src/obj/objtype.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index a2042ec860..993026407a 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -81,8 +81,7 @@ fn _mro(cls: PyObjectRef) -> Option> { /// Determines if `obj` actually an instance of `cls`, this doesn't call __instancecheck__, so only /// use this if `cls` is known to have not overridden the base __instancecheck__ magic method. pub fn isinstance(obj: &PyObjectRef, cls: &PyObjectRef) -> bool { - let mro = _mro(obj.typ()).unwrap(); - mro.into_iter().any(|c| c.is(&cls)) + issubclass(&obj.typ(), &cls) } fn type_instance_check(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -98,8 +97,8 @@ fn type_instance_check(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { /// so only use this if `cls` is known to have not overridden the base __subclasscheck__ magic /// method. pub fn issubclass(subclass: &PyObjectRef, cls: &PyObjectRef) -> bool { - let mro = _mro(subclass.clone()).unwrap(); - mro.into_iter().any(|c| c.is(&cls)) + let ref mro = subclass.payload::().unwrap().mro; + subclass.is(&cls) || mro.iter().any(|c| c.is(&cls)) } fn type_subclass_check(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From d7e1d694300819233f4c11e4a36578b87fee457d Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Mon, 11 Mar 2019 11:44:25 +0000 Subject: [PATCH 257/380] Avoid additional clone in objtype::subinstance. --- vm/src/obj/objtype.rs | 2 +- vm/src/pyobject.rs | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 993026407a..a8f86394ad 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -81,7 +81,7 @@ fn _mro(cls: PyObjectRef) -> Option> { /// Determines if `obj` actually an instance of `cls`, this doesn't call __instancecheck__, so only /// use this if `cls` is known to have not overridden the base __instancecheck__ magic method. pub fn isinstance(obj: &PyObjectRef, cls: &PyObjectRef) -> bool { - issubclass(&obj.typ(), &cls) + issubclass(obj.type_ref(), &cls) } fn type_instance_check(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 23c269324a..6fbf3114cb 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -761,19 +761,22 @@ pub trait FromPyObjectRef { } pub trait TypeProtocol { - fn typ(&self) -> PyObjectRef; + fn typ(&self) -> PyObjectRef { + self.type_ref().clone() + } + fn type_ref(&self) -> &PyObjectRef; } impl TypeProtocol for PyObjectRef { - fn typ(&self) -> PyObjectRef { - (**self).typ() + fn type_ref(&self) -> &PyObjectRef { + (**self).type_ref() } } impl TypeProtocol for PyObject { - fn typ(&self) -> PyObjectRef { + fn type_ref(&self) -> &PyObjectRef { match self.typ { - Some(ref typ) => typ.clone(), + Some(ref typ) => &typ, None => panic!("Object {:?} doesn't have a type!", self), } } From 5ab41f9c54e568137fadff7d1985c6bee7b5dca3 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Mon, 11 Mar 2019 11:59:17 -0500 Subject: [PATCH 258/380] Make mandelbrot.py work without browser module --- wasm/demo/snippets/mandelbrot.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/wasm/demo/snippets/mandelbrot.py b/wasm/demo/snippets/mandelbrot.py index 7d55137198..fa31f41079 100644 --- a/wasm/demo/snippets/mandelbrot.py +++ b/wasm/demo/snippets/mandelbrot.py @@ -1,4 +1,7 @@ -from browser import request_animation_frame +try: + from browser import request_animation_frame +except: + def request_animation_frame(cb): cb() w = 50.0 h = 50.0 From 31c6df31dcb51d86fc5608e9a15bd625ae829710 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Mon, 11 Mar 2019 12:14:37 -0500 Subject: [PATCH 259/380] Okay now it works without browser --- wasm/demo/snippets/mandelbrot.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/wasm/demo/snippets/mandelbrot.py b/wasm/demo/snippets/mandelbrot.py index fa31f41079..320c1445d7 100644 --- a/wasm/demo/snippets/mandelbrot.py +++ b/wasm/demo/snippets/mandelbrot.py @@ -1,7 +1,7 @@ try: from browser import request_animation_frame except: - def request_animation_frame(cb): cb() + request_animation_frame = None w = 50.0 h = 50.0 @@ -41,4 +41,5 @@ def mandel(): def gen_cb(_time=None): gen.__next__() request_animation_frame(gen_cb) -gen_cb() +if request_animation_frame: gen_cb() +else: list(gen) From 482025045fffe8c7c728dce5c442f09896790fb1 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Mon, 11 Mar 2019 12:17:34 -0500 Subject: [PATCH 260/380] Rearrange mandelbrot.py --- wasm/demo/snippets/mandelbrot.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/wasm/demo/snippets/mandelbrot.py b/wasm/demo/snippets/mandelbrot.py index 320c1445d7..2d664fdedf 100644 --- a/wasm/demo/snippets/mandelbrot.py +++ b/wasm/demo/snippets/mandelbrot.py @@ -1,8 +1,3 @@ -try: - from browser import request_animation_frame -except: - request_animation_frame = None - w = 50.0 h = 50.0 @@ -37,6 +32,9 @@ def mandel(): y += 1 yield +try: from browser import request_animation_frame +except: request_animation_frame = None + gen = mandel() def gen_cb(_time=None): gen.__next__() From 3711881a601d2f7e681710650a17b02db82757e6 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Mon, 11 Mar 2019 17:42:28 +0000 Subject: [PATCH 261/380] Add type_pyref() to TypeProtocol. --- vm/src/pyobject.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 0d348e1b68..6568e7e84c 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -791,6 +791,9 @@ pub trait TypeProtocol { fn typ(&self) -> PyObjectRef { self.type_ref().clone() } + fn type_pyref(&self) -> PyClassRef { + FromPyObjectRef::from_pyobj(self.type_ref()) + } fn type_ref(&self) -> &PyObjectRef; } @@ -1548,6 +1551,19 @@ pub trait PyValue: Any + fmt::Debug { fn required_type(ctx: &PyContext) -> PyObjectRef; } +impl FromPyObjectRef for PyRef { + fn from_pyobj(obj: &PyObjectRef) -> Self { + if let Some(_) = obj.payload::() { + PyRef { + obj: obj.clone(), + _payload: PhantomData, + } + } else { + panic!("Error getting inner type.") + } + } +} + #[cfg(test)] mod tests { use super::PyContext; From 4cbb1a38f1282c39154bec472420c1c586bf749d Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Mon, 11 Mar 2019 19:16:00 +0100 Subject: [PATCH 262/380] Convert most objint functions to new args style --- vm/src/obj/objint.rs | 866 ++++++++++++++++--------------------------- 1 file changed, 319 insertions(+), 547 deletions(-) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index a7bc9e2b29..3d1dc03b9f 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -6,7 +6,7 @@ use num_traits::{Pow, Signed, ToPrimitive, Zero}; use crate::format::FormatSpec; use crate::pyobject::{ - FromPyObjectRef, IntoPyObject, PyContext, PyFuncArgs, PyObject, PyObjectRef, PyRef, PyResult, + IntoPyObject, OptionalArg, PyContext, PyFuncArgs, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -26,6 +26,7 @@ pub type PyIntRef = PyRef; impl PyInt { pub fn new(i: T) -> Self { PyInt { + // TODO: this .clone()s a BigInt, which is not what we want. value: i.to_bigint().unwrap(), } } @@ -80,405 +81,348 @@ impl_try_from_object_int!( (u64, to_u64), ); -fn int_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(int, Some(vm.ctx.int_type()))]); - let v = get_value(int); - Ok(vm.new_str(v.to_string())) -} - -fn int_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(cls, None)], - optional = [(val_option, None)] - ); - if !objtype::issubclass(cls, &vm.ctx.int_type()) { - return Err(vm.new_type_error(format!("{:?} is not a subtype of int", cls))); +impl PyIntRef { + fn pass_value(self, _vm: &mut VirtualMachine) -> Self { + self } - let base = match args.get_optional_kwarg("base") { - Some(argument) => get_value(&argument).to_u32().unwrap(), - None => 10, - }; - let val = match val_option { - Some(val) => to_int(vm, val, base)?, - None => Zero::zero(), - }; - Ok(PyObject::new(PyInt::new(val), cls.clone())) -} - -// Casting function: -pub fn to_int(vm: &mut VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult { - let val = if objtype::isinstance(obj, &vm.ctx.int_type()) { - get_value(obj) - } else if objtype::isinstance(obj, &vm.ctx.float_type()) { - objfloat::get_value(obj).to_bigint().unwrap() - } else if objtype::isinstance(obj, &vm.ctx.str_type()) { - let s = objstr::get_value(obj); - match i32::from_str_radix(&s, base) { - Ok(v) => v.to_bigint().unwrap(), - Err(err) => { - trace!("Error occurred during int conversion {:?}", err); - return Err(vm.new_value_error(format!( - "invalid literal for int() with base {}: '{}'", - base, s - ))); - } + fn eq(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + if objtype::isinstance(&other, &vm.ctx.int_type()) { + vm.ctx.new_bool(self.value == get_value(&other)) + } else { + vm.ctx.not_implemented() } - } else { - let type_name = objtype::get_type_name(&obj.typ()); - return Err(vm.new_type_error(format!( - "int() argument must be a string or a number, not '{}'", - type_name - ))); - }; - Ok(val) -} - -// Retrieve inner int value: -pub fn get_value(obj: &PyObjectRef) -> BigInt { - obj.payload::().unwrap().value.clone() -} - -impl FromPyObjectRef for BigInt { - fn from_pyobj(obj: &PyObjectRef) -> BigInt { - get_value(obj) } -} -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_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, - args, - required = [(zelf, Some(vm.ctx.int_type())), (other, None)] - ); + fn ne(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + if objtype::isinstance(&other, &vm.ctx.int_type()) { + vm.ctx.new_bool(self.value != get_value(&other)) + } else { + vm.ctx.not_implemented() + } + } - 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()); - }; - Ok(vm.ctx.new_bool(result)) -} + fn lt(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + if objtype::isinstance(&other, &vm.ctx.int_type()) { + vm.ctx.new_bool(self.value < get_value(&other)) + } else { + vm.ctx.not_implemented() + } + } -fn int_ne(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.int_type())), (other, None)] - ); + fn le(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + if objtype::isinstance(&other, &vm.ctx.int_type()) { + vm.ctx.new_bool(self.value <= get_value(&other)) + } else { + vm.ctx.not_implemented() + } + } - 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()); - }; - Ok(vm.ctx.new_bool(result)) -} + fn gt(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + if objtype::isinstance(&other, &vm.ctx.int_type()) { + vm.ctx.new_bool(self.value > get_value(&other)) + } else { + vm.ctx.not_implemented() + } + } -fn int_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.int_type())), (other, None)] - ); + fn ge(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + if objtype::isinstance(&other, &vm.ctx.int_type()) { + vm.ctx.new_bool(self.value >= get_value(&other)) + } else { + vm.ctx.not_implemented() + } + } - if !objtype::isinstance(other, &vm.ctx.int_type()) { - return Ok(vm.ctx.not_implemented()); + fn add(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + if objtype::isinstance(&other, &vm.ctx.int_type()) { + vm.ctx.new_int((&self.value) + get_value(&other)) + } else { + vm.ctx.not_implemented() + } } - let zelf = BigInt::from_pyobj(zelf); - let other = BigInt::from_pyobj(other); - let result = zelf < other; - Ok(vm.ctx.new_bool(result)) -} + fn sub(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + if objtype::isinstance(&other, &vm.ctx.int_type()) { + vm.ctx.new_int((&self.value) - get_value(&other)) + } else { + vm.ctx.not_implemented() + } + } -fn int_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.int_type())), (other, None)] - ); + fn rsub(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + if objtype::isinstance(&other, &vm.ctx.int_type()) { + vm.ctx.new_int(get_value(&other) - (&self.value)) + } else { + vm.ctx.not_implemented() + } + } - if !objtype::isinstance(other, &vm.ctx.int_type()) { - return Ok(vm.ctx.not_implemented()); + fn mul(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + if objtype::isinstance(&other, &vm.ctx.int_type()) { + vm.ctx.new_int((&self.value) * get_value(&other)) + } else { + vm.ctx.not_implemented() + } } - let zelf = BigInt::from_pyobj(zelf); - let other = BigInt::from_pyobj(other); - let result = zelf <= other; - Ok(vm.ctx.new_bool(result)) -} + fn truediv(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&other, &vm.ctx.int_type()) { + div_ints(vm, &self.value, &get_value(&other)) + } else { + Ok(vm.ctx.not_implemented()) + } + } -fn int_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.int_type())), (other, None)] - ); + fn rtruediv(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&other, &vm.ctx.int_type()) { + div_ints(vm, &get_value(&other), &self.value) + } else { + Ok(vm.ctx.not_implemented()) + } + } - if !objtype::isinstance(other, &vm.ctx.int_type()) { - return Ok(vm.ctx.not_implemented()); + fn floordiv(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&other, &vm.ctx.int_type()) { + let v2 = get_value(&other); + if v2 != BigInt::zero() { + Ok(vm.ctx.new_int((&self.value) / v2)) + } else { + Err(vm.new_zero_division_error("integer floordiv by zero".to_string())) + } + } else { + Ok(vm.ctx.not_implemented()) + } } - let zelf = BigInt::from_pyobj(zelf); - let other = BigInt::from_pyobj(other); - let result = zelf > other; - Ok(vm.ctx.new_bool(result)) -} + fn lshift(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if !objtype::isinstance(&other, &vm.ctx.int_type()) { + return Err(vm.new_type_error(format!( + "unsupported operand type(s) for << '{}' and '{}'", + objtype::get_type_name(&self.as_object().typ()), + objtype::get_type_name(&other.typ()) + ))); + } -fn int_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.int_type())), (other, None)] - ); + if let Some(n_bits) = get_value(&other).to_usize() { + return Ok(vm.ctx.new_int((&self.value) << n_bits)); + } - if !objtype::isinstance(other, &vm.ctx.int_type()) { - return Ok(vm.ctx.not_implemented()); + // i2 failed `to_usize()` conversion + match get_value(&other) { + 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 int".to_string())) + } + _ => panic!("Failed converting {} to rust usize", get_value(&other)), + } } - let zelf = BigInt::from_pyobj(zelf); - let other = BigInt::from_pyobj(other); - let result = zelf >= other; - Ok(vm.ctx.new_bool(result)) -} + fn rshift(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if !objtype::isinstance(&other, &vm.ctx.int_type()) { + return Err(vm.new_type_error(format!( + "unsupported operand type(s) for >> '{}' and '{}'", + objtype::get_type_name(&self.as_object().typ()), + objtype::get_type_name(&other.typ()) + ))); + } -fn int_lshift(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(i, Some(vm.ctx.int_type())), (i2, None)] - ); + if let Some(n_bits) = get_value(&other).to_usize() { + return Ok(vm.ctx.new_int((&self.value) >> n_bits)); + } - 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()) - ))); + // i2 failed `to_usize()` conversion + match get_value(&other) { + 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 int".to_string())) + } + _ => panic!("Failed converting {} to rust usize", get_value(&other)), + } } - if let Some(n_bits) = get_value(i2).to_usize() { - return Ok(vm.ctx.new_int(get_value(i) << n_bits)); + fn xor(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + if objtype::isinstance(&other, &vm.ctx.int_type()) { + vm.ctx.new_int((&self.value) ^ get_value(&other)) + } else { + vm.ctx.not_implemented() + } } - // i2 failed `to_usize()` conversion - 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 int".to_string())) + fn rxor(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + if objtype::isinstance(&other, &vm.ctx.int_type()) { + vm.ctx.new_int(get_value(&other) ^ (&self.value)) + } else { + vm.ctx.not_implemented() } - _ => 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)] - ); + fn or(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + if objtype::isinstance(&other, &vm.ctx.int_type()) { + vm.ctx.new_int((&self.value) | get_value(&other)) + } else { + vm.ctx.not_implemented() + } + } - 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()) - ))); + fn and(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + if objtype::isinstance(&other, &vm.ctx.int_type()) { + let v2 = get_value(&other); + vm.ctx.new_int((&self.value) & v2) + } else { + vm.ctx.not_implemented() + } } - if let Some(n_bits) = get_value(i2).to_usize() { - return Ok(vm.ctx.new_int(get_value(i) >> n_bits)); + fn pow(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + if objtype::isinstance(&other, &vm.ctx.int_type()) { + let v2 = get_value(&other).to_u32().unwrap(); + vm.ctx.new_int(self.value.pow(v2)) + } else if objtype::isinstance(&other, &vm.ctx.float_type()) { + let v2 = objfloat::get_value(&other); + vm.ctx.new_float((self.value.to_f64().unwrap()).powf(v2)) + } else { + vm.ctx.not_implemented() + } } - // i2 failed `to_usize()` conversion - 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 int".to_string())) + fn mod_(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&other, &vm.ctx.int_type()) { + let v2 = get_value(&other); + if v2 != BigInt::zero() { + Ok(vm.ctx.new_int((&self.value) % v2)) + } else { + Err(vm.new_zero_division_error("integer modulo by zero".to_string())) + } + } else { + Ok(vm.ctx.not_implemented()) } - _ => 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); - let mut hasher = std::collections::hash_map::DefaultHasher::new(); - value.hash(&mut hasher); - let hash = hasher.finish(); - Ok(vm.ctx.new_int(hash)) -} + fn divmod(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&other, &vm.ctx.int_type()) { + let v2 = get_value(&other); + if v2 != BigInt::zero() { + let (r1, r2) = self.value.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 { + Ok(vm.ctx.not_implemented()) + } + } -fn int_abs(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).abs())) -} + fn neg(self, vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.new_int(-(&self.value)) + } -fn int_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.int_type())), (other, None)] - ); - if objtype::isinstance(other, &vm.ctx.int_type()) { - Ok(vm.ctx.new_int(get_value(zelf) + get_value(other))) - } else { - Ok(vm.ctx.not_implemented()) + fn hash(self, _vm: &mut VirtualMachine) -> u64 { + let mut hasher = std::collections::hash_map::DefaultHasher::new(); + self.value.hash(&mut hasher); + hasher.finish() } -} -fn int_radd(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - int_add(vm, args) -} + fn abs(self, vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.new_int(self.value.abs()) + } -fn int_float(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(i, Some(vm.ctx.int_type()))]); - let i = get_value(i); - Ok(vm.ctx.new_float(i.to_f64().unwrap())) -} + fn round(self, _precision: OptionalArg, _vm: &mut VirtualMachine) -> Self { + self + } -fn int_floordiv(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 (v1, v2) = (get_value(i), get_value(i2)); + fn float(self, _vm: &mut VirtualMachine) -> f64 { + self.value.to_f64().unwrap() + } - 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 { - Ok(vm.ctx.not_implemented()) + fn invert(self, vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.new_int(!(&self.value)) } -} -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 repr(self, _vm: &mut VirtualMachine) -> String { + self.value.to_string() + } -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))) -} + fn format(self, spec: PyRef, vm: &mut VirtualMachine) -> PyResult { + let format_spec = FormatSpec::parse(&spec.value); + match format_spec.format_int(&self.value) { + Ok(string) => Ok(string), + Err(err) => Err(vm.new_value_error(err.to_string())), + } + } -fn int_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (i, Some(vm.ctx.int_type())), - (format_spec, Some(vm.ctx.str_type())) - ] - ); - let string_value = objstr::get_value(format_spec); - let format_spec = FormatSpec::parse(&string_value); - let int_value = get_value(i); - match format_spec.format_int(&int_value) { - Ok(string) => Ok(vm.ctx.new_str(string)), - Err(err) => Err(vm.new_value_error(err.to_string())), + fn bool(self, _vm: &mut VirtualMachine) -> bool { + !self.value.is_zero() } -} -fn int_sub(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.int_type())), (other, None)] - ); - if objtype::isinstance(other, &vm.ctx.int_type()) { - Ok(vm.ctx.new_int(get_value(zelf) - get_value(other))) - } else { - Ok(vm.ctx.not_implemented()) + fn bit_length(self, _vm: &mut VirtualMachine) -> usize { + self.value.bits() } -} -fn int_rsub(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.int_type())), (other, None)] - ); - if objtype::isinstance(other, &vm.ctx.int_type()) { - Ok(vm.ctx.new_int(get_value(other) - get_value(zelf))) - } else { - Ok(vm.ctx.not_implemented()) + fn imag(self, _vm: &mut VirtualMachine) -> usize { + 0 } } -fn int_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn int_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [(zelf, Some(vm.ctx.int_type())), (other, None)] + required = [(cls, None)], + optional = [(val_option, None)] ); - if objtype::isinstance(other, &vm.ctx.int_type()) { - Ok(vm.ctx.new_int(get_value(zelf) * get_value(other))) - } else { - Ok(vm.ctx.not_implemented()) + if !objtype::issubclass(cls, &vm.ctx.int_type()) { + return Err(vm.new_type_error(format!("{:?} is not a subtype of int", cls))); } -} -fn int_rmul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - int_mul(vm, args) + let base = match args.get_optional_kwarg("base") { + Some(argument) => get_value(&argument).to_u32().unwrap(), + None => 10, + }; + let val = match val_option { + Some(val) => to_int(vm, val, base)?, + None => Zero::zero(), + }; + Ok(PyObject::new(PyInt::new(val), cls.clone())) } -fn int_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.int_type())), (other, None)] - ); - - if objtype::isinstance(other, &vm.ctx.int_type()) { - div_ints(vm, &get_value(zelf), &get_value(other)) +// Casting function: +pub fn to_int(vm: &mut VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult { + let val = if objtype::isinstance(obj, &vm.ctx.int_type()) { + get_value(obj) + } else if objtype::isinstance(obj, &vm.ctx.float_type()) { + objfloat::get_value(obj).to_bigint().unwrap() + } else if objtype::isinstance(obj, &vm.ctx.str_type()) { + let s = objstr::get_value(obj); + match i32::from_str_radix(&s, base) { + Ok(v) => v.to_bigint().unwrap(), + Err(err) => { + trace!("Error occurred during int conversion {:?}", err); + return Err(vm.new_value_error(format!( + "invalid literal for int() with base {}: '{}'", + base, s + ))); + } + } } else { - Ok(vm.ctx.not_implemented()) - } + let type_name = objtype::get_type_name(&obj.typ()); + return Err(vm.new_type_error(format!( + "int() argument must be a string or a number, not '{}'", + type_name + ))); + }; + Ok(val) } -fn int_rtruediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.int_type())), (other, None)] - ); - - if objtype::isinstance(other, &vm.ctx.int_type()) { - div_ints(vm, &get_value(other), &get_value(zelf)) - } else { - Ok(vm.ctx.not_implemented()) - } +// Retrieve inner int value: +pub fn get_value(obj: &PyObjectRef) -> BigInt { + obj.payload::().unwrap().value.clone() } #[inline] @@ -513,167 +457,7 @@ fn div_ints(vm: &mut VirtualMachine, i1: &BigInt, i2: &BigInt) -> PyResult { } } -fn int_mod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(i, Some(vm.ctx.int_type())), (i2, None)] - ); - let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.int_type()) { - 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 { - Ok(vm.ctx.not_implemented()) - } -} - -fn int_neg(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(i, Some(vm.ctx.int_type()))]); - let i = BigInt::from_pyobj(i); - Ok(vm.ctx.new_int(-i)) -} - -fn int_pos(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(i, Some(vm.ctx.int_type()))]); - Ok(i.clone()) -} - -fn int_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(i, Some(vm.ctx.int_type())), (i2, None)] - ); - 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))) - } 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))) - } else { - Ok(vm.ctx.not_implemented()) - } -} - -fn int_divmod(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 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 { - Ok(vm.ctx.not_implemented()) - } -} - -fn int_xor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(i, Some(vm.ctx.int_type())), (i2, None)] - ); - let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.int_type()) { - let v2 = get_value(i2); - Ok(vm.ctx.new_int(v1 ^ v2)) - } else { - Ok(vm.ctx.not_implemented()) - } -} - -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 { - Ok(vm.ctx.not_implemented()) - } -} - -fn int_or(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(i, Some(vm.ctx.int_type())), (i2, None)] - ); - let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.int_type()) { - let v2 = get_value(i2); - Ok(vm.ctx.new_int(v1 | v2)) - } else { - Ok(vm.ctx.not_implemented()) - } -} - -fn int_and(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(i, Some(vm.ctx.int_type())), (i2, None)] - ); - let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.int_type()) { - let v2 = get_value(i2); - Ok(vm.ctx.new_int(v1 & v2)) - } else { - Ok(vm.ctx.not_implemented()) - } -} - -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)) -} - -fn int_conjugate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(i, Some(vm.ctx.int_type()))]); - let v = get_value(i); - Ok(vm.ctx.new_int(v)) -} - -fn int_real(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(zelf, Some(vm.ctx.int_type()))]); - let value = BigInt::from_pyobj(zelf); - Ok(vm.ctx.new_int(value)) -} - -fn int_imag(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(_zelf, Some(vm.ctx.int_type()))]); - let value = BigInt::from(0); - Ok(vm.ctx.new_int(value)) -} - +#[rustfmt::skip] // to avoid line splitting pub fn init(context: &PyContext) { let int_doc = "int(x=0) -> integer int(x, base=10) -> integer @@ -691,61 +475,49 @@ Base 0 means to interpret the base from the string as an integer literal. 4"; 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)); - context.set_attr(&int_type, "__ge__", context.new_rustfunc(int_ge)); - context.set_attr(&int_type, "__abs__", context.new_rustfunc(int_abs)); - context.set_attr(&int_type, "__add__", context.new_rustfunc(int_add)); - context.set_attr(&int_type, "__radd__", context.new_rustfunc(int_radd)); - 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_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, "__int__", context.new_rustfunc(int_pass_value)); - context.set_attr( - &int_type, - "__floordiv__", - 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)); - context.set_attr(&int_type, "__rmul__", context.new_rustfunc(int_rmul)); - context.set_attr(&int_type, "__neg__", context.new_rustfunc(int_neg)); - context.set_attr(&int_type, "__or__", context.new_rustfunc(int_or)); - context.set_attr(&int_type, "__pos__", context.new_rustfunc(int_pos)); - context.set_attr(&int_type, "__pow__", context.new_rustfunc(int_pow)); - context.set_attr(&int_type, "__repr__", context.new_rustfunc(int_repr)); - context.set_attr(&int_type, "__sub__", context.new_rustfunc(int_sub)); - context.set_attr(&int_type, "__rsub__", context.new_rustfunc(int_rsub)); - 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, - "__rtruediv__", - context.new_rustfunc(int_rtruediv), - ); - 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( - &int_type, - "bit_length", - context.new_rustfunc(int_bit_length), - ); context.set_attr(&int_type, "__doc__", context.new_str(int_doc.to_string())); - context.set_attr(&int_type, "conjugate", context.new_rustfunc(int_conjugate)); - context.set_attr(&int_type, "real", context.new_property(int_real)); - context.set_attr(&int_type, "imag", context.new_property(int_imag)); + context.set_attr(&int_type, "__eq__", context.new_rustfunc(PyIntRef::eq)); + context.set_attr(&int_type, "__ne__", context.new_rustfunc(PyIntRef::ne)); + context.set_attr(&int_type, "__lt__", context.new_rustfunc(PyIntRef::lt)); + context.set_attr(&int_type, "__le__", context.new_rustfunc(PyIntRef::le)); + context.set_attr(&int_type, "__gt__", context.new_rustfunc(PyIntRef::gt)); + context.set_attr(&int_type, "__ge__", context.new_rustfunc(PyIntRef::ge)); + context.set_attr(&int_type, "__abs__", context.new_rustfunc(PyIntRef::abs)); + context.set_attr(&int_type, "__add__", context.new_rustfunc(PyIntRef::add)); + context.set_attr(&int_type, "__radd__", context.new_rustfunc(PyIntRef::add)); + context.set_attr(&int_type, "__and__", context.new_rustfunc(PyIntRef::and)); + context.set_attr(&int_type, "__divmod__", context.new_rustfunc(PyIntRef::divmod)); + context.set_attr(&int_type, "__float__", context.new_rustfunc(PyIntRef::float)); + context.set_attr(&int_type, "__round__", context.new_rustfunc(PyIntRef::round)); + context.set_attr(&int_type, "__ceil__", context.new_rustfunc(PyIntRef::pass_value)); + context.set_attr(&int_type, "__floor__", context.new_rustfunc(PyIntRef::pass_value)); + context.set_attr(&int_type, "__index__", context.new_rustfunc(PyIntRef::pass_value)); + context.set_attr(&int_type, "__trunc__", context.new_rustfunc(PyIntRef::pass_value)); + context.set_attr(&int_type, "__int__", context.new_rustfunc(PyIntRef::pass_value)); + context.set_attr(&int_type, "__floordiv__", context.new_rustfunc(PyIntRef::floordiv)); + context.set_attr(&int_type, "__hash__", context.new_rustfunc(PyIntRef::hash)); + context.set_attr(&int_type, "__lshift__", context.new_rustfunc(PyIntRef::lshift)); + context.set_attr(&int_type, "__rshift__", context.new_rustfunc(PyIntRef::rshift)); + context.set_attr(&int_type, "__new__", context.new_rustfunc(int_new)); + context.set_attr(&int_type, "__mod__", context.new_rustfunc(PyIntRef::mod_)); + context.set_attr(&int_type, "__mul__", context.new_rustfunc(PyIntRef::mul)); + context.set_attr(&int_type, "__rmul__", context.new_rustfunc(PyIntRef::mul)); + context.set_attr(&int_type, "__or__", context.new_rustfunc(PyIntRef::or)); + context.set_attr(&int_type, "__neg__", context.new_rustfunc(PyIntRef::neg)); + context.set_attr(&int_type, "__pos__", context.new_rustfunc(PyIntRef::pass_value)); + context.set_attr(&int_type, "__pow__", context.new_rustfunc(PyIntRef::pow)); + context.set_attr(&int_type, "__repr__", context.new_rustfunc(PyIntRef::repr)); + context.set_attr(&int_type, "__sub__", context.new_rustfunc(PyIntRef::sub)); + context.set_attr(&int_type, "__rsub__", context.new_rustfunc(PyIntRef::rsub)); + context.set_attr(&int_type, "__format__", context.new_rustfunc(PyIntRef::format)); + context.set_attr(&int_type, "__truediv__", context.new_rustfunc(PyIntRef::truediv)); + context.set_attr(&int_type, "__rtruediv__", context.new_rustfunc(PyIntRef::rtruediv)); + context.set_attr(&int_type, "__xor__", context.new_rustfunc(PyIntRef::xor)); + context.set_attr(&int_type, "__rxor__", context.new_rustfunc(PyIntRef::rxor)); + context.set_attr(&int_type, "__bool__", context.new_rustfunc(PyIntRef::bool)); + context.set_attr(&int_type, "__invert__", context.new_rustfunc(PyIntRef::invert)); + context.set_attr(&int_type, "bit_length", context.new_rustfunc(PyIntRef::bit_length)); + context.set_attr(&int_type, "conjugate", context.new_rustfunc(PyIntRef::pass_value)); + context.set_attr(&int_type, "real", context.new_property(PyIntRef::pass_value)); + context.set_attr(&int_type, "imag", context.new_property(PyIntRef::imag)); } From c00d368448e6afbf741d22dacb34acf7763187f1 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Mon, 11 Mar 2019 19:38:26 +0100 Subject: [PATCH 263/380] Move classmethod and staticmethod to own files. Also use extend_class more. --- vm/src/obj/mod.rs | 2 + vm/src/obj/objclassmethod.rs | 42 +++++++++++++ vm/src/obj/objfilter.rs | 12 ++-- vm/src/obj/objfunction.rs | 108 +++------------------------------- vm/src/obj/objstaticmethod.rs | 39 ++++++++++++ vm/src/obj/objzip.rs | 6 +- vm/src/pyobject.rs | 4 ++ 7 files changed, 104 insertions(+), 109 deletions(-) create mode 100644 vm/src/obj/objclassmethod.rs create mode 100644 vm/src/obj/objstaticmethod.rs diff --git a/vm/src/obj/mod.rs b/vm/src/obj/mod.rs index f32683fd25..0419d827f0 100644 --- a/vm/src/obj/mod.rs +++ b/vm/src/obj/mod.rs @@ -4,6 +4,7 @@ pub mod objbool; pub mod objbuiltinfunc; pub mod objbytearray; pub mod objbytes; +pub mod objclassmethod; pub mod objcode; pub mod objcomplex; pub mod objdict; @@ -27,6 +28,7 @@ pub mod objrange; pub mod objsequence; pub mod objset; pub mod objslice; +pub mod objstaticmethod; pub mod objstr; pub mod objsuper; pub mod objtuple; diff --git a/vm/src/obj/objclassmethod.rs b/vm/src/obj/objclassmethod.rs new file mode 100644 index 0000000000..9c8ea3f8f1 --- /dev/null +++ b/vm/src/obj/objclassmethod.rs @@ -0,0 +1,42 @@ +use crate::pyobject::{AttributeProtocol, PyContext, PyFuncArgs, PyResult, TypeProtocol}; +use crate::vm::VirtualMachine; + +pub fn init(context: &PyContext) { + let classmethod_type = &context.classmethod_type; + extend_class!(context, classmethod_type, { + "__get__" => context.new_rustfunc(classmethod_get), + "__new__" => context.new_rustfunc(classmethod_new) + }); +} + +fn classmethod_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + trace!("classmethod.__get__ {:?}", args.args); + arg_check!( + vm, + args, + required = [ + (cls, Some(vm.ctx.classmethod_type())), + (_inst, None), + (owner, None) + ] + ); + match cls.get_attr("function") { + Some(function) => { + let py_obj = owner.clone(); + let py_method = vm.ctx.new_bound_method(function, py_obj); + Ok(py_method) + } + None => Err(vm.new_attribute_error( + "Attribute Error: classmethod must have 'function' attribute".to_string(), + )), + } +} + +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 = 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/objfilter.rs b/vm/src/obj/objfilter.rs index 94cd15bb83..d39373739b 100644 --- a/vm/src/obj/objfilter.rs +++ b/vm/src/obj/objfilter.rs @@ -70,11 +70,9 @@ pub fn init(context: &PyContext) { 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)); + extend_class!(context, filter_type, { + "__new__" => context.new_rustfunc(filter_new), + "__doc__" => context.new_str(filter_doc.to_string()), + "__next__" => context.new_rustfunc(filter_next) + }); } diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index ac860857a4..f0b5519335 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -1,7 +1,6 @@ use crate::frame::Scope; use crate::pyobject::{ - AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, PyValue, - TypeProtocol, + IdProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -50,44 +49,15 @@ impl PyValue for PyMethod { 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_property(function_code), - ); + extend_class!(context, function_type, { + "__get__" => context.new_rustfunc(bind_method), + "__code__" => context.new_property(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), - ); - - let classmethod_type = &context.classmethod_type; - context.set_attr( - &classmethod_type, - "__get__", - context.new_rustfunc(classmethod_get), - ); - context.set_attr( - &classmethod_type, - "__new__", - context.new_rustfunc(classmethod_new), - ); - - let staticmethod_type = &context.staticmethod_type; - context.set_attr( - staticmethod_type, - "__get__", - context.new_rustfunc(staticmethod_get), - ); - context.set_attr( - staticmethod_type, - "__new__", - context.new_rustfunc(staticmethod_new), - ); + extend_class!(context, builtin_function_or_method_type, { + "__get__" => context.new_rustfunc(bind_method) + }); } fn bind_method(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -110,65 +80,3 @@ fn function_code(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { None => Err(vm.new_type_error("no code".to_string())), } } - -// Classmethod type methods: -fn classmethod_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - trace!("classmethod.__get__ {:?}", args.args); - arg_check!( - vm, - args, - required = [ - (cls, Some(vm.ctx.classmethod_type())), - (_inst, None), - (owner, None) - ] - ); - match cls.get_attr("function") { - Some(function) => { - let py_obj = owner.clone(); - let py_method = vm.ctx.new_bound_method(function, py_obj); - Ok(py_method) - } - None => Err(vm.new_attribute_error( - "Attribute Error: classmethod must have 'function' attribute".to_string(), - )), - } -} - -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 = vm.ctx.new_instance(cls.clone(), None); - vm.ctx.set_attr(&py_obj, "function", callable.clone()); - Ok(py_obj) -} - -// `staticmethod` methods. -fn staticmethod_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - trace!("staticmethod.__get__ {:?}", args.args); - arg_check!( - vm, - args, - required = [ - (cls, Some(vm.ctx.staticmethod_type())), - (_inst, None), - (_owner, None) - ] - ); - match cls.get_attr("function") { - Some(function) => Ok(function), - None => Err(vm.new_attribute_error( - "Attribute Error: staticmethod must have 'function' attribute".to_string(), - )), - } -} - -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 = 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/objstaticmethod.rs b/vm/src/obj/objstaticmethod.rs new file mode 100644 index 0000000000..6807a695ce --- /dev/null +++ b/vm/src/obj/objstaticmethod.rs @@ -0,0 +1,39 @@ +use crate::pyobject::{AttributeProtocol, PyContext, PyFuncArgs, PyResult, TypeProtocol}; +use crate::vm::VirtualMachine; + +pub fn init(context: &PyContext) { + let staticmethod_type = &context.staticmethod_type; + extend_class!(context, staticmethod_type, { + "__get__" => context.new_rustfunc(staticmethod_get), + "__new__" => context.new_rustfunc(staticmethod_new), + }); +} + +// `staticmethod` methods. +fn staticmethod_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + trace!("staticmethod.__get__ {:?}", args.args); + arg_check!( + vm, + args, + required = [ + (cls, Some(vm.ctx.staticmethod_type())), + (_inst, None), + (_owner, None) + ] + ); + match cls.get_attr("function") { + Some(function) => Ok(function), + None => Err(vm.new_attribute_error( + "Attribute Error: staticmethod must have 'function' attribute".to_string(), + )), + } +} + +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 = 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/objzip.rs b/vm/src/obj/objzip.rs index 8831564a8f..f108ef54b7 100644 --- a/vm/src/obj/objzip.rs +++ b/vm/src/obj/objzip.rs @@ -49,6 +49,8 @@ fn zip_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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)); + extend_class!(context, zip_type, { + "__new__" => context.new_rustfunc(zip_new), + "__next__" => context.new_rustfunc(zip_next) + }); } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 0d348e1b68..e4506dbe7c 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -19,6 +19,7 @@ use crate::obj::objbool; use crate::obj::objbuiltinfunc::PyBuiltinFunction; use crate::obj::objbytearray; use crate::obj::objbytes; +use crate::obj::objclassmethod; use crate::obj::objcode; use crate::obj::objcomplex::{self, PyComplex}; use crate::obj::objdict::{self, PyDict}; @@ -41,6 +42,7 @@ use crate::obj::objproperty; use crate::obj::objrange; use crate::obj::objset::{self, PySet}; use crate::obj::objslice; +use crate::obj::objstaticmethod; use crate::obj::objstr; use crate::obj::objsuper; use crate::obj::objtuple::{self, PyTuple}; @@ -296,6 +298,8 @@ impl PyContext { objobject::init(&context); objdict::init(&context); objfunction::init(&context); + objstaticmethod::init(&context); + objclassmethod::init(&context); objgenerator::init(&context); objint::init(&context); objfloat::init(&context); From 1e39512e7402365a864beefac6c9a2759a1c2544 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Mon, 11 Mar 2019 17:43:12 +0000 Subject: [PATCH 264/380] Convert _mro to iterator. --- vm/src/obj/objobject.rs | 18 ++++----- vm/src/obj/objtype.rs | 86 +++++++++++++++++++++++++++-------------- 2 files changed, 65 insertions(+), 39 deletions(-) diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 8b25e4a6b2..0906f2224a 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -1,3 +1,4 @@ +use super::objlist::PyList; use super::objstr; use super::objtype; use crate::obj::objproperty::PropertyBuilder; @@ -133,16 +134,13 @@ fn object_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_str(format!("<{} object at 0x{:x}>", type_name, address))) } -pub fn object_dir(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(obj, None)]); - +pub fn object_dir(obj: PyObjectRef, vm: &mut VirtualMachine) -> PyList { let attributes = get_attributes(&obj); - Ok(vm.ctx.new_list( - attributes - .keys() - .map(|k| vm.ctx.new_str(k.to_string())) - .collect(), - )) + let attributes: Vec = attributes + .keys() + .map(|k| vm.ctx.new_str(k.to_string())) + .collect(); + PyList::from(attributes) } fn object_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -260,7 +258,7 @@ fn object_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn get_attributes(obj: &PyObjectRef) -> PyAttributes { // Get class attributes: - let mut attributes = objtype::get_attributes(&obj.typ()); + let mut attributes = objtype::get_attributes(obj.type_pyref()); // Get instance attributes: if let Some(dict) = &obj.dict { diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index b5124c37b9..991326d404 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -2,13 +2,15 @@ use std::cell::RefCell; use std::collections::HashMap; use crate::pyobject::{ - AttributeProtocol, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObject, PyObjectRef, - PyRef, PyResult, PyValue, TypeProtocol, + AttributeProtocol, FromPyObjectRef, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObject, + PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; use super::objdict; +use super::objlist::PyList; use super::objstr; +use super::objtuple::PyTuple; #[derive(Clone, Debug)] pub struct PyClass { @@ -24,6 +26,41 @@ impl PyValue for PyClass { } } +struct IterMro<'a> { + cls: &'a PyClassRef, + offset: Option, +} + +impl<'a> Iterator for IterMro<'a> { + type Item = &'a PyObjectRef; + + fn next(&mut self) -> Option { + match self.offset { + None => { + self.offset = Some(0); + Some(&self.cls.as_object()) + } + Some(offset) => { + if offset < self.cls.mro.len() { + self.offset = Some(offset + 1); + Some(&self.cls.mro[offset]) + } else { + None + } + } + } + } +} + +impl PyClassRef { + fn iter_mro(&self) -> IterMro { + IterMro { + cls: self, + offset: None, + } + } +} + /* * The magical type type */ @@ -60,22 +97,12 @@ pub fn init(ctx: &PyContext) { }); } -fn type_mro(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(cls, Some(vm.ctx.type_type()))]); - match _mro(cls.clone()) { - Some(mro) => Ok(vm.context().new_tuple(mro)), - None => Err(vm.new_type_error("Only classes have an MRO.".to_string())), - } +fn type_mro(cls: PyClassRef, _vm: &mut VirtualMachine) -> PyResult { + Ok(PyTuple::from(_mro(&cls))) } -fn _mro(cls: PyObjectRef) -> Option> { - if let Some(PyClass { ref mro, .. }) = cls.payload::() { - let mut mro = mro.clone(); - mro.insert(0, cls.clone()); - Some(mro) - } else { - None - } +fn _mro(cls: &PyClassRef) -> Vec { + cls.iter_mro().cloned().collect() } /// Determines if `obj` actually an instance of `cls`, this doesn't call __instancecheck__, so only @@ -212,24 +239,22 @@ pub fn type_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult } } -pub fn type_dir(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(obj, None)]); - - let attributes = get_attributes(&obj); - Ok(vm.ctx.new_list( - attributes - .keys() - .map(|k| vm.ctx.new_str(k.to_string())) - .collect(), - )) +pub fn type_dir(obj: PyClassRef, vm: &mut VirtualMachine) -> PyList { + let attributes = get_attributes(obj); + let attributes: Vec = attributes + .keys() + .map(|k| vm.ctx.new_str(k.to_string())) + .collect(); + PyList::from(attributes) } -pub fn get_attributes(obj: &PyObjectRef) -> PyAttributes { +pub fn get_attributes(cls: PyClassRef) -> PyAttributes { // Gather all members here: let mut attributes = PyAttributes::new(); - let mut base_classes = _mro(obj.clone()).expect("Type get_attributes on non-type"); + let mut base_classes: Vec<&PyObjectRef> = cls.iter_mro().collect(); base_classes.reverse(); + for bc in base_classes { if let Some(ref dict) = &bc.dict { for (name, value) in dict.borrow().iter() { @@ -294,7 +319,10 @@ pub fn new( bases: Vec, dict: HashMap, ) -> PyResult { - let mros = bases.into_iter().map(|x| _mro(x).unwrap()).collect(); + let mros = bases + .into_iter() + .map(|x| _mro(&FromPyObjectRef::from_pyobj(&x))) + .collect(); let mro = linearise_mro(mros).unwrap(); Ok(PyObject { payload: Box::new(PyClass { From 633a9b03651f6263ab8c5cd360878cdf351ee251 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Mon, 11 Mar 2019 17:54:14 +0000 Subject: [PATCH 265/380] Move more methods onto PyClassRef. --- vm/src/obj/objtype.rs | 87 ++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 51 deletions(-) diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 991326d404..6125217a83 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -9,7 +9,7 @@ use crate::vm::VirtualMachine; use super::objdict; use super::objlist::PyList; -use super::objstr; +use super::objstr::{self, PyStringRef}; use super::objtuple::PyTuple; #[derive(Clone, Debug)] @@ -59,6 +59,35 @@ impl PyClassRef { offset: None, } } + + fn mro(self, _vm: &mut VirtualMachine) -> PyTuple { + PyTuple::from(_mro(&self)) + } + + fn dir(self, vm: &mut VirtualMachine) -> PyList { + let attributes = get_attributes(self); + let attributes: Vec = attributes + .keys() + .map(|k| vm.ctx.new_str(k.to_string())) + .collect(); + PyList::from(attributes) + } + + fn instance_check(self, obj: PyObjectRef, _vm: &mut VirtualMachine) -> bool { + isinstance(&obj, self.as_object()) + } + + fn subclass_check(self, subclass: PyObjectRef, _vm: &mut VirtualMachine) -> bool { + issubclass(&subclass, self.as_object()) + } + + fn repr(self, _vm: &mut VirtualMachine) -> String { + format!("", self.name) + } + + fn prepare(_name: PyStringRef, _bases: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + vm.new_dict() + } } /* @@ -86,21 +115,17 @@ pub fn init(ctx: &PyContext) { extend_class!(&ctx, &ctx.type_type, { "__call__" => ctx.new_rustfunc(type_call), "__new__" => ctx.new_rustfunc(type_new), - "__mro__" => ctx.new_property(type_mro), - "__repr__" => ctx.new_rustfunc(type_repr), - "__prepare__" => ctx.new_rustfunc(type_prepare), + "__mro__" => ctx.new_property(PyClassRef::mro), + "__repr__" => ctx.new_rustfunc(PyClassRef::repr), + "__prepare__" => ctx.new_rustfunc(PyClassRef::prepare), "__getattribute__" => ctx.new_rustfunc(type_getattribute), - "__instancecheck__" => ctx.new_rustfunc(type_instance_check), - "__subclasscheck__" => ctx.new_rustfunc(type_subclass_check), + "__instancecheck__" => ctx.new_rustfunc(PyClassRef::instance_check), + "__subclasscheck__" => ctx.new_rustfunc(PyClassRef::subclass_check), "__doc__" => ctx.new_str(type_doc.to_string()), - "__dir__" => ctx.new_rustfunc(type_dir), + "__dir__" => ctx.new_rustfunc(PyClassRef::dir), }); } -fn type_mro(cls: PyClassRef, _vm: &mut VirtualMachine) -> PyResult { - Ok(PyTuple::from(_mro(&cls))) -} - fn _mro(cls: &PyClassRef) -> Vec { cls.iter_mro().cloned().collect() } @@ -111,15 +136,6 @@ pub fn isinstance(obj: &PyObjectRef, cls: &PyObjectRef) -> bool { issubclass(obj.type_ref(), &cls) } -fn type_instance_check(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(typ, Some(vm.ctx.type_type())), (obj, None)] - ); - Ok(vm.new_bool(isinstance(obj, typ))) -} - /// Determines if `subclass` is actually a subclass of `cls`, this doesn't call __subclasscheck__, /// so only use this if `cls` is known to have not overridden the base __subclasscheck__ magic /// method. @@ -128,18 +144,6 @@ pub fn issubclass(subclass: &PyObjectRef, cls: &PyObjectRef) -> bool { subclass.is(&cls) || mro.iter().any(|c| c.is(&cls)) } -fn type_subclass_check(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (cls, Some(vm.ctx.type_type())), - (subclass, Some(vm.ctx.type_type())) - ] - ); - Ok(vm.new_bool(issubclass(subclass, cls))) -} - pub fn get_type_name(typ: &PyObjectRef) -> String { if let Some(PyClass { name, .. }) = &typ.payload::() { name.clone() @@ -239,15 +243,6 @@ pub fn type_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult } } -pub fn type_dir(obj: PyClassRef, vm: &mut VirtualMachine) -> PyList { - let attributes = get_attributes(obj); - let attributes: Vec = attributes - .keys() - .map(|k| vm.ctx.new_str(k.to_string())) - .collect(); - PyList::from(attributes) -} - pub fn get_attributes(cls: PyClassRef) -> PyAttributes { // Gather all members here: let mut attributes = PyAttributes::new(); @@ -335,16 +330,6 @@ pub fn new( .into_ref()) } -fn type_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(obj, Some(vm.ctx.type_type()))]); - let type_name = get_type_name(&obj); - Ok(vm.new_str(format!("", type_name))) -} - -fn type_prepare(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { - Ok(vm.new_dict()) -} - #[cfg(test)] mod tests { use super::{linearise_mro, new}; From 87e6d8d13dfe25da0be88829860e88bf7c1ecc99 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sun, 10 Mar 2019 21:25:59 +0200 Subject: [PATCH 266/380] Add __import__ --- tests/snippets/import.py | 5 +++++ vm/src/builtins.rs | 24 ++++++++++++++++++++++- vm/src/frame.rs | 41 +++++++++++++++++++++++----------------- vm/src/import.rs | 22 --------------------- vm/src/vm.rs | 11 +++++++---- 5 files changed, 59 insertions(+), 44 deletions(-) diff --git a/tests/snippets/import.py b/tests/snippets/import.py index 0a36e63429..4be2a63277 100644 --- a/tests/snippets/import.py +++ b/tests/snippets/import.py @@ -22,6 +22,11 @@ except ImportError: pass + +test = __import__("import_target") +assert test.X == import_target.X + + # TODO: Once we can determine current directory, use that to construct this # path: #import sys diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 5a0c151957..9611f63276 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -5,8 +5,10 @@ // use std::ops::Deref; use std::char; use std::io::{self, Write}; +use std::path::PathBuf; use crate::compile; +use crate::import::import_module; use crate::obj::objbool; use crate::obj::objdict; use crate::obj::objint; @@ -713,8 +715,27 @@ fn builtin_sum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(sum) } +// Should be renamed to builtin___import__? +fn builtin_import(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(name, Some(vm.ctx.str_type()))], + optional = [ + (_globals, Some(vm.ctx.dict_type())), + (_locals, Some(vm.ctx.dict_type())) + ] + ); + let current_path = { + let mut source_pathbuf = PathBuf::from(&vm.current_frame().code.source_path); + source_pathbuf.pop(); + source_pathbuf + }; + + import_module(vm, current_path, &objstr::get_value(name)) +} + // builtin_vars -// builtin___import__ pub fn make_module(ctx: &PyContext) -> PyObjectRef { let py_mod = py_module!(ctx, "__builtins__", { @@ -783,6 +804,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { "tuple" => ctx.tuple_type(), "type" => ctx.type_type(), "zip" => ctx.zip_type(), + "__import__" => ctx.new_rustfunc(builtin_import), // Constants "NotImplemented" => ctx.not_implemented.clone(), diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 48d3d6229c..51f5818531 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -1,6 +1,5 @@ use std::cell::RefCell; use std::fmt; -use std::path::PathBuf; use std::rc::Rc; use num_bigint::BigInt; @@ -9,7 +8,6 @@ use rustpython_parser::ast; use crate::builtins; use crate::bytecode; -use crate::import::{import, import_module}; use crate::obj::objbool; use crate::obj::objbuiltinfunc::PyBuiltinFunction; use crate::obj::objcode; @@ -22,8 +20,8 @@ use crate::obj::objslice::PySlice; use crate::obj::objstr; use crate::obj::objtype; use crate::pyobject::{ - DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, - TryFromObject, TypeProtocol, + AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectRef, + PyResult, PyValue, TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -806,30 +804,39 @@ impl Frame { module: &str, symbol: &Option, ) -> FrameResult { - let current_path = { - let mut source_pathbuf = PathBuf::from(&self.code.source_path); - source_pathbuf.pop(); - source_pathbuf + let import = vm.builtins.get_item("__import__"); + let module = match import { + Some(func) => vm.invoke(func, vec![vm.ctx.new_str(module.to_string())])?, + None => panic!("No __import__ in builtins"), }; - let obj = import(vm, current_path, module, symbol)?; + // 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_attr(symbol).map_or_else( + || { + let import_error = vm.context().exceptions.import_error.clone(); + Err(vm.new_exception(import_error, format!("cannot import name '{}'", symbol))) + }, + Ok, + ), + None => Ok(module), + }; // Push module on stack: - self.push_value(obj); + self.push_value(obj?); Ok(None) } fn import_star(&self, vm: &mut VirtualMachine, module: &str) -> FrameResult { - let current_path = { - let mut source_pathbuf = PathBuf::from(&self.code.source_path); - source_pathbuf.pop(); - source_pathbuf + let import = vm.builtins.get_item("__import__"); + let module = match import { + Some(func) => vm.invoke(func, vec![vm.ctx.new_str(module.to_string())])?, + None => panic!("No __import__ in builtins"), }; // Grab all the names from the module and put them in the context - let obj = import_module(vm, current_path, module)?; - - for (k, v) in obj.get_key_value_pairs().iter() { + for (k, v) in module.get_key_value_pairs().iter() { self.scope.store_name(&vm, &objstr::get_value(k), v.clone()); } Ok(None) diff --git a/vm/src/import.rs b/vm/src/import.rs index 3031ed38b7..a36168c287 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -63,28 +63,6 @@ pub fn import_module( Ok(module) } -pub fn import( - vm: &mut VirtualMachine, - current_path: PathBuf, - module_name: &str, - symbol: &Option, -) -> PyResult { - 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 - if let Some(symbol) = symbol { - module.get_attr(symbol).map_or_else( - || { - let import_error = vm.context().exceptions.import_error.clone(); - Err(vm.new_exception(import_error, format!("cannot import name '{}'", symbol))) - }, - Ok, - ) - } else { - Ok(module) - } -} - fn find_source(vm: &VirtualMachine, current_path: PathBuf, name: &str) -> Result { let sys_path = vm.sys_module.get_attr("path").unwrap(); let mut paths: Vec = objsequence::get_elements(&sys_path) diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 5dca659467..20c421b6a0 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -13,8 +13,7 @@ use std::sync::{Mutex, MutexGuard}; use crate::builtins; use crate::bytecode; -use crate::frame::ExecutionResult; -use crate::frame::Scope; +use crate::frame::{ExecutionResult, Frame, Scope}; use crate::obj::objbool; use crate::obj::objbuiltinfunc::PyBuiltinFunction; use crate::obj::objcode; @@ -94,9 +93,13 @@ impl VirtualMachine { result } - pub fn current_scope(&self) -> &Scope { + pub fn current_frame(&self) -> &Frame { let current_frame = &self.frames[self.frames.len() - 1]; - let frame = objframe::get_value(current_frame); + objframe::get_value(current_frame) + } + + pub fn current_scope(&self) -> &Scope { + let frame = self.current_frame(); &frame.scope } From 8c92636a8264e22f42deddade99077b5ab6fa34e Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sun, 10 Mar 2019 23:21:00 +0200 Subject: [PATCH 267/380] Fix wasm code --- wasm/lib/src/browser_module.rs | 15 +++++++-------- wasm/lib/src/convert.rs | 22 ++++++++-------------- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index 6469d64b1f..035954a7c7 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -4,9 +4,10 @@ use js_sys::Promise; use num_traits::cast::ToPrimitive; use rustpython_vm::obj::{objint, objstr}; use rustpython_vm::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, + AttributeProtocol, PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, + TypeProtocol, }; -use rustpython_vm::{import::import, VirtualMachine}; +use rustpython_vm::{import::import_module, VirtualMachine}; use std::path::PathBuf; use wasm_bindgen::{prelude::*, JsCast}; use wasm_bindgen_futures::{future_to_promise, JsFuture}; @@ -177,12 +178,10 @@ pub fn get_promise_value(obj: &PyObjectRef) -> Promise { } pub fn import_promise_type(vm: &mut VirtualMachine) -> PyResult { - import( - vm, - PathBuf::default(), - BROWSER_NAME, - &Some("Promise".into()), - ) + match import_module(vm, PathBuf::default(), BROWSER_NAME)?.get_attr("Promise".into()) { + Some(promise) => Ok(promise), + None => Err(vm.new_not_implemented_error("No Promise".to_string())), + } } fn promise_then(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index 2f0640f1b6..db2a4d4d7f 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -128,13 +128,10 @@ pub fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { } arr.into() } else { - let dumps = rustpython_vm::import::import( - vm, - std::path::PathBuf::default(), - "json", - &Some("dumps".into()), - ) - .expect("Couldn't get json.dumps function"); + let dumps = rustpython_vm::import::import_module(vm, std::path::PathBuf::default(), "json") + .expect("Couldn't get json module") + .get_attr("dump".into()) + .expect("Couldn't get json dumps"); match vm.invoke(dumps, pyobject::PyFuncArgs::new(vec![py_obj], vec![])) { Ok(value) => { let json = vm.to_pystr(&value).unwrap(); @@ -231,13 +228,10 @@ pub fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef { // 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("json.loads function to be available"); + let loads = rustpython_vm::import::import_module(vm, std::path::PathBuf::default(), "json") + .expect("Couldn't get json module") + .get_attr("loads".into()) + .expect("Couldn't get json dumps"); let json = match js_sys::JSON::stringify(&js_val) { Ok(json) => String::from(json), From a3823b454043057ee6bbc67a2184e2f840332c6a Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Mon, 11 Mar 2019 22:49:55 +0200 Subject: [PATCH 268/380] Test override __import__ --- tests/snippets/import.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/snippets/import.py b/tests/snippets/import.py index 4be2a63277..a481e3f1ae 100644 --- a/tests/snippets/import.py +++ b/tests/snippets/import.py @@ -26,6 +26,23 @@ test = __import__("import_target") assert test.X == import_target.X +import builtins +class OverrideImportContext(): + + def __enter__(self): + self.original_import = builtins.__import__ + + def __exit__(self, exc_type, exc_val, exc_tb): + builtins.__import__ = self.original_import + +with OverrideImportContext(): + def fake_import(name, globals=None, locals=None, fromlist=(), level=0): + return len(name) + + builtins.__import__ = fake_import + import test + assert test == 4 + # TODO: Once we can determine current directory, use that to construct this # path: From e3a5ac1378ce5ab089432673aa1430851d38bd85 Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Mon, 11 Mar 2019 19:17:07 +0100 Subject: [PATCH 269/380] Make objint::get_value return a reference --- vm/src/builtins.rs | 2 +- vm/src/obj/objenumerate.rs | 2 +- vm/src/obj/objfloat.rs | 2 +- vm/src/obj/objint.rs | 38 +++++++++++++++++--------------------- vm/src/obj/objrange.rs | 8 ++++---- vm/src/obj/objslice.rs | 6 +++--- vm/src/obj/objtuple.rs | 3 ++- vm/src/vm.rs | 2 +- 8 files changed, 30 insertions(+), 33 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 5a0c151957..bc98413373 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -674,7 +674,7 @@ fn builtin_round(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } 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))) + Ok(vm.ctx.new_int(objint::get_value(rounded).clone())) } } diff --git a/vm/src/obj/objenumerate.rs b/vm/src/obj/objenumerate.rs index b37f610628..44937377fb 100644 --- a/vm/src/obj/objenumerate.rs +++ b/vm/src/obj/objenumerate.rs @@ -30,7 +30,7 @@ fn enumerate_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { optional = [(start, Some(vm.ctx.int_type()))] ); let counter = if let Some(x) = start { - objint::get_value(x) + objint::get_value(x).clone() } else { BigInt::zero() }; diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index ae16b625aa..ee9502a531 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -45,7 +45,7 @@ impl PyFloatRef { let other_int = objint::get_value(&other); if let (Some(self_int), Some(other_float)) = (value.to_bigint(), other_int.to_f64()) { - value == other_float && self_int == other_int + value == other_float && self_int == *other_int } else { false } diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 3d1dc03b9f..dbabe79907 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -88,7 +88,7 @@ impl PyIntRef { fn eq(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { if objtype::isinstance(&other, &vm.ctx.int_type()) { - vm.ctx.new_bool(self.value == get_value(&other)) + vm.ctx.new_bool(self.value == *get_value(&other)) } else { vm.ctx.not_implemented() } @@ -96,7 +96,7 @@ impl PyIntRef { fn ne(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { if objtype::isinstance(&other, &vm.ctx.int_type()) { - vm.ctx.new_bool(self.value != get_value(&other)) + vm.ctx.new_bool(self.value != *get_value(&other)) } else { vm.ctx.not_implemented() } @@ -104,7 +104,7 @@ impl PyIntRef { fn lt(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { if objtype::isinstance(&other, &vm.ctx.int_type()) { - vm.ctx.new_bool(self.value < get_value(&other)) + vm.ctx.new_bool(self.value < *get_value(&other)) } else { vm.ctx.not_implemented() } @@ -112,7 +112,7 @@ impl PyIntRef { fn le(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { if objtype::isinstance(&other, &vm.ctx.int_type()) { - vm.ctx.new_bool(self.value <= get_value(&other)) + vm.ctx.new_bool(self.value <= *get_value(&other)) } else { vm.ctx.not_implemented() } @@ -120,7 +120,7 @@ impl PyIntRef { fn gt(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { if objtype::isinstance(&other, &vm.ctx.int_type()) { - vm.ctx.new_bool(self.value > get_value(&other)) + vm.ctx.new_bool(self.value > *get_value(&other)) } else { vm.ctx.not_implemented() } @@ -128,7 +128,7 @@ impl PyIntRef { fn ge(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { if objtype::isinstance(&other, &vm.ctx.int_type()) { - vm.ctx.new_bool(self.value >= get_value(&other)) + vm.ctx.new_bool(self.value >= *get_value(&other)) } else { vm.ctx.not_implemented() } @@ -185,7 +185,7 @@ impl PyIntRef { fn floordiv(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.int_type()) { let v2 = get_value(&other); - if v2 != BigInt::zero() { + if *v2 != BigInt::zero() { Ok(vm.ctx.new_int((&self.value) / v2)) } else { Err(vm.new_zero_division_error("integer floordiv by zero".to_string())) @@ -210,10 +210,8 @@ impl PyIntRef { // i2 failed `to_usize()` conversion match get_value(&other) { - 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()) => { + v if *v < BigInt::zero() => Err(vm.new_value_error("negative shift count".to_string())), + v if *v > BigInt::from(usize::max_value()) => { Err(vm.new_overflow_error("the number is too large to convert to int".to_string())) } _ => panic!("Failed converting {} to rust usize", get_value(&other)), @@ -235,10 +233,8 @@ impl PyIntRef { // i2 failed `to_usize()` conversion match get_value(&other) { - 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()) => { + v if *v < BigInt::zero() => Err(vm.new_value_error("negative shift count".to_string())), + v if *v > BigInt::from(usize::max_value()) => { Err(vm.new_overflow_error("the number is too large to convert to int".to_string())) } _ => panic!("Failed converting {} to rust usize", get_value(&other)), @@ -293,7 +289,7 @@ impl PyIntRef { fn mod_(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.int_type()) { let v2 = get_value(&other); - if v2 != BigInt::zero() { + if *v2 != BigInt::zero() { Ok(vm.ctx.new_int((&self.value) % v2)) } else { Err(vm.new_zero_division_error("integer modulo by zero".to_string())) @@ -306,8 +302,8 @@ impl PyIntRef { fn divmod(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.int_type()) { let v2 = get_value(&other); - if v2 != BigInt::zero() { - let (r1, r2) = self.value.div_rem(&v2); + if *v2 != BigInt::zero() { + let (r1, r2) = self.value.div_rem(v2); Ok(vm .ctx .new_tuple(vec![vm.ctx.new_int(r1), vm.ctx.new_int(r2)])) @@ -395,7 +391,7 @@ fn int_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // Casting function: pub fn to_int(vm: &mut VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult { let val = if objtype::isinstance(obj, &vm.ctx.int_type()) { - get_value(obj) + get_value(obj).clone() } else if objtype::isinstance(obj, &vm.ctx.float_type()) { objfloat::get_value(obj).to_bigint().unwrap() } else if objtype::isinstance(obj, &vm.ctx.str_type()) { @@ -421,8 +417,8 @@ pub fn to_int(vm: &mut VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult } // Retrieve inner int value: -pub fn get_value(obj: &PyObjectRef) -> BigInt { - obj.payload::().unwrap().value.clone() +pub fn get_value(obj: &PyObjectRef) -> &BigInt { + &obj.payload::().unwrap().value } #[inline] diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 1e0eee53f6..be8097ceae 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -207,19 +207,19 @@ fn range_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let start = if second.is_some() { - objint::get_value(first) + objint::get_value(first).clone() } else { BigInt::zero() }; let end = if let Some(pyint) = second { - objint::get_value(pyint) + objint::get_value(pyint).clone() } else { - objint::get_value(first) + objint::get_value(first).clone() }; let step = if let Some(pyint) = step { - objint::get_value(pyint) + objint::get_value(pyint).clone() } else { BigInt::one() }; diff --git a/vm/src/obj/objslice.rs b/vm/src/obj/objslice.rs index 5ab3c7a332..6b5f5c169e 100644 --- a/vm/src/obj/objslice.rs +++ b/vm/src/obj/objslice.rs @@ -55,9 +55,9 @@ fn slice_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }?; Ok(PyObject::new( PySlice { - start: start.map(|x| objint::get_value(x)), - stop: stop.map(|x| objint::get_value(x)), - step: step.map(|x| objint::get_value(x)), + start: start.map(|x| objint::get_value(x).clone()), + stop: stop.map(|x| objint::get_value(x).clone()), + step: step.map(|x| objint::get_value(x).clone()), }, cls.clone(), )) diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 2c692469fc..6be590a5ff 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -118,7 +118,8 @@ impl PyTupleRef { fn hash(self, vm: &mut VirtualMachine) -> PyResult { let mut hasher = std::collections::hash_map::DefaultHasher::new(); for element in self.elements.borrow().iter() { - let element_hash = objint::get_value(&vm.call_method(element, "__hash__", vec![])?); + let hash_result = vm.call_method(element, "__hash__", vec![])?; + let element_hash = objint::get_value(&hash_result); element_hash.hash(&mut hasher); } Ok(hasher.finish()) diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 5dca659467..51c490f600 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -861,7 +861,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.to_bigint().unwrap()); + assert_eq!(*value, 45_i32.to_bigint().unwrap()); } #[test] From dac75019e58d2940495c10d0b4db796edf9df2ab Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Mon, 11 Mar 2019 19:18:21 -0500 Subject: [PATCH 270/380] Remove builtin_print_html, move functionality to JS --- wasm/demo/src/main.js | 10 +++++++++- wasm/lib/Cargo.toml | 1 - wasm/lib/src/vm_class.rs | 10 +--------- wasm/lib/src/wasm_builtins.rs | 31 +------------------------------ 4 files changed, 11 insertions(+), 41 deletions(-) diff --git a/wasm/demo/src/main.js b/wasm/demo/src/main.js index b35c9aaa29..7e934dfc98 100644 --- a/wasm/demo/src/main.js +++ b/wasm/demo/src/main.js @@ -36,7 +36,15 @@ function runCodeFromTextarea() { const code = editor.getValue(); try { const result = rp.pyEval(code, { - stdout: consoleElement + stdout: output => { + const shouldScroll = + consoleElement.scrollHeight - consoleElement.scrollTop === + consoleElement.clientHeight; + consoleElement.value += output; + if (shouldScroll) { + consoleElement.scrollTop = consoleElement.scrollHeight; + } + } }); if (result !== null) { consoleElement.value += `\n${result}\n`; diff --git a/wasm/lib/Cargo.toml b/wasm/lib/Cargo.toml index 3f89ba9acb..769455aea1 100644 --- a/wasm/lib/Cargo.toml +++ b/wasm/lib/Cargo.toml @@ -26,7 +26,6 @@ features = [ "console", "Document", "Element", - "HtmlTextAreaElement", "Window", "Headers", "Request", diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index bdf3eb3254..be3bbd8133 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -273,8 +273,7 @@ impl WASMVirtualMachine { }| { fn error() -> JsValue { TypeError::new( - "Unknown stdout option, please pass a function, a textarea element, or \ - 'console'", + "Unknown stdout option, please pass a function or 'console'", ) .into() } @@ -284,13 +283,6 @@ impl WASMVirtualMachine { "console" => Box::new(wasm_builtins::builtin_print_console), _ => return Err(error()), } - } else if let Some(element) = stdout.dyn_ref::() { - let element = element.clone(); - Box::new( - move |vm: &mut VirtualMachine, args: PyFuncArgs| -> PyResult { - wasm_builtins::builtin_print_html(vm, args, &element) - }, - ) } else if stdout.is_function() { let func = js_sys::Function::from(stdout); Box::new( diff --git a/wasm/lib/src/wasm_builtins.rs b/wasm/lib/src/wasm_builtins.rs index a5867ec020..8bb6a2db2b 100644 --- a/wasm/lib/src/wasm_builtins.rs +++ b/wasm/lib/src/wasm_builtins.rs @@ -4,35 +4,16 @@ //! desktop. //! Implements functions listed here: https://docs.python.org/3/library/builtins.html. -use crate::convert; use js_sys::{self, Array}; use rustpython_vm::obj::{objstr, objtype}; use rustpython_vm::pyobject::{IdProtocol, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use rustpython_vm::VirtualMachine; -use wasm_bindgen::prelude::*; -use web_sys::{self, console, HtmlTextAreaElement}; +use web_sys::{self, console}; pub(crate) fn window() -> web_sys::Window { web_sys::window().expect("Window to be available") } -// The HTML id of the element element that act as our STDOUT - -pub fn print_to_html(text: &str, element: &HtmlTextAreaElement) -> Result<(), JsValue> { - let value = element.value(); - - let scroll_height = element.scroll_height(); - let scrolled_to_bottom = scroll_height - element.scroll_top() == element.client_height(); - - element.set_value(&format!("{}{}", value, text)); - - if scrolled_to_bottom { - element.scroll_with_x_and_y(0.0, scroll_height.into()); - } - - Ok(()) -} - pub fn format_print_args(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result { // Handle 'sep' kwarg: let sep_arg = args @@ -85,16 +66,6 @@ pub fn format_print_args(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result PyResult { - let output = format_print_args(vm, args)?; - print_to_html(&output, element).map_err(|err| convert::js_to_py(vm, err))?; - Ok(vm.get_none()) -} - pub fn builtin_print_console(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let arr = Array::new(); for arg in args.args { From 88fff7102eb97c267c59d86eb599b0cf89b647b1 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Mon, 11 Mar 2019 20:01:34 -0500 Subject: [PATCH 271/380] cargo fmt --- wasm/lib/src/vm_class.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index be3bbd8133..b3ef506272 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -272,10 +272,8 @@ impl WASMVirtualMachine { .. }| { fn error() -> JsValue { - TypeError::new( - "Unknown stdout option, please pass a function or 'console'", - ) - .into() + TypeError::new("Unknown stdout option, please pass a function or 'console'") + .into() } let print_fn: Box PyResult> = if let Some(s) = stdout.as_string() { From de2b9286766d0572cfb07dac76c84ab7cb09cbcf Mon Sep 17 00:00:00 2001 From: Tzu-Yin Hong Date: Tue, 12 Mar 2019 11:46:47 +0800 Subject: [PATCH 272/380] Format the code --- vm/src/obj/objdict.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index c86156e950..06bb822524 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -344,9 +344,7 @@ fn dict_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { (dict, Some(vm.ctx.dict_type())), (key, Some(vm.ctx.str_type())) ], - optional = [ - (default, None) - ] + optional = [(default, None)] ); // What we are looking for: From 5742e326690e4c8e87fa3b9352609a9c1259e267 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Tue, 12 Mar 2019 11:27:18 +0000 Subject: [PATCH 273/380] PropertyBuilder: Constraint on setter type is too strong. --- vm/src/obj/objproperty.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index 59117244d2..c44a4350bc 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -119,7 +119,7 @@ impl<'a, T> PropertyBuilder<'a, T> { } } - pub fn add_setter>(self, func: F) -> Self { + pub fn add_setter>(self, func: F) -> Self { let func = self.ctx.new_rustfunc(func); Self { ctx: self.ctx, From 48fba05e208b2d42a2414fed6b3e780bdd0c14be Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Tue, 12 Mar 2019 11:27:42 +0000 Subject: [PATCH 274/380] Change .__mro__ to read-only attribute so type.__mro__ works. --- tests/snippets/mro.py | 2 ++ vm/src/obj/objtype.rs | 11 ++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/snippets/mro.py b/tests/snippets/mro.py index 6f3476f91d..18acbb6916 100644 --- a/tests/snippets/mro.py +++ b/tests/snippets/mro.py @@ -18,3 +18,5 @@ class C(A, B): pass assert (C, A, B, X, Y, object) == C.__mro__ + +assert type.__mro__ == (type, object) diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 6125217a83..095479cf7d 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -9,6 +9,7 @@ use crate::vm::VirtualMachine; use super::objdict; use super::objlist::PyList; +use super::objproperty::PropertyBuilder; use super::objstr::{self, PyStringRef}; use super::objtuple::PyTuple; @@ -64,6 +65,10 @@ impl PyClassRef { PyTuple::from(_mro(&self)) } + fn set_mro(self, _value: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + Err(vm.new_attribute_error("read-only attribute".to_string())) + } + fn dir(self, vm: &mut VirtualMachine) -> PyList { let attributes = get_attributes(self); let attributes: Vec = attributes @@ -115,7 +120,11 @@ pub fn init(ctx: &PyContext) { extend_class!(&ctx, &ctx.type_type, { "__call__" => ctx.new_rustfunc(type_call), "__new__" => ctx.new_rustfunc(type_new), - "__mro__" => ctx.new_property(PyClassRef::mro), + "__mro__" => + PropertyBuilder::new(ctx) + .add_getter(PyClassRef::mro) + .add_setter(PyClassRef::set_mro) + .create(), "__repr__" => ctx.new_rustfunc(PyClassRef::repr), "__prepare__" => ctx.new_rustfunc(PyClassRef::prepare), "__getattribute__" => ctx.new_rustfunc(type_getattribute), From 3082f5faef1ad06a3a2baf1f03328d704c34f0ea Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Tue, 12 Mar 2019 13:43:30 +0000 Subject: [PATCH 275/380] Trivial implementations of IdProtocol and TypeProtocol for PyClassRef. --- vm/src/obj/objtype.rs | 16 ++++++++++++++++ vm/src/pyobject.rs | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 6125217a83..aa335aaa38 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -26,6 +26,22 @@ impl PyValue for PyClass { } } +impl IdProtocol for PyClassRef { + fn get_id(&self) -> usize { + self.as_object().get_id() + } + + fn is(&self, other: &Self) -> bool { + self.get_id() == other.get_id() + } +} + +impl TypeProtocol for PyClassRef { + fn type_ref(&self) -> &PyObjectRef { + &self.as_object().type_ref() + } +} + struct IterMro<'a> { cls: &'a PyClassRef, offset: Option, diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 10fdfd5579..9b13dfc6ba 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -774,7 +774,7 @@ impl fmt::Display for PyRef { pub trait IdProtocol { fn get_id(&self) -> usize; - fn is(&self, other: &PyObjectRef) -> bool; + fn is(&self, other: &Self) -> bool; } impl IdProtocol for PyObjectRef { @@ -782,7 +782,7 @@ impl IdProtocol for PyObjectRef { &*self as &PyObject as *const PyObject as usize } - fn is(&self, other: &PyObjectRef) -> bool { + fn is(&self, other: &Self) -> bool { self.get_id() == other.get_id() } } From 9f96b16624c99fd2a0f706fbb84a243f5f7745ac Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Tue, 12 Mar 2019 14:59:12 +0000 Subject: [PATCH 276/380] Turn PyClass.mro into a Vec --- vm/src/obj/objdict.rs | 5 +-- vm/src/obj/objtype.rs | 76 +++++++++++++++++++++++++----------------- vm/src/pyobject.rs | 27 +++++++++++---- vm/src/stdlib/types.rs | 23 +++++-------- 4 files changed, 77 insertions(+), 54 deletions(-) diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index c5cc3ec992..80cf7af163 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -3,8 +3,8 @@ use std::collections::HashMap; use std::ops::{Deref, DerefMut}; use crate::pyobject::{ - PyAttributes, PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectRef, PyRef, PyResult, - PyValue, TypeProtocol, + FromPyObjectRef, PyAttributes, PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectRef, + PyRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; @@ -339,6 +339,7 @@ fn dict_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: PyObjectRef) { + let object_type = FromPyObjectRef::from_pyobj(&object_type); // this is not ideal let ptr = PyObjectRef::into_raw(dict_type.clone()) as *mut PyObject; unsafe { diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index aa335aaa38..537ad852e0 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -15,7 +15,7 @@ use super::objtuple::PyTuple; #[derive(Clone, Debug)] pub struct PyClass { pub name: String, - pub mro: Vec, + pub mro: Vec, } pub type PyClassRef = PyRef; @@ -48,13 +48,13 @@ struct IterMro<'a> { } impl<'a> Iterator for IterMro<'a> { - type Item = &'a PyObjectRef; + type Item = &'a PyClassRef; fn next(&mut self) -> Option { match self.offset { None => { self.offset = Some(0); - Some(&self.cls.as_object()) + Some(&self.cls) } Some(offset) => { if offset < self.cls.mro.len() { @@ -77,7 +77,9 @@ impl PyClassRef { } fn mro(self, _vm: &mut VirtualMachine) -> PyTuple { - PyTuple::from(_mro(&self)) + let elements: Vec = + _mro(&self).iter().map(|x| x.as_object().clone()).collect(); + PyTuple::from(elements) } fn dir(self, vm: &mut VirtualMachine) -> PyList { @@ -111,6 +113,7 @@ impl PyClassRef { */ pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, _dict_type: PyObjectRef) { + let object_type = FromPyObjectRef::from_pyobj(&object_type); // this is not ideal let ptr = PyObjectRef::into_raw(type_type.clone()) as *mut PyObject; unsafe { @@ -142,7 +145,7 @@ pub fn init(ctx: &PyContext) { }); } -fn _mro(cls: &PyClassRef) -> Vec { +fn _mro(cls: &PyClassRef) -> Vec { cls.iter_mro().cloned().collect() } @@ -157,7 +160,7 @@ pub fn isinstance(obj: &PyObjectRef, cls: &PyObjectRef) -> bool { /// method. pub fn issubclass(subclass: &PyObjectRef, cls: &PyObjectRef) -> bool { let ref mro = subclass.payload::().unwrap().mro; - subclass.is(&cls) || mro.iter().any(|c| c.is(&cls)) + subclass.is(&cls) || mro.iter().any(|c| c.as_object().is(&cls)) } pub fn get_type_name(typ: &PyObjectRef) -> String { @@ -188,20 +191,34 @@ pub fn type_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { (dict, Some(vm.ctx.dict_type())) ] ); - let mut bases = vm.extract_elements(bases)?; - bases.push(vm.context().object()); - let name = objstr::get_value(name); - new( - typ.clone(), - &name, - bases, - objdict::py_dict_to_attributes(dict), - ) + type_new_class(vm, typ, name, bases, dict) } else { Err(vm.new_type_error(format!(": type_new: {:?}", args))) } } +pub fn type_new_class( + vm: &mut VirtualMachine, + typ: &PyObjectRef, + name: &PyObjectRef, + bases: &PyObjectRef, + dict: &PyObjectRef, +) -> PyResult { + let mut bases: Vec = vm + .extract_elements(bases)? + .iter() + .map(|x| FromPyObjectRef::from_pyobj(x)) + .collect(); + bases.push(FromPyObjectRef::from_pyobj(&vm.ctx.object())); + let name = objstr::get_value(name); + new( + typ.clone(), + &name, + bases, + objdict::py_dict_to_attributes(dict), + ) +} + pub fn type_call(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { debug!("type_call: {:?}", args); let cls = args.shift(); @@ -263,11 +280,11 @@ pub fn get_attributes(cls: PyClassRef) -> PyAttributes { // Gather all members here: let mut attributes = PyAttributes::new(); - let mut base_classes: Vec<&PyObjectRef> = cls.iter_mro().collect(); + let mut base_classes: Vec<&PyClassRef> = cls.iter_mro().collect(); base_classes.reverse(); for bc in base_classes { - if let Some(ref dict) = &bc.dict { + if let Some(ref dict) = &bc.as_object().dict { for (name, value) in dict.borrow().iter() { attributes.insert(name.to_string(), value.clone()); } @@ -277,9 +294,7 @@ pub fn get_attributes(cls: PyClassRef) -> PyAttributes { attributes } -fn take_next_base( - mut bases: Vec>, -) -> Option<(PyObjectRef, Vec>)> { +fn take_next_base(mut bases: Vec>) -> Option<(PyClassRef, Vec>)> { let mut next = None; bases = bases.into_iter().filter(|x| !x.is_empty()).collect(); @@ -306,7 +321,7 @@ fn take_next_base( None } -fn linearise_mro(mut bases: Vec>) -> Option> { +fn linearise_mro(mut bases: Vec>) -> Option> { debug!("Linearising MRO: {:?}", bases); let mut result = vec![]; loop { @@ -327,13 +342,10 @@ fn linearise_mro(mut bases: Vec>) -> Option> { pub fn new( typ: PyObjectRef, name: &str, - bases: Vec, + bases: Vec, dict: HashMap, ) -> PyResult { - let mros = bases - .into_iter() - .map(|x| _mro(&FromPyObjectRef::from_pyobj(&x))) - .collect(); + let mros = bases.into_iter().map(|x| _mro(&x)).collect(); let mro = linearise_mro(mros).unwrap(); Ok(PyObject { payload: Box::new(PyClass { @@ -348,10 +360,11 @@ pub fn new( #[cfg(test)] mod tests { + use super::FromPyObjectRef; use super::{linearise_mro, new}; - use super::{HashMap, IdProtocol, PyContext, PyObjectRef}; + use super::{HashMap, IdProtocol, PyClassRef, PyContext}; - fn map_ids(obj: Option>) -> Option> { + fn map_ids(obj: Option>) -> Option> { match obj { Some(vec) => Some(vec.into_iter().map(|x| x.get_id()).collect()), None => None, @@ -361,12 +374,15 @@ mod tests { #[test] fn test_linearise() { let context = PyContext::new(); - let object = context.object; - let type_type = context.type_type; + let object: PyClassRef = FromPyObjectRef::from_pyobj(&context.object); + let type_type = &context.type_type; 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(); + let a: PyClassRef = FromPyObjectRef::from_pyobj(&a); + let b: PyClassRef = FromPyObjectRef::from_pyobj(&b); + assert_eq!( map_ids(linearise_mro(vec![ vec![object.clone()], diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 9b13dfc6ba..d13291e989 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -160,7 +160,13 @@ pub fn create_type( _dict_type: &PyObjectRef, ) -> PyObjectRef { let dict = PyAttributes::new(); - objtype::new(type_type.clone(), name, vec![base.clone()], dict).unwrap() + objtype::new( + type_type.clone(), + name, + vec![FromPyObjectRef::from_pyobj(base)], + dict, + ) + .unwrap() } #[derive(Debug)] @@ -188,8 +194,9 @@ impl PyContext { let object_type = _nothing(); let dict_type = _nothing(); - objtype::create_type(type_type.clone(), object_type.clone(), dict_type.clone()); + // Must create object first, as type creation will fail if the base type is not a type. objobject::create_object(type_type.clone(), object_type.clone(), dict_type.clone()); + objtype::create_type(type_type.clone(), object_type.clone(), dict_type.clone()); objdict::create_type(type_type.clone(), object_type.clone(), dict_type.clone()); let module_type = create_type("module", &type_type, &object_type, &dict_type); @@ -536,7 +543,13 @@ impl PyContext { } pub fn new_class(&self, name: &str, base: PyObjectRef) -> PyObjectRef { - objtype::new(self.type_type(), name, vec![base], PyAttributes::new()).unwrap() + objtype::new( + self.type_type(), + name, + vec![FromPyObjectRef::from_pyobj(&base)], + PyAttributes::new(), + ) + .unwrap() } pub fn new_scope(&self) -> Scope { @@ -691,7 +704,7 @@ impl Default for PyObject { /// A `PyRef` can be directly returned from a built-in function to handle /// situations (such as when implementing in-place methods such as `__iadd__`) /// where a reference to the same object must be returned. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct PyRef { // invariant: this obj must always have payload of type T obj: PyObjectRef, @@ -844,7 +857,7 @@ impl AttributeProtocol for PyObjectRef { return Some(item); } for class in mro { - if let Some(item) = class_get_item(class, attr_name) { + if let Some(item) = class_get_item(class.as_object(), attr_name) { return Some(item); } } @@ -865,7 +878,7 @@ impl AttributeProtocol for PyObjectRef { fn has_attr(&self, attr_name: &str) -> bool { if let Some(PyClass { ref mro, .. }) = self.payload::() { return class_has_item(self, attr_name) - || mro.iter().any(|d| class_has_item(d, attr_name)); + || mro.iter().any(|d| class_has_item(d.as_object(), attr_name)); } if let Some(PyModule { ref dict, .. }) = self.payload::() { @@ -1563,7 +1576,7 @@ impl FromPyObjectRef for PyRef { _payload: PhantomData, } } else { - panic!("Error getting inner type.") + panic!("Error getting inner type: {:?}", obj.typ) } } } diff --git a/vm/src/stdlib/types.rs b/vm/src/stdlib/types.rs index d875cbb95d..0a340188f3 100644 --- a/vm/src/stdlib/types.rs +++ b/vm/src/stdlib/types.rs @@ -2,8 +2,8 @@ * Dynamic type creation and names for built in types. */ -use crate::obj::{objsequence, objstr, objtype}; -use crate::pyobject::{PyAttributes, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; +use crate::obj::objtype; +use crate::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use crate::VirtualMachine; fn types_new_class(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -14,20 +14,13 @@ fn types_new_class(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { optional = [(bases, None), (_kwds, None), (_exec_body, None)] ); - let name = objstr::get_value(name); - - let bases = match bases { - Some(b) => { - if objtype::isinstance(b, &vm.ctx.tuple_type()) { - objsequence::get_elements(b).to_vec() - } else { - return Err(vm.new_type_error("Bases must be a tuple".to_string())); - } - } - None => vec![vm.ctx.object()], + let ref type_type = vm.ctx.type_type(); + let bases: PyObjectRef = match bases { + Some(bases) => bases.clone(), + None => vm.ctx.new_tuple(vec![]), }; - - objtype::new(vm.ctx.type_type(), &name, bases, PyAttributes::new()) + let dict = vm.ctx.new_dict(); + objtype::type_new_class(vm, &type_type, name, &bases, &dict) } pub fn make_module(ctx: &PyContext) -> PyObjectRef { From e6c460fd97764f49df286bc9302b7dd9dbac2fe4 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Tue, 12 Mar 2019 18:35:15 +0200 Subject: [PATCH 277/380] Add import method to VirtualMachine --- vm/src/frame.rs | 12 ++---------- vm/src/vm.rs | 8 ++++++++ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 51f5818531..2519733cb7 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -804,11 +804,7 @@ impl Frame { module: &str, symbol: &Option, ) -> FrameResult { - let import = vm.builtins.get_item("__import__"); - let module = match import { - Some(func) => vm.invoke(func, vec![vm.ctx.new_str(module.to_string())])?, - None => panic!("No __import__ in builtins"), - }; + let module = vm.import(module)?; // If we're importing a symbol, look it up and use it, otherwise construct a module and return // that @@ -829,11 +825,7 @@ impl Frame { } fn import_star(&self, vm: &mut VirtualMachine, module: &str) -> FrameResult { - let import = vm.builtins.get_item("__import__"); - let module = match import { - Some(func) => vm.invoke(func, vec![vm.ctx.new_str(module.to_string())])?, - None => panic!("No __import__ in builtins"), - }; + let module = vm.import(module)?; // Grab all the names from the module and put them in the context for (k, v) in module.get_key_value_pairs().iter() { diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 20c421b6a0..0d3ac5bef9 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -234,6 +234,14 @@ impl VirtualMachine { self.call_method(obj, "__repr__", vec![]) } + pub fn import(&mut self, module: &str) -> PyResult { + let builtins_import = self.builtins.get_item("__import__"); + match builtins_import { + Some(func) => self.invoke(func, vec![self.ctx.new_str(module.to_string())]), + None => panic!("No __import__ in builtins"), + } + } + /// Determines if `obj` is an instance of `cls`, either directly, indirectly or virtually via /// the __instancecheck__ magic method. pub fn isinstance(&mut self, obj: &PyObjectRef, cls: &PyObjectRef) -> PyResult { From 2c6baacbac94c777972e312993427cc5d4f32b6d Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Tue, 12 Mar 2019 19:31:18 +0200 Subject: [PATCH 278/380] Return ImportError __import__ is not in builtins --- vm/src/vm.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 0d3ac5bef9..851345f1d4 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -238,7 +238,10 @@ impl VirtualMachine { let builtins_import = self.builtins.get_item("__import__"); match builtins_import { Some(func) => self.invoke(func, vec![self.ctx.new_str(module.to_string())]), - None => panic!("No __import__ in builtins"), + None => Err(self.new_exception( + self.ctx.exceptions.import_error.clone(), + "__import__ not found".to_string(), + )), } } From 6a3e82efd91a6819d26c7b6c26add4471a2c19b8 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Tue, 12 Mar 2019 20:21:59 +0200 Subject: [PATCH 279/380] Fix typo --- 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 db2a4d4d7f..a76f802447 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -130,7 +130,7 @@ pub fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { } else { let dumps = rustpython_vm::import::import_module(vm, std::path::PathBuf::default(), "json") .expect("Couldn't get json module") - .get_attr("dump".into()) + .get_attr("dumps".into()) .expect("Couldn't get json dumps"); match vm.invoke(dumps, pyobject::PyFuncArgs::new(vec![py_obj], vec![])) { Ok(value) => { From 336aa53b13038845dd35ddf3884f11b00ff469ff Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Tue, 12 Mar 2019 18:44:35 +0000 Subject: [PATCH 280/380] Allow IdProtocol to compare PyRef and non-PyRef. --- vm/src/obj/objlist.rs | 8 ++++---- vm/src/obj/objsequence.rs | 2 +- vm/src/obj/objtype.rs | 6 +----- vm/src/pyobject.rs | 11 ++++++----- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 287a34c8fb..290007e555 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -160,7 +160,7 @@ impl PyListRef { fn count(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { let mut count: usize = 0; for element in self.elements.borrow().iter() { - if needle.is(&element) { + if needle.is(element) { count += 1; } else { let py_equal = vm._eq(element.clone(), needle.clone())?; @@ -174,7 +174,7 @@ impl PyListRef { fn contains(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { for element in self.elements.borrow().iter() { - if needle.is(&element) { + if needle.is(element) { return Ok(true); } let py_equal = vm._eq(element.clone(), needle.clone())?; @@ -188,7 +188,7 @@ impl PyListRef { fn index(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { for (index, element) in self.elements.borrow().iter().enumerate() { - if needle.is(&element) { + if needle.is(element) { return Ok(index); } let py_equal = vm._eq(needle.clone(), element.clone())?; @@ -218,7 +218,7 @@ impl PyListRef { fn remove(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult<()> { let mut ri: Option = None; for (index, element) in self.elements.borrow().iter().enumerate() { - if needle.is(&element) { + if needle.is(element) { ri = Some(index); break; } diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index 120868d452..893e8cd82d 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -191,7 +191,7 @@ pub fn seq_equal( ) -> Result { if zelf.len() == other.len() { for (a, b) in Iterator::zip(zelf.iter(), other.iter()) { - if !a.is(&b) { + if !a.is(b) { let eq = vm._eq(a.clone(), b.clone())?; let value = objbool::boolval(vm, eq)?; if !value { diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 537ad852e0..1578bc16e1 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -30,10 +30,6 @@ impl IdProtocol for PyClassRef { fn get_id(&self) -> usize { self.as_object().get_id() } - - fn is(&self, other: &Self) -> bool { - self.get_id() == other.get_id() - } } impl TypeProtocol for PyClassRef { @@ -160,7 +156,7 @@ pub fn isinstance(obj: &PyObjectRef, cls: &PyObjectRef) -> bool { /// method. pub fn issubclass(subclass: &PyObjectRef, cls: &PyObjectRef) -> bool { let ref mro = subclass.payload::().unwrap().mro; - subclass.is(&cls) || mro.iter().any(|c| c.as_object().is(&cls)) + subclass.is(cls) || mro.iter().any(|c| c.is(cls)) } pub fn get_type_name(typ: &PyObjectRef) -> String { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index d13291e989..6e8f00aeb3 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -787,17 +787,18 @@ impl fmt::Display for PyRef { pub trait IdProtocol { fn get_id(&self) -> usize; - fn is(&self, other: &Self) -> bool; + fn is(&self, other: &T) -> bool + where + T: IdProtocol, + { + self.get_id() == other.get_id() + } } impl IdProtocol for PyObjectRef { fn get_id(&self) -> usize { &*self as &PyObject as *const PyObject as usize } - - fn is(&self, other: &Self) -> bool { - self.get_id() == other.get_id() - } } pub trait FromPyObjectRef { From b334689e55e496646d5b9c82fe01a440dad3b501 Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Tue, 12 Mar 2019 21:15:22 +0100 Subject: [PATCH 281/380] Remove Option from PyObject.typ; Refactor type hierarchy initialization. --- vm/src/exceptions.rs | 71 ++++++------------ vm/src/obj/objdict.rs | 18 +---- vm/src/obj/objobject.rs | 19 +---- vm/src/obj/objtype.rs | 16 +--- vm/src/pyobject.rs | 161 ++++++++++++++++++++-------------------- vm/src/stdlib/json.rs | 1 - 6 files changed, 109 insertions(+), 177 deletions(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index cf3018426d..94853d5d06 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -108,56 +108,29 @@ pub struct ExceptionZoo { } impl ExceptionZoo { - pub fn new( - type_type: &PyObjectRef, - object_type: &PyObjectRef, - dict_type: &PyObjectRef, - ) -> Self { + pub fn new(type_type: &PyObjectRef, object_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 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 = - 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, &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 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, - &runtime_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); + let base_exception_type = create_type("BaseException", &type_type, &object_type); + let exception_type = create_type("Exception", &type_type, &base_exception_type); + let arithmetic_error = create_type("ArithmeticError", &type_type, &exception_type); + let assertion_error = create_type("AssertionError", &type_type, &exception_type); + let attribute_error = create_type("AttributeError", &type_type, &exception_type); + let import_error = create_type("ImportError", &type_type, &exception_type); + let index_error = create_type("IndexError", &type_type, &exception_type); + let key_error = create_type("KeyError", &type_type, &exception_type); + let name_error = create_type("NameError", &type_type, &exception_type); + let os_error = create_type("OSError", &type_type, &exception_type); + let runtime_error = create_type("RuntimeError", &type_type, &exception_type); + let stop_iteration = create_type("StopIteration", &type_type, &exception_type); + let syntax_error = create_type("SyntaxError", &type_type, &exception_type); + let type_error = create_type("TypeError", &type_type, &exception_type); + let value_error = create_type("ValueError", &type_type, &exception_type); + let overflow_error = create_type("OverflowError", &type_type, &arithmetic_error); + let zero_division_error = create_type("ZeroDivisionError", &type_type, &arithmetic_error); + let module_not_found_error = create_type("ModuleNotFoundError", &type_type, &import_error); + let not_implemented_error = create_type("NotImplementedError", &type_type, &runtime_error); + let file_not_found_error = create_type("FileNotFoundError", &type_type, &os_error); + let permission_error = create_type("PermissionError", &type_type, &os_error); ExceptionZoo { arithmetic_error, diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 783da9239a..248ab3d539 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -3,8 +3,8 @@ use std::collections::HashMap; use std::ops::{Deref, DerefMut}; use crate::pyobject::{ - FromPyObjectRef, PyAttributes, PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectRef, - PyRef, PyResult, PyValue, TypeProtocol, + PyAttributes, PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectRef, PyRef, PyResult, + PyValue, TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; @@ -362,20 +362,6 @@ fn dict_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: PyObjectRef) { - let object_type = FromPyObjectRef::from_pyobj(&object_type); - // this is not ideal - let ptr = PyObjectRef::into_raw(dict_type.clone()) as *mut PyObject; - unsafe { - (*ptr).payload = Box::new(objtype::PyClass { - name: String::from("dict"), - mro: vec![object_type], - }); - (*ptr).dict = Some(RefCell::new(HashMap::new())); - (*ptr).typ = Some(type_type.clone()); - } -} - pub fn init(context: &PyContext) { let dict_type = &context.dict_type; context.set_attr(&dict_type, "__len__", context.new_rustfunc(dict_len)); diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 0906f2224a..78df11c739 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -3,12 +3,10 @@ use super::objstr; use super::objtype; use crate::obj::objproperty::PropertyBuilder; use crate::pyobject::{ - AttributeProtocol, DictProtocol, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObject, - PyObjectRef, PyRef, PyResult, TypeProtocol, + AttributeProtocol, DictProtocol, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObjectRef, + PyRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; -use std::cell::RefCell; -use std::collections::HashMap; #[derive(Clone, Debug)] pub struct PyInstance; @@ -22,19 +20,6 @@ pub fn new_instance(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { Ok(obj) } -pub fn create_object(type_type: PyObjectRef, object_type: PyObjectRef, _dict_type: PyObjectRef) { - // this is not ideal - let ptr = PyObjectRef::into_raw(object_type.clone()) as *mut PyObject; - unsafe { - (*ptr).payload = Box::new(objtype::PyClass { - name: String::from("object"), - mro: vec![], - }); - (*ptr).dict = Some(RefCell::new(HashMap::new())); - (*ptr).typ = Some(type_type.clone()); - } -} - fn object_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 1578bc16e1..dd317c5175 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -108,20 +108,6 @@ impl PyClassRef { * The magical type type */ -pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, _dict_type: PyObjectRef) { - let object_type = FromPyObjectRef::from_pyobj(&object_type); - // this is not ideal - let ptr = PyObjectRef::into_raw(type_type.clone()) as *mut PyObject; - unsafe { - (*ptr).payload = Box::new(PyClass { - name: String::from("type"), - mro: vec![object_type], - }); - (*ptr).dict = Some(RefCell::new(PyAttributes::new())); - (*ptr).typ = Some(type_type); - } -} - pub fn init(ctx: &PyContext) { let type_doc = "type(object_or_name, bases, dict)\n\ type(object) -> the object's type\n\ @@ -349,7 +335,7 @@ pub fn new( mro, }), dict: Some(RefCell::new(dict)), - typ: Some(typ), + typ, } .into_ref()) } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 6e8f00aeb3..0535779487 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -4,7 +4,9 @@ use std::collections::HashMap; use std::fmt; use std::iter; use std::marker::PhantomData; +use std::mem; use std::ops::{Deref, RangeInclusive}; +use std::ptr; use std::rc::Rc; use num_bigint::BigInt; @@ -148,17 +150,7 @@ pub struct PyContext { pub exceptions: exceptions::ExceptionZoo, } -fn _nothing() -> PyObjectRef { - let obj: PyObject = Default::default(); - obj.into_ref() -} - -pub fn create_type( - name: &str, - type_type: &PyObjectRef, - base: &PyObjectRef, - _dict_type: &PyObjectRef, -) -> PyObjectRef { +pub fn create_type(name: &str, type_type: &PyObjectRef, base: &PyObjectRef) -> PyObjectRef { let dict = PyAttributes::new(); objtype::new( type_type.clone(), @@ -187,69 +179,93 @@ impl PyValue for PyEllipsis { } } +fn init_type_hierarchy() -> (PyObjectRef, PyObjectRef) { + // `type` inherits from `object` + // and both `type` and `object are instances of `type`. + // to produce this circular dependency, we need an unsafe block. + // (and yes, this will never get dropped. TODO?) + unsafe { + let object_type = PyObject { + typ: mem::uninitialized(), // ! + dict: Some(RefCell::new(PyAttributes::new())), + payload: Box::new(PyClass { + name: String::from("object"), + mro: vec![], + }), + } + .into_ref(); + + let type_type = PyObject { + typ: mem::uninitialized(), // ! + dict: Some(RefCell::new(PyAttributes::new())), + payload: Box::new(PyClass { + name: String::from("type"), + mro: vec![FromPyObjectRef::from_pyobj(&object_type)], + }), + } + .into_ref(); + + let object_type_ptr = PyObjectRef::into_raw(object_type.clone()) as *mut PyObject; + let type_type_ptr = PyObjectRef::into_raw(type_type.clone()) as *mut PyObject; + ptr::write(&mut (*object_type_ptr).typ, type_type.clone()); + ptr::write(&mut (*type_type_ptr).typ, type_type.clone()); + + (type_type, object_type) + } +} + // Basic objects: impl PyContext { pub fn new() -> Self { - let type_type = _nothing(); - let object_type = _nothing(); - let dict_type = _nothing(); - - // Must create object first, as type creation will fail if the base type is not a type. - objobject::create_object(type_type.clone(), object_type.clone(), dict_type.clone()); - objtype::create_type(type_type.clone(), object_type.clone(), dict_type.clone()); - objdict::create_type(type_type.clone(), object_type.clone(), dict_type.clone()); - - let module_type = create_type("module", &type_type, &object_type, &dict_type); - 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 readonly_property_type = - create_type("readonly_property", &type_type, &object_type, &dict_type); - let super_type = create_type("super", &type_type, &object_type, &dict_type); - let weakref_type = create_type("ref", &type_type, &object_type, &dict_type); - let generator_type = create_type("generator", &type_type, &object_type, &dict_type); - let bound_method_type = create_type("method", &type_type, &object_type, &dict_type); - let str_type = create_type("str", &type_type, &object_type, &dict_type); - let list_type = create_type("list", &type_type, &object_type, &dict_type); - let set_type = create_type("set", &type_type, &object_type, &dict_type); - let frozenset_type = create_type("frozenset", &type_type, &object_type, &dict_type); - let int_type = create_type("int", &type_type, &object_type, &dict_type); - let float_type = create_type("float", &type_type, &object_type, &dict_type); - let frame_type = create_type("frame", &type_type, &object_type, &dict_type); - let complex_type = create_type("complex", &type_type, &object_type, &dict_type); - let bytes_type = create_type("bytes", &type_type, &object_type, &dict_type); - 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 ellipsis_type = create_type("EllipsisType", &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); - 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 (type_type, object_type) = init_type_hierarchy(); + + let dict_type = create_type("dict", &type_type, &object_type); + let module_type = create_type("module", &type_type, &object_type); + let classmethod_type = create_type("classmethod", &type_type, &object_type); + let staticmethod_type = create_type("staticmethod", &type_type, &object_type); + let function_type = create_type("function", &type_type, &object_type); + let builtin_function_or_method_type = + create_type("builtin_function_or_method", &type_type, &object_type); + let property_type = create_type("property", &type_type, &object_type); + let readonly_property_type = create_type("readonly_property", &type_type, &object_type); + let super_type = create_type("super", &type_type, &object_type); + let weakref_type = create_type("ref", &type_type, &object_type); + let generator_type = create_type("generator", &type_type, &object_type); + let bound_method_type = create_type("method", &type_type, &object_type); + let str_type = create_type("str", &type_type, &object_type); + let list_type = create_type("list", &type_type, &object_type); + let set_type = create_type("set", &type_type, &object_type); + let frozenset_type = create_type("frozenset", &type_type, &object_type); + let int_type = create_type("int", &type_type, &object_type); + let float_type = create_type("float", &type_type, &object_type); + let frame_type = create_type("frame", &type_type, &object_type); + let complex_type = create_type("complex", &type_type, &object_type); + let bytes_type = create_type("bytes", &type_type, &object_type); + let bytearray_type = create_type("bytearray", &type_type, &object_type); + let tuple_type = create_type("tuple", &type_type, &object_type); + let iter_type = create_type("iter", &type_type, &object_type); + let ellipsis_type = create_type("EllipsisType", &type_type, &object_type); + let enumerate_type = create_type("enumerate", &type_type, &object_type); + let filter_type = create_type("filter", &type_type, &object_type); + let map_type = create_type("map", &type_type, &object_type); + let zip_type = create_type("zip", &type_type, &object_type); + let bool_type = create_type("bool", &type_type, &int_type); + let memoryview_type = create_type("memoryview", &type_type, &object_type); + let code_type = create_type("code", &type_type, &int_type); + let range_type = create_type("range", &type_type, &object_type); + let slice_type = create_type("slice", &type_type, &object_type); + let exceptions = exceptions::ExceptionZoo::new(&type_type, &object_type); let none = PyObject::new( objnone::PyNone, - create_type("NoneType", &type_type, &object_type, &dict_type), + create_type("NoneType", &type_type, &object_type), ); let ellipsis = PyObject::new(PyEllipsis, ellipsis_type.clone()); let not_implemented = PyObject::new( PyNotImplemented, - create_type("NotImplementedType", &type_type, &object_type, &dict_type), + create_type("NotImplementedType", &type_type, &object_type), ); let true_value = PyObject::new(PyInt::new(BigInt::one()), bool_type.clone()); @@ -614,7 +630,7 @@ impl PyContext { PyAttributes::new() }; PyObject { - typ: Some(class), + typ: class, dict: Some(RefCell::new(dict)), payload: Box::new(objobject::PyInstance), } @@ -680,21 +696,11 @@ impl Default for PyContext { /// python class, and carries some rust payload optionally. This rust /// payload can be a rust float or rust int in case of float and int objects. pub struct PyObject { - pub typ: Option, + pub typ: PyObjectRef, pub dict: Option>, // __dict__ member pub payload: Box, } -impl Default for PyObject { - fn default() -> Self { - PyObject { - typ: None, - dict: None, - payload: Box::new(()), - } - } -} - /// A reference to a Python object. /// /// Note that a `PyRef` can only deref to a shared / immutable reference. @@ -823,10 +829,7 @@ impl TypeProtocol for PyObjectRef { impl TypeProtocol for PyObject { fn type_ref(&self) -> &PyObjectRef { - match self.typ { - Some(ref typ) => &typ, - None => panic!("Object {:?} doesn't have a type!", self), - } + &self.typ } } @@ -1546,7 +1549,7 @@ impl PyValue for PyIteratorValue { impl PyObject { pub fn new(payload: T, typ: PyObjectRef) -> PyObjectRef { PyObject { - typ: Some(typ), + typ, dict: Some(RefCell::new(PyAttributes::new())), payload: Box::new(payload), } diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index a23028773f..7d900de52f 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -234,7 +234,6 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { "JSONDecodeError", &ctx.type_type, &ctx.exceptions.exception_type, - &ctx.dict_type, ); py_module!(ctx, "json", { From a8f7c80e37081d4e44a443776035869c7b7e4e0e Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Tue, 12 Mar 2019 22:07:24 +0100 Subject: [PATCH 282/380] Use default opt-level for release builds. --- Cargo.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a1f366cd43..e74ef7b026 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,3 @@ rustpython_parser = {path = "parser"} rustpython_vm = {path = "vm"} rustyline = "2.1.0" xdg = "2.2.0" - -[profile.release] -opt-level = "s" From e49d7146a0fa8e6c6014d046d77a30b571f10b69 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 12 Mar 2019 18:31:59 -0500 Subject: [PATCH 283/380] Add PyValuePayload trait and use it for PyObject.payload --- vm/src/obj/objobject.rs | 8 +++++++- vm/src/pyobject.rs | 33 ++++++++++++++++++++++----------- vm/src/stdlib/re.rs | 4 ++-- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 0906f2224a..60f4001103 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -4,7 +4,7 @@ use super::objtype; use crate::obj::objproperty::PropertyBuilder; use crate::pyobject::{ AttributeProtocol, DictProtocol, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObject, - PyObjectRef, PyRef, PyResult, TypeProtocol, + PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; use std::cell::RefCell; @@ -13,6 +13,12 @@ use std::collections::HashMap; #[derive(Clone, Debug)] pub struct PyInstance; +impl PyValue for PyInstance { + fn required_type(_ctx: &PyContext) -> PyObjectRef { + panic!("no specific type for PyInstance, don't type check me") + } +} + pub type PyInstanceRef = PyRef; pub fn new_instance(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 6568e7e84c..7490d332d6 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -591,11 +591,7 @@ impl PyContext { } pub fn new_instance(&self, class: PyObjectRef, dict: Option) -> PyObjectRef { - let dict = if let Some(dict) = dict { - dict - } else { - PyAttributes::new() - }; + let dict = dict.unwrap_or_default(); PyObject { typ: Some(class), dict: Some(RefCell::new(dict)), @@ -665,7 +661,7 @@ impl Default for PyContext { pub struct PyObject { pub typ: Option, pub dict: Option>, // __dict__ member - pub payload: Box, + pub payload: Box, } impl Default for PyObject { @@ -1526,7 +1522,7 @@ impl PyValue for PyIteratorValue { } impl PyObject { - pub fn new(payload: T, typ: PyObjectRef) -> PyObjectRef { + pub fn new(payload: T, typ: PyObjectRef) -> PyObjectRef { PyObject { typ: Some(typ), dict: Some(RefCell::new(PyAttributes::new())), @@ -1541,16 +1537,31 @@ impl PyObject { } pub fn payload(&self) -> Option<&T> { - self.payload.downcast_ref() + let payload: &dyn Any = &self.payload; + payload.downcast_ref() } } -// The intention is for this to replace `PyObjectPayload` once everything is -// converted to use `PyObjectPayload::AnyRustvalue`. -pub trait PyValue: Any + fmt::Debug { +pub trait PyValue: fmt::Debug + 'static { fn required_type(ctx: &PyContext) -> PyObjectRef; } +pub trait PyValuePayload: Any + fmt::Debug + 'static { + fn required_type(&self, ctx: &PyContext) -> PyObjectRef; +} + +impl PyValuePayload for T { + fn required_type(&self, ctx: &PyContext) -> PyObjectRef { + T::required_type(ctx) + } +} + +impl PyValuePayload for () { + fn required_type(&self, _ctx: &PyContext) -> PyObjectRef { + panic!("No specific python type for rust unit, don't type check") + } +} + impl FromPyObjectRef for PyRef { fn from_pyobj(obj: &PyObjectRef) -> Self { if let Some(_) = obj.payload::() { diff --git a/vm/src/stdlib/re.rs b/vm/src/stdlib/re.rs index 3aee2bf61f..4d6c024dae 100644 --- a/vm/src/stdlib/re.rs +++ b/vm/src/stdlib/re.rs @@ -197,7 +197,7 @@ fn match_end(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { /// Retrieve inner rust regex from python object: fn get_regex<'a>(obj: &'a PyObjectRef) -> &'a Regex { // TODO: Regex shouldn't be stored in payload directly, create newtype wrapper - if let Some(regex) = obj.payload.downcast_ref::() { + if let Some(regex) = obj.payload::() { return regex; } panic!("Inner error getting regex {:?}", obj); @@ -205,7 +205,7 @@ fn get_regex<'a>(obj: &'a PyObjectRef) -> &'a Regex { /// Retrieve inner rust match from python object: fn get_match<'a>(obj: &'a PyObjectRef) -> &'a PyMatch { - if let Some(value) = obj.payload.downcast_ref::() { + if let Some(value) = obj.payload::() { return value; } panic!("Inner error getting match {:?}", obj); From c0e572767e18b08f2c06974649f145d9c0e6d935 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 12 Mar 2019 18:59:43 -0500 Subject: [PATCH 284/380] Rename PyValuePayload to PyObjectPayload --- vm/src/pyobject.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 407ddffa2d..4cb8c849ab 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -694,7 +694,7 @@ impl Default for PyContext { pub struct PyObject { pub typ: PyObjectRef, pub dict: Option>, // __dict__ member - pub payload: Box, + pub payload: Box, } /// A reference to a Python object. @@ -1543,7 +1543,7 @@ impl PyValue for PyIteratorValue { } impl PyObject { - pub fn new(payload: T, typ: PyObjectRef) -> PyObjectRef { + pub fn new(payload: T, typ: PyObjectRef) -> PyObjectRef { PyObject { typ, dict: Some(RefCell::new(PyAttributes::new())), @@ -1567,17 +1567,17 @@ pub trait PyValue: fmt::Debug + 'static { fn required_type(ctx: &PyContext) -> PyObjectRef; } -pub trait PyValuePayload: Any + fmt::Debug + 'static { +pub trait PyObjectPayload: Any + fmt::Debug + 'static { fn required_type(&self, ctx: &PyContext) -> PyObjectRef; } -impl PyValuePayload for T { +impl PyObjectPayload for T { fn required_type(&self, ctx: &PyContext) -> PyObjectRef { T::required_type(ctx) } } -impl PyValuePayload for () { +impl PyObjectPayload for () { fn required_type(&self, _ctx: &PyContext) -> PyObjectRef { panic!("No specific python type for rust unit, don't type check") } From d305d4f74bab33a469143bcc310ece7fd5d6fe96 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 12 Mar 2019 19:06:12 -0500 Subject: [PATCH 285/380] Clean up a bit --- vm/src/obj/objobject.rs | 4 ++-- vm/src/pyobject.rs | 16 ++-------------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index a6c383ebb4..02689fea0b 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -12,8 +12,8 @@ use crate::vm::VirtualMachine; pub struct PyInstance; impl PyValue for PyInstance { - fn required_type(_ctx: &PyContext) -> PyObjectRef { - panic!("no specific type for PyInstance, don't type check me") + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.object() } } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 4cb8c849ab..dcb5f34c7a 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1567,21 +1567,9 @@ pub trait PyValue: fmt::Debug + 'static { fn required_type(ctx: &PyContext) -> PyObjectRef; } -pub trait PyObjectPayload: Any + fmt::Debug + 'static { - fn required_type(&self, ctx: &PyContext) -> PyObjectRef; -} - -impl PyObjectPayload for T { - fn required_type(&self, ctx: &PyContext) -> PyObjectRef { - T::required_type(ctx) - } -} +pub trait PyObjectPayload: Any + fmt::Debug + 'static {} -impl PyObjectPayload for () { - fn required_type(&self, _ctx: &PyContext) -> PyObjectRef { - panic!("No specific python type for rust unit, don't type check") - } -} +impl PyObjectPayload for T {} impl FromPyObjectRef for PyRef { fn from_pyobj(obj: &PyObjectRef) -> Self { From f31003614a60b64891ef3e3212cf6f2b2133111f Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 12 Mar 2019 19:42:52 -0500 Subject: [PATCH 286/380] Fix wasm --- wasm/lib/src/browser_module.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index 035954a7c7..e09a63ac6a 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -171,7 +171,7 @@ impl PyPromise { } pub fn get_promise_value(obj: &PyObjectRef) -> Promise { - if let Some(promise) = obj.payload.downcast_ref::() { + if let Some(promise) = obj.payload::() { return promise.value.clone(); } panic!("Inner error getting promise") From 7bac1285d4cb3578c3bf5bd0ebc5926044d23aee Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 12 Mar 2019 20:26:33 -0500 Subject: [PATCH 287/380] Fix all the tests --- vm/src/pyobject.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index dcb5f34c7a..9c6ae2a4fc 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1558,8 +1558,7 @@ impl PyObject { } pub fn payload(&self) -> Option<&T> { - let payload: &dyn Any = &self.payload; - payload.downcast_ref() + self.payload.as_any().downcast_ref() } } @@ -1567,9 +1566,15 @@ pub trait PyValue: fmt::Debug + 'static { fn required_type(ctx: &PyContext) -> PyObjectRef; } -pub trait PyObjectPayload: Any + fmt::Debug + 'static {} +pub trait PyObjectPayload: Any + fmt::Debug + 'static { + fn as_any(&self) -> &dyn Any; +} -impl PyObjectPayload for T {} +impl PyObjectPayload for T { + fn as_any(&self) -> &dyn Any { + self + } +} impl FromPyObjectRef for PyRef { fn from_pyobj(obj: &PyObjectRef) -> Self { From fb8351c5d1e1187bfab245b52270d81312e46bee Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 12 Mar 2019 20:59:03 -0500 Subject: [PATCH 288/380] Inline some functions --- vm/src/pyobject.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 9c6ae2a4fc..69021af7eb 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1557,6 +1557,7 @@ impl PyObject { Rc::new(self) } + #[inline] pub fn payload(&self) -> Option<&T> { self.payload.as_any().downcast_ref() } @@ -1571,6 +1572,7 @@ pub trait PyObjectPayload: Any + fmt::Debug + 'static { } impl PyObjectPayload for T { + #[inline] fn as_any(&self) -> &dyn Any { self } From bde86d32b141816d74664ba76d13a077018867b4 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 12 Mar 2019 22:46:55 -0500 Subject: [PATCH 289/380] Use compile::Mode::Single for the demo terminal --- vm/src/frame.rs | 11 +++--- wasm/demo/src/main.js | 71 +++++++++++++++++++------------------- wasm/lib/src/vm_class.rs | 74 ++++++++++++++++++++-------------------- 3 files changed, 77 insertions(+), 79 deletions(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 2519733cb7..cafb72dd28 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -687,13 +687,10 @@ impl Frame { let expr = self.pop_value(); if !expr.is(&vm.get_none()) { let repr = vm.to_repr(&expr)?; - builtins::builtin_print( - vm, - PyFuncArgs { - args: vec![repr], - kwargs: vec![], - }, - )?; + // TODO: implement sys.displayhook + if let Some(print) = vm.ctx.get_attr(&vm.builtins, "print") { + vm.invoke(print, vec![repr])?; + } } Ok(None) } diff --git a/wasm/demo/src/main.js b/wasm/demo/src/main.js index 2f6de563d1..e4fb244347 100644 --- a/wasm/demo/src/main.js +++ b/wasm/demo/src/main.js @@ -74,57 +74,58 @@ snippets.addEventListener('change', updateSnippet); // option selected for the `select`, but the textarea won't be updated) updateSnippet(); -const prompt = ">>>>> "; +const prompt = '>>>>> '; const term = new Terminal(); term.open(document.getElementById('terminal')); term.write(prompt); function removeNonAscii(str) { - if ((str===null) || (str==='')) - return false; - else - str = str.toString(); + if (str === null || str === '') return false; + else str = str.toString(); return str.replace(/[^\x20-\x7E]/g, ''); } function printToConsole(data) { - term.write(removeNonAscii(data) + "\r\n"); + term.write(removeNonAscii(data) + '\r\n'); } -const terminalVM = rp.vmStore.init("term_vm"); +const terminalVM = rp.vmStore.init('term_vm'); terminalVM.setStdout(printToConsole); -var input = ""; -term.on("data", (data) => { - const code = data.charCodeAt(0); - if (code == 13) { // CR - if (input[input.length - 1] == ':') { - input += data - term.write("\r\n....."); - } else { - term.write("\r\n"); - try { - terminalVM.exec(input); - } catch (err) { - if (err instanceof WebAssembly.RuntimeError) { - err = window.__RUSTPYTHON_ERROR || err; +var input = ''; +term.on('data', data => { + const code = data.charCodeAt(0); + if (code == 13) { + // CR + if (input[input.length - 1] == ':') { + input += data; + term.write('\r\n.....'); + } else { + term.write('\r\n'); + try { + terminalVM.execSingle(input); + } catch (err) { + if (err instanceof WebAssembly.RuntimeError) { + err = window.__RUSTPYTHON_ERROR || err; + } + printToConsole(err); } - printToConsole(err); + term.write(prompt); + input = ''; } - term.write(prompt); - input = ""; - } - } else if (code == 127) { - if (input.length > 0) { - term.write("\b \b"); - input = input.slice(0, -1); + } else if (code == 127) { + if (input.length > 0) { + term.write('\b \b'); + input = input.slice(0, -1); + } + } else if (code < 32 || code == 127) { + // Control + return; + } else { + // Visible + term.write(data); + input += data; } - } else if (code < 32 || code == 127) { // Control - return; - } else { // Visible - term.write(data); - input += data; - } }); diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index 18de108957..0b89491f0c 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -265,43 +265,38 @@ impl WASMVirtualMachine { #[wasm_bindgen(js_name = setStdout)] pub fn set_stdout(&self, stdout: JsValue) -> Result<(), JsValue> { - self.with( - move |StoredVirtualMachine { - ref mut vm, - ref mut scope, - .. - }| { - let print_fn: Box PyResult> = - if let Some(selector) = stdout.as_string() { - Box::new( - move |vm: &mut VirtualMachine, args: PyFuncArgs| -> PyResult { - wasm_builtins::builtin_print_html(vm, args, &selector) - }, - ) - } else if stdout.is_function() { - let func = js_sys::Function::from(stdout); - Box::new( - move |vm: &mut VirtualMachine, args: PyFuncArgs| -> PyResult { - func.call1( - &JsValue::UNDEFINED, - &wasm_builtins::format_print_args(vm, args)?.into(), - ) - .map_err(|err| convert::js_to_py(vm, err))?; - Ok(vm.get_none()) - }, - ) - } else if stdout.is_undefined() || stdout.is_null() { - Box::new(wasm_builtins::builtin_print_console) - } else { - return Err(TypeError::new( - "stdout must be null, a function or a css selector", - ) - .into()); - }; - scope.store_name(&vm, "print", vm.ctx.new_rustfunc(print_fn)); - Ok(()) - }, - )? + self.with(move |StoredVirtualMachine { ref mut vm, .. }| { + let print_fn: Box PyResult> = + if let Some(selector) = stdout.as_string() { + Box::new( + move |vm: &mut VirtualMachine, args: PyFuncArgs| -> PyResult { + wasm_builtins::builtin_print_html(vm, args, &selector) + }, + ) + } else if stdout.is_function() { + let func = js_sys::Function::from(stdout); + Box::new( + move |vm: &mut VirtualMachine, args: PyFuncArgs| -> PyResult { + func.call1( + &JsValue::UNDEFINED, + &wasm_builtins::format_print_args(vm, args)?.into(), + ) + .map_err(|err| convert::js_to_py(vm, err))?; + Ok(vm.get_none()) + }, + ) + } else if stdout.is_undefined() || stdout.is_null() { + Box::new(wasm_builtins::builtin_print_console) + } else { + return Err(TypeError::new( + "stdout must be null, a function or a css selector", + ) + .into()); + }; + let rustfunc = vm.ctx.new_rustfunc(print_fn); + vm.ctx.set_attr(&vm.builtins, "print", rustfunc); + Ok(()) + })? } #[wasm_bindgen(js_name = injectModule)] @@ -391,4 +386,9 @@ impl WASMVirtualMachine { pub fn eval(&self, source: String) -> Result { self.run(source, compile::Mode::Eval) } + + #[wasm_bindgen(js_name = execSingle)] + pub fn exec_single(&self, source: String) -> Result { + self.run(source, compile::Mode::Single) + } } From df68acb36b67e7b41d3e2502c126dfde1c597957 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 12 Mar 2019 23:20:17 -0500 Subject: [PATCH 290/380] cargo fmt --- wasm/lib/src/vm_class.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index 0b89491f0c..0a59afec2c 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -288,10 +288,9 @@ impl WASMVirtualMachine { } else if stdout.is_undefined() || stdout.is_null() { Box::new(wasm_builtins::builtin_print_console) } else { - return Err(TypeError::new( - "stdout must be null, a function or a css selector", - ) - .into()); + return Err( + TypeError::new("stdout must be null, a function or a css selector").into() + ); }; let rustfunc = vm.ctx.new_rustfunc(print_fn); vm.ctx.set_attr(&vm.builtins, "print", rustfunc); From c0b7ddfd2cf19bd2c9d51a7c4111218e4a3a145b Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Wed, 13 Mar 2019 13:32:57 +0100 Subject: [PATCH 291/380] Add write method to io.StringIO --- tests/snippets/test_io.py | 13 ++++++ vm/src/stdlib/io.rs | 87 +++++++++++++++++++++++---------------- 2 files changed, 64 insertions(+), 36 deletions(-) create mode 100644 tests/snippets/test_io.py diff --git a/tests/snippets/test_io.py b/tests/snippets/test_io.py new file mode 100644 index 0000000000..f135262b62 --- /dev/null +++ b/tests/snippets/test_io.py @@ -0,0 +1,13 @@ + + +import io + +f = io.StringIO() +f.write('bladibla') +assert f.getvalue() == 'bladibla' + +print('fubar', file=f, end='') +print(f.getvalue()) + +# TODO: +# assert f.getvalue() == 'bladiblafubar' diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 02171df344..c4a35c936d 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -3,6 +3,7 @@ */ //library imports +use std::cell::RefCell; use std::collections::HashSet; use std::fs::File; use std::io::prelude::*; @@ -21,7 +22,8 @@ use crate::obj::objint; use crate::obj::objstr; use crate::pyobject::{ - AttributeProtocol, BufferProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol, + AttributeProtocol, BufferProtocol, PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, + PyValue, TypeProtocol, }; use crate::import; @@ -37,16 +39,45 @@ fn compute_c_flag(mode: &str) -> u16 { } } -fn string_io_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { - // arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - // TODO +#[derive(Debug)] +struct PyStringIO { + data: RefCell, +} + +impl PyValue for PyStringIO { + fn required_type(_ctx: &PyContext) -> PyObjectRef { + unimplemented!(); + } +} + +fn string_io_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(cls, None)]); + + Ok(PyObject::new( + PyStringIO { + data: RefCell::new(String::default()), + }, + cls.clone(), + )) +} + +fn string_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(instance, None), (data, Some(vm.ctx.str_type()))] + ); + let value = instance.payload::().unwrap(); + let data = objstr::get_value(data); + value.data.borrow_mut().push_str(&data); Ok(vm.get_none()) } fn string_io_getvalue(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args); - // TODO - Ok(vm.get_none()) + arg_check!(vm, args, required = [(instance, None)]); + let value = instance.payload::().unwrap(); + let text = value.data.borrow().clone(); + Ok(vm.new_str(text)) } fn bytes_io_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { @@ -101,8 +132,7 @@ fn buffered_reader_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //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(); - vm.invoke(raw_read, PyFuncArgs::new(vec![buffer.clone()], vec![])) + vm.call_method(&raw, "readinto", vec![buffer.clone()]) .map_err(|_| vm.new_value_error("IO Error".to_string()))?; //Copy bytes from the buffer vector into the results vector @@ -110,9 +140,8 @@ fn buffered_reader_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { result.extend_from_slice(&bytes.value.borrow()); }; - 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(); + let py_len = vm.call_method(&buffer, "__len__", PyFuncArgs::default())?; + length = objint::get_value(&py_len).to_usize().unwrap(); } Ok(vm.ctx.new_bytes(result)) @@ -178,9 +207,8 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } //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_length = vm.call_method(obj, "__len__", PyFuncArgs::default())?; + let length = objint::get_value(&py_length).to_u64().unwrap(); let file_no = file_io.get_attr("fileno").unwrap(); let raw_fd = objint::get_value(&file_no).to_i64().unwrap(); @@ -247,10 +275,9 @@ fn buffered_writer_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult ); 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![])) + vm.call_method(&raw, "write", vec![obj.clone()]) } fn text_io_wrapper_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -268,9 +295,8 @@ 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()); - if let Ok(bytes) = vm.invoke(read.unwrap(), PyFuncArgs::default()) { + if let Ok(bytes) = vm.call_method(&raw, "read", PyFuncArgs::default()) { let value = objbytes::get_value(&bytes).to_vec(); //format bytes into string @@ -326,10 +352,7 @@ 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 = vec![file.clone(), vm.ctx.new_str(modes[0].to_string())]; let file_io = vm.invoke(file_io_class, file_args)?; //Create Buffered class to consume FileIO. The type of buffered class depends on @@ -337,26 +360,17 @@ 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, vec![file_io.clone()]) // reading => BufferedReader } else { - vm.invoke( - buffered_reader_class, - PyFuncArgs::new(vec![file_io.clone()], vec![]), - ) + vm.invoke(buffered_reader_class, vec![file_io.clone()]) //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![]), - ) + vm.invoke(text_io_wrapper_class, vec![buffered.unwrap()]) } else { // If the mode is binary this Buffered class is returned directly at // this point. @@ -409,7 +423,8 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { //StringIO: in-memory text let string_io = py_class!(ctx, "StringIO", text_io_base.clone(), { - "__init__" => ctx.new_rustfunc(string_io_init), + "__new__" => ctx.new_rustfunc(string_io_new), + "write" => ctx.new_rustfunc(string_io_write), "getvalue" => ctx.new_rustfunc(string_io_getvalue) }); From 6436d2cb9c37c23e5ec0099f5591c765d749bc6c Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Wed, 13 Mar 2019 08:16:41 -0500 Subject: [PATCH 292/380] fmt again --- 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 0a59afec2c..f12419e532 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -289,7 +289,7 @@ impl WASMVirtualMachine { Box::new(wasm_builtins::builtin_print_console) } else { return Err( - TypeError::new("stdout must be null, a function or a css selector").into() + TypeError::new("stdout must be null, a function or a css selector").into(), ); }; let rustfunc = vm.ctx.new_rustfunc(print_fn); From 067fc0927d2e6447a05c1e8ca2b19ff2feb2e1a2 Mon Sep 17 00:00:00 2001 From: ben Date: Thu, 14 Mar 2019 07:24:49 +1300 Subject: [PATCH 293/380] Remove type parameter from PropertyBuilder --- vm/src/obj/objproperty.rs | 12 +++--------- vm/src/pyobject.rs | 4 ++-- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index c44a4350bc..7cb4ea8498 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -2,8 +2,6 @@ */ -use std::marker::PhantomData; - use crate::obj::objstr::PyStringRef; use crate::obj::objtype::PyClassRef; use crate::pyobject::{ @@ -92,30 +90,27 @@ impl PyPropertyRef { } } -pub struct PropertyBuilder<'a, T> { +pub struct PropertyBuilder<'a> { ctx: &'a PyContext, getter: Option, setter: Option, - _return: PhantomData, } -impl<'a, T> PropertyBuilder<'a, T> { +impl<'a> PropertyBuilder<'a> { pub fn new(ctx: &'a PyContext) -> Self { Self { ctx, getter: None, setter: None, - _return: PhantomData, } } - pub fn add_getter>(self, func: F) -> Self { + pub fn add_getter>(self, func: F) -> Self { let func = self.ctx.new_rustfunc(func); Self { ctx: self.ctx, getter: Some(func), setter: self.setter, - _return: PhantomData, } } @@ -125,7 +120,6 @@ impl<'a, T> PropertyBuilder<'a, T> { ctx: self.ctx, getter: self.getter, setter: Some(func), - _return: PhantomData, } } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 0535779487..d23c68b665 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -596,9 +596,9 @@ impl PyContext { PyObject::new(Frame::new(code, scope), self.frame_type()) } - pub fn new_property(&self, f: F) -> PyObjectRef + pub fn new_property(&self, f: F) -> PyObjectRef where - F: IntoPyNativeFunc, + F: IntoPyNativeFunc, { PropertyBuilder::new(self).add_getter(f).create() } From f647f346f23c219d2b1c6ee7ed0f8eb241e0ccca Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Wed, 13 Mar 2019 20:07:57 +0100 Subject: [PATCH 294/380] Use Into instead of ToBigInt to avoid copies --- vm/src/obj/objint.rs | 7 ++----- vm/src/pyobject.rs | 3 +-- vm/src/vm.rs | 4 ++-- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index dbabe79907..582dd2ddb1 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -24,11 +24,8 @@ pub struct PyInt { pub type PyIntRef = PyRef; impl PyInt { - pub fn new(i: T) -> Self { - PyInt { - // TODO: this .clone()s a BigInt, which is not what we want. - value: i.to_bigint().unwrap(), - } + pub fn new>(i: T) -> Self { + PyInt { value: i.into() } } } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 0535779487..303d0cc695 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -10,7 +10,6 @@ use std::ptr; use std::rc::Rc; use num_bigint::BigInt; -use num_bigint::ToBigInt; use num_complex::Complex64; use num_traits::{One, Zero}; @@ -508,7 +507,7 @@ impl PyContext { self.new_instance(self.object(), None) } - pub fn new_int(&self, i: T) -> PyObjectRef { + pub fn new_int>(&self, i: T) -> PyObjectRef { PyObject::new(PyInt::new(i), self.int_type()) } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index d57d0e4388..26e6e57bae 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -32,7 +32,7 @@ use crate::pyobject::{ }; use crate::stdlib; use crate::sysmodule; -use num_bigint::ToBigInt; +use num_bigint::BigInt; // use objects::objects; @@ -109,7 +109,7 @@ impl VirtualMachine { } /// Create a new python int object. - pub fn new_int(&self, i: T) -> PyObjectRef { + pub fn new_int>(&self, i: T) -> PyObjectRef { self.ctx.new_int(i) } From 6b1598dbb46f21c6095729c461bbd26e11916106 Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Wed, 13 Mar 2019 20:21:07 +0100 Subject: [PATCH 295/380] Change some methods to return BigInt directly --- vm/src/obj/objint.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 582dd2ddb1..107ec7e309 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -29,6 +29,12 @@ impl PyInt { } } +impl IntoPyObject for BigInt { + fn into_pyobject(self, ctx: &PyContext) -> PyResult { + Ok(ctx.new_int(self)) + } +} + impl PyValue for PyInt { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.int_type() @@ -312,8 +318,8 @@ impl PyIntRef { } } - fn neg(self, vm: &mut VirtualMachine) -> PyObjectRef { - vm.ctx.new_int(-(&self.value)) + fn neg(self, _vm: &mut VirtualMachine) -> BigInt { + -(&self.value) } fn hash(self, _vm: &mut VirtualMachine) -> u64 { @@ -322,8 +328,8 @@ impl PyIntRef { hasher.finish() } - fn abs(self, vm: &mut VirtualMachine) -> PyObjectRef { - vm.ctx.new_int(self.value.abs()) + fn abs(self, _vm: &mut VirtualMachine) -> BigInt { + self.value.abs() } fn round(self, _precision: OptionalArg, _vm: &mut VirtualMachine) -> Self { @@ -334,8 +340,8 @@ impl PyIntRef { self.value.to_f64().unwrap() } - fn invert(self, vm: &mut VirtualMachine) -> PyObjectRef { - vm.ctx.new_int(!(&self.value)) + fn invert(self, _vm: &mut VirtualMachine) -> BigInt { + !(&self.value) } fn repr(self, _vm: &mut VirtualMachine) -> String { From 4b655b9ab750730efa401b1a6f5ab03b0158cf3f Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Wed, 13 Mar 2019 22:48:16 -0500 Subject: [PATCH 296/380] Fix recursive Scope Debug impl --- vm/src/frame.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 2519733cb7..4183d4fce7 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -76,12 +76,19 @@ impl<'a, T> Iterator for Iter<'a, T> { } } -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Scope { locals: RcList, pub globals: PyObjectRef, } +impl fmt::Debug for Scope { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // TODO: have a more informative Debug impl that DOESN'T recurse and cause a stack overflow + f.write_str("Scope") + } +} + impl Scope { pub fn new(locals: Option, globals: PyObjectRef) -> Scope { let locals = match locals { @@ -99,10 +106,7 @@ impl Scope { } pub fn get_only_locals(&self) -> Option { - match self.locals.iter().next() { - Some(dict) => Some(dict.clone()), - None => None, - } + self.locals.iter().next().cloned() } pub fn child_scope_with_locals(&self, locals: PyObjectRef) -> Scope { @@ -1212,21 +1216,18 @@ impl fmt::Debug for Frame { .borrow() .iter() .map(|elem| format!("\n > {:?}", elem)) - .collect::>() - .join(""); + .collect::(); let block_str = self .blocks .borrow() .iter() .map(|elem| format!("\n > {:?}", elem)) - .collect::>() - .join(""); + .collect::(); let local_str = match self.scope.get_locals().payload::() { Some(dict) => objdict::get_key_value_pairs_from_content(&dict.entries.borrow()) .iter() .map(|elem| format!("\n {:?} = {:?}", elem.0, elem.1)) - .collect::>() - .join(""), + .collect::(), None => panic!("locals unexpectedly not wrapping a dict!",), }; write!( From 3f301bd6848a1e6f786b85c238d4c494e5292564 Mon Sep 17 00:00:00 2001 From: ben Date: Mon, 11 Mar 2019 20:38:54 +1300 Subject: [PATCH 297/380] Implement property attributes and functions --- tests/snippets/property.py | 15 +++++++++ vm/src/obj/objnone.rs | 9 ++++++ vm/src/obj/objproperty.rs | 62 ++++++++++++++++++++++++++++++++++++++ vm/src/pyobject.rs | 23 ++++++++++---- 4 files changed, 103 insertions(+), 6 deletions(-) diff --git a/tests/snippets/property.py b/tests/snippets/property.py index a55870e3b8..50f8e8fc8d 100644 --- a/tests/snippets/property.py +++ b/tests/snippets/property.py @@ -30,3 +30,18 @@ def foo(self): with assertRaises(TypeError): property.__new__(object) + + +p1 = property("a", "b", "c") + +assert p1.fget == "a" +assert p1.fset == "b" +assert p1.fdel == "c" + +assert p1.getter(1).fget == 1 +assert p1.setter(2).fset == 2 +assert p1.deleter(3).fdel == 3 + +assert p1.getter(None).fget == "a" +assert p1.setter(None).fset == "b" +assert p1.deleter(None).fdel == "c" diff --git a/vm/src/obj/objnone.rs b/vm/src/obj/objnone.rs index 6e3bca6804..7bddd280d0 100644 --- a/vm/src/obj/objnone.rs +++ b/vm/src/obj/objnone.rs @@ -21,6 +21,15 @@ impl IntoPyObject for () { } } +impl IntoPyObject for Option { + fn into_pyobject(self, ctx: &PyContext) -> PyResult { + match self { + Some(x) => x.into_pyobject(ctx), + None => Ok(ctx.none()), + } + } +} + impl PyNoneRef { fn repr(self, _vm: &mut VirtualMachine) -> PyResult { Ok("None".to_string()) diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index c44a4350bc..acd98283eb 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -67,6 +67,8 @@ impl PyPropertyRef { ) } + // Descriptor methods + fn get(self, obj: PyObjectRef, _owner: PyClassRef, vm: &mut VirtualMachine) -> PyResult { if let Some(getter) = self.getter.as_ref() { vm.invoke(getter.clone(), obj) @@ -90,6 +92,58 @@ impl PyPropertyRef { Err(vm.new_attribute_error("can't delete attribute".to_string())) } } + + // Access functions + + fn fget(self, _vm: &mut VirtualMachine) -> Option { + self.getter.clone() + } + + fn fset(self, _vm: &mut VirtualMachine) -> Option { + self.setter.clone() + } + + fn fdel(self, _vm: &mut VirtualMachine) -> Option { + self.deleter.clone() + } + + // Python builder functions + + fn getter(self, getter: Option, vm: &mut VirtualMachine) -> PyResult { + Self::new_with_type( + vm, + PyProperty { + getter: getter.or_else(|| self.getter.clone()), + setter: self.setter.clone(), + deleter: self.deleter.clone(), + }, + self.typ(), + ) + } + + fn setter(self, setter: Option, vm: &mut VirtualMachine) -> PyResult { + Self::new_with_type( + vm, + PyProperty { + getter: self.getter.clone(), + setter: setter.or_else(|| self.setter.clone()), + deleter: self.deleter.clone(), + }, + self.typ(), + ) + } + + fn deleter(self, deleter: Option, vm: &mut VirtualMachine) -> PyResult { + Self::new_with_type( + vm, + PyProperty { + getter: self.getter.clone(), + setter: self.setter.clone(), + deleter: deleter.or_else(|| self.deleter.clone()), + }, + self.typ(), + ) + } } pub struct PropertyBuilder<'a, T> { @@ -190,5 +244,13 @@ pub fn init(context: &PyContext) { "__get__" => context.new_rustfunc(PyPropertyRef::get), "__set__" => context.new_rustfunc(PyPropertyRef::set), "__delete__" => context.new_rustfunc(PyPropertyRef::delete), + + "fget" => context.new_property(PyPropertyRef::fget), + "fset" => context.new_property(PyPropertyRef::fset), + "fdel" => context.new_property(PyPropertyRef::fdel), + + "getter" => context.new_rustfunc(PyPropertyRef::getter), + "setter" => context.new_rustfunc(PyPropertyRef::setter), + "deleter" => context.new_rustfunc(PyPropertyRef::deleter), }); } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 0535779487..1757dd113b 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -745,6 +745,13 @@ impl PyRef { pub fn into_object(self) -> PyObjectRef { self.obj } + + pub fn typ(&self) -> PyClassRef { + PyRef { + obj: self.obj.typ(), + _payload: PhantomData, + } + } } impl Deref for PyRef @@ -1211,6 +1218,16 @@ impl TryFromObject for PyObjectRef { } } +impl TryFromObject for Option { + fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { + if vm.get_none().is(&obj) { + Ok(None) + } else { + T::try_from_object(vm, obj).map(|x| Some(x)) + } + } +} + /// A map of keyword arguments to their values. /// /// A built-in function with a `KwArgs` parameter is analagous to a Python @@ -1389,12 +1406,6 @@ where } } -// TODO: Allow a built-in function to return an `Option`, i.e.: -// -// impl IntoPyObject for Option -// -// Option::None should map to a Python `None`. - // Allows a built-in function to return any built-in object payload without // explicitly implementing `IntoPyObject`. impl IntoPyObject for T From 5b41cc4b0d9c83d60d6316898562b10ef18e138f Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Wed, 13 Mar 2019 14:44:14 +0000 Subject: [PATCH 298/380] Fix subclasses of string. (Fixes #130) --- tests/snippets/subclass_str.py | 22 ++++++++++++++++++++++ vm/src/obj/objstr.rs | 29 +++++++++++++++++------------ 2 files changed, 39 insertions(+), 12 deletions(-) create mode 100644 tests/snippets/subclass_str.py diff --git a/tests/snippets/subclass_str.py b/tests/snippets/subclass_str.py new file mode 100644 index 0000000000..706566ec6d --- /dev/null +++ b/tests/snippets/subclass_str.py @@ -0,0 +1,22 @@ +from testutils import assertRaises + +x = "An interesting piece of text" +assert x is str(x) + +class Stringy(str): + def __new__(cls, value=""): + return str.__new__(cls, value) + + def __init__(self, value): + self.x = "substr" + +y = Stringy(1) +assert type(y) is Stringy, "Type of Stringy should be stringy" +assert type(str(y)) is str, "Str of a str-subtype should be a str." + +assert y + " other" == "1 other" +assert y.x == "substr" + +## Base strings currently get an attribute dict, but shouldn't. +# with assertRaises(AttributeError): +# "hello".x = 5 diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index a0e2e110d7..1ce1cdc747 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -7,15 +7,15 @@ use unicode_segmentation::UnicodeSegmentation; use crate::format::{FormatParseError, FormatPart, FormatString}; use crate::pyobject::{ - IntoPyObject, OptionalArg, PyContext, PyFuncArgs, PyIterable, PyObjectRef, PyRef, PyResult, - PyValue, TypeProtocol, + IdProtocol, IntoPyObject, OptionalArg, PyContext, PyFuncArgs, PyIterable, PyObjectRef, PyRef, + PyResult, PyValue, TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; use super::objint; use super::objsequence::PySliceableSequence; use super::objslice::PySlice; -use super::objtype; +use super::objtype::{self, PyClassRef}; #[derive(Clone, Debug)] pub struct PyString { @@ -788,16 +788,21 @@ fn perform_format( // TODO: should with following format // class str(object='') // class str(object=b'', encoding='utf-8', errors='strict') -fn str_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - if args.args.len() == 1 { - return Ok(vm.new_str("".to_string())); - } - - if args.args.len() > 2 { - panic!("str expects exactly one parameter"); +fn str_new( + cls: PyClassRef, + object: OptionalArg, + vm: &mut VirtualMachine, +) -> PyResult { + let string = match object { + OptionalArg::Present(ref input) => vm.to_str(input)?, + OptionalArg::Missing => vm.new_str("".to_string()), }; - - vm.to_str(&args.args[1]) + if string.typ().is(&cls) { + TryFromObject::try_from_object(vm, string) + } else { + let payload = string.payload::().unwrap(); + PyRef::new_with_type(vm, payload.clone(), cls) + } } impl PySliceableSequence for String { From b204c2b33ef1db0559f3282a4164a4b55cbd7d42 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Thu, 14 Mar 2019 09:37:19 +0000 Subject: [PATCH 299/380] Add PyValue::into_ref and into_ref_with_type. --- vm/src/pyobject.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 2223bd619e..c33653d07f 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1578,8 +1578,29 @@ impl PyObject { // The intention is for this to replace `PyObjectPayload` once everything is // converted to use `PyObjectPayload::AnyRustvalue`. -pub trait PyValue: Any + fmt::Debug { +pub trait PyValue: Any + fmt::Debug + Sized { fn required_type(ctx: &PyContext) -> PyObjectRef; + + fn into_ref(self, ctx: &PyContext) -> PyRef { + PyRef { + obj: PyObject::new(self, Self::required_type(ctx)), + _payload: PhantomData, + } + } + + fn into_ref_with_type(self, vm: &mut VirtualMachine, cls: PyClassRef) -> PyResult> { + let required_type = Self::required_type(&vm.ctx); + if objtype::issubclass(&cls.obj, &required_type) { + Ok(PyRef { + obj: PyObject::new(self, cls.obj), + _payload: PhantomData, + }) + } else { + let subtype = vm.to_pystr(&cls.obj)?; + let basetype = vm.to_pystr(&required_type)?; + Err(vm.new_type_error(format!("{} is not a subtype of {}", subtype, basetype))) + } + } } impl FromPyObjectRef for PyRef { From 2af998ea869e668f2e42eb16cfab25ccf58a6838 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Thu, 14 Mar 2019 17:54:38 -0500 Subject: [PATCH 300/380] Prevent recursion for `Debug` impls --- vm/src/frame.rs | 9 ++++++++- vm/src/obj/objdict.rs | 10 +++++++++- vm/src/obj/objlist.rs | 10 +++++++++- vm/src/obj/objset.rs | 13 ++++++++++--- vm/src/obj/objtuple.rs | 10 +++++++++- 5 files changed, 45 insertions(+), 7 deletions(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 4183d4fce7..a403814267 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -1211,11 +1211,18 @@ impl Frame { impl fmt::Debug for Frame { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + trace!("formatting stack"); let stack_str = self .stack .borrow() .iter() - .map(|elem| format!("\n > {:?}", elem)) + .map(|elem| { + if elem.payload.as_any().is::() { + "\n > {frame}".to_string() + } else { + format!("\n > {:?}", elem) + } + }) .collect::(); let block_str = self .blocks diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 248ab3d539..2a0bffe45e 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -1,5 +1,6 @@ use std::cell::{Cell, RefCell}; use std::collections::HashMap; +use std::fmt; use std::ops::{Deref, DerefMut}; use crate::pyobject::{ @@ -14,13 +15,20 @@ use super::objtype; pub type DictContentType = HashMap; -#[derive(Default, Debug)] +#[derive(Default)] pub struct PyDict { // TODO: should be private pub entries: RefCell, } pub type PyDictRef = PyRef; +impl fmt::Debug for PyDict { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // TODO: implement more detailed, non-recursive Debug formatter + f.write_str("dict") + } +} + impl PyValue for PyDict { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.dict_type() diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 290007e555..ff9d7c884c 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -14,13 +14,21 @@ use crate::pyobject::{ }; use crate::vm::{ReprGuard, VirtualMachine}; use num_traits::ToPrimitive; +use std::fmt; -#[derive(Debug, Default)] +#[derive(Default)] pub struct PyList { // TODO: shouldn't be public pub elements: RefCell>, } +impl fmt::Debug for PyList { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // TODO: implement more detailed, non-recursive Debug formatter + f.write_str("list") + } +} + impl From> for PyList { fn from(elements: Vec) -> Self { PyList { diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index bb8483a0e2..f46c6bcaed 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -3,8 +3,8 @@ */ use std::cell::{Cell, RefCell}; -use std::collections::hash_map::DefaultHasher; -use std::collections::HashMap; +use std::collections::{hash_map::DefaultHasher, HashMap}; +use std::fmt; use std::hash::{Hash, Hasher}; use super::objbool; @@ -17,11 +17,18 @@ use crate::pyobject::{ }; use crate::vm::{ReprGuard, VirtualMachine}; -#[derive(Debug, Default)] +#[derive(Default)] pub struct PySet { elements: RefCell>, } +impl fmt::Debug for PySet { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // TODO: implement more detailed, non-recursive Debug formatter + f.write_str("set") + } +} + impl PyValue for PySet { fn required_type(ctx: &PyContext) -> PyObjectRef { ctx.set_type() diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 6be590a5ff..ccbfc46daf 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -1,4 +1,5 @@ use std::cell::{Cell, RefCell}; +use std::fmt; use std::hash::{Hash, Hasher}; use crate::pyobject::{ @@ -15,13 +16,20 @@ use super::objsequence::{ use super::objstr; use super::objtype; -#[derive(Debug, Default)] +#[derive(Default)] pub struct PyTuple { // TODO: shouldn't be public // TODO: tuples are immutable, remove this RefCell pub elements: RefCell>, } +impl fmt::Debug for PyTuple { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // TODO: implement more informational, non-recursive Debug formatter + f.write_str("tuple") + } +} + impl From> for PyTuple { fn from(elements: Vec) -> Self { PyTuple { From d7299bac99c4d96defc1096f2f5c6da8960ed1d0 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Thu, 14 Mar 2019 18:13:26 -0500 Subject: [PATCH 301/380] Remove the `Rc`s around the WASM global HashMaps --- wasm/lib/src/convert.rs | 2 +- wasm/lib/src/vm_class.rs | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index a76f802447..38cdb931c3 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -47,7 +47,7 @@ pub fn py_err_to_js_err(vm: &mut VirtualMachine, py_err: &PyObjectRef) -> JsValu .ok() .and_then(|lineno| lineno.to_u32()) { - Reflect::set(&js_err, &"row".into(), &lineno.into()); + let _ = Reflect::set(&js_err, &"row".into(), &lineno.into()); } } } diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index b3ef506272..297f4306c3 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -41,12 +41,13 @@ impl StoredVirtualMachine { } } -// It's fine that it's thread local, since WASM doesn't even have threads yet. thread_local! probably -// gets compiled down to a normal-ish static varible, like Atomic* types: +// It's fine that it's thread local, since WASM doesn't even have threads yet. thread_local! +// probably gets compiled down to a normal-ish static varible, like Atomic* types do: // https://rustwasm.github.io/2018/10/24/multithreading-rust-and-wasm.html#atomic-instructions thread_local! { - static STORED_VMS: Rc>>>> = Rc::default(); - static ACTIVE_VMS: Rc>> = Rc::default(); + static STORED_VMS: RefCell>>> = + RefCell::default(); + static ACTIVE_VMS: RefCell> = RefCell::default(); } #[wasm_bindgen(js_name = vmStore)] From 8c1f18157d0d40455961b7666279217a44e05d0f Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Thu, 14 Mar 2019 20:41:23 -0500 Subject: [PATCH 302/380] Remove leftover trace from debugging --- vm/src/frame.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index a403814267..8bf23bf986 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -1211,7 +1211,6 @@ impl Frame { impl fmt::Debug for Frame { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - trace!("formatting stack"); let stack_str = self .stack .borrow() From 737ec12fa5feffeb68bcc0494a83a223aa889e43 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 14 Mar 2019 20:08:41 -0700 Subject: [PATCH 303/380] Rename PyValue::{required_type => class} --- vm/src/frame.rs | 2 +- vm/src/obj/objbuiltinfunc.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/objenumerate.rs | 2 +- vm/src/obj/objfilter.rs | 2 +- vm/src/obj/objfloat.rs | 2 +- vm/src/obj/objfunction.rs | 4 ++-- vm/src/obj/objgenerator.rs | 2 +- vm/src/obj/objint.rs | 2 +- vm/src/obj/objlist.rs | 2 +- vm/src/obj/objmap.rs | 2 +- vm/src/obj/objmemory.rs | 2 +- vm/src/obj/objmodule.rs | 2 +- vm/src/obj/objnone.rs | 2 +- vm/src/obj/objobject.rs | 2 +- vm/src/obj/objproperty.rs | 4 ++-- vm/src/obj/objrange.rs | 2 +- vm/src/obj/objset.rs | 2 +- vm/src/obj/objslice.rs | 2 +- vm/src/obj/objstr.rs | 2 +- vm/src/obj/objtuple.rs | 2 +- vm/src/obj/objtype.rs | 2 +- vm/src/obj/objweakref.rs | 2 +- vm/src/obj/objzip.rs | 2 +- vm/src/pyobject.rs | 30 +++++++++++++++--------------- vm/src/stdlib/re.rs | 4 ++-- vm/src/stdlib/socket.rs | 2 +- wasm/lib/src/browser_module.rs | 2 +- 32 files changed, 49 insertions(+), 49 deletions(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 8bf23bf986..29e7b410bf 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -184,7 +184,7 @@ pub struct Frame { } impl PyValue for Frame { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.frame_type() } } diff --git a/vm/src/obj/objbuiltinfunc.rs b/vm/src/obj/objbuiltinfunc.rs index 94a8bc59ba..bd5e1f73d2 100644 --- a/vm/src/obj/objbuiltinfunc.rs +++ b/vm/src/obj/objbuiltinfunc.rs @@ -8,7 +8,7 @@ pub struct PyBuiltinFunction { } impl PyValue for PyBuiltinFunction { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.builtin_function_or_method_type() } } diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index d737efaaea..1d276fc295 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -29,7 +29,7 @@ impl PyByteArray { } impl PyValue for PyByteArray { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.bytearray_type() } } diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index c78e919bbc..c71fa5ffe5 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -30,7 +30,7 @@ impl Deref for PyBytes { } impl PyValue for PyBytes { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.bytes_type() } } diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index 36aaf4eab8..fe008f9041 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -26,7 +26,7 @@ impl fmt::Debug for PyCode { } impl PyValue for PyCode { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.code_type() } } diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index 75cd455f82..aed2597b79 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -14,7 +14,7 @@ pub struct PyComplex { } impl PyValue for PyComplex { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.complex_type() } } diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 2a0bffe45e..b63988231b 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -30,7 +30,7 @@ impl fmt::Debug for PyDict { } impl PyValue for PyDict { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.dict_type() } } diff --git a/vm/src/obj/objenumerate.rs b/vm/src/obj/objenumerate.rs index 44937377fb..57264f258f 100644 --- a/vm/src/obj/objenumerate.rs +++ b/vm/src/obj/objenumerate.rs @@ -17,7 +17,7 @@ pub struct PyEnumerate { } impl PyValue for PyEnumerate { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.enumerate_type() } } diff --git a/vm/src/obj/objfilter.rs b/vm/src/obj/objfilter.rs index d39373739b..d7ba626e5e 100644 --- a/vm/src/obj/objfilter.rs +++ b/vm/src/obj/objfilter.rs @@ -13,7 +13,7 @@ pub struct PyFilter { } impl PyValue for PyFilter { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.filter_type() } } diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index ee9502a531..dc79afd69c 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -16,7 +16,7 @@ pub struct PyFloat { } impl PyValue for PyFloat { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.float_type() } } diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index f0b5519335..ba09ba55b3 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -23,7 +23,7 @@ impl PyFunction { } impl PyValue for PyFunction { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.function_type() } } @@ -42,7 +42,7 @@ impl PyMethod { } impl PyValue for PyMethod { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.bound_method_type() } } diff --git a/vm/src/obj/objgenerator.rs b/vm/src/obj/objgenerator.rs index 8e2434c384..fcdb234412 100644 --- a/vm/src/obj/objgenerator.rs +++ b/vm/src/obj/objgenerator.rs @@ -14,7 +14,7 @@ pub struct PyGenerator { } impl PyValue for PyGenerator { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.generator_type() } } diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 107ec7e309..68b38fbd83 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -36,7 +36,7 @@ impl IntoPyObject for BigInt { } impl PyValue for PyInt { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.int_type() } } diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index ff9d7c884c..f483f3a3d2 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -38,7 +38,7 @@ impl From> for PyList { } impl PyValue for PyList { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.list_type() } } diff --git a/vm/src/obj/objmap.rs b/vm/src/obj/objmap.rs index c183da0df8..b35db3b7aa 100644 --- a/vm/src/obj/objmap.rs +++ b/vm/src/obj/objmap.rs @@ -12,7 +12,7 @@ pub struct PyMap { } impl PyValue for PyMap { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.map_type() } } diff --git a/vm/src/obj/objmemory.rs b/vm/src/obj/objmemory.rs index 0170ee435c..9ef7176150 100644 --- a/vm/src/obj/objmemory.rs +++ b/vm/src/obj/objmemory.rs @@ -9,7 +9,7 @@ pub struct PyMemoryView { } impl PyValue for PyMemoryView { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.memoryview_type() } } diff --git a/vm/src/obj/objmodule.rs b/vm/src/obj/objmodule.rs index 297ee6dc00..045d385ef1 100644 --- a/vm/src/obj/objmodule.rs +++ b/vm/src/obj/objmodule.rs @@ -9,7 +9,7 @@ pub struct PyModule { pub type PyModuleRef = PyRef; impl PyValue for PyModule { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.module_type() } } diff --git a/vm/src/obj/objnone.rs b/vm/src/obj/objnone.rs index 7bddd280d0..7204fcfdcc 100644 --- a/vm/src/obj/objnone.rs +++ b/vm/src/obj/objnone.rs @@ -8,7 +8,7 @@ pub struct PyNone; pub type PyNoneRef = PyRef; impl PyValue for PyNone { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.none().typ() } } diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 02689fea0b..7f92015042 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -12,7 +12,7 @@ use crate::vm::VirtualMachine; pub struct PyInstance; impl PyValue for PyInstance { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.object() } } diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index af081bbf9f..d888b345aa 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -16,7 +16,7 @@ pub struct PyReadOnlyProperty { } impl PyValue for PyReadOnlyProperty { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.readonly_property_type() } } @@ -38,7 +38,7 @@ pub struct PyProperty { } impl PyValue for PyProperty { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.property_type() } } diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index be8097ceae..af4ed6369c 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -24,7 +24,7 @@ pub struct PyRange { } impl PyValue for PyRange { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.range_type() } } diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index f46c6bcaed..2926e1eade 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -30,7 +30,7 @@ impl fmt::Debug for PySet { } impl PyValue for PySet { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.set_type() } } diff --git a/vm/src/obj/objslice.rs b/vm/src/obj/objslice.rs index 6b5f5c169e..654b3f60a8 100644 --- a/vm/src/obj/objslice.rs +++ b/vm/src/obj/objslice.rs @@ -14,7 +14,7 @@ pub struct PySlice { } impl PyValue for PySlice { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.slice_type() } } diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 1ce1cdc747..14da63b033 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -597,7 +597,7 @@ impl PyStringRef { } impl PyValue for PyString { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.str_type() } } diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index ccbfc46daf..d634a55d1d 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -39,7 +39,7 @@ impl From> for PyTuple { } impl PyValue for PyTuple { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.tuple_type() } } diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 69c4a96ad3..73297c40d0 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -22,7 +22,7 @@ pub struct PyClass { pub type PyClassRef = PyRef; impl PyValue for PyClass { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.type_type() } } diff --git a/vm/src/obj/objweakref.rs b/vm/src/obj/objweakref.rs index d2e2d08dc4..5d24f0ddd5 100644 --- a/vm/src/obj/objweakref.rs +++ b/vm/src/obj/objweakref.rs @@ -23,7 +23,7 @@ impl PyWeak { } impl PyValue for PyWeak { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.weakref_type() } } diff --git a/vm/src/obj/objzip.rs b/vm/src/obj/objzip.rs index f108ef54b7..ee7f016fe0 100644 --- a/vm/src/obj/objzip.rs +++ b/vm/src/obj/objzip.rs @@ -11,7 +11,7 @@ pub struct PyZip { } impl PyValue for PyZip { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.zip_type() } } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index a68edc9c83..4e5a0bc288 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -164,7 +164,7 @@ pub fn create_type(name: &str, type_type: &PyObjectRef, base: &PyObjectRef) -> P pub struct PyNotImplemented; impl PyValue for PyNotImplemented { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.not_implemented().typ() } } @@ -173,7 +173,7 @@ impl PyValue for PyNotImplemented { pub struct PyEllipsis; impl PyValue for PyEllipsis { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.ellipsis_type.clone() } } @@ -715,21 +715,21 @@ pub struct PyRef { impl PyRef { pub fn new(ctx: &PyContext, payload: T) -> Self { PyRef { - obj: PyObject::new(payload, T::required_type(ctx)), + obj: PyObject::new(payload, T::class(ctx)), _payload: PhantomData, } } pub fn new_with_type(vm: &mut VirtualMachine, payload: T, cls: PyClassRef) -> PyResult { - let required_type = T::required_type(&vm.ctx); - if objtype::issubclass(&cls.obj, &required_type) { + let class = T::class(&vm.ctx); + if objtype::issubclass(&cls.obj, &class) { Ok(PyRef { obj: PyObject::new(payload, cls.obj), _payload: PhantomData, }) } else { let subtype = vm.to_pystr(&cls.obj)?; - let basetype = vm.to_pystr(&required_type)?; + let basetype = vm.to_pystr(&class)?; Err(vm.new_type_error(format!("{} is not a subtype of {}", subtype, basetype))) } } @@ -765,13 +765,13 @@ where T: PyValue, { fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { - if objtype::isinstance(&obj, &T::required_type(&vm.ctx)) { + if objtype::isinstance(&obj, &T::class(&vm.ctx)) { Ok(PyRef { obj, _payload: PhantomData, }) } else { - let expected_type = vm.to_pystr(&T::required_type(&vm.ctx))?; + let expected_type = vm.to_pystr(&T::class(&vm.ctx))?; let actual_type = vm.to_pystr(&obj.typ())?; Err(vm.new_type_error(format!( "Expected type {}, not {}", @@ -1408,7 +1408,7 @@ where T: PyValue + Sized, { fn into_pyobject(self, ctx: &PyContext) -> PyResult { - Ok(PyObject::new(self, T::required_type(ctx))) + Ok(PyObject::new(self, T::class(ctx))) } } @@ -1547,7 +1547,7 @@ pub struct PyIteratorValue { } impl PyValue for PyIteratorValue { - fn required_type(ctx: &PyContext) -> PyObjectRef { + fn class(ctx: &PyContext) -> PyObjectRef { ctx.iter_type() } } @@ -1574,25 +1574,25 @@ impl PyObject { } pub trait PyValue: fmt::Debug + Sized + 'static { - fn required_type(ctx: &PyContext) -> PyObjectRef; + fn class(ctx: &PyContext) -> PyObjectRef; fn into_ref(self, ctx: &PyContext) -> PyRef { PyRef { - obj: PyObject::new(self, Self::required_type(ctx)), + obj: PyObject::new(self, Self::class(ctx)), _payload: PhantomData, } } fn into_ref_with_type(self, vm: &mut VirtualMachine, cls: PyClassRef) -> PyResult> { - let required_type = Self::required_type(&vm.ctx); - if objtype::issubclass(&cls.obj, &required_type) { + let class = Self::class(&vm.ctx); + if objtype::issubclass(&cls.obj, &class) { Ok(PyRef { obj: PyObject::new(self, cls.obj), _payload: PhantomData, }) } else { let subtype = vm.to_pystr(&cls.obj)?; - let basetype = vm.to_pystr(&required_type)?; + let basetype = vm.to_pystr(&class)?; Err(vm.new_type_error(format!("{} is not a subtype of {}", subtype, basetype))) } } diff --git a/vm/src/stdlib/re.rs b/vm/src/stdlib/re.rs index 4d6c024dae..b9ce043e37 100644 --- a/vm/src/stdlib/re.rs +++ b/vm/src/stdlib/re.rs @@ -17,7 +17,7 @@ use crate::pyobject::{ use crate::VirtualMachine; impl PyValue for Regex { - fn required_type(_ctx: &PyContext) -> PyObjectRef { + fn class(_ctx: &PyContext) -> PyObjectRef { // TODO unimplemented!() } @@ -111,7 +111,7 @@ struct PyMatch { } impl PyValue for PyMatch { - fn required_type(_ctx: &PyContext) -> PyObjectRef { + fn class(_ctx: &PyContext) -> PyObjectRef { // TODO unimplemented!() } diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index c473fc4676..45f4530cc7 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -119,7 +119,7 @@ pub struct Socket { } impl PyValue for Socket { - fn required_type(_ctx: &PyContext) -> PyObjectRef { + fn class(_ctx: &PyContext) -> PyObjectRef { // TODO unimplemented!() } diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index e09a63ac6a..2e964607f9 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -158,7 +158,7 @@ pub struct PyPromise { } impl PyValue for PyPromise { - fn required_type(_ctx: &PyContext) -> PyObjectRef { + fn class(_ctx: &PyContext) -> PyObjectRef { // TODO unimplemented!() } From fa9e48a5b3960110f4d509f0c7f249721242f122 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 14 Mar 2019 21:38:57 -0700 Subject: [PATCH 304/380] Take &mut VirtualMachine insteadof &PyContext --- vm/src/frame.rs | 4 +-- vm/src/obj/objbool.rs | 4 +-- vm/src/obj/objbuiltinfunc.rs | 7 ++--- vm/src/obj/objbytearray.rs | 4 +-- vm/src/obj/objbytes.rs | 4 +-- vm/src/obj/objcode.rs | 4 +-- vm/src/obj/objcomplex.rs | 4 +-- vm/src/obj/objdict.rs | 4 +-- vm/src/obj/objenumerate.rs | 4 +-- vm/src/obj/objfilter.rs | 4 +-- vm/src/obj/objfloat.rs | 8 +++--- vm/src/obj/objfunction.rs | 8 +++--- vm/src/obj/objgenerator.rs | 4 +-- vm/src/obj/objint.rs | 12 ++++----- vm/src/obj/objlist.rs | 4 +-- vm/src/obj/objmap.rs | 4 +-- vm/src/obj/objmemory.rs | 4 +-- vm/src/obj/objmodule.rs | 4 +-- vm/src/obj/objnone.rs | 14 +++++----- vm/src/obj/objobject.rs | 4 +-- vm/src/obj/objproperty.rs | 8 +++--- vm/src/obj/objrange.rs | 4 +-- vm/src/obj/objset.rs | 4 +-- vm/src/obj/objslice.rs | 4 +-- vm/src/obj/objstr.rs | 8 +++--- vm/src/obj/objtuple.rs | 4 +-- vm/src/obj/objtype.rs | 4 +-- vm/src/obj/objweakref.rs | 4 +-- vm/src/obj/objzip.rs | 4 +-- vm/src/pyobject.rs | 47 +++++++++++++++++----------------- vm/src/stdlib/re.rs | 19 +++++++------- vm/src/stdlib/socket.rs | 2 +- wasm/lib/src/browser_module.rs | 2 +- 33 files changed, 112 insertions(+), 111 deletions(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 29e7b410bf..5be248d06d 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -184,8 +184,8 @@ pub struct Frame { } impl PyValue for Frame { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.frame_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.frame_type() } } diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index f71c2ea181..486bb2dd7e 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -12,8 +12,8 @@ use super::objtuple::PyTuple; use super::objtype; impl IntoPyObject for bool { - fn into_pyobject(self, ctx: &PyContext) -> PyResult { - Ok(ctx.new_bool(self)) + fn into_pyobject(self, vm: &mut VirtualMachine) -> PyResult { + Ok(vm.ctx.new_bool(self)) } } diff --git a/vm/src/obj/objbuiltinfunc.rs b/vm/src/obj/objbuiltinfunc.rs index bd5e1f73d2..ae8dfbf99c 100644 --- a/vm/src/obj/objbuiltinfunc.rs +++ b/vm/src/obj/objbuiltinfunc.rs @@ -1,6 +1,7 @@ use std::fmt; -use crate::pyobject::{PyContext, PyNativeFunc, PyObjectRef, PyValue}; +use crate::pyobject::{PyNativeFunc, PyObjectRef, PyValue}; +use crate::vm::VirtualMachine; pub struct PyBuiltinFunction { // TODO: shouldn't be public @@ -8,8 +9,8 @@ pub struct PyBuiltinFunction { } impl PyValue for PyBuiltinFunction { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.builtin_function_or_method_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.builtin_function_or_method_type() } } diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index 1d276fc295..d4641ae7df 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -29,8 +29,8 @@ impl PyByteArray { } impl PyValue for PyByteArray { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.bytearray_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.bytearray_type() } } diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index c71fa5ffe5..5247d35c4d 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -30,8 +30,8 @@ impl Deref for PyBytes { } impl PyValue for PyBytes { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.bytes_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.bytes_type() } } diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index fe008f9041..9897a47a0c 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -26,8 +26,8 @@ impl fmt::Debug for PyCode { } impl PyValue for PyCode { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.code_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.code_type() } } diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index aed2597b79..0d2d878b04 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -14,8 +14,8 @@ pub struct PyComplex { } impl PyValue for PyComplex { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.complex_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.complex_type() } } diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index b63988231b..3b37a801be 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -30,8 +30,8 @@ impl fmt::Debug for PyDict { } impl PyValue for PyDict { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.dict_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.dict_type() } } diff --git a/vm/src/obj/objenumerate.rs b/vm/src/obj/objenumerate.rs index 57264f258f..4551edd910 100644 --- a/vm/src/obj/objenumerate.rs +++ b/vm/src/obj/objenumerate.rs @@ -17,8 +17,8 @@ pub struct PyEnumerate { } impl PyValue for PyEnumerate { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.enumerate_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.enumerate_type() } } diff --git a/vm/src/obj/objfilter.rs b/vm/src/obj/objfilter.rs index d7ba626e5e..5533c3e54e 100644 --- a/vm/src/obj/objfilter.rs +++ b/vm/src/obj/objfilter.rs @@ -13,8 +13,8 @@ pub struct PyFilter { } impl PyValue for PyFilter { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.filter_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.filter_type() } } diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index dc79afd69c..7c5389a58b 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -16,14 +16,14 @@ pub struct PyFloat { } impl PyValue for PyFloat { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.float_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.float_type() } } impl IntoPyObject for f64 { - fn into_pyobject(self, ctx: &PyContext) -> PyResult { - Ok(ctx.new_float(self)) + fn into_pyobject(self, vm: &mut VirtualMachine) -> PyResult { + Ok(vm.ctx.new_float(self)) } } diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index ba09ba55b3..d9d57cfce4 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -23,8 +23,8 @@ impl PyFunction { } impl PyValue for PyFunction { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.function_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.function_type() } } @@ -42,8 +42,8 @@ impl PyMethod { } impl PyValue for PyMethod { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.bound_method_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.bound_method_type() } } diff --git a/vm/src/obj/objgenerator.rs b/vm/src/obj/objgenerator.rs index fcdb234412..2bdd581958 100644 --- a/vm/src/obj/objgenerator.rs +++ b/vm/src/obj/objgenerator.rs @@ -14,8 +14,8 @@ pub struct PyGenerator { } impl PyValue for PyGenerator { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.generator_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.generator_type() } } diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 68b38fbd83..f1f81c5c50 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -30,22 +30,22 @@ impl PyInt { } impl IntoPyObject for BigInt { - fn into_pyobject(self, ctx: &PyContext) -> PyResult { - Ok(ctx.new_int(self)) + fn into_pyobject(self, vm: &mut VirtualMachine) -> PyResult { + Ok(vm.ctx.new_int(self)) } } impl PyValue for PyInt { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.int_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.int_type() } } macro_rules! impl_into_pyobject_int { ($($t:ty)*) => {$( impl IntoPyObject for $t { - fn into_pyobject(self, ctx: &PyContext) -> PyResult { - Ok(ctx.new_int(self)) + fn into_pyobject(self, vm: &mut VirtualMachine) -> PyResult { + Ok(vm.ctx.new_int(self)) } } )*}; diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index f483f3a3d2..f0baedb1b6 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -38,8 +38,8 @@ impl From> for PyList { } impl PyValue for PyList { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.list_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.list_type() } } diff --git a/vm/src/obj/objmap.rs b/vm/src/obj/objmap.rs index b35db3b7aa..4ba8fd26ba 100644 --- a/vm/src/obj/objmap.rs +++ b/vm/src/obj/objmap.rs @@ -12,8 +12,8 @@ pub struct PyMap { } impl PyValue for PyMap { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.map_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.map_type() } } diff --git a/vm/src/obj/objmemory.rs b/vm/src/obj/objmemory.rs index 9ef7176150..5632ccac90 100644 --- a/vm/src/obj/objmemory.rs +++ b/vm/src/obj/objmemory.rs @@ -9,8 +9,8 @@ pub struct PyMemoryView { } impl PyValue for PyMemoryView { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.memoryview_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.memoryview_type() } } diff --git a/vm/src/obj/objmodule.rs b/vm/src/obj/objmodule.rs index 045d385ef1..6ef1cd012c 100644 --- a/vm/src/obj/objmodule.rs +++ b/vm/src/obj/objmodule.rs @@ -9,8 +9,8 @@ pub struct PyModule { pub type PyModuleRef = PyRef; impl PyValue for PyModule { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.module_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.module_type() } } diff --git a/vm/src/obj/objnone.rs b/vm/src/obj/objnone.rs index 7204fcfdcc..beb553326c 100644 --- a/vm/src/obj/objnone.rs +++ b/vm/src/obj/objnone.rs @@ -8,24 +8,24 @@ pub struct PyNone; pub type PyNoneRef = PyRef; impl PyValue for PyNone { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.none().typ() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.none().typ() } } // This allows a built-in function to not return a value, mapping to // Python's behavior of returning `None` in this situation. impl IntoPyObject for () { - fn into_pyobject(self, ctx: &PyContext) -> PyResult { - Ok(ctx.none()) + fn into_pyobject(self, vm: &mut VirtualMachine) -> PyResult { + Ok(vm.ctx.none()) } } impl IntoPyObject for Option { - fn into_pyobject(self, ctx: &PyContext) -> PyResult { + fn into_pyobject(self, vm: &mut VirtualMachine) -> PyResult { match self { - Some(x) => x.into_pyobject(ctx), - None => Ok(ctx.none()), + Some(x) => x.into_pyobject(vm), + None => Ok(vm.ctx.none()), } } } diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 7f92015042..2423f5e93a 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -12,8 +12,8 @@ use crate::vm::VirtualMachine; pub struct PyInstance; impl PyValue for PyInstance { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.object() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.object() } } diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index d888b345aa..a66557e953 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -16,8 +16,8 @@ pub struct PyReadOnlyProperty { } impl PyValue for PyReadOnlyProperty { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.readonly_property_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.readonly_property_type() } } @@ -38,8 +38,8 @@ pub struct PyProperty { } impl PyValue for PyProperty { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.property_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.property_type() } } diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index af4ed6369c..f5c045928d 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -24,8 +24,8 @@ pub struct PyRange { } impl PyValue for PyRange { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.range_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.range_type() } } diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 2926e1eade..f33208915d 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -30,8 +30,8 @@ impl fmt::Debug for PySet { } impl PyValue for PySet { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.set_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.set_type() } } diff --git a/vm/src/obj/objslice.rs b/vm/src/obj/objslice.rs index 654b3f60a8..3bb55ced2a 100644 --- a/vm/src/obj/objslice.rs +++ b/vm/src/obj/objslice.rs @@ -14,8 +14,8 @@ pub struct PySlice { } impl PyValue for PySlice { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.slice_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.slice_type() } } diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 14da63b033..0a969b6861 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -597,14 +597,14 @@ impl PyStringRef { } impl PyValue for PyString { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.str_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.str_type() } } impl IntoPyObject for String { - fn into_pyobject(self, ctx: &PyContext) -> PyResult { - Ok(ctx.new_str(self)) + fn into_pyobject(self, vm: &mut VirtualMachine) -> PyResult { + Ok(vm.ctx.new_str(self)) } } diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index d634a55d1d..1702c2237a 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -39,8 +39,8 @@ impl From> for PyTuple { } impl PyValue for PyTuple { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.tuple_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.tuple_type() } } diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 73297c40d0..8fd7772f21 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -22,8 +22,8 @@ pub struct PyClass { pub type PyClassRef = PyRef; impl PyValue for PyClass { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.type_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.type_type() } } diff --git a/vm/src/obj/objweakref.rs b/vm/src/obj/objweakref.rs index 5d24f0ddd5..ab9a5881b4 100644 --- a/vm/src/obj/objweakref.rs +++ b/vm/src/obj/objweakref.rs @@ -23,8 +23,8 @@ impl PyWeak { } impl PyValue for PyWeak { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.weakref_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.weakref_type() } } diff --git a/vm/src/obj/objzip.rs b/vm/src/obj/objzip.rs index ee7f016fe0..35213919da 100644 --- a/vm/src/obj/objzip.rs +++ b/vm/src/obj/objzip.rs @@ -11,8 +11,8 @@ pub struct PyZip { } impl PyValue for PyZip { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.zip_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.zip_type() } } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 4e5a0bc288..9bd94ec707 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -164,8 +164,8 @@ pub fn create_type(name: &str, type_type: &PyObjectRef, base: &PyObjectRef) -> P pub struct PyNotImplemented; impl PyValue for PyNotImplemented { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.not_implemented().typ() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.not_implemented().typ() } } @@ -173,8 +173,8 @@ impl PyValue for PyNotImplemented { pub struct PyEllipsis; impl PyValue for PyEllipsis { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.ellipsis_type.clone() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.ellipsis_type.clone() } } @@ -713,15 +713,15 @@ pub struct PyRef { } impl PyRef { - pub fn new(ctx: &PyContext, payload: T) -> Self { + pub fn new(vm: &mut VirtualMachine, payload: T) -> Self { PyRef { - obj: PyObject::new(payload, T::class(ctx)), + obj: PyObject::new(payload, T::class(vm)), _payload: PhantomData, } } pub fn new_with_type(vm: &mut VirtualMachine, payload: T, cls: PyClassRef) -> PyResult { - let class = T::class(&vm.ctx); + let class = T::class(vm); if objtype::issubclass(&cls.obj, &class) { Ok(PyRef { obj: PyObject::new(payload, cls.obj), @@ -765,13 +765,14 @@ where T: PyValue, { fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { - if objtype::isinstance(&obj, &T::class(&vm.ctx)) { + if objtype::isinstance(&obj, &T::class(vm)) { Ok(PyRef { obj, _payload: PhantomData, }) } else { - let expected_type = vm.to_pystr(&T::class(&vm.ctx))?; + let class = T::class(vm); + let expected_type = vm.to_pystr(&class)?; let actual_type = vm.to_pystr(&obj.typ())?; Err(vm.new_type_error(format!( "Expected type {}, not {}", @@ -782,7 +783,7 @@ where } impl IntoPyObject for PyRef { - fn into_pyobject(self, _ctx: &PyContext) -> PyResult { + fn into_pyobject(self, _vm: &mut VirtualMachine) -> PyResult { Ok(self.obj) } } @@ -1383,11 +1384,11 @@ pub trait TryFromObject: Sized { /// and should be implemented by many primitive Rust types, allowing a built-in /// function to simply return a `bool` or a `usize` for example. pub trait IntoPyObject { - fn into_pyobject(self, ctx: &PyContext) -> PyResult; + fn into_pyobject(self, vm: &mut VirtualMachine) -> PyResult; } impl IntoPyObject for PyObjectRef { - fn into_pyobject(self, _ctx: &PyContext) -> PyResult { + fn into_pyobject(self, _vm: &mut VirtualMachine) -> PyResult { Ok(self) } } @@ -1396,8 +1397,8 @@ impl IntoPyObject for PyResult where T: IntoPyObject, { - fn into_pyobject(self, ctx: &PyContext) -> PyResult { - self.and_then(|res| T::into_pyobject(res, ctx)) + fn into_pyobject(self, vm: &mut VirtualMachine) -> PyResult { + self.and_then(|res| T::into_pyobject(res, vm)) } } @@ -1407,8 +1408,8 @@ impl IntoPyObject for T where T: PyValue + Sized, { - fn into_pyobject(self, ctx: &PyContext) -> PyResult { - Ok(PyObject::new(self, T::class(ctx))) + fn into_pyobject(self, vm: &mut VirtualMachine) -> PyResult { + Ok(PyObject::new(self, T::class(vm))) } } @@ -1524,7 +1525,7 @@ macro_rules! into_py_native_func_tuple { Box::new(move |vm, args| { let ($($n,)*) = args.bind::<($($T,)*)>(vm)?; - (self)($($n,)* vm).into_pyobject(&vm.ctx) + (self)($($n,)* vm).into_pyobject(vm) }) } } @@ -1547,8 +1548,8 @@ pub struct PyIteratorValue { } impl PyValue for PyIteratorValue { - fn class(ctx: &PyContext) -> PyObjectRef { - ctx.iter_type() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.iter_type() } } @@ -1574,17 +1575,17 @@ impl PyObject { } pub trait PyValue: fmt::Debug + Sized + 'static { - fn class(ctx: &PyContext) -> PyObjectRef; + fn class(vm: &mut VirtualMachine) -> PyObjectRef; - fn into_ref(self, ctx: &PyContext) -> PyRef { + fn into_ref(self, vm: &mut VirtualMachine) -> PyRef { PyRef { - obj: PyObject::new(self, Self::class(ctx)), + obj: PyObject::new(self, Self::class(vm)), _payload: PhantomData, } } fn into_ref_with_type(self, vm: &mut VirtualMachine, cls: PyClassRef) -> PyResult> { - let class = Self::class(&vm.ctx); + let class = Self::class(vm); if objtype::issubclass(&cls.obj, &class) { Ok(PyRef { obj: PyObject::new(self, cls.obj), diff --git a/vm/src/stdlib/re.rs b/vm/src/stdlib/re.rs index b9ce043e37..5b992b877c 100644 --- a/vm/src/stdlib/re.rs +++ b/vm/src/stdlib/re.rs @@ -5,21 +5,21 @@ * system. */ -// extern crate regex; -use crate::import; -use regex::{Match, Regex}; use std::path::PathBuf; +use regex::{Match, Regex}; + +use crate::import; use crate::obj::objstr; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, + AttributeProtocol, PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, + TypeProtocol, }; use crate::VirtualMachine; impl PyValue for Regex { - fn class(_ctx: &PyContext) -> PyObjectRef { - // TODO - unimplemented!() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.import("re").unwrap().get_attr("Pattern").unwrap() } } @@ -111,9 +111,8 @@ struct PyMatch { } impl PyValue for PyMatch { - fn class(_ctx: &PyContext) -> PyObjectRef { - // TODO - unimplemented!() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.import("re").unwrap().get_attr("Match").unwrap() } } diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index 45f4530cc7..91097217e4 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -119,7 +119,7 @@ pub struct Socket { } impl PyValue for Socket { - fn class(_ctx: &PyContext) -> PyObjectRef { + fn class(_vm: &mut VirtualMachine) -> PyObjectRef { // TODO unimplemented!() } diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index 2e964607f9..11ea35ea21 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -158,7 +158,7 @@ pub struct PyPromise { } impl PyValue for PyPromise { - fn class(_ctx: &PyContext) -> PyObjectRef { + fn class(_vm: &mut VirtualMachine) -> PyObjectRef { // TODO unimplemented!() } From 976afeda31bd3b2e6f8aada4aff733aac9778bd1 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 14 Mar 2019 23:08:07 -0700 Subject: [PATCH 305/380] Add helper for obtaining class from module --- vm/src/stdlib/re.rs | 2 +- vm/src/vm.rs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/vm/src/stdlib/re.rs b/vm/src/stdlib/re.rs index 5b992b877c..3ecafec80c 100644 --- a/vm/src/stdlib/re.rs +++ b/vm/src/stdlib/re.rs @@ -112,7 +112,7 @@ struct PyMatch { impl PyValue for PyMatch { fn class(vm: &mut VirtualMachine) -> PyObjectRef { - vm.import("re").unwrap().get_attr("Match").unwrap() + vm.class("re", "Match") } } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 26e6e57bae..45b5d7d3d6 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -103,6 +103,13 @@ impl VirtualMachine { &frame.scope } + pub fn class(&mut self, module: &str, class: &str) -> PyObjectRef { + self.import(module) + .unwrap_or_else(|_| panic!("unable to import {}", module)) + .get_attr(class) + .unwrap_or_else(|| panic!("module {} has no class {}", module, class)) + } + /// Create a new python string object. pub fn new_str(&self, s: String) -> PyObjectRef { self.ctx.new_str(s) From 3d95b70e0f28f695a37f3b3a1803b829390a9678 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Fri, 15 Mar 2019 09:13:55 +0200 Subject: [PATCH 306/380] Add selenium test for wasm --- tests/wasm/test_demo.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 tests/wasm/test_demo.py diff --git a/tests/wasm/test_demo.py b/tests/wasm/test_demo.py new file mode 100644 index 0000000000..89a3f73fa4 --- /dev/null +++ b/tests/wasm/test_demo.py @@ -0,0 +1,40 @@ +import time + +from selenium import webdriver +from selenium.webdriver.chrome.options import Options +import pytest + +RUN_CODE_TEMPLATE = """ +var output = ""; +save_output = function(text) {{ + output += text +}}; +var vm = window.rp.vmStore.init('test_vm'); +vm.setStdout(save_output); +vm.exec('{}'); +vm.destroy(); +return output; +""" + +@pytest.fixture(scope="module") +def driver(request): + options = Options() + options.headless = True + options.add_argument('--disable-gpu') + driver = webdriver.Chrome(options=options) + driver.get("http://localhost:8080") + assert "RustPython" in driver.title + time.sleep(5) + yield driver + driver.close() + + +@pytest.mark.parametrize("script, output", + [ + ("print(5)", "5"), + ("a=5;b=4;print(a+b)", "9") + ] +) +def test_demo(driver, script, output): + script = RUN_CODE_TEMPLATE.format(script) + assert driver.execute_script(script).strip() == output From f281aab7219651ba0a647c9e4f0d834ab5290043 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Fri, 15 Mar 2019 09:59:57 +0200 Subject: [PATCH 307/380] Move selenium test to wasm folder --- wasm/tests/Pipfile | 13 ++++ wasm/tests/Pipfile.lock | 87 +++++++++++++++++++++++++ {tests/wasm => wasm/tests}/test_demo.py | 0 3 files changed, 100 insertions(+) create mode 100644 wasm/tests/Pipfile create mode 100644 wasm/tests/Pipfile.lock rename {tests/wasm => wasm/tests}/test_demo.py (100%) diff --git a/wasm/tests/Pipfile b/wasm/tests/Pipfile new file mode 100644 index 0000000000..547282c585 --- /dev/null +++ b/wasm/tests/Pipfile @@ -0,0 +1,13 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +pytest = "*" +selenium = "*" + +[dev-packages] + +[requires] +python_version = "3" diff --git a/wasm/tests/Pipfile.lock b/wasm/tests/Pipfile.lock new file mode 100644 index 0000000000..7f8b97f879 --- /dev/null +++ b/wasm/tests/Pipfile.lock @@ -0,0 +1,87 @@ +{ + "_meta": { + "hash": { + "sha256": "e7ebbd25509f50c886b2e9773e88b011af0cbc4bfcfebb386024be80d2c3aa49" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "atomicwrites": { + "hashes": [ + "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", + "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6" + ], + "version": "==1.3.0" + }, + "attrs": { + "hashes": [ + "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", + "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399" + ], + "version": "==19.1.0" + }, + "more-itertools": { + "hashes": [ + "sha256:0125e8f60e9e031347105eb1682cef932f5e97d7b9a1a28d9bf00c22a5daef40", + "sha256:590044e3942351a1bdb1de960b739ff4ce277960f2425ad4509446dbace8d9d1" + ], + "markers": "python_version > '2.7'", + "version": "==6.0.0" + }, + "pluggy": { + "hashes": [ + "sha256:19ecf9ce9db2fce065a7a0586e07cfb4ac8614fe96edf628a264b1c70116cf8f", + "sha256:84d306a647cc805219916e62aab89caa97a33a1dd8c342e87a37f91073cd4746" + ], + "version": "==0.9.0" + }, + "py": { + "hashes": [ + "sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", + "sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53" + ], + "version": "==1.8.0" + }, + "pytest": { + "hashes": [ + "sha256:592eaa2c33fae68c7d75aacf042efc9f77b27c08a6224a4f59beab8d9a420523", + "sha256:ad3ad5c450284819ecde191a654c09b0ec72257a2c711b9633d677c71c9850c4" + ], + "index": "pypi", + "version": "==4.3.1" + }, + "selenium": { + "hashes": [ + "sha256:2d7131d7bc5a5b99a2d9b04aaf2612c411b03b8ca1b1ee8d3de5845a9be2cb3c", + "sha256:deaf32b60ad91a4611b98d8002757f29e6f2c2d5fcaf202e1c9ad06d6772300d" + ], + "index": "pypi", + "version": "==3.141.0" + }, + "six": { + "hashes": [ + "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", + "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + ], + "version": "==1.12.0" + }, + "urllib3": { + "hashes": [ + "sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39", + "sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22" + ], + "version": "==1.24.1" + } + }, + "develop": {} +} diff --git a/tests/wasm/test_demo.py b/wasm/tests/test_demo.py similarity index 100% rename from tests/wasm/test_demo.py rename to wasm/tests/test_demo.py From 111208b46fd4bf2049f26b2b436f2b9752227098 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Fri, 15 Mar 2019 11:36:25 +0000 Subject: [PATCH 308/380] Remove PyRef::new/new_with_type. --- vm/src/obj/objproperty.rs | 60 ++++++++++++++++----------------------- vm/src/obj/objstr.rs | 2 +- vm/src/obj/objweakref.rs | 2 +- vm/src/pyobject.rs | 21 -------------- 4 files changed, 26 insertions(+), 59 deletions(-) diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index af081bbf9f..c77f78964f 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -54,15 +54,12 @@ impl PyPropertyRef { _doc: OptionalArg, vm: &mut VirtualMachine, ) -> PyResult { - Self::new_with_type( - vm, - PyProperty { - getter: fget.into_option(), - setter: fset.into_option(), - deleter: fdel.into_option(), - }, - cls, - ) + PyProperty { + getter: fget.into_option(), + setter: fset.into_option(), + deleter: fdel.into_option(), + } + .into_ref_with_type(vm, cls) } // Descriptor methods @@ -108,39 +105,30 @@ impl PyPropertyRef { // Python builder functions fn getter(self, getter: Option, vm: &mut VirtualMachine) -> PyResult { - Self::new_with_type( - vm, - PyProperty { - getter: getter.or_else(|| self.getter.clone()), - setter: self.setter.clone(), - deleter: self.deleter.clone(), - }, - self.typ(), - ) + PyProperty { + getter: getter.or_else(|| self.getter.clone()), + setter: self.setter.clone(), + deleter: self.deleter.clone(), + } + .into_ref_with_type(vm, self.typ()) } fn setter(self, setter: Option, vm: &mut VirtualMachine) -> PyResult { - Self::new_with_type( - vm, - PyProperty { - getter: self.getter.clone(), - setter: setter.or_else(|| self.setter.clone()), - deleter: self.deleter.clone(), - }, - self.typ(), - ) + PyProperty { + getter: self.getter.clone(), + setter: setter.or_else(|| self.setter.clone()), + deleter: self.deleter.clone(), + } + .into_ref_with_type(vm, self.typ()) } fn deleter(self, deleter: Option, vm: &mut VirtualMachine) -> PyResult { - Self::new_with_type( - vm, - PyProperty { - getter: self.getter.clone(), - setter: self.setter.clone(), - deleter: deleter.or_else(|| self.deleter.clone()), - }, - self.typ(), - ) + PyProperty { + getter: self.getter.clone(), + setter: self.setter.clone(), + deleter: deleter.or_else(|| self.deleter.clone()), + } + .into_ref_with_type(vm, self.typ()) } } diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 1ce1cdc747..73d1f1c2e5 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -801,7 +801,7 @@ fn str_new( TryFromObject::try_from_object(vm, string) } else { let payload = string.payload::().unwrap(); - PyRef::new_with_type(vm, payload.clone(), cls) + payload.clone().into_ref_with_type(vm, cls) } } diff --git a/vm/src/obj/objweakref.rs b/vm/src/obj/objweakref.rs index d2e2d08dc4..eab9c7622c 100644 --- a/vm/src/obj/objweakref.rs +++ b/vm/src/obj/objweakref.rs @@ -33,7 +33,7 @@ pub type PyWeakRef = PyRef; impl PyWeakRef { // TODO callbacks fn create(cls: PyClassRef, referent: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { - Self::new_with_type(vm, PyWeak::downgrade(referent), cls) + PyWeak::downgrade(referent).into_ref_with_type(vm, cls) } fn call(self, vm: &mut VirtualMachine) -> PyObjectRef { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index a68edc9c83..fa0585ef24 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -713,27 +713,6 @@ pub struct PyRef { } impl PyRef { - pub fn new(ctx: &PyContext, payload: T) -> Self { - PyRef { - obj: PyObject::new(payload, T::required_type(ctx)), - _payload: PhantomData, - } - } - - pub fn new_with_type(vm: &mut VirtualMachine, payload: T, cls: PyClassRef) -> PyResult { - let required_type = T::required_type(&vm.ctx); - if objtype::issubclass(&cls.obj, &required_type) { - Ok(PyRef { - obj: PyObject::new(payload, cls.obj), - _payload: PhantomData, - }) - } else { - let subtype = vm.to_pystr(&cls.obj)?; - let basetype = vm.to_pystr(&required_type)?; - Err(vm.new_type_error(format!("{} is not a subtype of {}", subtype, basetype))) - } - } - pub fn as_object(&self) -> &PyObjectRef { &self.obj } From a0acc7aa6afc8bd9066737e08e8d3e42812aaf13 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Fri, 15 Mar 2019 12:21:05 +0000 Subject: [PATCH 309/380] Avoid use of PyObject in frame.rs. --- vm/src/frame.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 8bf23bf986..fa18f616ff 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -20,8 +20,8 @@ use crate::obj::objslice::PySlice; use crate::obj::objstr; use crate::obj::objtype; use crate::pyobject::{ - AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectRef, - PyResult, PyValue, TryFromObject, TypeProtocol, + AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, + PyValue, TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -409,8 +409,8 @@ impl Frame { let stop = out[1].take(); let step = if out.len() == 3 { out[2].take() } else { None }; - let obj = PyObject::new(PySlice { start, stop, step }, vm.ctx.slice_type()); - self.push_value(obj); + let obj = PySlice { start, stop, step }.into_ref(&vm.ctx); + self.push_value(obj.into_object()); Ok(None) } bytecode::Instruction::ListAppend { i } => { @@ -702,11 +702,9 @@ impl Frame { Ok(None) } bytecode::Instruction::LoadBuildClass => { - let rustfunc = PyObject::new( - PyBuiltinFunction::new(Box::new(builtins::builtin_build_class_)), - vm.ctx.type_type(), - ); - self.push_value(rustfunc); + let rustfunc = PyBuiltinFunction::new(Box::new(builtins::builtin_build_class_)) + .into_ref(&vm.ctx); + self.push_value(rustfunc.into_object()); Ok(None) } bytecode::Instruction::UnpackSequence { size } => { From 7aa9b942435a2864055f454717a44e9198765fdb Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Fri, 15 Mar 2019 12:21:48 +0000 Subject: [PATCH 310/380] Avoid use of PyObject in objbytearray. --- vm/src/obj/objbytearray.rs | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index d737efaaea..403d1f6d31 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -5,12 +5,12 @@ use std::fmt::Write; use std::ops::{Deref, DerefMut}; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, + OptionalArg, PyContext, PyFuncArgs, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, }; use super::objint; -use super::objtype; +use super::objtype::{self, PyClassRef}; use crate::vm::VirtualMachine; use num_traits::ToPrimitive; @@ -19,6 +19,7 @@ pub struct PyByteArray { // TODO: shouldn't be public pub value: RefCell>, } +type PyByteArrayRef = PyRef; impl PyByteArray { pub fn new(data: Vec) -> Self { @@ -144,20 +145,14 @@ pub fn init(context: &PyContext) { ); } -fn bytearray_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(cls, None)], - optional = [(val_option, None)] - ); - if !objtype::issubclass(cls, &vm.ctx.bytearray_type()) { - return Err(vm.new_type_error(format!("{:?} is not a subtype of bytearray", cls))); - } - +fn bytearray_new( + cls: PyClassRef, + val_option: OptionalArg, + vm: &mut VirtualMachine, +) -> PyResult { // Create bytes data: - let value = if let Some(ival) = val_option { - let elements = vm.extract_elements(ival)?; + let value = if let OptionalArg::Present(ival) = val_option { + let elements = vm.extract_elements(&ival)?; let mut data_bytes = vec![]; for elem in elements.iter() { let v = objint::to_int(vm, elem, 10)?; @@ -172,7 +167,7 @@ fn bytearray_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } else { vec![] }; - Ok(PyObject::new(PyByteArray::new(value), cls.clone())) + PyByteArray::new(value).into_ref_with_type(vm, cls.clone()) } fn bytesarray_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From 871ac15675427c733b6c100a5a1d4a559c283618 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Fri, 15 Mar 2019 12:22:19 +0000 Subject: [PATCH 311/380] Avoid use of PyObject in objbytes. --- vm/src/obj/objbytes.rs | 45 ++++++++++++++++-------------------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index c78e919bbc..5f29a2ec56 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -3,9 +3,10 @@ use std::hash::{Hash, Hasher}; use std::ops::Deref; use super::objint; -use super::objtype; +use super::objtype::{self, PyClassRef}; use crate::pyobject::{ - PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, + OptionalArg, PyContext, PyFuncArgs, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue, + TypeProtocol, }; use crate::vm::VirtualMachine; use num_traits::ToPrimitive; @@ -14,6 +15,7 @@ use num_traits::ToPrimitive; pub struct PyBytes { value: Vec, } +type PyBytesRef = PyRef; impl PyBytes { pub fn new(data: Vec) -> Self { @@ -69,20 +71,14 @@ pub fn init(context: &PyContext) { ); } -fn bytes_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(cls, None)], - optional = [(val_option, None)] - ); - if !objtype::issubclass(cls, &vm.ctx.bytes_type()) { - return Err(vm.new_type_error(format!("{:?} is not a subtype of bytes", cls))); - } - +fn bytes_new( + cls: PyClassRef, + val_option: OptionalArg, + vm: &mut VirtualMachine, +) -> PyResult { // Create bytes data: - let value = if let Some(ival) = val_option { - let elements = vm.extract_elements(ival)?; + let value = if let OptionalArg::Present(ival) = val_option { + let elements = vm.extract_elements(&ival)?; let mut data_bytes = vec![]; for elem in elements.iter() { let v = objint::to_int(vm, elem, 10)?; @@ -94,7 +90,7 @@ fn bytes_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vec![] }; - Ok(PyObject::new(PyBytes::new(value), cls.clone())) + PyBytes::new(value).into_ref_with_type(vm, cls) } fn bytes_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -199,16 +195,9 @@ fn bytes_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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( - PyIteratorValue { - position: Cell::new(0), - iterated_obj: obj.clone(), - }, - vm.ctx.iter_type(), - ); - - Ok(iter_obj) +fn bytes_iter(obj: PyBytesRef, _vm: &mut VirtualMachine) -> PyIteratorValue { + PyIteratorValue { + position: Cell::new(0), + iterated_obj: obj.into_object(), + } } From abb4d41e5b9a64e1a6af0ebabdbebdf33ff7e2ad Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Fri, 15 Mar 2019 10:03:35 +0200 Subject: [PATCH 312/380] Add selenium test to travis --- .travis.yml | 20 ++++++++++++++++++++ wasm/demo/package.json | 7 +++++-- wasm/tests/.travis-runner.sh | 23 +++++++++++++++++++++++ wasm/tests/test_demo.py | 12 +++++------- 4 files changed, 53 insertions(+), 9 deletions(-) create mode 100755 wasm/tests/.travis-runner.sh diff --git a/.travis.yml b/.travis.yml index cbab6ae216..aff28cd6a6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -115,6 +115,26 @@ matrix: - TRAVIS_RUST_VERSION=nightly - REGULAR_TEST=false - CODE_COVERAGE=true + - name: test WASM + language: python + python: 3.6 + cache: + pip: true + # Because we're using the Python Travis environment, we can't use + # the built-in cargo cacher + directories: + - /home/travis/.cargo + - target + addons: + firefox: latest + install: + - nvm install node + - pip install pipenv + script: + - wasm/tests/.travis-runner.sh + env: + - REGULAR_TEST=true + - TRAVIS_RUST_VERSION=stable allow_failures: - rust: nightly env: REGULAR_TEST=true diff --git a/wasm/demo/package.json b/wasm/demo/package.json index 2184c6db84..a03c80aced 100644 --- a/wasm/demo/package.json +++ b/wasm/demo/package.json @@ -16,12 +16,15 @@ "raw-loader": "^1.0.0", "webpack": "^4.16.3", "webpack-cli": "^3.1.0", - "webpack-dev-server": "^3.1.5" + "webpack-dev-server": "^3.1.5", + "start-server-and-test": "^1.7.11" }, "scripts": { "dev": "webpack-dev-server -d", "build": "webpack", - "dist": "webpack --mode production" + "dist": "webpack --mode production", + "test": "cd ../tests; pipenv run pytest", + "ci": "start-server-and-test dev http-get://localhost:8080 test" }, "repository": { "type": "git", diff --git a/wasm/tests/.travis-runner.sh b/wasm/tests/.travis-runner.sh new file mode 100755 index 0000000000..add96c6b24 --- /dev/null +++ b/wasm/tests/.travis-runner.sh @@ -0,0 +1,23 @@ +#!/bin/sh -eux +# This script is intended to be run in Travis from the root of the repository + +# Install Rust +curl -sSf https://build.travis-ci.org/files/rustup-init.sh | sh -s -- --default-toolchain=$TRAVIS_RUST_VERSION -y +export PATH=$HOME/.cargo/bin:$PATH + +# install wasm-pack +if [ ! -f $HOME/.cargo/bin/wasm-pack ]; then + curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh +fi + +# install geckodriver +wget https://github.com/mozilla/geckodriver/releases/download/v0.24.0/geckodriver-v0.24.0-linux32.tar.gz +mkdir geckodriver +tar -xzf geckodriver-v0.24.0-linux32.tar.gz -C geckodriver +export PATH=$PATH:$PWD/geckodriver + +# Install pipenv +pip install pipenv +(cd wasm/tests; pipenv install) + +(cd wasm/demo; npm install; npm run ci) diff --git a/wasm/tests/test_demo.py b/wasm/tests/test_demo.py index 89a3f73fa4..9dce406811 100644 --- a/wasm/tests/test_demo.py +++ b/wasm/tests/test_demo.py @@ -1,7 +1,7 @@ import time from selenium import webdriver -from selenium.webdriver.chrome.options import Options +from selenium.webdriver.firefox.options import Options import pytest RUN_CODE_TEMPLATE = """ @@ -19,15 +19,13 @@ @pytest.fixture(scope="module") def driver(request): options = Options() - options.headless = True - options.add_argument('--disable-gpu') - driver = webdriver.Chrome(options=options) + options.add_argument('-headless') + driver = webdriver.Firefox(options=options) driver.get("http://localhost:8080") - assert "RustPython" in driver.title time.sleep(5) yield driver - driver.close() - + driver.close() + @pytest.mark.parametrize("script, output", [ From e8e8544b4b38726bdc44af51999c81802c843787 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Fri, 15 Mar 2019 12:22:34 +0000 Subject: [PATCH 313/380] Avoid use of PyObject in objcomplex. --- vm/src/obj/objcomplex.rs | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index 75cd455f82..046693ab79 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -1,8 +1,8 @@ use super::objfloat; use super::objint; -use super::objtype; +use super::objtype::{self, PyClassRef}; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, + OptionalArg, PyContext, PyFuncArgs, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; use num_complex::Complex64; @@ -12,6 +12,7 @@ use num_traits::ToPrimitive; pub struct PyComplex { value: Complex64, } +type PyComplexRef = PyRef; impl PyValue for PyComplex { fn required_type(ctx: &PyContext) -> PyObjectRef { @@ -65,31 +66,24 @@ pub fn get_value(obj: &PyObjectRef) -> Complex64 { obj.payload::().unwrap().value } -fn complex_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(cls, None)], - optional = [(real, None), (imag, None)] - ); - - if !objtype::issubclass(cls, &vm.ctx.complex_type()) { - return Err(vm.new_type_error(format!("{:?} is not a subtype of complex", cls))); - } - +fn complex_new( + cls: PyClassRef, + real: OptionalArg, + imag: OptionalArg, + vm: &mut VirtualMachine, +) -> PyResult { let real = match real { - None => 0.0, - Some(value) => objfloat::make_float(vm, value)?, + OptionalArg::Missing => 0.0, + OptionalArg::Present(ref value) => objfloat::make_float(vm, value)?, }; let imag = match imag { - None => 0.0, - Some(value) => objfloat::make_float(vm, value)?, + OptionalArg::Missing => 0.0, + OptionalArg::Present(ref value) => objfloat::make_float(vm, value)?, }; let value = Complex64::new(real, imag); - - Ok(PyObject::new(PyComplex { value }, cls.clone())) + PyComplex { value }.into_ref_with_type(vm, cls) } fn complex_real(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From cf82a256da68721fadc6420da4ebd5b3914feb46 Mon Sep 17 00:00:00 2001 From: Chylli Date: Sat, 16 Mar 2019 11:16:09 +0800 Subject: [PATCH 314/380] test any --- tests/snippets/builtin_any.py | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/snippets/builtin_any.py diff --git a/tests/snippets/builtin_any.py b/tests/snippets/builtin_any.py new file mode 100644 index 0000000000..0600145fd6 --- /dev/null +++ b/tests/snippets/builtin_any.py @@ -0,0 +1,9 @@ +def anything(a): + return a + +class Test: + def __iter__(self): + while True: + yield True + +assert True == any(map(anything, Test())) From 7bfd2e6e844e331fe4cd7eeaaf96715deb8ed512 Mon Sep 17 00:00:00 2001 From: Joey Date: Fri, 15 Mar 2019 21:27:11 -0700 Subject: [PATCH 315/380] Move function items out of pyobject module --- vm/src/builtins.rs | 9 +- vm/src/exceptions.rs | 3 +- vm/src/frame.rs | 5 +- vm/src/function.rs | 436 +++++++++++++++++++++++++++++++++ vm/src/lib.rs | 1 + vm/src/obj/objbool.rs | 3 +- vm/src/obj/objbuiltinfunc.rs | 3 +- vm/src/obj/objbytearray.rs | 11 +- vm/src/obj/objbytes.rs | 11 +- vm/src/obj/objclassmethod.rs | 3 +- vm/src/obj/objcode.rs | 8 +- vm/src/obj/objcomplex.rs | 13 +- vm/src/obj/objdict.rs | 5 +- vm/src/obj/objellipsis.rs | 3 +- vm/src/obj/objenumerate.rs | 13 +- vm/src/obj/objfilter.rs | 3 +- vm/src/obj/objframe.rs | 3 +- vm/src/obj/objfunction.rs | 5 +- vm/src/obj/objgenerator.rs | 5 +- vm/src/obj/objint.rs | 5 +- vm/src/obj/objiter.rs | 5 +- vm/src/obj/objlist.rs | 17 +- vm/src/obj/objmap.rs | 5 +- vm/src/obj/objmemory.rs | 5 +- vm/src/obj/objnone.rs | 3 +- vm/src/obj/objobject.rs | 12 +- vm/src/obj/objproperty.rs | 8 +- vm/src/obj/objrange.rs | 3 +- vm/src/obj/objset.rs | 10 +- vm/src/obj/objslice.rs | 11 +- vm/src/obj/objstaticmethod.rs | 3 +- vm/src/obj/objstr.rs | 5 +- vm/src/obj/objsuper.rs | 6 +- vm/src/obj/objtuple.rs | 4 +- vm/src/obj/objtype.rs | 5 +- vm/src/obj/objzip.rs | 5 +- vm/src/pyobject.rs | 436 +-------------------------------- vm/src/stdlib/ast.rs | 14 +- vm/src/stdlib/dis.rs | 3 +- vm/src/stdlib/io.rs | 10 +- vm/src/stdlib/json.rs | 4 +- vm/src/stdlib/keyword.rs | 9 +- vm/src/stdlib/math.rs | 9 +- vm/src/stdlib/os.rs | 9 +- vm/src/stdlib/platform.rs | 7 +- vm/src/stdlib/pystruct.rs | 13 +- vm/src/stdlib/random.rs | 8 +- vm/src/stdlib/re.rs | 6 +- vm/src/stdlib/socket.rs | 5 +- vm/src/stdlib/time_module.rs | 8 +- vm/src/stdlib/tokenize.rs | 8 +- vm/src/stdlib/types.rs | 3 +- vm/src/sysmodule.rs | 11 +- vm/src/vm.rs | 4 +- wasm/lib/src/browser_module.rs | 19 +- wasm/lib/src/convert.rs | 19 +- wasm/lib/src/vm_class.rs | 22 +- wasm/lib/src/wasm_builtins.rs | 6 +- 58 files changed, 665 insertions(+), 623 deletions(-) create mode 100644 vm/src/function.rs diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 86f6e84722..7e27d9f268 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -7,6 +7,8 @@ use std::char; use std::io::{self, Write}; use std::path::PathBuf; +use num_traits::{Signed, ToPrimitive}; + use crate::compile; use crate::import::import_module; use crate::obj::objbool; @@ -17,16 +19,15 @@ use crate::obj::objstr; use crate::obj::objtype; use crate::frame::Scope; +use crate::function::PyFuncArgs; use crate::pyobject::{ - AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol, + AttributeProtocol, IdProtocol, PyContext, PyObjectRef, PyResult, TypeProtocol, }; +use crate::vm::VirtualMachine; #[cfg(not(target_arch = "wasm32"))] use crate::stdlib::io::io_open; -use crate::vm::VirtualMachine; -use num_traits::{Signed, ToPrimitive}; - fn get_locals(vm: &mut VirtualMachine) -> PyObjectRef { let d = vm.new_dict(); // TODO: implement dict_iter_items? diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 94853d5d06..419033808c 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -1,8 +1,9 @@ +use crate::function::PyFuncArgs; use crate::obj::objsequence; use crate::obj::objstr; use crate::obj::objtype; use crate::pyobject::{ - create_type, AttributeProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol, + create_type, AttributeProtocol, PyContext, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 5be248d06d..98369efd85 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -8,6 +8,7 @@ use rustpython_parser::ast; use crate::builtins; use crate::bytecode; +use crate::function::PyFuncArgs; use crate::obj::objbool; use crate::obj::objbuiltinfunc::PyBuiltinFunction; use crate::obj::objcode; @@ -20,8 +21,8 @@ use crate::obj::objslice::PySlice; use crate::obj::objstr; use crate::obj::objtype; use crate::pyobject::{ - AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectRef, - PyResult, PyValue, TryFromObject, TypeProtocol, + AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyObject, PyObjectRef, PyResult, + PyValue, TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; diff --git a/vm/src/function.rs b/vm/src/function.rs new file mode 100644 index 0000000000..7b4adebd0c --- /dev/null +++ b/vm/src/function.rs @@ -0,0 +1,436 @@ +use std::collections::HashMap; +use std::iter; +use std::ops::RangeInclusive; + +use crate::obj::objtype; +use crate::pyobject::{IntoPyObject, PyObjectRef, PyResult, TryFromObject, TypeProtocol}; +use crate::vm::VirtualMachine; + +use self::OptionalArg::*; + +/// The `PyFuncArgs` struct is one of the most used structs then creating +/// a rust function that can be called from python. It holds both positional +/// arguments, as well as keyword arguments passed to the function. +#[derive(Debug, Default, Clone)] +pub struct PyFuncArgs { + pub args: Vec, + pub kwargs: Vec<(String, PyObjectRef)>, +} + +/// Conversion from vector of python objects to function arguments. +impl From> for PyFuncArgs { + fn from(args: Vec) -> Self { + PyFuncArgs { + args: args, + kwargs: vec![], + } + } +} + +impl From for PyFuncArgs { + fn from(arg: PyObjectRef) -> Self { + PyFuncArgs { + args: vec![arg], + kwargs: vec![], + } + } +} + +impl PyFuncArgs { + pub fn new(mut args: Vec, kwarg_names: Vec) -> PyFuncArgs { + let mut kwargs = vec![]; + for name in kwarg_names.iter().rev() { + kwargs.push((name.clone(), args.pop().unwrap())); + } + PyFuncArgs { args, kwargs } + } + + pub fn insert(&self, item: PyObjectRef) -> PyFuncArgs { + let mut args = PyFuncArgs { + args: self.args.clone(), + kwargs: self.kwargs.clone(), + }; + args.args.insert(0, item); + args + } + + pub fn shift(&mut self) -> PyObjectRef { + self.args.remove(0) + } + + pub fn get_kwarg(&self, key: &str, default: PyObjectRef) -> PyObjectRef { + for (arg_name, arg_value) in self.kwargs.iter() { + if arg_name == key { + return arg_value.clone(); + } + } + default.clone() + } + + pub fn get_optional_kwarg(&self, key: &str) -> Option { + for (arg_name, arg_value) in self.kwargs.iter() { + if arg_name == key { + return Some(arg_value.clone()); + } + } + None + } + + pub fn get_optional_kwarg_with_type( + &self, + key: &str, + ty: PyObjectRef, + vm: &mut VirtualMachine, + ) -> Result, PyObjectRef> { + match self.get_optional_kwarg(key) { + Some(kwarg) => { + if objtype::isinstance(&kwarg, &ty) { + Ok(Some(kwarg)) + } else { + let expected_ty_name = vm.to_pystr(&ty)?; + let actual_ty_name = vm.to_pystr(&kwarg.typ())?; + Err(vm.new_type_error(format!( + "argument of type {} is required for named parameter `{}` (got: {})", + expected_ty_name, key, actual_ty_name + ))) + } + } + None => Ok(None), + } + } + + /// Serializes these arguments into an iterator starting with the positional + /// arguments followed by keyword arguments. + fn into_iter(self) -> impl Iterator { + self.args.into_iter().map(PyArg::Positional).chain( + self.kwargs + .into_iter() + .map(|(name, value)| PyArg::Keyword(name, value)), + ) + } + + /// Binds these arguments to their respective values. + /// + /// If there is an insufficient number of arguments, there are leftover + /// arguments after performing the binding, or if an argument is not of + /// the expected type, a TypeError is raised. + /// + /// If the given `FromArgs` includes any conversions, exceptions raised + /// during the conversion will halt the binding and return the error. + fn bind(self, vm: &mut VirtualMachine) -> PyResult { + let given_args = self.args.len(); + let mut args = self.into_iter().peekable(); + let bound = match T::from_args(vm, &mut args) { + Ok(args) => args, + Err(ArgumentError::TooFewArgs) => { + return Err(vm.new_type_error(format!( + "Expected at least {} arguments ({} given)", + T::arity().start(), + given_args, + ))); + } + Err(ArgumentError::Exception(ex)) => { + return Err(ex); + } + }; + + match args.next() { + None => Ok(bound), + Some(PyArg::Positional(_)) => Err(vm.new_type_error(format!( + "Expected at most {} arguments ({} given)", + T::arity().end(), + given_args, + ))), + Some(PyArg::Keyword(name, _)) => { + Err(vm.new_type_error(format!("Unexpected keyword argument {}", name))) + } + } + } +} + +pub enum PyArg { + Positional(PyObjectRef), + Keyword(String, PyObjectRef), +} + +pub enum ArgumentError { + TooFewArgs, + Exception(PyObjectRef), +} + +impl From for ArgumentError { + fn from(ex: PyObjectRef) -> Self { + ArgumentError::Exception(ex) + } +} + +/// Implemented by any type that can be accepted as a parameter to a built-in +/// function. +/// +pub trait FromArgs: Sized { + /// The range of positional arguments permitted by the function signature. + /// + /// Returns an empty range if not applicable. + fn arity() -> RangeInclusive { + 0..=0 + } + + /// Extracts this item from the next argument(s). + fn from_args( + vm: &mut VirtualMachine, + args: &mut iter::Peekable, + ) -> Result + where + I: Iterator; +} +/// A map of keyword arguments to their values. +/// +/// A built-in function with a `KwArgs` parameter is analagous to a Python +/// function with `*kwargs`. All remaining keyword arguments are extracted +/// (and hence the function will permit an arbitrary number of them). +/// +/// `KwArgs` optionally accepts a generic type parameter to allow type checks +/// or conversions of each argument. +pub struct KwArgs(HashMap); + +impl FromArgs for KwArgs +where + T: TryFromObject, +{ + fn from_args( + vm: &mut VirtualMachine, + args: &mut iter::Peekable, + ) -> Result + where + I: Iterator, + { + let mut kwargs = HashMap::new(); + while let Some(PyArg::Keyword(name, value)) = args.next() { + kwargs.insert(name, T::try_from_object(vm, value)?); + } + Ok(KwArgs(kwargs)) + } +} + +/// A list of positional argument values. +/// +/// A built-in function with a `Args` parameter is analagous to a Python +/// function with `*args`. All remaining positional arguments are extracted +/// (and hence the function will permit an arbitrary number of them). +/// +/// `Args` optionally accepts a generic type parameter to allow type checks +/// or conversions of each argument. +pub struct Args(Vec); + +impl FromArgs for Args +where + T: TryFromObject, +{ + fn from_args( + vm: &mut VirtualMachine, + args: &mut iter::Peekable, + ) -> Result + where + I: Iterator, + { + let mut varargs = Vec::new(); + while let Some(PyArg::Positional(value)) = args.next() { + varargs.push(T::try_from_object(vm, value)?); + } + Ok(Args(varargs)) + } +} + +impl FromArgs for T +where + T: TryFromObject, +{ + fn arity() -> RangeInclusive { + 1..=1 + } + + fn from_args( + vm: &mut VirtualMachine, + args: &mut iter::Peekable, + ) -> Result + where + I: Iterator, + { + if let Some(PyArg::Positional(value)) = args.next() { + Ok(T::try_from_object(vm, value)?) + } else { + Err(ArgumentError::TooFewArgs) + } + } +} + +/// An argument that may or may not be provided by the caller. +/// +/// This style of argument is not possible in pure Python. +pub enum OptionalArg { + Present(T), + Missing, +} + +impl OptionalArg { + pub fn into_option(self) -> Option { + match self { + Present(value) => Some(value), + Missing => None, + } + } +} + +impl FromArgs for OptionalArg +where + T: TryFromObject, +{ + fn arity() -> RangeInclusive { + 0..=1 + } + + fn from_args( + vm: &mut VirtualMachine, + args: &mut iter::Peekable, + ) -> Result + where + I: Iterator, + { + Ok(if let Some(PyArg::Positional(_)) = args.peek() { + let value = if let Some(PyArg::Positional(value)) = args.next() { + value + } else { + unreachable!() + }; + Present(T::try_from_object(vm, value)?) + } else { + Missing + }) + } +} + +// For functions that accept no arguments. Implemented explicitly instead of via +// macro below to avoid unused warnings. +impl FromArgs for () { + fn from_args( + _vm: &mut VirtualMachine, + _args: &mut iter::Peekable, + ) -> Result + where + I: Iterator, + { + Ok(()) + } +} + +// A tuple of types that each implement `FromArgs` represents a sequence of +// arguments that can be bound and passed to a built-in function. +// +// Technically, a tuple can contain tuples, which can contain tuples, and so on, +// so this actually represents a tree of values to be bound from arguments, but +// in practice this is only used for the top-level parameters. +macro_rules! tuple_from_py_func_args { + ($($T:ident),+) => { + impl<$($T),+> FromArgs for ($($T,)+) + where + $($T: FromArgs),+ + { + fn arity() -> RangeInclusive { + let mut min = 0; + let mut max = 0; + $( + let (start, end) = $T::arity().into_inner(); + min += start; + max += end; + )+ + min..=max + } + + fn from_args( + vm: &mut VirtualMachine, + args: &mut iter::Peekable + ) -> Result + where + I: Iterator + { + Ok(($($T::from_args(vm, args)?,)+)) + } + } + }; +} + +// Implement `FromArgs` for up to 5-tuples, allowing built-in functions to bind +// up to 5 top-level parameters (note that `Args`, `KwArgs`, nested tuples, etc. +// count as 1, so this should actually be more than enough). +tuple_from_py_func_args!(A); +tuple_from_py_func_args!(A, B); +tuple_from_py_func_args!(A, B, C); +tuple_from_py_func_args!(A, B, C, D); +tuple_from_py_func_args!(A, B, C, D, E); + +/// A built-in Python function. +pub type PyNativeFunc = Box PyResult + 'static>; + +/// Implemented by types that are or can generate built-in functions. +/// +/// For example, any function that: +/// +/// - Accepts a sequence of types that implement `FromArgs`, followed by a +/// `&mut VirtualMachine` +/// - Returns some type that implements `IntoPyObject` +/// +/// will generate a `PyNativeFunc` that performs the appropriate type and arity +/// checking, any requested conversions, and then if successful call the function +/// with the bound values. +/// +/// A bare `PyNativeFunc` also implements this trait, allowing the above to be +/// done manually, for rare situations that don't fit into this model. +pub trait IntoPyNativeFunc { + fn into_func(self) -> PyNativeFunc; +} + +impl IntoPyNativeFunc for F +where + F: Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult + 'static, +{ + fn into_func(self) -> PyNativeFunc { + Box::new(self) + } +} + +impl IntoPyNativeFunc for PyNativeFunc { + fn into_func(self) -> PyNativeFunc { + self + } +} + +// This is the "magic" that allows rust functions of varying signatures to +// generate native python functions. +// +// Note that this could be done without a macro - it is simply to avoid repetition. +macro_rules! into_py_native_func_tuple { + ($(($n:tt, $T:ident)),*) => { + impl IntoPyNativeFunc<($($T,)*), R> for F + where + F: Fn($($T,)* &mut VirtualMachine) -> R + 'static, + $($T: FromArgs,)* + ($($T,)*): FromArgs, + R: IntoPyObject, + { + fn into_func(self) -> PyNativeFunc { + Box::new(move |vm, args| { + let ($($n,)*) = args.bind::<($($T,)*)>(vm)?; + + (self)($($n,)* vm).into_pyobject(vm) + }) + } + } + }; +} + +into_py_native_func_tuple!(); +into_py_native_func_tuple!((a, A)); +into_py_native_func_tuple!((a, A), (b, B)); +into_py_native_func_tuple!((a, A), (b, B), (c, C)); +into_py_native_func_tuple!((a, A), (b, B), (c, C), (d, D)); +into_py_native_func_tuple!((a, A), (b, B), (c, C), (d, D), (e, E)); diff --git a/vm/src/lib.rs b/vm/src/lib.rs index b32b91cb7d..78696f9f3f 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -41,6 +41,7 @@ pub mod eval; mod exceptions; pub mod format; pub mod frame; +pub mod function; pub mod import; pub mod obj; pub mod pyobject; diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index 486bb2dd7e..815354b94c 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -1,6 +1,7 @@ use num_traits::Zero; -use crate::pyobject::{IntoPyObject, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; +use crate::function::PyFuncArgs; +use crate::pyobject::{IntoPyObject, PyContext, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; use super::objdict::PyDict; diff --git a/vm/src/obj/objbuiltinfunc.rs b/vm/src/obj/objbuiltinfunc.rs index ae8dfbf99c..ca181fea4d 100644 --- a/vm/src/obj/objbuiltinfunc.rs +++ b/vm/src/obj/objbuiltinfunc.rs @@ -1,6 +1,7 @@ use std::fmt; -use crate::pyobject::{PyNativeFunc, PyObjectRef, PyValue}; +use crate::function::PyNativeFunc; +use crate::pyobject::{PyObjectRef, PyValue}; use crate::vm::VirtualMachine; pub struct PyBuiltinFunction { diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index d4641ae7df..4863c51d34 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -4,15 +4,14 @@ use std::cell::RefCell; use std::fmt::Write; use std::ops::{Deref, DerefMut}; -use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, -}; +use num_traits::ToPrimitive; -use super::objint; +use crate::function::PyFuncArgs; +use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol}; +use crate::vm::VirtualMachine; +use super::objint; use super::objtype; -use crate::vm::VirtualMachine; -use num_traits::ToPrimitive; #[derive(Debug)] pub struct PyByteArray { diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 5247d35c4d..08c80b764e 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -2,13 +2,16 @@ use std::cell::Cell; use std::hash::{Hash, Hasher}; use std::ops::Deref; -use super::objint; -use super::objtype; +use num_traits::ToPrimitive; + +use crate::function::PyFuncArgs; use crate::pyobject::{ - PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, + PyContext, PyIteratorValue, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; -use num_traits::ToPrimitive; + +use super::objint; +use super::objtype; #[derive(Debug)] pub struct PyBytes { diff --git a/vm/src/obj/objclassmethod.rs b/vm/src/obj/objclassmethod.rs index 9c8ea3f8f1..c2322b1836 100644 --- a/vm/src/obj/objclassmethod.rs +++ b/vm/src/obj/objclassmethod.rs @@ -1,4 +1,5 @@ -use crate::pyobject::{AttributeProtocol, PyContext, PyFuncArgs, PyResult, TypeProtocol}; +use crate::function::PyFuncArgs; +use crate::pyobject::{AttributeProtocol, PyContext, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; pub fn init(context: &PyContext) { diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index 9897a47a0c..2b0a4b9354 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -2,12 +2,12 @@ */ +use std::fmt; + use crate::bytecode; -use crate::pyobject::{ - IdProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, PyValue, TypeProtocol, -}; +use crate::function::PyFuncArgs; +use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; -use std::fmt; pub struct PyCode { code: bytecode::CodeObject, diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index 0d2d878b04..e5723f1565 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -1,12 +1,13 @@ +use num_complex::Complex64; +use num_traits::ToPrimitive; + +use crate::function::PyFuncArgs; +use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol}; +use crate::vm::VirtualMachine; + use super::objfloat; use super::objint; use super::objtype; -use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, -}; -use crate::vm::VirtualMachine; -use num_complex::Complex64; -use num_traits::ToPrimitive; #[derive(Debug, Copy, Clone, PartialEq)] pub struct PyComplex { diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 3b37a801be..6bd86f6510 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -3,9 +3,10 @@ use std::collections::HashMap; use std::fmt; use std::ops::{Deref, DerefMut}; +use crate::function::PyFuncArgs; use crate::pyobject::{ - PyAttributes, PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectRef, PyRef, PyResult, - PyValue, TypeProtocol, + PyAttributes, PyContext, PyIteratorValue, PyObject, PyObjectRef, PyRef, PyResult, PyValue, + TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; diff --git a/vm/src/obj/objellipsis.rs b/vm/src/obj/objellipsis.rs index 48120ea771..a0ed8f6441 100644 --- a/vm/src/obj/objellipsis.rs +++ b/vm/src/obj/objellipsis.rs @@ -1,4 +1,5 @@ -use crate::pyobject::{PyContext, PyFuncArgs, PyResult, TypeProtocol}; +use crate::function::PyFuncArgs; +use crate::pyobject::{PyContext, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; pub fn init(context: &PyContext) { diff --git a/vm/src/obj/objenumerate.rs b/vm/src/obj/objenumerate.rs index 4551edd910..76452e56cb 100644 --- a/vm/src/obj/objenumerate.rs +++ b/vm/src/obj/objenumerate.rs @@ -1,15 +1,16 @@ use std::cell::RefCell; use std::ops::AddAssign; -use super::objint; -use super::objiter; -use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, -}; -use crate::vm::VirtualMachine; use num_bigint::BigInt; use num_traits::Zero; +use crate::function::PyFuncArgs; +use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol}; +use crate::vm::VirtualMachine; + +use super::objint; +use super::objiter; + #[derive(Debug)] pub struct PyEnumerate { counter: RefCell, diff --git a/vm/src/obj/objfilter.rs b/vm/src/obj/objfilter.rs index 5533c3e54e..da329c809a 100644 --- a/vm/src/obj/objfilter.rs +++ b/vm/src/obj/objfilter.rs @@ -1,5 +1,6 @@ +use crate::function::PyFuncArgs; use crate::pyobject::{ - IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, + IdProtocol, PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; // Required for arg_check! to use isinstance diff --git a/vm/src/obj/objframe.rs b/vm/src/obj/objframe.rs index e77f143590..c3ebad300d 100644 --- a/vm/src/obj/objframe.rs +++ b/vm/src/obj/objframe.rs @@ -3,7 +3,8 @@ */ use crate::frame::Frame; -use crate::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; +use crate::function::PyFuncArgs; +use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; pub fn init(context: &PyContext) { diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index d9d57cfce4..f2c0bbafe9 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -1,7 +1,6 @@ use crate::frame::Scope; -use crate::pyobject::{ - IdProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, PyValue, TypeProtocol, -}; +use crate::function::PyFuncArgs; +use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; #[derive(Debug)] diff --git a/vm/src/obj/objgenerator.rs b/vm/src/obj/objgenerator.rs index 2bdd581958..611b89150f 100644 --- a/vm/src/obj/objgenerator.rs +++ b/vm/src/obj/objgenerator.rs @@ -3,9 +3,8 @@ */ use crate::frame::{ExecutionResult, Frame}; -use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, -}; +use crate::function::PyFuncArgs; +use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; #[derive(Debug)] diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index f1f81c5c50..d1b1388cea 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -5,9 +5,10 @@ use num_integer::Integer; use num_traits::{Pow, Signed, ToPrimitive, Zero}; use crate::format::FormatSpec; +use crate::function::{OptionalArg, PyFuncArgs}; use crate::pyobject::{ - IntoPyObject, OptionalArg, PyContext, PyFuncArgs, PyObject, PyObjectRef, PyRef, PyResult, - PyValue, TryFromObject, TypeProtocol, + IntoPyObject, PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, + TypeProtocol, }; use crate::vm::VirtualMachine; diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 153750feef..4c527776d7 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -2,9 +2,8 @@ * Various types to support iteration. */ -use crate::pyobject::{ - PyContext, PyFuncArgs, PyIteratorValue, PyObjectRef, PyResult, TypeProtocol, -}; +use crate::function::PyFuncArgs; +use crate::pyobject::{PyContext, PyIteratorValue, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; use super::objbool; diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index f0baedb1b6..361e3622de 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -1,4 +1,14 @@ use std::cell::{Cell, RefCell}; +use std::fmt; + +use num_traits::ToPrimitive; + +use crate::function::{OptionalArg, PyFuncArgs}; +use crate::pyobject::{ + IdProtocol, PyContext, PyIteratorValue, PyObject, PyObjectRef, PyRef, PyResult, PyValue, + TypeProtocol, +}; +use crate::vm::{ReprGuard, VirtualMachine}; use super::objbool; use super::objint; @@ -8,13 +18,6 @@ use super::objsequence::{ }; use super::objstr; use super::objtype; -use crate::pyobject::{ - IdProtocol, OptionalArg, PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectRef, PyRef, - PyResult, PyValue, TypeProtocol, -}; -use crate::vm::{ReprGuard, VirtualMachine}; -use num_traits::ToPrimitive; -use std::fmt; #[derive(Default)] pub struct PyList { diff --git a/vm/src/obj/objmap.rs b/vm/src/obj/objmap.rs index 4ba8fd26ba..2de5bc5eff 100644 --- a/vm/src/obj/objmap.rs +++ b/vm/src/obj/objmap.rs @@ -1,6 +1,5 @@ -use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, -}; +use crate::function::PyFuncArgs; +use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; use super::objiter; diff --git a/vm/src/obj/objmemory.rs b/vm/src/obj/objmemory.rs index 5632ccac90..5386faaf2e 100644 --- a/vm/src/obj/objmemory.rs +++ b/vm/src/obj/objmemory.rs @@ -1,6 +1,5 @@ -use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, -}; +use crate::function::PyFuncArgs; +use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; #[derive(Debug)] diff --git a/vm/src/obj/objnone.rs b/vm/src/obj/objnone.rs index beb553326c..fe63ecd984 100644 --- a/vm/src/obj/objnone.rs +++ b/vm/src/obj/objnone.rs @@ -1,5 +1,6 @@ +use crate::function::PyFuncArgs; use crate::pyobject::{ - IntoPyObject, PyContext, PyFuncArgs, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, + IntoPyObject, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 2423f5e93a..bea7737ebc 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -1,13 +1,15 @@ -use super::objlist::PyList; -use super::objstr; -use super::objtype; +use crate::function::PyFuncArgs; use crate::obj::objproperty::PropertyBuilder; use crate::pyobject::{ - AttributeProtocol, DictProtocol, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObjectRef, - PyRef, PyResult, PyValue, TypeProtocol, + AttributeProtocol, DictProtocol, IdProtocol, PyAttributes, PyContext, PyObjectRef, PyRef, + PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; +use super::objlist::PyList; +use super::objstr; +use super::objtype; + #[derive(Clone, Debug)] pub struct PyInstance; diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index a66557e953..fae80cec6e 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -2,12 +2,12 @@ */ +use crate::function::IntoPyNativeFunc; +use crate::function::OptionalArg; use crate::obj::objstr::PyStringRef; use crate::obj::objtype::PyClassRef; -use crate::pyobject::{ - IntoPyNativeFunc, OptionalArg, PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue, -}; -use crate::VirtualMachine; +use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue}; +use crate::vm::VirtualMachine; /// Read-only property, doesn't have __set__ or __delete__ #[derive(Debug)] diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index f5c045928d..49f1a9e988 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -5,8 +5,9 @@ use num_bigint::{BigInt, Sign}; use num_integer::Integer; use num_traits::{One, Signed, ToPrimitive, Zero}; +use crate::function::PyFuncArgs; use crate::pyobject::{ - PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, + PyContext, PyIteratorValue, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index f33208915d..0e234e3cad 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -7,15 +7,17 @@ use std::collections::{hash_map::DefaultHasher, HashMap}; use std::fmt; use std::hash::{Hash, Hasher}; +use crate::function::PyFuncArgs; +use crate::pyobject::{ + PyContext, PyIteratorValue, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, +}; +use crate::vm::{ReprGuard, VirtualMachine}; + use super::objbool; use super::objint; use super::objiter; use super::objstr; use super::objtype; -use crate::pyobject::{ - PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, -}; -use crate::vm::{ReprGuard, VirtualMachine}; #[derive(Default)] pub struct PySet { diff --git a/vm/src/obj/objslice.rs b/vm/src/obj/objslice.rs index 3bb55ced2a..8d65bc331e 100644 --- a/vm/src/obj/objslice.rs +++ b/vm/src/obj/objslice.rs @@ -1,10 +1,11 @@ -use super::objint; -use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, -}; -use crate::vm::VirtualMachine; use num_bigint::BigInt; +use crate::function::PyFuncArgs; +use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol}; +use crate::vm::VirtualMachine; + +use super::objint; + #[derive(Debug)] pub struct PySlice { // TODO: should be private diff --git a/vm/src/obj/objstaticmethod.rs b/vm/src/obj/objstaticmethod.rs index 6807a695ce..9d1a96ed2a 100644 --- a/vm/src/obj/objstaticmethod.rs +++ b/vm/src/obj/objstaticmethod.rs @@ -1,4 +1,5 @@ -use crate::pyobject::{AttributeProtocol, PyContext, PyFuncArgs, PyResult, TypeProtocol}; +use crate::function::PyFuncArgs; +use crate::pyobject::{AttributeProtocol, PyContext, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; pub fn init(context: &PyContext) { diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 0a969b6861..71a0737ca9 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -6,9 +6,10 @@ use num_traits::ToPrimitive; use unicode_segmentation::UnicodeSegmentation; use crate::format::{FormatParseError, FormatPart, FormatString}; +use crate::function::{OptionalArg, PyFuncArgs}; use crate::pyobject::{ - IdProtocol, IntoPyObject, OptionalArg, PyContext, PyFuncArgs, PyIterable, PyObjectRef, PyRef, - PyResult, PyValue, TryFromObject, TypeProtocol, + IdProtocol, IntoPyObject, PyContext, PyIterable, PyObjectRef, PyRef, PyResult, PyValue, + TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index 79c516d4c0..a3d877e14d 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -6,10 +6,12 @@ https://github.com/python/cpython/blob/50b48572d9a90c5bb36e2bef6179548ea927a35a/ */ -use super::objtype; -use crate::pyobject::{PyContext, PyFuncArgs, PyResult, TypeProtocol}; +use crate::function::PyFuncArgs; +use crate::pyobject::{PyContext, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; +use super::objtype; + pub fn init(context: &PyContext) { let super_type = &context.super_type; diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 1702c2237a..dc6d7d01c6 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -2,9 +2,9 @@ use std::cell::{Cell, RefCell}; use std::fmt; use std::hash::{Hash, Hasher}; +use crate::function::OptionalArg; use crate::pyobject::{ - IdProtocol, OptionalArg, PyContext, PyIteratorValue, PyObject, PyObjectRef, PyRef, PyResult, - PyValue, + IdProtocol, PyContext, PyIteratorValue, PyObject, PyObjectRef, PyRef, PyResult, PyValue, }; use crate::vm::{ReprGuard, VirtualMachine}; diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 8fd7772f21..860d30da31 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -1,9 +1,10 @@ use std::cell::RefCell; use std::collections::HashMap; +use crate::function::PyFuncArgs; use crate::pyobject::{ - AttributeProtocol, FromPyObjectRef, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObject, - PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, + AttributeProtocol, FromPyObjectRef, IdProtocol, PyAttributes, PyContext, PyObject, PyObjectRef, + PyRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; diff --git a/vm/src/obj/objzip.rs b/vm/src/obj/objzip.rs index 35213919da..8250337e0a 100644 --- a/vm/src/obj/objzip.rs +++ b/vm/src/obj/objzip.rs @@ -1,6 +1,5 @@ -use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, -}; +use crate::function::PyFuncArgs; +use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; use super::objiter; diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 9bd94ec707..9bfa7d4b3d 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -2,10 +2,9 @@ use std::any::Any; use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::fmt; -use std::iter; use std::marker::PhantomData; use std::mem; -use std::ops::{Deref, RangeInclusive}; +use std::ops::Deref; use std::ptr; use std::rc::Rc; @@ -16,6 +15,7 @@ use num_traits::{One, Zero}; use crate::bytecode; use crate::exceptions; use crate::frame::{Frame, Scope}; +use crate::function::{IntoPyNativeFunc, PyFuncArgs}; use crate::obj::objbool; use crate::obj::objbuiltinfunc::PyBuiltinFunction; use crate::obj::objbytearray; @@ -40,6 +40,7 @@ use crate::obj::objmodule::{self, PyModule}; use crate::obj::objnone; use crate::obj::objobject; use crate::obj::objproperty; +use crate::obj::objproperty::PropertyBuilder; use crate::obj::objrange; use crate::obj::objset::{self, PySet}; use crate::obj::objslice; @@ -975,166 +976,6 @@ impl fmt::Debug for PyObject { } } -/// The `PyFuncArgs` struct is one of the most used structs then creating -/// a rust function that can be called from python. It holds both positional -/// arguments, as well as keyword arguments passed to the function. -#[derive(Debug, Default, Clone)] -pub struct PyFuncArgs { - pub args: Vec, - pub kwargs: Vec<(String, PyObjectRef)>, -} - -/// Conversion from vector of python objects to function arguments. -impl From> for PyFuncArgs { - fn from(args: Vec) -> Self { - PyFuncArgs { - args: args, - kwargs: vec![], - } - } -} - -impl From for PyFuncArgs { - fn from(arg: PyObjectRef) -> Self { - PyFuncArgs { - args: vec![arg], - kwargs: vec![], - } - } -} - -impl PyFuncArgs { - pub fn new(mut args: Vec, kwarg_names: Vec) -> PyFuncArgs { - let mut kwargs = vec![]; - for name in kwarg_names.iter().rev() { - kwargs.push((name.clone(), args.pop().unwrap())); - } - PyFuncArgs { args, kwargs } - } - - pub fn insert(&self, item: PyObjectRef) -> PyFuncArgs { - let mut args = PyFuncArgs { - args: self.args.clone(), - kwargs: self.kwargs.clone(), - }; - args.args.insert(0, item); - args - } - - pub fn shift(&mut self) -> PyObjectRef { - self.args.remove(0) - } - - pub fn get_kwarg(&self, key: &str, default: PyObjectRef) -> PyObjectRef { - for (arg_name, arg_value) in self.kwargs.iter() { - if arg_name == key { - return arg_value.clone(); - } - } - default.clone() - } - - pub fn get_optional_kwarg(&self, key: &str) -> Option { - for (arg_name, arg_value) in self.kwargs.iter() { - if arg_name == key { - return Some(arg_value.clone()); - } - } - None - } - - pub fn get_optional_kwarg_with_type( - &self, - key: &str, - ty: PyObjectRef, - vm: &mut VirtualMachine, - ) -> Result, PyObjectRef> { - match self.get_optional_kwarg(key) { - Some(kwarg) => { - if objtype::isinstance(&kwarg, &ty) { - Ok(Some(kwarg)) - } else { - let expected_ty_name = vm.to_pystr(&ty)?; - let actual_ty_name = vm.to_pystr(&kwarg.typ())?; - Err(vm.new_type_error(format!( - "argument of type {} is required for named parameter `{}` (got: {})", - expected_ty_name, key, actual_ty_name - ))) - } - } - None => Ok(None), - } - } - - /// Serializes these arguments into an iterator starting with the positional - /// arguments followed by keyword arguments. - fn into_iter(self) -> impl Iterator { - self.args.into_iter().map(PyArg::Positional).chain( - self.kwargs - .into_iter() - .map(|(name, value)| PyArg::Keyword(name, value)), - ) - } - - /// Binds these arguments to their respective values. - /// - /// If there is an insufficient number of arguments, there are leftover - /// arguments after performing the binding, or if an argument is not of - /// the expected type, a TypeError is raised. - /// - /// If the given `FromArgs` includes any conversions, exceptions raised - /// during the conversion will halt the binding and return the error. - fn bind(self, vm: &mut VirtualMachine) -> PyResult { - let given_args = self.args.len(); - let mut args = self.into_iter().peekable(); - let bound = match T::from_args(vm, &mut args) { - Ok(args) => args, - Err(ArgumentError::TooFewArgs) => { - return Err(vm.new_type_error(format!( - "Expected at least {} arguments ({} given)", - T::arity().start(), - given_args, - ))); - } - Err(ArgumentError::Exception(ex)) => { - return Err(ex); - } - }; - - match args.next() { - None => Ok(bound), - Some(PyArg::Positional(_)) => Err(vm.new_type_error(format!( - "Expected at most {} arguments ({} given)", - T::arity().end(), - given_args, - ))), - Some(PyArg::Keyword(name, _)) => { - Err(vm.new_type_error(format!("Unexpected keyword argument {}", name))) - } - } - } -} - -/// Implemented by any type that can be accepted as a parameter to a built-in -/// function. -/// -pub trait FromArgs: Sized { - /// The range of positional arguments permitted by the function signature. - /// - /// Returns an empty range if not applicable. - fn arity() -> RangeInclusive { - 0..=0 - } - - /// Extracts this item from the next argument(s). - fn from_args( - vm: &mut VirtualMachine, - args: &mut iter::Peekable, - ) -> Result - where - I: Iterator; -} - /// An iterable Python object. /// /// `PyIterable` implements `FromArgs` so that a built-in function can accept @@ -1224,151 +1065,6 @@ impl TryFromObject for Option { } } -/// A map of keyword arguments to their values. -/// -/// A built-in function with a `KwArgs` parameter is analagous to a Python -/// function with `*kwargs`. All remaining keyword arguments are extracted -/// (and hence the function will permit an arbitrary number of them). -/// -/// `KwArgs` optionally accepts a generic type parameter to allow type checks -/// or conversions of each argument. -pub struct KwArgs(HashMap); - -impl FromArgs for KwArgs -where - T: TryFromObject, -{ - fn from_args( - vm: &mut VirtualMachine, - args: &mut iter::Peekable, - ) -> Result - where - I: Iterator, - { - let mut kwargs = HashMap::new(); - while let Some(PyArg::Keyword(name, value)) = args.next() { - kwargs.insert(name, T::try_from_object(vm, value)?); - } - Ok(KwArgs(kwargs)) - } -} - -/// A list of positional argument values. -/// -/// A built-in function with a `Args` parameter is analagous to a Python -/// function with `*args`. All remaining positional arguments are extracted -/// (and hence the function will permit an arbitrary number of them). -/// -/// `Args` optionally accepts a generic type parameter to allow type checks -/// or conversions of each argument. -pub struct Args(Vec); - -impl FromArgs for Args -where - T: TryFromObject, -{ - fn from_args( - vm: &mut VirtualMachine, - args: &mut iter::Peekable, - ) -> Result - where - I: Iterator, - { - let mut varargs = Vec::new(); - while let Some(PyArg::Positional(value)) = args.next() { - varargs.push(T::try_from_object(vm, value)?); - } - Ok(Args(varargs)) - } -} - -impl FromArgs for T -where - T: TryFromObject, -{ - fn arity() -> RangeInclusive { - 1..=1 - } - - fn from_args( - vm: &mut VirtualMachine, - args: &mut iter::Peekable, - ) -> Result - where - I: Iterator, - { - if let Some(PyArg::Positional(value)) = args.next() { - Ok(T::try_from_object(vm, value)?) - } else { - Err(ArgumentError::TooFewArgs) - } - } -} - -/// An argument that may or may not be provided by the caller. -/// -/// This style of argument is not possible in pure Python. -pub enum OptionalArg { - Present(T), - Missing, -} - -use self::OptionalArg::*; -use crate::obj::objproperty::PropertyBuilder; - -impl OptionalArg { - pub fn into_option(self) -> Option { - match self { - Present(value) => Some(value), - Missing => None, - } - } -} - -impl FromArgs for OptionalArg -where - T: TryFromObject, -{ - fn arity() -> RangeInclusive { - 0..=1 - } - - fn from_args( - vm: &mut VirtualMachine, - args: &mut iter::Peekable, - ) -> Result - where - I: Iterator, - { - Ok(if let Some(PyArg::Positional(_)) = args.peek() { - let value = if let Some(PyArg::Positional(value)) = args.next() { - value - } else { - unreachable!() - }; - Present(T::try_from_object(vm, value)?) - } else { - Missing - }) - } -} - -pub enum PyArg { - Positional(PyObjectRef), - Keyword(String, PyObjectRef), -} - -pub enum ArgumentError { - TooFewArgs, - Exception(PyObjectRef), -} - -impl From for ArgumentError { - fn from(ex: PyObjectRef) -> Self { - ArgumentError::Exception(ex) - } -} - /// Implemented by any type that can be created from a Python object. /// /// Any type that implements `TryFromObject` is automatically `FromArgs`, and @@ -1413,132 +1109,6 @@ where } } -// For functions that accept no arguments. Implemented explicitly instead of via -// macro below to avoid unused warnings. -impl FromArgs for () { - fn from_args( - _vm: &mut VirtualMachine, - _args: &mut iter::Peekable, - ) -> Result - where - I: Iterator, - { - Ok(()) - } -} - -// A tuple of types that each implement `FromArgs` represents a sequence of -// arguments that can be bound and passed to a built-in function. -// -// Technically, a tuple can contain tuples, which can contain tuples, and so on, -// so this actually represents a tree of values to be bound from arguments, but -// in practice this is only used for the top-level parameters. -macro_rules! tuple_from_py_func_args { - ($($T:ident),+) => { - impl<$($T),+> FromArgs for ($($T,)+) - where - $($T: FromArgs),+ - { - fn arity() -> RangeInclusive { - let mut min = 0; - let mut max = 0; - $( - let (start, end) = $T::arity().into_inner(); - min += start; - max += end; - )+ - min..=max - } - - fn from_args( - vm: &mut VirtualMachine, - args: &mut iter::Peekable - ) -> Result - where - I: Iterator - { - Ok(($($T::from_args(vm, args)?,)+)) - } - } - }; -} - -// Implement `FromArgs` for up to 5-tuples, allowing built-in functions to bind -// up to 5 top-level parameters (note that `Args`, `KwArgs`, nested tuples, etc. -// count as 1, so this should actually be more than enough). -tuple_from_py_func_args!(A); -tuple_from_py_func_args!(A, B); -tuple_from_py_func_args!(A, B, C); -tuple_from_py_func_args!(A, B, C, D); -tuple_from_py_func_args!(A, B, C, D, E); - -/// A built-in Python function. -pub type PyNativeFunc = Box PyResult + 'static>; - -/// Implemented by types that are or can generate built-in functions. -/// -/// For example, any function that: -/// -/// - Accepts a sequence of types that implement `FromArgs`, followed by a -/// `&mut VirtualMachine` -/// - Returns some type that implements `IntoPyObject` -/// -/// will generate a `PyNativeFunc` that performs the appropriate type and arity -/// checking, any requested conversions, and then if successful call the function -/// with the bound values. -/// -/// A bare `PyNativeFunc` also implements this trait, allowing the above to be -/// done manually, for rare situations that don't fit into this model. -pub trait IntoPyNativeFunc { - fn into_func(self) -> PyNativeFunc; -} - -impl IntoPyNativeFunc for F -where - F: Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult + 'static, -{ - fn into_func(self) -> PyNativeFunc { - Box::new(self) - } -} - -impl IntoPyNativeFunc for PyNativeFunc { - fn into_func(self) -> PyNativeFunc { - self - } -} - -// This is the "magic" that allows rust functions of varying signatures to -// generate native python functions. -// -// Note that this could be done without a macro - it is simply to avoid repetition. -macro_rules! into_py_native_func_tuple { - ($(($n:tt, $T:ident)),*) => { - impl IntoPyNativeFunc<($($T,)*), R> for F - where - F: Fn($($T,)* &mut VirtualMachine) -> R + 'static, - $($T: FromArgs,)* - ($($T,)*): FromArgs, - R: IntoPyObject, - { - fn into_func(self) -> PyNativeFunc { - Box::new(move |vm, args| { - let ($($n,)*) = args.bind::<($($T,)*)>(vm)?; - - (self)($($n,)* vm).into_pyobject(vm) - }) - } - } - }; -} - -into_py_native_func_tuple!(); -into_py_native_func_tuple!((a, A)); -into_py_native_func_tuple!((a, A), (b, B)); -into_py_native_func_tuple!((a, A), (b, B), (c, C)); -into_py_native_func_tuple!((a, A), (b, B), (c, C), (d, D)); -into_py_native_func_tuple!((a, A), (b, B), (c, C), (d, D), (e, E)); - // TODO: This is a workaround and shouldn't exist. // Each iterable type should have its own distinct iterator type. #[derive(Debug)] diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index 3875157b8d..c3c7c3903a 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -3,14 +3,16 @@ //! This module makes use of the parser logic, and translates all ast nodes //! into python ast.AST objects. -extern crate rustpython_parser; +use std::ops::Deref; -use self::rustpython_parser::{ast, parser}; -use crate::obj::objstr; -use crate::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; -use crate::VirtualMachine; use num_complex::Complex64; -use std::ops::Deref; + +use rustpython_parser::{ast, parser}; + +use crate::function::PyFuncArgs; +use crate::obj::objstr; +use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; +use crate::vm::VirtualMachine; /* * Idea: maybe we can create a sort of struct with some helper functions? diff --git a/vm/src/stdlib/dis.rs b/vm/src/stdlib/dis.rs index 96d727a3f0..ccc8930ba0 100644 --- a/vm/src/stdlib/dis.rs +++ b/vm/src/stdlib/dis.rs @@ -1,5 +1,6 @@ +use crate::function::PyFuncArgs; use crate::obj::objcode; -use crate::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; +use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; fn dis_dis(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 02171df344..f9231ee230 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -2,29 +2,25 @@ * I/O core tools. */ -//library imports use std::collections::HashSet; use std::fs::File; use std::io::prelude::*; use std::io::BufReader; use std::path::PathBuf; -//3rd party imports use num_bigint::ToBigInt; use num_traits::ToPrimitive; -//custom imports use super::os; +use crate::function::PyFuncArgs; +use crate::import; use crate::obj::objbytearray::PyByteArray; use crate::obj::objbytes; use crate::obj::objint; use crate::obj::objstr; - use crate::pyobject::{ - AttributeProtocol, BufferProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol, + AttributeProtocol, BufferProtocol, PyContext, PyObjectRef, PyResult, TypeProtocol, }; - -use crate::import; use crate::vm::VirtualMachine; fn compute_c_flag(mode: &str) -> u16 { diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index 7d900de52f..84b82389ed 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -5,14 +5,14 @@ use serde::de::{DeserializeSeed, Visitor}; use serde::ser::{SerializeMap, SerializeSeq}; use serde_json; +use crate::function::PyFuncArgs; use crate::obj::{ objbool, objdict, objfloat, objint, objsequence, objstr::{self, PyString}, objtype, }; use crate::pyobject::{ - create_type, DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, - TypeProtocol, + create_type, DictProtocol, IdProtocol, PyContext, PyObjectRef, PyResult, TypeProtocol, }; use crate::VirtualMachine; use num_traits::cast::ToPrimitive; diff --git a/vm/src/stdlib/keyword.rs b/vm/src/stdlib/keyword.rs index 362d68ece3..ff192cffb6 100644 --- a/vm/src/stdlib/keyword.rs +++ b/vm/src/stdlib/keyword.rs @@ -2,11 +2,12 @@ * Testing if a string is a keyword. */ -extern crate rustpython_parser; -use self::rustpython_parser::lexer; +use rustpython_parser::lexer; + +use crate::function::PyFuncArgs; use crate::obj::objstr; -use crate::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; -use crate::VirtualMachine; +use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; +use crate::vm::VirtualMachine; fn keyword_iskeyword(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); diff --git a/vm/src/stdlib/math.rs b/vm/src/stdlib/math.rs index 19e5899a2a..a8f035625b 100644 --- a/vm/src/stdlib/math.rs +++ b/vm/src/stdlib/math.rs @@ -3,12 +3,13 @@ * */ -use crate::obj::objfloat; -use crate::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; -use crate::VirtualMachine; use statrs::function::erf::{erf, erfc}; use statrs::function::gamma::{gamma, ln_gamma}; -use std; + +use crate::function::PyFuncArgs; +use crate::obj::objfloat; +use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; +use crate::vm::VirtualMachine; // Helper macro: macro_rules! make_math_func { diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 7745ca2053..0bb616ff01 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -1,18 +1,13 @@ -//library imports use std::fs::File; use std::fs::OpenOptions; use std::io::ErrorKind; -// use std::env; -//3rd party imports use num_traits::cast::ToPrimitive; -//custom imports +use crate::function::PyFuncArgs; use crate::obj::objint; use crate::obj::objstr; -// use crate::obj::objdict; - -use crate::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; +use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; #[cfg(unix)] diff --git a/vm/src/stdlib/platform.rs b/vm/src/stdlib/platform.rs index dd0bc79bcb..f22b8be0e2 100644 --- a/vm/src/stdlib/platform.rs +++ b/vm/src/stdlib/platform.rs @@ -1,7 +1,6 @@ -extern crate rustc_version_runtime; - -use crate::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult}; -use crate::VirtualMachine; +use crate::function::PyFuncArgs; +use crate::pyobject::{PyContext, PyObjectRef, PyResult}; +use crate::vm::VirtualMachine; pub fn make_module(ctx: &PyContext) -> PyObjectRef { py_module!(ctx, "platform", { diff --git a/vm/src/stdlib/pystruct.rs b/vm/src/stdlib/pystruct.rs index 8052492b2b..5da654b691 100644 --- a/vm/src/stdlib/pystruct.rs +++ b/vm/src/stdlib/pystruct.rs @@ -7,15 +7,16 @@ * https://docs.rs/byteorder/1.2.6/byteorder/ */ -extern crate byteorder; -use self::byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; +use std::io::{Cursor, Read, Write}; -use crate::obj::{objbool, objbytes, objfloat, objint, objstr, objtype}; -use crate::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; -use crate::VirtualMachine; +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use num_bigint::BigInt; use num_traits::ToPrimitive; -use std::io::{Cursor, Read, Write}; + +use crate::function::PyFuncArgs; +use crate::obj::{objbool, objbytes, objfloat, objint, objstr, objtype}; +use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; +use crate::VirtualMachine; #[derive(Debug)] struct FormatCode { diff --git a/vm/src/stdlib/random.rs b/vm/src/stdlib/random.rs index 31b0162837..2ad1e51c0b 100644 --- a/vm/src/stdlib/random.rs +++ b/vm/src/stdlib/random.rs @@ -1,11 +1,11 @@ //! Random module. -extern crate rand; +use rand::distributions::{Distribution, Normal}; +use crate::function::PyFuncArgs; use crate::obj::objfloat; -use crate::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; -use crate::stdlib::random::rand::distributions::{Distribution, Normal}; -use crate::VirtualMachine; +use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; +use crate::vm::VirtualMachine; pub fn make_module(ctx: &PyContext) -> PyObjectRef { py_module!(ctx, "random", { diff --git a/vm/src/stdlib/re.rs b/vm/src/stdlib/re.rs index 5b992b877c..afbb40dc77 100644 --- a/vm/src/stdlib/re.rs +++ b/vm/src/stdlib/re.rs @@ -9,13 +9,13 @@ use std::path::PathBuf; use regex::{Match, Regex}; +use crate::function::PyFuncArgs; use crate::import; use crate::obj::objstr; use crate::pyobject::{ - AttributeProtocol, PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, - TypeProtocol, + AttributeProtocol, PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, }; -use crate::VirtualMachine; +use crate::vm::VirtualMachine; impl PyValue for Regex { fn class(vm: &mut VirtualMachine) -> PyObjectRef { diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index 91097217e4..f09a52f552 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -5,13 +5,12 @@ use std::io::Write; use std::net::{SocketAddr, TcpListener, TcpStream, ToSocketAddrs, UdpSocket}; use std::ops::Deref; +use crate::function::PyFuncArgs; use crate::obj::objbytes; use crate::obj::objint; use crate::obj::objsequence::get_elements; use crate::obj::objstr; -use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, -}; +use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; use num_traits::ToPrimitive; diff --git a/vm/src/stdlib/time_module.rs b/vm/src/stdlib/time_module.rs index 05c9b640c9..c43ae037ef 100644 --- a/vm/src/stdlib/time_module.rs +++ b/vm/src/stdlib/time_module.rs @@ -1,11 +1,13 @@ //! The python `time` module. -use crate::obj::objfloat; -use crate::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; -use crate::VirtualMachine; use std::thread; use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use crate::function::PyFuncArgs; +use crate::obj::objfloat; +use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; +use crate::vm::VirtualMachine; + fn time_sleep(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(seconds, Some(vm.ctx.float_type()))]); let seconds = objfloat::get_value(seconds); diff --git a/vm/src/stdlib/tokenize.rs b/vm/src/stdlib/tokenize.rs index 1da20655df..1027e1f030 100644 --- a/vm/src/stdlib/tokenize.rs +++ b/vm/src/stdlib/tokenize.rs @@ -2,14 +2,14 @@ * python tokenize module. */ -extern crate rustpython_parser; use std::iter::FromIterator; -use self::rustpython_parser::lexer; +use rustpython_parser::lexer; +use crate::function::PyFuncArgs; use crate::obj::objstr; -use crate::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; -use crate::VirtualMachine; +use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; +use crate::vm::VirtualMachine; fn tokenize_tokenize(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(readline, Some(vm.ctx.str_type()))]); diff --git a/vm/src/stdlib/types.rs b/vm/src/stdlib/types.rs index 0a340188f3..208dc1bc26 100644 --- a/vm/src/stdlib/types.rs +++ b/vm/src/stdlib/types.rs @@ -2,8 +2,9 @@ * Dynamic type creation and names for built in types. */ +use crate::function::PyFuncArgs; use crate::obj::objtype; -use crate::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; +use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; use crate::VirtualMachine; fn types_new_class(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index cc82792ced..e2713d7834 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -1,10 +1,13 @@ -use crate::obj::objint; -use crate::pyobject::{DictProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; -use crate::vm::VirtualMachine; -use num_traits::ToPrimitive; use std::rc::Rc; use std::{env, mem}; +use num_traits::ToPrimitive; + +use crate::function::PyFuncArgs; +use crate::obj::objint; +use crate::pyobject::{DictProtocol, PyContext, PyObjectRef, PyResult, TypeProtocol}; +use crate::vm::VirtualMachine; + /* * The magic sys module. */ diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 26e6e57bae..f10e193be1 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -14,6 +14,7 @@ use std::sync::{Mutex, MutexGuard}; use crate::builtins; use crate::bytecode; use crate::frame::{ExecutionResult, Frame, Scope}; +use crate::function::PyFuncArgs; use crate::obj::objbool; use crate::obj::objbuiltinfunc::PyBuiltinFunction; use crate::obj::objcode; @@ -27,8 +28,7 @@ use crate::obj::objstr; use crate::obj::objtuple::PyTuple; use crate::obj::objtype; use crate::pyobject::{ - AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, - TypeProtocol, + AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyObjectRef, PyResult, TypeProtocol, }; use crate::stdlib; use crate::sysmodule; diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index 11ea35ea21..3130289d7c 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -1,16 +1,21 @@ -use crate::{convert, vm_class::AccessibleVM, wasm_builtins::window}; +use std::path::PathBuf; + use futures::Future; use js_sys::Promise; use num_traits::cast::ToPrimitive; +use wasm_bindgen::prelude::*; +use wasm_bindgen::JsCast; +use wasm_bindgen_futures::{future_to_promise, JsFuture}; + +use rustpython_vm::function::PyFuncArgs; +use rustpython_vm::import::import_module; use rustpython_vm::obj::{objint, objstr}; use rustpython_vm::pyobject::{ - AttributeProtocol, PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, - TypeProtocol, + AttributeProtocol, PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, }; -use rustpython_vm::{import::import_module, VirtualMachine}; -use std::path::PathBuf; -use wasm_bindgen::{prelude::*, JsCast}; -use wasm_bindgen_futures::{future_to_promise, JsFuture}; +use rustpython_vm::VirtualMachine; + +use crate::{convert, vm_class::AccessibleVM, wasm_builtins::window}; enum FetchResponseFormat { Json, diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index a76f802447..27383bea98 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -1,13 +1,14 @@ -use crate::browser_module; -use crate::vm_class::{AccessibleVM, WASMVirtualMachine}; use js_sys::{Array, ArrayBuffer, Object, Promise, Reflect, Uint8Array}; use num_traits::cast::ToPrimitive; +use wasm_bindgen::{closure::Closure, prelude::*, JsCast}; + +use rustpython_vm::function::PyFuncArgs; use rustpython_vm::obj::{objbytes, objint, objsequence, objtype}; -use rustpython_vm::pyobject::{ - self, AttributeProtocol, DictProtocol, PyFuncArgs, PyObjectRef, PyResult, -}; +use rustpython_vm::pyobject::{AttributeProtocol, DictProtocol, PyObjectRef, PyResult}; use rustpython_vm::VirtualMachine; -use wasm_bindgen::{closure::Closure, prelude::*, JsCast}; + +use crate::browser_module; +use crate::vm_class::{AccessibleVM, WASMVirtualMachine}; pub fn py_err_to_js_err(vm: &mut VirtualMachine, py_err: &PyObjectRef) -> JsValue { macro_rules! map_exceptions { @@ -83,7 +84,7 @@ pub fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { let vm = &mut acc_vm .upgrade() .expect("acc. VM to be invalid when WASM vm is valid"); - let mut py_func_args = rustpython_vm::pyobject::PyFuncArgs::default(); + let mut py_func_args = PyFuncArgs::default(); if let Some(ref args) = args { for arg in args.values() { py_func_args.args.push(js_to_py(vm, arg?)); @@ -132,7 +133,7 @@ pub fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { .expect("Couldn't get json module") .get_attr("dumps".into()) .expect("Couldn't get json dumps"); - match vm.invoke(dumps, pyobject::PyFuncArgs::new(vec![py_obj], vec![])) { + match vm.invoke(dumps, PyFuncArgs::new(vec![py_obj], vec![])) { Ok(value) => { let json = vm.to_pystr(&value).unwrap(); js_sys::JSON::parse(&json).unwrap_or(JsValue::UNDEFINED) @@ -239,7 +240,7 @@ pub fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef { }; let py_json = vm.new_str(json); - vm.invoke(loads, pyobject::PyFuncArgs::new(vec![py_json], vec![])) + vm.invoke(loads, PyFuncArgs::new(vec![py_json], vec![])) // can safely unwrap because we know it's valid JSON .unwrap() } diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index b3ef506272..d3bfa8307c 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -1,18 +1,20 @@ -use crate::browser_module::setup_browser_module; -use crate::convert; -use crate::wasm_builtins; -use js_sys::{Object, Reflect, SyntaxError, TypeError}; -use rustpython_vm::{ - compile, - frame::{NameProtocol, Scope}, - pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult}, - VirtualMachine, -}; use std::cell::RefCell; use std::collections::HashMap; use std::rc::{Rc, Weak}; + +use js_sys::{Object, Reflect, SyntaxError, TypeError}; use wasm_bindgen::{prelude::*, JsCast}; +use rustpython_vm::compile; +use rustpython_vm::frame::{NameProtocol, Scope}; +use rustpython_vm::function::PyFuncArgs; +use rustpython_vm::pyobject::{PyContext, PyObjectRef, PyResult}; +use rustpython_vm::VirtualMachine; + +use crate::browser_module::setup_browser_module; +use crate::convert; +use crate::wasm_builtins; + pub trait HeldRcInner {} impl HeldRcInner for T {} diff --git a/wasm/lib/src/wasm_builtins.rs b/wasm/lib/src/wasm_builtins.rs index 8bb6a2db2b..cc3435bbaa 100644 --- a/wasm/lib/src/wasm_builtins.rs +++ b/wasm/lib/src/wasm_builtins.rs @@ -5,10 +5,12 @@ //! Implements functions listed here: https://docs.python.org/3/library/builtins.html. use js_sys::{self, Array}; +use web_sys::{self, console}; + +use rustpython_vm::function::PyFuncArgs; use rustpython_vm::obj::{objstr, objtype}; -use rustpython_vm::pyobject::{IdProtocol, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; +use rustpython_vm::pyobject::{IdProtocol, PyObjectRef, PyResult, TypeProtocol}; use rustpython_vm::VirtualMachine; -use web_sys::{self, console}; pub(crate) fn window() -> web_sys::Window { web_sys::window().expect("Window to be available") From 144a8ae1799714a38088ce997f3cedafc5bffdef Mon Sep 17 00:00:00 2001 From: Chylli Date: Sat, 16 Mar 2019 13:33:14 +0800 Subject: [PATCH 316/380] more test --- tests/snippets/builtin_any.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/snippets/builtin_any.py b/tests/snippets/builtin_any.py index 0600145fd6..0091ae50d4 100644 --- a/tests/snippets/builtin_any.py +++ b/tests/snippets/builtin_any.py @@ -1,3 +1,6 @@ +assert any([1]); +assert not any([]); + def anything(a): return a @@ -6,4 +9,4 @@ def __iter__(self): while True: yield True -assert True == any(map(anything, Test())) +assert any(map(anything, Test())) From 40a4e373af4197a861c561b1154baa9a9d5fe0d8 Mon Sep 17 00:00:00 2001 From: Chylli Date: Sat, 16 Mar 2019 13:52:57 +0800 Subject: [PATCH 317/380] try --- vm/src/builtins.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 86f6e84722..bd65a9950c 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -64,13 +64,19 @@ fn builtin_all(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn builtin_any(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(iterable, None)]); - let items = vm.extract_elements(iterable)?; - for item in items { + let iterator = objiter::get_iter(vm, iterable)?; + + loop { + let item = match objiter::get_next_object(vm, &iterator)? { + Some(obj) => obj, + None => break, + }; let result = objbool::boolval(vm, item)?; if result { return Ok(vm.new_bool(true)); } } + Ok(vm.new_bool(false)) } From 6923490afc316e05a2be84000f83ea48b0fcb7ac Mon Sep 17 00:00:00 2001 From: Chylli Date: Sat, 16 Mar 2019 13:55:29 +0800 Subject: [PATCH 318/380] more test --- tests/snippets/builtin_any.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/snippets/builtin_any.py b/tests/snippets/builtin_any.py index 0091ae50d4..a8679d5fe3 100644 --- a/tests/snippets/builtin_any.py +++ b/tests/snippets/builtin_any.py @@ -1,6 +1,7 @@ assert any([1]); assert not any([]); - +assert not any([0,0,0,0]); +assert any([0,0,1,0,0]); def anything(a): return a From 1f1b48c612c0e1ae76228dc83703da2767af7363 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Sat, 16 Mar 2019 09:21:12 +0000 Subject: [PATCH 319/380] into_ref now needs the vm rather than a context. --- vm/src/frame.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 3eb9a0203b..28ca2b33b7 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -409,7 +409,7 @@ impl Frame { let stop = out[1].take(); let step = if out.len() == 3 { out[2].take() } else { None }; - let obj = PySlice { start, stop, step }.into_ref(&vm.ctx); + let obj = PySlice { start, stop, step }.into_ref(vm); self.push_value(obj.into_object()); Ok(None) } @@ -699,8 +699,8 @@ impl Frame { Ok(None) } bytecode::Instruction::LoadBuildClass => { - let rustfunc = PyBuiltinFunction::new(Box::new(builtins::builtin_build_class_)) - .into_ref(&vm.ctx); + let rustfunc = + PyBuiltinFunction::new(Box::new(builtins::builtin_build_class_)).into_ref(vm); self.push_value(rustfunc.into_object()); Ok(None) } From da6c56a001a83f85a0184d635166638b476bce17 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Fri, 15 Mar 2019 12:27:55 +0000 Subject: [PATCH 320/380] Use extend_class in declaration of dictionary. --- vm/src/obj/objdict.rs | 45 +++++++++++++++---------------------------- 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 3b37a801be..fa136d5556 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -371,34 +371,19 @@ fn dict_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn init(context: &PyContext) { - let dict_type = &context.dict_type; - context.set_attr(&dict_type, "__len__", context.new_rustfunc(dict_len)); - context.set_attr( - &dict_type, - "__contains__", - context.new_rustfunc(dict_contains), - ); - context.set_attr( - &dict_type, - "__delitem__", - context.new_rustfunc(dict_delitem), - ); - context.set_attr( - &dict_type, - "__getitem__", - context.new_rustfunc(dict_getitem), - ); - context.set_attr(&dict_type, "__iter__", context.new_rustfunc(dict_iter)); - context.set_attr(&dict_type, "__new__", context.new_rustfunc(dict_new)); - context.set_attr(&dict_type, "__repr__", context.new_rustfunc(dict_repr)); - context.set_attr( - &dict_type, - "__setitem__", - context.new_rustfunc(dict_setitem), - ); - context.set_attr(&dict_type, "clear", context.new_rustfunc(dict_clear)); - context.set_attr(&dict_type, "values", context.new_rustfunc(dict_values)); - context.set_attr(&dict_type, "items", context.new_rustfunc(dict_items)); - context.set_attr(&dict_type, "keys", context.new_rustfunc(dict_iter)); - context.set_attr(&dict_type, "get", context.new_rustfunc(dict_get)); + extend_class!(context, &context.dict_type, { + "__len__" => context.new_rustfunc(dict_len), + "__contains__" => context.new_rustfunc(dict_contains), + "__delitem__" => context.new_rustfunc(dict_delitem), + "__getitem__" => context.new_rustfunc(dict_getitem), + "__iter__" => context.new_rustfunc(dict_iter), + "__new__" => context.new_rustfunc(dict_new), + "__repr__" => context.new_rustfunc(dict_repr), + "__setitem__" => context.new_rustfunc(dict_setitem), + "clear" => context.new_rustfunc(dict_clear), + "values" => context.new_rustfunc(dict_values), + "items" => context.new_rustfunc(dict_items), + "keys" => context.new_rustfunc(dict_iter), + "get" => context.new_rustfunc(dict_get), + }); } From a718a5a72303b7b42f9d8caeceffda264cc6269d Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Fri, 15 Mar 2019 13:21:35 +0000 Subject: [PATCH 321/380] Refactor dict methods to new style declarations. --- vm/src/obj/objdict.rs | 288 ++++++++++++++++-------------------------- 1 file changed, 109 insertions(+), 179 deletions(-) diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index fa136d5556..63131fef9b 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -4,8 +4,8 @@ use std::fmt; use std::ops::{Deref, DerefMut}; use crate::pyobject::{ - PyAttributes, PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectRef, PyRef, PyResult, - PyValue, TypeProtocol, + OptionalArg, PyAttributes, PyContext, PyFuncArgs, PyIteratorValue, PyObjectRef, PyRef, + PyResult, PyValue, TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; @@ -133,7 +133,6 @@ pub fn attributes_to_py_dict(vm: &mut VirtualMachine, attributes: PyAttributes) } // Python dict methods: - fn dict_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -174,216 +173,147 @@ fn dict_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(dict) } -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())) -} +impl PyDictRef { + fn len(self, _vm: &mut VirtualMachine) -> usize { + self.entries.borrow().len() + } -fn dict_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(dict_obj, Some(vm.ctx.dict_type()))]); - - 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)); - } + fn repr(self, vm: &mut VirtualMachine) -> PyResult { + let s = if let Some(_guard) = ReprGuard::enter(self.as_object()) { + let elements = get_key_value_pairs(self.as_object()); + 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)); + } - format!("{{{}}}", str_parts.join(", ")) - } else { - "{...}".to_string() - }; - Ok(vm.new_str(s)) -} + format!("{{{}}}", str_parts.join(", ")) + } else { + "{...}".to_string() + }; + Ok(vm.new_str(s)) + } -pub fn dict_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (dict, Some(vm.ctx.dict_type())), - (needle, Some(vm.ctx.str_type())) - ] - ); + fn contains(self, needle: PyObjectRef, _vm: &mut VirtualMachine) -> bool { + let needle = objstr::get_value(&needle); + self.entries.borrow().contains_key(&needle) + } - let needle = objstr::get_value(&needle); - for element in get_elements(dict).iter() { - if &needle == element.0 { - return Ok(vm.new_bool(true)); + fn delitem(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult<()> { + let needle = objstr::get_value(&needle); + // Delete the item: + let mut elements = self.entries.borrow_mut(); + match elements.remove(&needle) { + Some(_) => Ok(()), + None => Err(vm.new_value_error(format!("Key not found: {}", needle))), } } - Ok(vm.new_bool(false)) -} - -fn dict_delitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (dict, Some(vm.ctx.dict_type())), - (needle, Some(vm.ctx.str_type())) - ] - ); - - // What we are looking for: - let needle = objstr::get_value(&needle); - - // Delete the item: - let mut elements = get_mut_elements(dict); - match elements.remove(&needle) { - Some(_) => Ok(vm.get_none()), - None => Err(vm.new_value_error(format!("Key not found: {}", needle))), + fn clear(self, _vm: &mut VirtualMachine) { + self.entries.borrow_mut().clear() } -} -fn dict_clear(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(dict, Some(vm.ctx.dict_type()))]); - get_mut_elements(dict).clear(); - Ok(vm.get_none()) -} - -/// When iterating over a dictionary, we iterate over the keys of it. -fn dict_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(dict, Some(vm.ctx.dict_type()))]); + /// When iterating over a dictionary, we iterate over the keys of it. + fn iter(self, vm: &mut VirtualMachine) -> PyIteratorValue { + let keys = self + .entries + .borrow() + .values() + .map(|(k, _v)| k.clone()) + .collect(); + let key_list = vm.ctx.new_list(keys); - let keys = get_elements(dict) - .values() - .map(|(k, _v)| k.clone()) - .collect(); - let key_list = vm.ctx.new_list(keys); - - let iter_obj = PyObject::new( PyIteratorValue { position: Cell::new(0), iterated_obj: key_list, - }, - vm.ctx.iter_type(), - ); - - Ok(iter_obj) -} - -fn dict_values(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(dict, Some(vm.ctx.dict_type()))]); + } + } - let values = get_elements(dict) - .values() - .map(|(_k, v)| v.clone()) - .collect(); - let values_list = vm.ctx.new_list(values); + fn values(self, vm: &mut VirtualMachine) -> PyIteratorValue { + let values = self + .entries + .borrow() + .values() + .map(|(_k, v)| v.clone()) + .collect(); + let values_list = vm.ctx.new_list(values); - let iter_obj = PyObject::new( PyIteratorValue { position: Cell::new(0), iterated_obj: values_list, - }, - vm.ctx.iter_type(), - ); - - Ok(iter_obj) -} - -fn dict_items(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(dict, Some(vm.ctx.dict_type()))]); + } + } - let items = get_elements(dict) - .values() - .map(|(k, v)| vm.ctx.new_tuple(vec![k.clone(), v.clone()])) - .collect(); - let items_list = vm.ctx.new_list(items); + fn items(self, vm: &mut VirtualMachine) -> PyIteratorValue { + let items = self + .entries + .borrow() + .values() + .map(|(k, v)| vm.ctx.new_tuple(vec![k.clone(), v.clone()])) + .collect(); + let items_list = vm.ctx.new_list(items); - let iter_obj = PyObject::new( PyIteratorValue { position: Cell::new(0), iterated_obj: items_list, - }, - vm.ctx.iter_type(), - ); - - Ok(iter_obj) -} - -fn dict_setitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (dict, Some(vm.ctx.dict_type())), - (needle, Some(vm.ctx.str_type())), - (value, None) - ] - ); - - set_item(dict, vm, needle, value); - - Ok(vm.get_none()) -} + } + } -fn dict_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (dict, Some(vm.ctx.dict_type())), - (needle, Some(vm.ctx.str_type())) - ] - ); + fn setitem(self, needle: PyObjectRef, value: PyObjectRef, _vm: &mut VirtualMachine) { + let mut elements = self.entries.borrow_mut(); + set_item_in_content(&mut elements, &needle, &value) + } - // What we are looking for: - let needle = objstr::get_value(&needle); + fn getitem(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + // What we are looking for: + let needle = objstr::get_value(&needle); - let elements = get_elements(dict); - if elements.contains_key(&needle) { - Ok(elements[&needle].1.clone()) - } else { - Err(vm.new_value_error(format!("Key not found: {}", needle))) + let elements = self.entries.borrow(); + if elements.contains_key(&needle) { + Ok(elements[&needle].1.clone()) + } else { + Err(vm.new_value_error(format!("Key not found: {}", needle))) + } } -} -fn dict_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (dict, Some(vm.ctx.dict_type())), - (key, Some(vm.ctx.str_type())) - ], - optional = [(default, None)] - ); - - // What we are looking for: - let key = objstr::get_value(&key); + fn get( + self, + key: PyObjectRef, + default: OptionalArg, + vm: &mut VirtualMachine, + ) -> PyObjectRef { + // What we are looking for: + let key = objstr::get_value(&key); - let elements = get_elements(dict); - if elements.contains_key(&key) { - Ok(elements[&key].1.clone()) - } else if let Some(value) = default { - Ok(value.clone()) - } else { - Ok(vm.get_none()) + let elements = self.entries.borrow(); + if elements.contains_key(&key) { + elements[&key].1.clone() + } else { + match default { + OptionalArg::Present(value) => value, + OptionalArg::Missing => vm.ctx.none(), + } + } } } pub fn init(context: &PyContext) { extend_class!(context, &context.dict_type, { - "__len__" => context.new_rustfunc(dict_len), - "__contains__" => context.new_rustfunc(dict_contains), - "__delitem__" => context.new_rustfunc(dict_delitem), - "__getitem__" => context.new_rustfunc(dict_getitem), - "__iter__" => context.new_rustfunc(dict_iter), + "__len__" => context.new_rustfunc(PyDictRef::len), + "__contains__" => context.new_rustfunc(PyDictRef::contains), + "__delitem__" => context.new_rustfunc(PyDictRef::delitem), + "__getitem__" => context.new_rustfunc(PyDictRef::getitem), + "__iter__" => context.new_rustfunc(PyDictRef::iter), "__new__" => context.new_rustfunc(dict_new), - "__repr__" => context.new_rustfunc(dict_repr), - "__setitem__" => context.new_rustfunc(dict_setitem), - "clear" => context.new_rustfunc(dict_clear), - "values" => context.new_rustfunc(dict_values), - "items" => context.new_rustfunc(dict_items), - "keys" => context.new_rustfunc(dict_iter), - "get" => context.new_rustfunc(dict_get), + "__repr__" => context.new_rustfunc(PyDictRef::repr), + "__setitem__" => context.new_rustfunc(PyDictRef::setitem), + "clear" => context.new_rustfunc(PyDictRef::clear), + "values" => context.new_rustfunc(PyDictRef::values), + "items" => context.new_rustfunc(PyDictRef::items), + "keys" => context.new_rustfunc(PyDictRef::iter), + "get" => context.new_rustfunc(PyDictRef::get), }); } From 2f5c3ef1dfabdb9970f358914038ca178c0b43b7 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Fri, 15 Mar 2019 14:01:36 +0000 Subject: [PATCH 322/380] Use PyStringRef to check type of dictionary keys. --- vm/src/obj/objdict.rs | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 63131fef9b..17b1674c03 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -10,7 +10,7 @@ use crate::pyobject::{ use crate::vm::{ReprGuard, VirtualMachine}; use super::objiter; -use super::objstr; +use super::objstr::{self, PyStringRef}; use super::objtype; pub type DictContentType = HashMap; @@ -197,18 +197,17 @@ impl PyDictRef { Ok(vm.new_str(s)) } - fn contains(self, needle: PyObjectRef, _vm: &mut VirtualMachine) -> bool { - let needle = objstr::get_value(&needle); - self.entries.borrow().contains_key(&needle) + fn contains(self, key: PyStringRef, _vm: &mut VirtualMachine) -> bool { + self.entries.borrow().contains_key(&key.value) } - fn delitem(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult<()> { - let needle = objstr::get_value(&needle); + fn delitem(self, key: PyStringRef, vm: &mut VirtualMachine) -> PyResult<()> { + let ref key = key.value; // Delete the item: let mut elements = self.entries.borrow_mut(); - match elements.remove(&needle) { + match elements.remove(key) { Some(_) => Ok(()), - None => Err(vm.new_value_error(format!("Key not found: {}", needle))), + None => Err(vm.new_value_error(format!("Key not found: {}", key))), } } @@ -267,30 +266,30 @@ impl PyDictRef { set_item_in_content(&mut elements, &needle, &value) } - fn getitem(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { - // What we are looking for: - let needle = objstr::get_value(&needle); + fn getitem(self, key: PyStringRef, vm: &mut VirtualMachine) -> PyResult { + let ref key = key.value; + // What we are looking for: let elements = self.entries.borrow(); - if elements.contains_key(&needle) { - Ok(elements[&needle].1.clone()) + if elements.contains_key(key) { + Ok(elements[key].1.clone()) } else { - Err(vm.new_value_error(format!("Key not found: {}", needle))) + Err(vm.new_value_error(format!("Key not found: {}", key))) } } fn get( self, - key: PyObjectRef, + key: PyStringRef, default: OptionalArg, vm: &mut VirtualMachine, ) -> PyObjectRef { // What we are looking for: - let key = objstr::get_value(&key); + let ref key = key.value; let elements = self.entries.borrow(); - if elements.contains_key(&key) { - elements[&key].1.clone() + if elements.contains_key(key) { + elements[key].1.clone() } else { match default { OptionalArg::Present(value) => value, From 9eeeeeae48b3b083c99846ef1c35953cbaeb4419 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Fri, 15 Mar 2019 16:53:30 +0000 Subject: [PATCH 323/380] Change return type of vm.to_repr and vm.to_str to PyStringRef. --- vm/src/builtins.rs | 10 ++++------ vm/src/exceptions.rs | 9 ++++----- vm/src/frame.rs | 6 +++--- vm/src/obj/objdict.rs | 4 +--- vm/src/obj/objlist.rs | 7 +++---- vm/src/obj/objobject.rs | 20 ++++++++------------ vm/src/obj/objset.rs | 5 ++--- vm/src/obj/objstr.rs | 2 +- vm/src/obj/objtuple.rs | 3 +-- vm/src/vm.rs | 16 +++++++++------- wasm/lib/src/browser_module.rs | 6 +++--- 11 files changed, 39 insertions(+), 49 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 86f6e84722..d3084e57a4 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -13,7 +13,7 @@ use crate::obj::objbool; use crate::obj::objdict; use crate::obj::objint; use crate::obj::objiter; -use crate::obj::objstr; +use crate::obj::objstr::{self, PyStringRef}; use crate::obj::objtype; use crate::frame::Scope; @@ -625,8 +625,7 @@ pub fn builtin_print(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } else { write!(stdout_lock, " ").unwrap(); } - let v = vm.to_str(&a)?; - let s = objstr::borrow_value(&v); + let ref s = vm.to_str(&a)?.value; write!(stdout_lock, "{}", s).unwrap(); } @@ -643,9 +642,8 @@ pub fn builtin_print(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.get_none()) } -fn builtin_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(obj, None)]); - vm.to_repr(obj) +fn builtin_repr(obj: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + vm.to_repr(&obj) } fn builtin_reversed(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 94853d5d06..0f3553535b 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -1,5 +1,4 @@ use crate::obj::objsequence; -use crate::obj::objstr; use crate::obj::objtype; use crate::pyobject::{ create_type, AttributeProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol, @@ -30,19 +29,19 @@ pub fn print_exception(vm: &mut VirtualMachine, exc: &PyObjectRef) { if objtype::isinstance(&element, &vm.ctx.tuple_type()) { let element = objsequence::get_elements(&element); let filename = if let Ok(x) = vm.to_str(&element[0]) { - objstr::get_value(&x) + x.value.clone() } else { "".to_string() }; let lineno = if let Ok(x) = vm.to_str(&element[1]) { - objstr::get_value(&x) + x.value.clone() } else { "".to_string() }; let obj_name = if let Ok(x) = vm.to_str(&element[2]) { - objstr::get_value(&x) + x.value.clone() } else { "".to_string() }; @@ -58,7 +57,7 @@ pub fn print_exception(vm: &mut VirtualMachine, exc: &PyObjectRef) { } match vm.to_str(exc) { - Ok(txt) => println!("{}", objstr::get_value(&txt)), + Ok(txt) => println!("{}", txt.value), Err(err) => println!("Error during error {:?}", err), } } diff --git a/vm/src/frame.rs b/vm/src/frame.rs index d1cadb001b..3fedd8d3c8 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -693,7 +693,7 @@ impl Frame { let repr = vm.to_repr(&expr)?; // TODO: implement sys.displayhook if let Some(print) = vm.ctx.get_attr(&vm.builtins, "print") { - vm.invoke(print, vec![repr])?; + vm.invoke(print, vec![repr.into_object()])?; } } Ok(None) @@ -764,8 +764,8 @@ impl Frame { bytecode::Instruction::FormatValue { conversion, spec } => { use ast::ConversionFlag::*; let value = match conversion { - Some(Str) => vm.to_str(&self.pop_value())?, - Some(Repr) => vm.to_repr(&self.pop_value())?, + Some(Str) => vm.to_str(&self.pop_value())?.into_object(), + Some(Repr) => vm.to_repr(&self.pop_value())?.into_object(), Some(Ascii) => self.pop_value(), // TODO None => self.pop_value(), }; diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 17b1674c03..5503ceb56d 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -185,9 +185,7 @@ impl PyDictRef { 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)); + str_parts.push(format!("{}: {}", key_repr.value, value_repr.value)); } format!("{{{}}}", str_parts.join(", ")) diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index f0baedb1b6..8bf8337c56 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -6,7 +6,6 @@ use super::objsequence::{ get_elements, get_elements_cell, get_item, seq_equal, seq_ge, seq_gt, seq_le, seq_lt, seq_mul, PySliceableSequence, }; -use super::objstr; use super::objtype; use crate::pyobject::{ IdProtocol, OptionalArg, PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectRef, PyRef, @@ -151,7 +150,7 @@ impl PyListRef { let mut str_parts = vec![]; for elem in self.elements.borrow().iter() { let s = vm.to_repr(elem)?; - str_parts.push(objstr::get_value(&s)); + str_parts.push(s.value.clone()); } format!("[{}]", str_parts.join(", ")) } else { @@ -204,7 +203,7 @@ impl PyListRef { return Ok(index); } } - let needle_str = objstr::get_value(&vm.to_str(&needle).unwrap()); + let ref needle_str = vm.to_str(&needle)?.value; Err(vm.new_value_error(format!("'{}' is not in list", needle_str))) } @@ -241,7 +240,7 @@ impl PyListRef { self.elements.borrow_mut().remove(index); Ok(()) } else { - let needle_str = objstr::get_value(&vm.to_str(&needle)?); + let ref needle_str = vm.to_str(&needle)?.value; Err(vm.new_value_error(format!("'{}' is not in list", needle_str))) } } diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 2423f5e93a..43166ceb10 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -1,5 +1,5 @@ use super::objlist::PyList; -use super::objstr; +use super::objstr::{self, PyStringRef}; use super::objtype; use crate::obj::objproperty::PropertyBuilder; use crate::pyobject::{ @@ -134,17 +134,13 @@ pub fn object_dir(obj: PyObjectRef, vm: &mut VirtualMachine) -> PyList { PyList::from(attributes) } -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) +fn object_format( + obj: PyObjectRef, + format_spec: PyStringRef, + vm: &mut VirtualMachine, +) -> PyResult { + if format_spec.value.is_empty() { + vm.to_str(&obj) } else { Err(vm.new_type_error("unsupported format string passed to object.__format__".to_string())) } diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index f33208915d..721712ad4b 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -10,7 +10,6 @@ use std::hash::{Hash, Hasher}; use super::objbool; use super::objint; use super::objiter; -use super::objstr; use super::objtype; use crate::pyobject::{ PyContext, PyFuncArgs, PyIteratorValue, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, @@ -211,7 +210,7 @@ fn set_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut str_parts = vec![]; for elem in elements.values() { let part = vm.to_repr(elem)?; - str_parts.push(objstr::get_value(&part)); + str_parts.push(part.value.clone()); } format!("{{{}}}", str_parts.join(", ")) @@ -582,7 +581,7 @@ fn frozenset_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut str_parts = vec![]; for elem in elements.values() { let part = vm.to_repr(elem)?; - str_parts.push(objstr::get_value(&part)); + str_parts.push(part.value.clone()); } format!("frozenset({{{}}})", str_parts.join(", ")) diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 0a969b6861..8345137f04 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -794,7 +794,7 @@ fn str_new( vm: &mut VirtualMachine, ) -> PyResult { let string = match object { - OptionalArg::Present(ref input) => vm.to_str(input)?, + OptionalArg::Present(ref input) => vm.to_str(input)?.into_object(), OptionalArg::Missing => vm.new_str("".to_string()), }; if string.typ().is(&cls) { diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 1702c2237a..75aca61861 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -13,7 +13,6 @@ use super::objint; 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; #[derive(Default)] @@ -152,7 +151,7 @@ impl PyTupleRef { let mut str_parts = vec![]; for elem in self.elements.borrow().iter() { let s = vm.to_repr(elem)?; - str_parts.push(objstr::get_value(&s)); + str_parts.push(s.value.clone()); } if str_parts.len() == 1 { diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 45b5d7d3d6..9eaf1fc3ec 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -23,12 +23,12 @@ use crate::obj::objgenerator; use crate::obj::objiter; use crate::obj::objlist::PyList; use crate::obj::objsequence; -use crate::obj::objstr; +use crate::obj::objstr::PyStringRef; use crate::obj::objtuple::PyTuple; use crate::obj::objtype; use crate::pyobject::{ AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, - TypeProtocol, + TryFromObject, TypeProtocol, }; use crate::stdlib; use crate::sysmodule; @@ -228,17 +228,19 @@ impl VirtualMachine { } // Container of the virtual machine state: - pub fn to_str(&mut self, obj: &PyObjectRef) -> PyResult { - self.call_method(&obj, "__str__", vec![]) + pub fn to_str(&mut self, obj: &PyObjectRef) -> PyResult { + let str = self.call_method(&obj, "__str__", vec![])?; + TryFromObject::try_from_object(self, str) } pub fn to_pystr(&mut self, obj: &PyObjectRef) -> Result { let py_str_obj = self.to_str(obj)?; - Ok(objstr::get_value(&py_str_obj)) + Ok(py_str_obj.value.clone()) } - pub fn to_repr(&mut self, obj: &PyObjectRef) -> PyResult { - self.call_method(obj, "__repr__", vec![]) + pub fn to_repr(&mut self, obj: &PyObjectRef) -> PyResult { + let repr = self.call_method(obj, "__repr__", vec![])?; + TryFromObject::try_from_object(self, repr) } pub fn import(&mut self, module: &str) -> PyResult { diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index 11ea35ea21..ed8da51fb0 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -70,9 +70,9 @@ fn browser_fetch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if let Some(headers) = headers { let h = request.headers(); for (key, value) in rustpython_vm::obj::objdict::get_key_value_pairs(&headers) { - let key = objstr::get_value(&vm.to_str(&key)?); - let value = objstr::get_value(&vm.to_str(&value)?); - h.set(&key, &value) + let ref key = vm.to_str(&key)?.value; + let ref value = vm.to_str(&value)?.value; + h.set(key, value) .map_err(|err| convert::js_py_typeerror(vm, err))?; } } From 60ed1bd0a7178ce4a04562206ca87adc62e049cc Mon Sep 17 00:00:00 2001 From: Joey Date: Sat, 16 Mar 2019 10:18:46 -0700 Subject: [PATCH 324/380] Handle unexpected positional argument in KwArgs --- vm/src/function.rs | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/vm/src/function.rs b/vm/src/function.rs index 7b4adebd0c..a24768aeb2 100644 --- a/vm/src/function.rs +++ b/vm/src/function.rs @@ -129,6 +129,13 @@ impl PyFuncArgs { given_args, ))); } + Err(ArgumentError::TooManyArgs) => { + return Err(vm.new_type_error(format!( + "Expected at most {} arguments ({} given)", + T::arity().end(), + given_args, + ))); + } Err(ArgumentError::Exception(ex)) => { return Err(ex); } @@ -153,8 +160,15 @@ pub enum PyArg { Keyword(String, PyObjectRef), } +/// An error encountered while binding arguments to the parameters of a Python +/// function call. pub enum ArgumentError { + /// The call provided fewer positional arguments than the function requires. TooFewArgs, + /// The call provided more positional arguments than the function accepts. + TooManyArgs, + /// An exception was raised while binding arguments to the function + /// parameters. Exception(PyObjectRef), } @@ -205,10 +219,19 @@ where I: Iterator, { let mut kwargs = HashMap::new(); - while let Some(PyArg::Keyword(name, value)) = args.next() { - kwargs.insert(name, T::try_from_object(vm, value)?); + loop { + match args.next() { + Some(PyArg::Keyword(name, value)) => { + kwargs.insert(name, T::try_from_object(vm, value)?); + } + Some(PyArg::Positional(_)) => { + return Err(ArgumentError::TooManyArgs); + } + None => { + return Ok(KwArgs(kwargs)); + } + } } - Ok(KwArgs(kwargs)) } } From 7be5b8dafe142daf97a622c11052d644ea341ad2 Mon Sep 17 00:00:00 2001 From: Joey Date: Sat, 16 Mar 2019 16:07:59 -0700 Subject: [PATCH 325/380] FromArgs: use PyFuncArgs instead of iterator --- vm/src/function.rs | 130 ++++++++++++--------------------------------- 1 file changed, 35 insertions(+), 95 deletions(-) diff --git a/vm/src/function.rs b/vm/src/function.rs index a24768aeb2..717a539593 100644 --- a/vm/src/function.rs +++ b/vm/src/function.rs @@ -1,5 +1,4 @@ use std::collections::HashMap; -use std::iter; use std::ops::RangeInclusive; use crate::obj::objtype; @@ -99,14 +98,16 @@ impl PyFuncArgs { } } - /// Serializes these arguments into an iterator starting with the positional - /// arguments followed by keyword arguments. - fn into_iter(self) -> impl Iterator { - self.args.into_iter().map(PyArg::Positional).chain( - self.kwargs - .into_iter() - .map(|(name, value)| PyArg::Keyword(name, value)), - ) + pub fn next_positional(&mut self) -> Option { + if self.args.is_empty() { + None + } else { + Some(self.args.remove(0)) + } + } + + pub fn remaining_keyword<'a>(&'a mut self) -> impl Iterator + 'a { + self.kwargs.drain(..) } /// Binds these arguments to their respective values. @@ -117,10 +118,9 @@ impl PyFuncArgs { /// /// If the given `FromArgs` includes any conversions, exceptions raised /// during the conversion will halt the binding and return the error. - fn bind(self, vm: &mut VirtualMachine) -> PyResult { + fn bind(mut self, vm: &mut VirtualMachine) -> PyResult { let given_args = self.args.len(); - let mut args = self.into_iter().peekable(); - let bound = match T::from_args(vm, &mut args) { + let bound = match T::from_args(vm, &mut self) { Ok(args) => args, Err(ArgumentError::TooFewArgs) => { return Err(vm.new_type_error(format!( @@ -141,25 +141,20 @@ impl PyFuncArgs { } }; - match args.next() { - None => Ok(bound), - Some(PyArg::Positional(_)) => Err(vm.new_type_error(format!( + if !self.args.is_empty() { + Err(vm.new_type_error(format!( "Expected at most {} arguments ({} given)", T::arity().end(), given_args, - ))), - Some(PyArg::Keyword(name, _)) => { - Err(vm.new_type_error(format!("Unexpected keyword argument {}", name))) - } + ))) + } else if !self.kwargs.is_empty() { + Err(vm.new_type_error(format!("Unexpected keyword argument {}", self.kwargs[0].0))) + } else { + Ok(bound) } } } -pub enum PyArg { - Positional(PyObjectRef), - Keyword(String, PyObjectRef), -} - /// An error encountered while binding arguments to the parameters of a Python /// function call. pub enum ArgumentError { @@ -190,12 +185,7 @@ pub trait FromArgs: Sized { } /// Extracts this item from the next argument(s). - fn from_args( - vm: &mut VirtualMachine, - args: &mut iter::Peekable, - ) -> Result - where - I: Iterator; + fn from_args(vm: &mut VirtualMachine, args: &mut PyFuncArgs) -> Result; } /// A map of keyword arguments to their values. /// @@ -211,27 +201,12 @@ impl FromArgs for KwArgs where T: TryFromObject, { - fn from_args( - vm: &mut VirtualMachine, - args: &mut iter::Peekable, - ) -> Result - where - I: Iterator, - { + fn from_args(vm: &mut VirtualMachine, args: &mut PyFuncArgs) -> Result { let mut kwargs = HashMap::new(); - loop { - match args.next() { - Some(PyArg::Keyword(name, value)) => { - kwargs.insert(name, T::try_from_object(vm, value)?); - } - Some(PyArg::Positional(_)) => { - return Err(ArgumentError::TooManyArgs); - } - None => { - return Ok(KwArgs(kwargs)); - } - } + for (name, value) in args.remaining_keyword() { + kwargs.insert(name, T::try_from_object(vm, value)?); } + Ok(KwArgs(kwargs)) } } @@ -249,15 +224,9 @@ impl FromArgs for Args where T: TryFromObject, { - fn from_args( - vm: &mut VirtualMachine, - args: &mut iter::Peekable, - ) -> Result - where - I: Iterator, - { + fn from_args(vm: &mut VirtualMachine, args: &mut PyFuncArgs) -> Result { let mut varargs = Vec::new(); - while let Some(PyArg::Positional(value)) = args.next() { + while let Some(value) = args.next_positional() { varargs.push(T::try_from_object(vm, value)?); } Ok(Args(varargs)) @@ -272,14 +241,8 @@ where 1..=1 } - fn from_args( - vm: &mut VirtualMachine, - args: &mut iter::Peekable, - ) -> Result - where - I: Iterator, - { - if let Some(PyArg::Positional(value)) = args.next() { + fn from_args(vm: &mut VirtualMachine, args: &mut PyFuncArgs) -> Result { + if let Some(value) = args.next_positional() { Ok(T::try_from_object(vm, value)?) } else { Err(ArgumentError::TooFewArgs) @@ -312,36 +275,19 @@ where 0..=1 } - fn from_args( - vm: &mut VirtualMachine, - args: &mut iter::Peekable, - ) -> Result - where - I: Iterator, - { - Ok(if let Some(PyArg::Positional(_)) = args.peek() { - let value = if let Some(PyArg::Positional(value)) = args.next() { - value - } else { - unreachable!() - }; - Present(T::try_from_object(vm, value)?) + fn from_args(vm: &mut VirtualMachine, args: &mut PyFuncArgs) -> Result { + if let Some(value) = args.next_positional() { + Ok(Present(T::try_from_object(vm, value)?)) } else { - Missing - }) + Ok(Missing) + } } } // For functions that accept no arguments. Implemented explicitly instead of via // macro below to avoid unused warnings. impl FromArgs for () { - fn from_args( - _vm: &mut VirtualMachine, - _args: &mut iter::Peekable, - ) -> Result - where - I: Iterator, - { + fn from_args(_vm: &mut VirtualMachine, _args: &mut PyFuncArgs) -> Result { Ok(()) } } @@ -369,13 +315,7 @@ macro_rules! tuple_from_py_func_args { min..=max } - fn from_args( - vm: &mut VirtualMachine, - args: &mut iter::Peekable - ) -> Result - where - I: Iterator - { + fn from_args(vm: &mut VirtualMachine, args: &mut PyFuncArgs) -> Result { Ok(($($T::from_args(vm, args)?,)+)) } } From d3b971475427826576ef7be29f3b1a77cb88a176 Mon Sep 17 00:00:00 2001 From: Joey Date: Sat, 16 Mar 2019 11:23:20 -0700 Subject: [PATCH 326/380] Convert print() builtin to new args style --- vm/src/builtins.rs | 108 ++++++++++++++++++++++++------------------ vm/src/function.rs | 16 ++++++- vm/src/obj/objbool.rs | 10 +++- vm/src/obj/objstr.rs | 9 ++++ 4 files changed, 94 insertions(+), 49 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 58f298392f..0404356c4a 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -2,9 +2,9 @@ //! //! Implements functions listed here: https://docs.python.org/3/library/builtins.html -// use std::ops::Deref; use std::char; use std::io::{self, Write}; +use std::iter; use std::path::PathBuf; use num_traits::{Signed, ToPrimitive}; @@ -15,13 +15,14 @@ use crate::obj::objbool; use crate::obj::objdict; use crate::obj::objint; use crate::obj::objiter; -use crate::obj::objstr; +use crate::obj::objstr::{self, PyString, PyStringRef}; use crate::obj::objtype; use crate::frame::Scope; -use crate::function::PyFuncArgs; +use crate::function::{Args, ArgumentError, FromArgs, PyArg, PyFuncArgs}; use crate::pyobject::{ - AttributeProtocol, IdProtocol, PyContext, PyObjectRef, PyResult, TypeProtocol, + AttributeProtocol, IdProtocol, PyContext, PyObjectRef, PyResult, PyValue, TryFromObject, + TypeProtocol, }; use crate::vm::VirtualMachine; @@ -583,71 +584,84 @@ 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") - .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!( - "sep must be None or a string, not {}", - objtype::get_type_name(&obj.typ()) - ))); - } - } - let sep_str = sep_arg.as_ref().map(|obj| objstr::borrow_value(obj)); - - // Handle 'end' kwarg: - 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!( - "end must be None or a string, not {}", - objtype::get_type_name(&obj.typ()) - ))); +#[derive(Debug)] +pub struct PrintOptions { + sep: Option, + end: Option, + flush: bool, +} + +// In the future, this impl will be generated w/ a derive macro. +impl FromArgs for PrintOptions { + fn from_args( + vm: &mut VirtualMachine, + args: &mut iter::Peekable, + ) -> Result + where + I: Iterator, + { + let mut sep: Option = Some(PyString::from(" ").into_ref(vm)); + let mut end: Option = Some(PyString::from("\n").into_ref(vm)); + let mut flush = false; + + while let Some(arg) = args.next() { + match arg { + PyArg::Keyword(name, value) => match name.as_str() { + "sep" => { + sep = TryFromObject::try_from_object(vm, value)?; + } + "end" => { + end = TryFromObject::try_from_object(vm, value)?; + } + "flush" => { + flush = TryFromObject::try_from_object(vm, value)?; + } + _ => { + return Err(ArgumentError::InvalidKeywordArgument(name)); + } + }, + PyArg::Positional(_) => { + return Err(ArgumentError::TooManyArgs); + } + } } - } - let end_str = end_arg.as_ref().map(|obj| objstr::borrow_value(obj)); - // Handle 'flush' kwarg: - let flush = if let Some(flush) = &args.get_optional_kwarg("flush") { - objbool::boolval(vm, flush.clone()).unwrap() - } else { - false - }; + Ok(PrintOptions { sep, end, flush }) + } +} +pub fn builtin_print( + objects: Args, + options: PrintOptions, + vm: &mut VirtualMachine, +) -> PyResult<()> { let stdout = io::stdout(); let mut stdout_lock = stdout.lock(); let mut first = true; - for a in &args.args { + for object in objects { if first { first = false; - } else if let Some(ref sep_str) = sep_str { - write!(stdout_lock, "{}", sep_str).unwrap(); + } else if let Some(ref sep) = options.sep { + write!(stdout_lock, "{}", sep.value).unwrap(); } else { write!(stdout_lock, " ").unwrap(); } - let v = vm.to_str(&a)?; + let v = vm.to_str(&object)?; let s = objstr::borrow_value(&v); write!(stdout_lock, "{}", s).unwrap(); } - if let Some(end_str) = end_str { - write!(stdout_lock, "{}", end_str).unwrap(); + if let Some(end) = options.end { + write!(stdout_lock, "{}", end.value).unwrap(); } else { writeln!(stdout_lock).unwrap(); } - if flush { + if options.flush { stdout_lock.flush().unwrap(); } - Ok(vm.get_none()) + Ok(()) } fn builtin_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/function.rs b/vm/src/function.rs index 717a539593..b9b81f5675 100644 --- a/vm/src/function.rs +++ b/vm/src/function.rs @@ -136,6 +136,9 @@ impl PyFuncArgs { given_args, ))); } + Err(ArgumentError::InvalidKeywordArgument(name)) => { + return Err(vm.new_type_error(format!("{} is an invalid keyword argument", name))); + } Err(ArgumentError::Exception(ex)) => { return Err(ex); } @@ -162,6 +165,8 @@ pub enum ArgumentError { TooFewArgs, /// The call provided more positional arguments than the function accepts. TooManyArgs, + /// The function doesn't accept a keyword argument with the given name. + InvalidKeywordArgument(String), /// An exception was raised while binding arguments to the function /// parameters. Exception(PyObjectRef), @@ -218,7 +223,7 @@ where /// /// `Args` optionally accepts a generic type parameter to allow type checks /// or conversions of each argument. -pub struct Args(Vec); +pub struct Args(Vec); impl FromArgs for Args where @@ -233,6 +238,15 @@ where } } +impl IntoIterator for Args { + type Item = T; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + impl FromArgs for T where T: TryFromObject, diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index 815354b94c..1a5b10d015 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -1,7 +1,9 @@ use num_traits::Zero; use crate::function::PyFuncArgs; -use crate::pyobject::{IntoPyObject, PyContext, PyObjectRef, PyResult, TypeProtocol}; +use crate::pyobject::{ + IntoPyObject, PyContext, PyObjectRef, PyResult, TryFromObject, TypeProtocol, +}; use crate::vm::VirtualMachine; use super::objdict::PyDict; @@ -18,6 +20,12 @@ impl IntoPyObject for bool { } } +impl TryFromObject for bool { + fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { + boolval(vm, obj) + } +} + pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { if let Some(s) = obj.payload::() { return Ok(!s.value.is_empty()); diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 1abae6f27a..594684af28 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -1,6 +1,7 @@ use std::hash::{Hash, Hasher}; use std::ops::Range; use std::str::FromStr; +use std::string::ToString; use num_traits::ToPrimitive; use unicode_segmentation::UnicodeSegmentation; @@ -24,6 +25,14 @@ pub struct PyString { pub value: String, } +impl From for PyString { + fn from(t: T) -> PyString { + PyString { + value: t.to_string(), + } + } +} + pub type PyStringRef = PyRef; impl PyStringRef { From e83686cf3cb9dca351bd54fa8963628f740ca526 Mon Sep 17 00:00:00 2001 From: Joey Date: Sat, 16 Mar 2019 12:48:42 -0700 Subject: [PATCH 327/380] Add clarifying note to KwArgs docs --- vm/src/function.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/vm/src/function.rs b/vm/src/function.rs index b9b81f5675..caf0598319 100644 --- a/vm/src/function.rs +++ b/vm/src/function.rs @@ -192,14 +192,21 @@ pub trait FromArgs: Sized { /// Extracts this item from the next argument(s). fn from_args(vm: &mut VirtualMachine, args: &mut PyFuncArgs) -> Result; } + /// A map of keyword arguments to their values. /// /// A built-in function with a `KwArgs` parameter is analagous to a Python -/// function with `*kwargs`. All remaining keyword arguments are extracted +/// function with `**kwargs`. All remaining keyword arguments are extracted /// (and hence the function will permit an arbitrary number of them). /// /// `KwArgs` optionally accepts a generic type parameter to allow type checks /// or conversions of each argument. +/// +/// Note: +/// +/// KwArgs is only for functions that accept arbitrary keyword arguments. For +/// functions that accept only *specific* named arguments, a rust struct with +/// an appropriate FromArgs implementation must be created. pub struct KwArgs(HashMap); impl FromArgs for KwArgs From 55e7982835228b934315b504b946befc9e93d78a Mon Sep 17 00:00:00 2001 From: Joey Date: Sat, 16 Mar 2019 12:52:43 -0700 Subject: [PATCH 328/380] Don't need while let --- vm/src/builtins.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 0404356c4a..79de72e351 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -604,7 +604,7 @@ impl FromArgs for PrintOptions { let mut end: Option = Some(PyString::from("\n").into_ref(vm)); let mut flush = false; - while let Some(arg) = args.next() { + for arg in args { match arg { PyArg::Keyword(name, value) => match name.as_str() { "sep" => { From 6550c52fa9ba0471d9206478206e47f874892c27 Mon Sep 17 00:00:00 2001 From: Joey Date: Sat, 16 Mar 2019 13:10:49 -0700 Subject: [PATCH 329/380] None doesn't mean no sep/end --- vm/src/builtins.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 79de72e351..fe99fc32d7 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -15,14 +15,13 @@ use crate::obj::objbool; use crate::obj::objdict; use crate::obj::objint; use crate::obj::objiter; -use crate::obj::objstr::{self, PyString, PyStringRef}; +use crate::obj::objstr::{self, PyStringRef}; use crate::obj::objtype; use crate::frame::Scope; use crate::function::{Args, ArgumentError, FromArgs, PyArg, PyFuncArgs}; use crate::pyobject::{ - AttributeProtocol, IdProtocol, PyContext, PyObjectRef, PyResult, PyValue, TryFromObject, - TypeProtocol, + AttributeProtocol, IdProtocol, PyContext, PyObjectRef, PyResult, TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -600,8 +599,8 @@ impl FromArgs for PrintOptions { where I: Iterator, { - let mut sep: Option = Some(PyString::from(" ").into_ref(vm)); - let mut end: Option = Some(PyString::from("\n").into_ref(vm)); + let mut sep: Option = None; + let mut end: Option = None; let mut flush = false; for arg in args { From 7cb889d42ca3e603bb682bd391b1233d7b0c6ec6 Mon Sep 17 00:00:00 2001 From: Joey Date: Sat, 16 Mar 2019 16:38:28 -0700 Subject: [PATCH 330/380] Rebase against #693 and simplify --- vm/src/builtins.rs | 52 ++++++++++++++-------------------------------- vm/src/function.rs | 13 ++++++++++++ 2 files changed, 29 insertions(+), 36 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index fe99fc32d7..8b3853cbf3 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -4,7 +4,6 @@ use std::char; use std::io::{self, Write}; -use std::iter; use std::path::PathBuf; use num_traits::{Signed, ToPrimitive}; @@ -19,7 +18,7 @@ use crate::obj::objstr::{self, PyStringRef}; use crate::obj::objtype; use crate::frame::Scope; -use crate::function::{Args, ArgumentError, FromArgs, PyArg, PyFuncArgs}; +use crate::function::{Args, ArgumentError, FromArgs, PyFuncArgs}; use crate::pyobject::{ AttributeProtocol, IdProtocol, PyContext, PyObjectRef, PyResult, TryFromObject, TypeProtocol, }; @@ -592,40 +591,21 @@ pub struct PrintOptions { // In the future, this impl will be generated w/ a derive macro. impl FromArgs for PrintOptions { - fn from_args( - vm: &mut VirtualMachine, - args: &mut iter::Peekable, - ) -> Result - where - I: Iterator, - { - let mut sep: Option = None; - let mut end: Option = None; - let mut flush = false; - - for arg in args { - match arg { - PyArg::Keyword(name, value) => match name.as_str() { - "sep" => { - sep = TryFromObject::try_from_object(vm, value)?; - } - "end" => { - end = TryFromObject::try_from_object(vm, value)?; - } - "flush" => { - flush = TryFromObject::try_from_object(vm, value)?; - } - _ => { - return Err(ArgumentError::InvalidKeywordArgument(name)); - } - }, - PyArg::Positional(_) => { - return Err(ArgumentError::TooManyArgs); - } - } - } - - Ok(PrintOptions { sep, end, flush }) + fn from_args(vm: &mut VirtualMachine, args: &mut PyFuncArgs) -> Result { + Ok(PrintOptions { + sep: TryFromObject::try_from_object( + vm, + args.take_keyword("sep").unwrap_or_else(|| vm.ctx.none()), + )?, + end: TryFromObject::try_from_object( + vm, + args.take_keyword("end").unwrap_or_else(|| vm.ctx.none()), + )?, + flush: TryFromObject::try_from_object( + vm, + args.take_keyword("flush").unwrap_or_else(|| vm.ctx.none()), + )?, + }) } } diff --git a/vm/src/function.rs b/vm/src/function.rs index caf0598319..74603580df 100644 --- a/vm/src/function.rs +++ b/vm/src/function.rs @@ -106,6 +106,19 @@ impl PyFuncArgs { } } + pub fn take_keyword(&mut self, name: &str) -> Option { + // TODO: change kwarg representation so this scan isn't necessary + if let Some(index) = self + .kwargs + .iter() + .position(|(arg_name, _)| arg_name == name) + { + Some(self.kwargs.remove(index).1) + } else { + None + } + } + pub fn remaining_keyword<'a>(&'a mut self) -> impl Iterator + 'a { self.kwargs.drain(..) } From 18b57188bab11d67ffc392f9fce3391ad8dc14b3 Mon Sep 17 00:00:00 2001 From: Joey Date: Sat, 16 Mar 2019 15:44:29 -0700 Subject: [PATCH 331/380] Add a custom derive for FromArgs --- .gitignore | 1 + Cargo.lock | 64 +++++++++++++++++++++++++++------------------- Cargo.toml | 2 +- derive/Cargo.toml | 13 ++++++++++ derive/src/lib.rs | 48 ++++++++++++++++++++++++++++++++++ vm/Cargo.toml | 1 + vm/src/builtins.rs | 26 +++---------------- vm/src/lib.rs | 2 ++ 8 files changed, 106 insertions(+), 51 deletions(-) create mode 100644 derive/Cargo.toml create mode 100644 derive/src/lib.rs diff --git a/.gitignore b/.gitignore index c0bc411bd4..967a51a834 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /target +/*/target **/*.rs.bk **/*.bytecode __pycache__ diff --git a/Cargo.lock b/Cargo.lock index 484e7a9c5b..818d0af89e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -262,8 +262,8 @@ name = "failure_derive" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "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)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -575,7 +575,7 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "0.4.20" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -596,10 +596,10 @@ dependencies = [ [[package]] name = "quote" -version = "0.6.3" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -741,6 +741,15 @@ dependencies = [ "xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rustpython_derive" +version = "0.1.0" +dependencies = [ + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rustpython_parser" version = "0.0.1" @@ -771,6 +780,7 @@ dependencies = [ "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)", "rustc_version_runtime 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rustpython_derive 0.1.0", "rustpython_parser 0.0.1", "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)", @@ -843,8 +853,8 @@ name = "serde_derive" version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "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)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -946,18 +956,18 @@ name = "syn" version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "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)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "syn" -version = "0.15.23" +version = "0.15.29" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "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)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -966,8 +976,8 @@ name = "synstructure" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "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)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1096,9 +1106,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "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)", - "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen-shared 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1117,7 +1127,7 @@ name = "wasm-bindgen-macro" version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen-macro-support 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1126,9 +1136,9 @@ name = "wasm-bindgen-macro-support" version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "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)", - "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen-backend 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen-shared 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1146,9 +1156,9 @@ dependencies = [ "failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "heck 0.3.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)", - "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen-backend 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", "weedle 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1304,10 +1314,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "c2261d544c2bb6aa3b10022b0be371b9c7c64f762ef28c6f5d4f1ef6d97b5930" "checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" "checksum proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1b06e2f335f48d24442b35a19df506a835fb3547bc3c06ef27340da9acf5cae7" -"checksum proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "3d7b7eaaa90b4a90a932a9ea6666c95a389e424eff347f0f793979289429feee" +"checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8" -"checksum quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e44651a0dc4cdd99f71c83b561e221f714912d11af1a4dff0631f923d53af035" +"checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1" "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" "checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" "checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372" @@ -1343,7 +1353,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741" -"checksum syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9545a6a093a3f0bd59adb472700acc08cad3776f860f16a897dfce8c88721cbc" +"checksum syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1825685f977249735d510a242a6727b46efe914bb67e38d30c071b1b72b1d5c2" "checksum synstructure 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "85bb9b7550d063ea184027c9b8c20ac167cd36d3e06b3a40bceb9d746dc1a7b7" "checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1" "checksum termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "adc4587ead41bf016f11af03e55a624c06568b5a19db4e90fde573d805074f83" diff --git a/Cargo.toml b/Cargo.toml index e74ef7b026..c109e3fea4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Windel Bouwman", "Shing Lyu "] edition = "2018" [workspace] -members = [".", "vm", "wasm/lib", "parser"] +members = [".", "derive", "vm", "wasm/lib", "parser"] [dependencies] log="0.4.1" diff --git a/derive/Cargo.toml b/derive/Cargo.toml new file mode 100644 index 0000000000..c15d47ad58 --- /dev/null +++ b/derive/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "rustpython_derive" +version = "0.1.0" +authors = ["Joey "] +edition = "2018" + +[lib] +proc-macro = true + +[dependencies] +syn = "0.15.29" +quote = "0.6.11" +proc-macro2 = "0.4.27" diff --git a/derive/src/lib.rs b/derive/src/lib.rs new file mode 100644 index 0000000000..f52ca3e555 --- /dev/null +++ b/derive/src/lib.rs @@ -0,0 +1,48 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use syn::{Data, DeriveInput, Fields}; + +#[proc_macro_derive(FromArgs)] +pub fn derive_from_args(input: TokenStream) -> TokenStream { + let ast: DeriveInput = syn::parse(input).unwrap(); + + let gen = impl_from_args(&ast); + gen.to_string().parse().unwrap() +} + +fn impl_from_args(input: &DeriveInput) -> TokenStream2 { + // FIXME: This references types using `crate` instead of `rustpython_vm` + // so that it can be used in the latter. How can we support both? + let fields = match input.data { + Data::Struct(ref data) => { + match data.fields { + Fields::Named(ref fields) => fields.named.iter().map(|field| { + let name = &field.ident; + quote! { + #name: crate::pyobject::TryFromObject::try_from_object( + vm, + args.take_keyword(stringify!(#name)).unwrap_or_else(|| vm.ctx.none()) + )?, + } + }), + Fields::Unnamed(_) | Fields::Unit => unimplemented!(), // TODO: better error message + } + } + Data::Enum(_) | Data::Union(_) => unimplemented!(), // TODO: better error message + }; + + let name = &input.ident; + quote! { + impl crate::function::FromArgs for #name { + fn from_args( + vm: &mut crate::vm::VirtualMachine, + args: &mut crate::function::PyFuncArgs + ) -> Result { + Ok(#name { #(#fields)* }) + } + } + } +} diff --git a/vm/Cargo.toml b/vm/Cargo.toml index e245aadbcc..e1b1cc9ffa 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -13,6 +13,7 @@ num-integer = "0.1.39" num-rational = "0.2.1" rand = "0.5" log = "0.3" +rustpython_derive = {path = "../derive"} rustpython_parser = {path = "../parser"} serde = "1.0.66" serde_derive = "1.0.66" diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 8b3853cbf3..9472c1131a 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -18,9 +18,9 @@ use crate::obj::objstr::{self, PyStringRef}; use crate::obj::objtype; use crate::frame::Scope; -use crate::function::{Args, ArgumentError, FromArgs, PyFuncArgs}; +use crate::function::{Args, PyFuncArgs}; use crate::pyobject::{ - AttributeProtocol, IdProtocol, PyContext, PyObjectRef, PyResult, TryFromObject, TypeProtocol, + AttributeProtocol, IdProtocol, PyContext, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -582,33 +582,13 @@ fn builtin_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -#[derive(Debug)] +#[derive(Debug, FromArgs)] pub struct PrintOptions { sep: Option, end: Option, flush: bool, } -// In the future, this impl will be generated w/ a derive macro. -impl FromArgs for PrintOptions { - fn from_args(vm: &mut VirtualMachine, args: &mut PyFuncArgs) -> Result { - Ok(PrintOptions { - sep: TryFromObject::try_from_object( - vm, - args.take_keyword("sep").unwrap_or_else(|| vm.ctx.none()), - )?, - end: TryFromObject::try_from_object( - vm, - args.take_keyword("end").unwrap_or_else(|| vm.ctx.none()), - )?, - flush: TryFromObject::try_from_object( - vm, - args.take_keyword("flush").unwrap_or_else(|| vm.ctx.none()), - )?, - }) - } -} - pub fn builtin_print( objects: Args, options: PrintOptions, diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 78696f9f3f..1d5d8e0122 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -25,6 +25,8 @@ extern crate serde_json; extern crate statrs; extern crate rustpython_parser; +#[macro_use] +extern crate rustpython_derive; //extern crate eval; use eval::eval::*; // use py_code_object::{Function, NativeType, PyCodeObject}; From 31917250223f70f55460ce508a683ba1fbefd84f Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Mon, 18 Mar 2019 17:48:46 +0100 Subject: [PATCH 332/380] Refactor into new-style PyObject --- vm/src/stdlib/io.rs | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index c4a35c936d..9e96b84c01 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -22,8 +22,8 @@ use crate::obj::objint; use crate::obj::objstr; use crate::pyobject::{ - AttributeProtocol, BufferProtocol, PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, - PyValue, TypeProtocol, + AttributeProtocol, BufferProtocol, PyContext, PyFuncArgs, PyObject, PyObjectRef, PyRef, + PyResult, PyValue, TypeProtocol, }; use crate::import; @@ -44,12 +44,25 @@ struct PyStringIO { data: RefCell, } +type PyStringIORef = PyRef; + impl PyValue for PyStringIO { fn required_type(_ctx: &PyContext) -> PyObjectRef { unimplemented!(); } } +impl PyStringIORef { + fn write(self, data: objstr::PyStringRef, _vm: &mut VirtualMachine) { + let data = data.value.clone(); + self.data.borrow_mut().push_str(&data); + } + + fn getvalue(self, _vm: &mut VirtualMachine) -> String { + self.data.borrow().clone() + } +} + fn string_io_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(cls, None)]); @@ -61,25 +74,6 @@ fn string_io_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { )) } -fn string_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(instance, None), (data, Some(vm.ctx.str_type()))] - ); - let value = instance.payload::().unwrap(); - let data = objstr::get_value(data); - value.data.borrow_mut().push_str(&data); - Ok(vm.get_none()) -} - -fn string_io_getvalue(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(instance, None)]); - let value = instance.payload::().unwrap(); - let text = value.data.borrow().clone(); - Ok(vm.new_str(text)) -} - fn bytes_io_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { // TODO Ok(vm.get_none()) @@ -424,8 +418,8 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { //StringIO: in-memory text let string_io = py_class!(ctx, "StringIO", text_io_base.clone(), { "__new__" => ctx.new_rustfunc(string_io_new), - "write" => ctx.new_rustfunc(string_io_write), - "getvalue" => ctx.new_rustfunc(string_io_getvalue) + "write" => ctx.new_rustfunc(PyStringIORef::write), + "getvalue" => ctx.new_rustfunc(PyStringIORef::getvalue) }); //BytesIO: in-memory bytes From ac2aecd6fe891c09523978dd09dd99a1d61ae0a9 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Mon, 18 Mar 2019 20:00:55 +0100 Subject: [PATCH 333/380] Disable file argument in test. --- tests/snippets/test_io.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/snippets/test_io.py b/tests/snippets/test_io.py index f135262b62..bc49b72870 100644 --- a/tests/snippets/test_io.py +++ b/tests/snippets/test_io.py @@ -6,7 +6,8 @@ f.write('bladibla') assert f.getvalue() == 'bladibla' -print('fubar', file=f, end='') +# TODO: +# print('fubar', file=f, end='') print(f.getvalue()) # TODO: From 76dbd8c6e801e3d6f47eb44b75fcae477653c57a Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Mon, 18 Mar 2019 20:21:35 +0100 Subject: [PATCH 334/380] Apply cargo formatting. --- vm/src/stdlib/io.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index c830f2cdc4..aba31198aa 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -20,7 +20,8 @@ use crate::obj::objbytes; use crate::obj::objint; use crate::obj::objstr; use crate::pyobject::{ - AttributeProtocol, BufferProtocol, PyContext, PyObjectRef, PyResult, TypeProtocol, PyValue, PyObject, PyRef + AttributeProtocol, BufferProtocol, PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue, + TypeProtocol, }; use crate::vm::VirtualMachine; From 50cc9fa91f9640e0b7b97c39643ab62576936c39 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Mon, 18 Mar 2019 21:56:54 +0200 Subject: [PATCH 335/380] super works in class methods --- tests/snippets/class.py | 26 +++++++++++++++++---- vm/src/obj/objsuper.rs | 51 +++++++++++++++++++++++++++++++++-------- 2 files changed, 64 insertions(+), 13 deletions(-) diff --git a/tests/snippets/class.py b/tests/snippets/class.py index ba2d423ea1..42144d720b 100644 --- a/tests/snippets/class.py +++ b/tests/snippets/class.py @@ -50,12 +50,30 @@ class Bar2(Bar): def __init__(self): super().__init__(101) +bar2 = Bar2() +assert bar2.get_x() == 101 -# TODO: make this work: -# bar2 = Bar2() -# assert bar2.get_x() == 101 +class A(): + def test(self): + return 100 -a = super(int, 2) +class B(): + def test1(self): + return 200 + +class C(A,B): + def test(self): + return super().test() + + def test1(self): + return super().test1() + +c = C() +assert c.test() == 100 +assert c.test1() == 200 + +a = super(bool, True) assert isinstance(a, super) assert type(a) is super +assert a.conjugate() == 1 diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index a3d877e14d..46ac5628a1 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -7,7 +7,8 @@ https://github.com/python/cpython/blob/50b48572d9a90c5bb36e2bef6179548ea927a35a/ */ use crate::function::PyFuncArgs; -use crate::pyobject::{PyContext, PyResult, TypeProtocol}; +use crate::obj::objtype::PyClass; +use crate::pyobject::{DictProtocol, PyContext, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; use super::objtype; @@ -30,6 +31,11 @@ pub fn init(context: &PyContext) { super().cmeth(arg)\n"; context.set_attr(&super_type, "__init__", context.new_rustfunc(super_init)); + context.set_attr( + &super_type, + "__getattribute__", + context.new_rustfunc(super_getattribute), + ); context.set_attr( &super_type, "__doc__", @@ -37,6 +43,33 @@ pub fn init(context: &PyContext) { ); } +fn super_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [ + (obj, Some(vm.ctx.object())), + (name_str, Some(vm.ctx.str_type())) + ] + ); + + match vm.ctx.get_attr(obj, "obj") { + Some(inst) => match inst.typ().payload::() { + Some(PyClass { ref mro, .. }) => { + for class in mro { + if let Ok(item) = vm.get_attribute(class.as_object().clone(), name_str.clone()) + { + return Ok(vm.ctx.new_bound_method(item, inst.clone())); + } + } + Err(vm.new_attribute_error(format!("{} has no attribute '{}'", inst, name_str))) + } + _ => panic!("not Class"), + }, + None => panic!("No obj"), + } +} + fn super_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("super.__init__ {:?}", args.args); arg_check!( @@ -50,11 +83,10 @@ fn super_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let py_type = if let Some(ty) = py_type { ty.clone() } else { - // TODO: implement complex logic here.... - unimplemented!("TODO: get frame and determine instance and class?"); - // let frame = vm.get_current_frame(); - // - // vm.get_none() + match vm.get_locals().get_item("self") { + Some(obj) => obj.typ().clone(), + _ => panic!("No self"), + } }; // Check type argument: @@ -70,7 +102,10 @@ fn super_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let py_obj = if let Some(obj) = py_obj { obj.clone() } else { - vm.get_none() + match vm.get_locals().get_item("self") { + Some(obj) => obj, + _ => panic!("No self"), + } }; // Check obj type: @@ -80,8 +115,6 @@ fn super_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { )); } - // TODO: how to store those types? - vm.ctx.set_attr(inst, "type", py_type); vm.ctx.set_attr(inst, "obj", py_obj); Ok(vm.get_none()) From f78f55840494702ab478f00781bc56b193d9e0e6 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 19 Mar 2019 10:52:39 -0500 Subject: [PATCH 336/380] Fix clippy lints --- src/main.rs | 2 +- vm/src/builtins.rs | 26 ++++++++++---------------- vm/src/function.rs | 2 +- vm/src/lib.rs | 2 +- vm/src/macros.rs | 8 ++++---- vm/src/obj/objlist.rs | 4 ++-- vm/src/obj/objtype.rs | 3 ++- vm/src/pyobject.rs | 13 +++++++++---- vm/src/stdlib/re.rs | 4 ++-- vm/src/stdlib/types.rs | 3 +-- vm/src/vm.rs | 7 +------ 11 files changed, 34 insertions(+), 40 deletions(-) diff --git a/src/main.rs b/src/main.rs index 4be5c9ece6..1b92a5ee46 100644 --- a/src/main.rs +++ b/src/main.rs @@ -186,7 +186,7 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult { debug!("You entered {:?}", line); input.push_str(&line); input.push_str("\n"); - repl.add_history_entry(line.trim_end().as_ref()); + repl.add_history_entry(line.trim_end()); match shell_exec(vm, &input, vars.clone()) { Err(CompileError::Parse(ParseError::EOF(_))) => { diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 85ce77af06..c591933f96 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -66,11 +66,7 @@ fn builtin_any(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(iterable, None)]); let iterator = objiter::get_iter(vm, iterable)?; - loop { - let item = match objiter::get_next_object(vm, &iterator)? { - Some(obj) => obj, - None => break, - }; + while let Some(item) = objiter::get_next_object(vm, &iterator)? { let result = objbool::boolval(vm, item)?; if result { return Ok(vm.new_bool(true)); @@ -259,18 +255,16 @@ fn make_scope( Some(arg) => { if arg.is(&vm.get_none()) { None + } else if vm.isinstance(arg, &dict_type)? { + Some(arg) } else { - if vm.isinstance(arg, &dict_type)? { - Some(arg) - } else { - let arg_typ = arg.typ(); - let actual_type = vm.to_pystr(&arg_typ)?; - let expected_type_name = vm.to_pystr(&dict_type)?; - return Err(vm.new_type_error(format!( - "globals must be a {}, not {}", - expected_type_name, actual_type - ))); - } + let arg_typ = arg.typ(); + let actual_type = vm.to_pystr(&arg_typ)?; + let expected_type_name = vm.to_pystr(&dict_type)?; + return Err(vm.new_type_error(format!( + "globals must be a {}, not {}", + expected_type_name, actual_type + ))); } } None => None, diff --git a/vm/src/function.rs b/vm/src/function.rs index 74603580df..8ce7ad12a3 100644 --- a/vm/src/function.rs +++ b/vm/src/function.rs @@ -20,7 +20,7 @@ pub struct PyFuncArgs { impl From> for PyFuncArgs { fn from(args: Vec) -> Self { PyFuncArgs { - args: args, + args, kwargs: vec![], } } diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 1d5d8e0122..2783d4c5c5 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -6,7 +6,7 @@ //! - Base objects // for methods like vm.to_str(), not the typical use of 'to' as a method prefix -#![allow(clippy::wrong_self_convention)] +#![allow(clippy::wrong_self_convention, clippy::let_and_return)] #[macro_use] extern crate bitflags; diff --git a/vm/src/macros.rs b/vm/src/macros.rs index 788914b519..4d4e6ebe2e 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -117,10 +117,10 @@ macro_rules! py_module { ( $ctx:expr, $module_name:expr, { $($name:expr => $value:expr),* $(,)* }) => { { let py_mod = $ctx.new_module($module_name, $ctx.new_dict()); - $( - $ctx.set_attr(&py_mod, $name, $value); - )* - py_mod + $( + $ctx.set_attr(&py_mod, $name, $value); + )* + py_mod } } } diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 2780f75820..731892e5fb 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -206,7 +206,7 @@ impl PyListRef { return Ok(index); } } - let ref needle_str = vm.to_str(&needle)?.value; + let needle_str = vm.to_pystr(&needle)?; Err(vm.new_value_error(format!("'{}' is not in list", needle_str))) } @@ -243,7 +243,7 @@ impl PyListRef { self.elements.borrow_mut().remove(index); Ok(()) } else { - let ref needle_str = vm.to_str(&needle)?.value; + let needle_str = vm.to_pystr(&needle)?; Err(vm.new_value_error(format!("'{}' is not in list", needle_str))) } } diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 860d30da31..ba675d38d3 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -151,7 +151,7 @@ pub fn isinstance(obj: &PyObjectRef, cls: &PyObjectRef) -> bool { /// so only use this if `cls` is known to have not overridden the base __subclasscheck__ magic /// method. pub fn issubclass(subclass: &PyObjectRef, cls: &PyObjectRef) -> bool { - let ref mro = subclass.payload::().unwrap().mro; + let mro = &subclass.payload::().unwrap().mro; subclass.is(cls) || mro.iter().any(|c| c.is(cls)) } @@ -331,6 +331,7 @@ fn linearise_mro(mut bases: Vec>) -> Option> { Some(result) } +#[allow(clippy::implicit_hasher)] pub fn new( typ: PyObjectRef, name: &str, diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index afcf41c89a..c90824ead7 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -908,7 +908,7 @@ impl DictProtocol for PyObjectRef { } fn get_key_value_pairs(&self) -> Vec<(PyObjectRef, PyObjectRef)> { - if let Some(_) = self.payload::() { + if self.payload_is::() { objdict::get_key_value_pairs(self) } else if let Some(PyModule { ref dict, .. }) = self.payload::() { dict.get_key_value_pairs() @@ -1039,7 +1039,7 @@ impl TryFromObject for Option { if vm.get_none().is(&obj) { Ok(None) } else { - T::try_from_object(vm, obj).map(|x| Some(x)) + T::try_from_object(vm, obj).map(Some) } } } @@ -1118,9 +1118,14 @@ impl PyObject { } #[inline] - pub fn payload(&self) -> Option<&T> { + pub fn payload(&self) -> Option<&T> { self.payload.as_any().downcast_ref() } + + #[inline] + pub fn payload_is(&self) -> bool { + self.payload.as_any().is::() + } } pub trait PyValue: fmt::Debug + Sized + 'static { @@ -1161,7 +1166,7 @@ impl PyObjectPayload for T { impl FromPyObjectRef for PyRef { fn from_pyobj(obj: &PyObjectRef) -> Self { - if let Some(_) = obj.payload::() { + if obj.payload_is::() { PyRef { obj: obj.clone(), _payload: PhantomData, diff --git a/vm/src/stdlib/re.rs b/vm/src/stdlib/re.rs index ff9a429d2b..eda5be75f1 100644 --- a/vm/src/stdlib/re.rs +++ b/vm/src/stdlib/re.rs @@ -194,7 +194,7 @@ fn match_end(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } /// Retrieve inner rust regex from python object: -fn get_regex<'a>(obj: &'a PyObjectRef) -> &'a Regex { +fn get_regex(obj: &PyObjectRef) -> &Regex { // TODO: Regex shouldn't be stored in payload directly, create newtype wrapper if let Some(regex) = obj.payload::() { return regex; @@ -203,7 +203,7 @@ fn get_regex<'a>(obj: &'a PyObjectRef) -> &'a Regex { } /// Retrieve inner rust match from python object: -fn get_match<'a>(obj: &'a PyObjectRef) -> &'a PyMatch { +fn get_match(obj: &PyObjectRef) -> &PyMatch { if let Some(value) = obj.payload::() { return value; } diff --git a/vm/src/stdlib/types.rs b/vm/src/stdlib/types.rs index 208dc1bc26..9768f8dc13 100644 --- a/vm/src/stdlib/types.rs +++ b/vm/src/stdlib/types.rs @@ -15,13 +15,12 @@ fn types_new_class(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { optional = [(bases, None), (_kwds, None), (_exec_body, None)] ); - let ref type_type = vm.ctx.type_type(); let bases: PyObjectRef = match bases { Some(bases) => bases.clone(), None => vm.ctx.new_tuple(vec![]), }; let dict = vm.ctx.new_dict(); - objtype::type_new_class(vm, &type_type, name, &bases, &dict) + objtype::type_new_class(vm, &vm.ctx.type_type(), name, &bases, &dict) } pub fn make_module(ctx: &PyContext) -> PyObjectRef { diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 49bc2f9e26..ccb844409b 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -360,12 +360,7 @@ impl VirtualMachine { } pub fn invoke_with_locals(&mut self, function: PyObjectRef, locals: PyObjectRef) -> PyResult { - if let Some(PyFunction { - code, - scope, - defaults: _, - }) = &function.payload() - { + if let Some(PyFunction { code, scope, .. }) = &function.payload() { let scope = scope.child_scope_with_locals(locals); let frame = self.ctx.new_frame(code.clone(), scope); return self.run_frame_full(frame); From c3d0fddfcf556b46152fbbb45632c23648786d91 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Tue, 19 Mar 2019 18:00:43 +0200 Subject: [PATCH 337/380] Add PySuper --- vm/src/obj/objsuper.rs | 55 ++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index 46ac5628a1..594624213d 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -8,11 +8,24 @@ https://github.com/python/cpython/blob/50b48572d9a90c5bb36e2bef6179548ea927a35a/ use crate::function::PyFuncArgs; use crate::obj::objtype::PyClass; -use crate::pyobject::{DictProtocol, PyContext, PyResult, TypeProtocol}; +use crate::pyobject::{ + DictProtocol, PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, +}; use crate::vm::VirtualMachine; use super::objtype; +#[derive(Debug)] +pub struct PySuper { + obj: PyObjectRef, +} + +impl PyValue for PySuper { + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.super_type() + } +} + pub fn init(context: &PyContext) { let super_type = &context.super_type; @@ -30,7 +43,7 @@ pub fn init(context: &PyContext) { 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, "__new__", context.new_rustfunc(super_new)); context.set_attr( &super_type, "__getattribute__", @@ -48,37 +61,39 @@ fn super_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm, args, required = [ - (obj, Some(vm.ctx.object())), + (super_obj, Some(vm.ctx.super_type())), (name_str, Some(vm.ctx.str_type())) ] ); - match vm.ctx.get_attr(obj, "obj") { - Some(inst) => match inst.typ().payload::() { - Some(PyClass { ref mro, .. }) => { - for class in mro { - if let Ok(item) = vm.get_attribute(class.as_object().clone(), name_str.clone()) - { - return Ok(vm.ctx.new_bound_method(item, inst.clone())); - } + let inst = super_obj.payload::().unwrap().obj.clone(); + + match inst.typ().payload::() { + Some(PyClass { ref mro, .. }) => { + for class in mro { + if let Ok(item) = vm.get_attribute(class.as_object().clone(), name_str.clone()) { + return Ok(vm.ctx.new_bound_method(item, inst.clone())); } - Err(vm.new_attribute_error(format!("{} has no attribute '{}'", inst, name_str))) } - _ => panic!("not Class"), - }, - None => panic!("No obj"), + Err(vm.new_attribute_error(format!("{} has no attribute '{}'", inst, name_str))) + } + _ => panic!("not Class"), } } -fn super_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - trace!("super.__init__ {:?}", args.args); +fn super_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + trace!("super.__new__ {:?}", args.args); arg_check!( vm, args, - required = [(inst, None)], + required = [(cls, None)], optional = [(py_type, None), (py_obj, None)] ); + if !objtype::issubclass(cls, &vm.ctx.super_type()) { + return Err(vm.new_type_error(format!("{:?} is not a subtype of super", cls))); + } + // Get the type: let py_type = if let Some(ty) = py_type { ty.clone() @@ -115,7 +130,5 @@ fn super_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { )); } - vm.ctx.set_attr(inst, "obj", py_obj); - - Ok(vm.get_none()) + Ok(PyObject::new(PySuper { obj: py_obj }, cls.clone())) } From aeaa94966f92f293d117d8b57c83e949aa5399da Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Tue, 19 Mar 2019 16:24:19 +0000 Subject: [PATCH 338/380] Remove PyObject::new from objmap. --- vm/src/obj/objmap.rs | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/vm/src/obj/objmap.rs b/vm/src/obj/objmap.rs index 2de5bc5eff..029d3bae91 100644 --- a/vm/src/obj/objmap.rs +++ b/vm/src/obj/objmap.rs @@ -1,14 +1,16 @@ -use crate::function::PyFuncArgs; -use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol}; +use crate::function::{Args, PyFuncArgs}; +use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; use super::objiter; +use super::objtype::PyClassRef; #[derive(Debug)] pub struct PyMap { mapper: PyObjectRef, iterators: Vec, } +type PyMapRef = PyRef; impl PyValue for PyMap { fn class(vm: &mut VirtualMachine) -> PyObjectRef { @@ -16,26 +18,21 @@ impl PyValue for PyMap { } } -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 - .iter() - .map(|iterable| objiter::get_iter(vm, iterable)) - .collect::, _>>()?; - Ok(PyObject::new( - PyMap { - mapper: function.clone(), - iterators, - }, - cls.clone(), - )) +fn map_new( + cls: PyClassRef, + function: PyObjectRef, + iterables: Args, + vm: &mut VirtualMachine, +) -> PyResult { + let iterators = iterables + .into_iter() + .map(|iterable| objiter::get_iter(vm, &iterable)) + .collect::, _>>()?; + PyMap { + mapper: function.clone(), + iterators, } + .into_ref_with_type(vm, cls.clone()) } fn map_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From d38e89a8112e7344afb1e75ffcdac6feb7d6d05c Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Tue, 19 Mar 2019 16:28:53 +0000 Subject: [PATCH 339/380] Remove PyObject::new from objgenerator. --- vm/src/obj/objgenerator.rs | 10 ++++------ vm/src/vm.rs | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/vm/src/obj/objgenerator.rs b/vm/src/obj/objgenerator.rs index 611b89150f..2fe08c1387 100644 --- a/vm/src/obj/objgenerator.rs +++ b/vm/src/obj/objgenerator.rs @@ -4,13 +4,14 @@ use crate::frame::{ExecutionResult, Frame}; use crate::function::PyFuncArgs; -use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol}; +use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; #[derive(Debug)] pub struct PyGenerator { frame: PyObjectRef, } +type PyGeneratorRef = PyRef; impl PyValue for PyGenerator { fn class(vm: &mut VirtualMachine) -> PyObjectRef { @@ -37,11 +38,8 @@ pub fn init(context: &PyContext) { ); } -pub fn new_generator(vm: &mut VirtualMachine, frame: PyObjectRef) -> PyResult { - Ok(PyObject::new( - PyGenerator { frame }, - vm.ctx.generator_type.clone(), - )) +pub fn new_generator(frame: PyObjectRef, vm: &mut VirtualMachine) -> PyGeneratorRef { + PyGenerator { frame }.into_ref(vm) } fn generator_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 49bc2f9e26..3b3799d0ff 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -353,7 +353,7 @@ impl VirtualMachine { // If we have a generator, create a new generator if code_object.is_generator { - objgenerator::new_generator(self, frame) + Ok(objgenerator::new_generator(frame, self).into_object()) } else { self.run_frame_full(frame) } From 638196bce4ead266d01c25dac3a9236c09bf2ed6 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Tue, 19 Mar 2019 16:42:40 +0000 Subject: [PATCH 340/380] Eliminate PyObject::new from objenumerate. --- vm/src/obj/objenumerate.rs | 43 +++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/vm/src/obj/objenumerate.rs b/vm/src/obj/objenumerate.rs index 76452e56cb..fb7ae7c716 100644 --- a/vm/src/obj/objenumerate.rs +++ b/vm/src/obj/objenumerate.rs @@ -4,18 +4,20 @@ use std::ops::AddAssign; use num_bigint::BigInt; use num_traits::Zero; -use crate::function::PyFuncArgs; -use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol}; +use crate::function::{OptionalArg, PyFuncArgs}; +use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; -use super::objint; +use super::objint::PyIntRef; use super::objiter; +use super::objtype::PyClassRef; #[derive(Debug)] pub struct PyEnumerate { counter: RefCell, iterator: PyObjectRef, } +type PyEnumerateRef = PyRef; impl PyValue for PyEnumerate { fn class(vm: &mut VirtualMachine) -> PyObjectRef { @@ -23,26 +25,23 @@ impl PyValue for PyEnumerate { } } -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).clone() - } else { - BigInt::zero() +fn enumerate_new( + cls: PyClassRef, + iterable: PyObjectRef, + start: OptionalArg, + vm: &mut VirtualMachine, +) -> PyResult { + let counter = match start { + OptionalArg::Present(start) => start.value.clone(), + OptionalArg::Missing => BigInt::zero(), }; - let iterator = objiter::get_iter(vm, iterable)?; - Ok(PyObject::new( - PyEnumerate { - counter: RefCell::new(counter), - iterator, - }, - cls.clone(), - )) + + let iterator = objiter::get_iter(vm, &iterable)?; + PyEnumerate { + counter: RefCell::new(counter.clone()), + iterator, + } + .into_ref_with_type(vm, cls) } fn enumerate_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From c6e7d8901feec0c9664122593073494d6e373d77 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Tue, 19 Mar 2019 17:14:51 +0000 Subject: [PATCH 341/380] Remove use of PyObject::new from objset. --- vm/src/obj/objset.rs | 164 ++++++++++++++++--------------------------- 1 file changed, 59 insertions(+), 105 deletions(-) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 7f54c0d657..2b46261468 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -7,21 +7,22 @@ use std::collections::{hash_map::DefaultHasher, HashMap}; use std::fmt; use std::hash::{Hash, Hasher}; -use crate::function::PyFuncArgs; +use crate::function::{OptionalArg, PyFuncArgs}; use crate::pyobject::{ - PyContext, PyIteratorValue, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, + PyContext, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; use super::objbool; use super::objint; use super::objiter; -use super::objtype; +use super::objtype::{self, PyClassRef}; #[derive(Default)] pub struct PySet { elements: RefCell>, } +pub type PySetRef = PyRef; impl fmt::Debug for PySet { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -151,23 +152,20 @@ fn set_clear(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } /* Create a new object of sub-type of set */ -fn set_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(cls, None)], - optional = [(iterable, None)] - ); - - if !objtype::issubclass(cls, &vm.ctx.set_type()) { +fn set_new( + cls: PyClassRef, + iterable: OptionalArg, + vm: &mut VirtualMachine, +) -> PyResult { + if !objtype::issubclass(cls.as_object(), &vm.ctx.set_type()) { return Err(vm.new_type_error(format!("{} is not a subtype of set", cls))); } let elements: HashMap = match iterable { - None => HashMap::new(), - Some(iterable) => { + OptionalArg::Missing => HashMap::new(), + OptionalArg::Present(iterable) => { let mut elements = HashMap::new(); - let iterator = objiter::get_iter(vm, iterable)?; + let iterator = objiter::get_iter(vm, &iterable)?; while let Ok(v) = vm.call_method(&iterator, "__next__", vec![]) { insert_into_set(vm, &mut elements, &v)?; } @@ -175,12 +173,10 @@ fn set_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } }; - Ok(PyObject::new( - PySet { - elements: RefCell::new(elements), - }, - cls.clone(), - )) + PySet { + elements: RefCell::new(elements), + } + .into_ref_with_type(vm, cls) } fn set_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -190,16 +186,12 @@ fn set_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.context().new_int(elements.len())) } -fn set_copy(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - trace!("set.copy called with: {:?}", args); - arg_check!(vm, args, required = [(s, Some(vm.ctx.set_type()))]); - let elements = get_elements(s); - Ok(PyObject::new( - PySet { - elements: RefCell::new(elements), - }, - vm.ctx.set_type(), - )) +fn set_copy(obj: PySetRef, _vm: &mut VirtualMachine) -> PySet { + trace!("set.copy called with: {:?}", obj); + let elements = obj.elements.borrow().clone(); + PySet { + elements: RefCell::new(elements), + } } fn set_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -335,67 +327,47 @@ fn set_compare_inner( Ok(vm.new_bool(true)) } -fn set_union(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (zelf, Some(vm.ctx.set_type())), - (other, Some(vm.ctx.set_type())) - ] - ); - - let mut elements = get_elements(zelf).clone(); - elements.extend(get_elements(other).clone()); +fn set_union(zelf: PySetRef, other: PySetRef, _vm: &mut VirtualMachine) -> PySet { + let mut elements = zelf.elements.borrow().clone(); + elements.extend(other.elements.borrow().clone()); - Ok(PyObject::new( - PySet { - elements: RefCell::new(elements), - }, - vm.ctx.set_type(), - )) + PySet { + elements: RefCell::new(elements), + } } -fn set_intersection(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - set_combine_inner(vm, args, SetCombineOperation::Intersection) +fn set_intersection(zelf: PySetRef, other: PySetRef, vm: &mut VirtualMachine) -> PyResult { + set_combine_inner(zelf, other, vm, SetCombineOperation::Intersection) } -fn set_difference(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - set_combine_inner(vm, args, SetCombineOperation::Difference) +fn set_difference(zelf: PySetRef, other: PySetRef, vm: &mut VirtualMachine) -> PyResult { + set_combine_inner(zelf, other, vm, SetCombineOperation::Difference) } -fn set_symmetric_difference(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (zelf, Some(vm.ctx.set_type())), - (other, Some(vm.ctx.set_type())) - ] - ); - +fn set_symmetric_difference( + zelf: PySetRef, + other: PySetRef, + vm: &mut VirtualMachine, +) -> PyResult { let mut elements = HashMap::new(); - for element in get_elements(zelf).iter() { - let value = vm.call_method(other, "__contains__", vec![element.1.clone()])?; + for element in zelf.elements.borrow().iter() { + let value = vm.call_method(other.as_object(), "__contains__", vec![element.1.clone()])?; if !objbool::get_value(&value) { elements.insert(element.0.clone(), element.1.clone()); } } - for element in get_elements(other).iter() { - let value = vm.call_method(zelf, "__contains__", vec![element.1.clone()])?; + for element in other.elements.borrow().iter() { + let value = vm.call_method(zelf.as_object(), "__contains__", vec![element.1.clone()])?; if !objbool::get_value(&value) { elements.insert(element.0.clone(), element.1.clone()); } } - Ok(PyObject::new( - PySet { - elements: RefCell::new(elements), - }, - vm.ctx.set_type(), - )) + Ok(PySet { + elements: RefCell::new(elements), + }) } enum SetCombineOperation { @@ -404,23 +376,15 @@ enum SetCombineOperation { } fn set_combine_inner( + zelf: PySetRef, + other: PySetRef, vm: &mut VirtualMachine, - args: PyFuncArgs, op: SetCombineOperation, -) -> PyResult { - arg_check!( - vm, - args, - required = [ - (zelf, Some(vm.ctx.set_type())), - (other, Some(vm.ctx.set_type())) - ] - ); - +) -> PyResult { let mut elements = HashMap::new(); - for element in get_elements(zelf).iter() { - let value = vm.call_method(other, "__contains__", vec![element.1.clone()])?; + for element in zelf.elements.borrow().iter() { + let value = vm.call_method(other.as_object(), "__contains__", vec![element.1.clone()])?; let should_add = match op { SetCombineOperation::Intersection => objbool::get_value(&value), SetCombineOperation::Difference => !objbool::get_value(&value), @@ -430,12 +394,9 @@ fn set_combine_inner( } } - Ok(PyObject::new( - PySet { - elements: RefCell::new(elements), - }, - vm.ctx.set_type(), - )) + Ok(PySet { + elements: RefCell::new(elements), + }) } fn set_pop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -557,20 +518,13 @@ fn set_ixor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(zelf.clone()) } -fn set_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(zelf, Some(vm.ctx.set_type()))]); - - let items = get_elements(zelf).values().cloned().collect(); +fn set_iter(zelf: PySetRef, vm: &mut VirtualMachine) -> PyIteratorValue { + let items = zelf.elements.borrow().values().cloned().collect(); let set_list = vm.ctx.new_list(items); - let iter_obj = PyObject::new( - PyIteratorValue { - position: Cell::new(0), - iterated_obj: set_list, - }, - vm.ctx.iter_type(), - ); - - Ok(iter_obj) + PyIteratorValue { + position: Cell::new(0), + iterated_obj: set_list, + } } fn frozenset_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From 5384e07bf3cc3fdf0e0e69f62c98183b96477de9 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Tue, 19 Mar 2019 17:15:14 +0000 Subject: [PATCH 342/380] Remove PyObject::new from tuple and list. --- vm/src/obj/objlist.rs | 13 +++++-------- vm/src/obj/objtuple.rs | 23 ++++++++++------------- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 2780f75820..08d3ff2f4c 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -119,14 +119,11 @@ impl PyListRef { ) } - fn iter(self, vm: &mut VirtualMachine) -> PyObjectRef { - PyObject::new( - PyIteratorValue { - position: Cell::new(0), - iterated_obj: self.into_object(), - }, - vm.ctx.iter_type(), - ) + fn iter(self, _vm: &mut VirtualMachine) -> PyIteratorValue { + PyIteratorValue { + position: Cell::new(0), + iterated_obj: self.into_object(), + } } fn setitem(self, key: PyObjectRef, value: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 212005fdfa..11d6a36947 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -4,7 +4,7 @@ use std::hash::{Hash, Hasher}; use crate::function::OptionalArg; use crate::pyobject::{ - IdProtocol, PyContext, PyIteratorValue, PyObject, PyObjectRef, PyRef, PyResult, PyValue, + IdProtocol, PyContext, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue, }; use crate::vm::{ReprGuard, VirtualMachine}; @@ -13,7 +13,7 @@ use super::objint; use super::objsequence::{ get_elements, get_item, seq_equal, seq_ge, seq_gt, seq_le, seq_lt, seq_mul, }; -use super::objtype; +use super::objtype::{self, PyClassRef}; #[derive(Default)] pub struct PyTuple { @@ -132,14 +132,11 @@ impl PyTupleRef { Ok(hasher.finish()) } - fn iter(self, vm: &mut VirtualMachine) -> PyObjectRef { - PyObject::new( - PyIteratorValue { - position: Cell::new(0), - iterated_obj: self.into_object(), - }, - vm.ctx.iter_type(), - ) + fn iter(self, _vm: &mut VirtualMachine) -> PyIteratorValue { + PyIteratorValue { + position: Cell::new(0), + iterated_obj: self.into_object(), + } } fn len(self, _vm: &mut VirtualMachine) -> usize { @@ -207,10 +204,10 @@ impl PyTupleRef { } fn tuple_new( - cls: PyRef, + cls: PyClassRef, iterable: OptionalArg, vm: &mut VirtualMachine, -) -> PyResult { +) -> PyResult { if !objtype::issubclass(cls.as_object(), &vm.ctx.tuple_type()) { return Err(vm.new_type_error(format!("{} is not a subtype of tuple", cls))); } @@ -221,7 +218,7 @@ fn tuple_new( vec![] }; - Ok(PyObject::new(PyTuple::from(elements), cls.into_object())) + PyTuple::from(elements).into_ref_with_type(vm, cls) } #[rustfmt::skip] // to avoid line splitting From ab53883f67a3b4f4cb3d74681aba657b3cba4764 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 19 Mar 2019 19:53:22 -0500 Subject: [PATCH 343/380] Change varargs from Option> to its own enum --- parser/src/ast.rs | 29 ++++++++++++++++++++-- parser/src/python.lalrpop | 16 ++++++------ vm/src/bytecode.rs | 37 ++++++++++++++++++++++++---- vm/src/compile.rs | 22 +++++++---------- vm/src/vm.rs | 52 +++++++++++++++++++-------------------- 5 files changed, 102 insertions(+), 54 deletions(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 182ab65d6e..b27c231d40 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -274,8 +274,8 @@ impl Expression { pub struct Parameters { pub args: Vec, pub kwonlyargs: Vec, - pub vararg: Option>, // Optionally we handle optionally named '*args' or '*' - pub kwarg: Option>, + pub vararg: Varargs, // Optionally we handle optionally named '*args' or '*' + pub kwarg: Varargs, pub defaults: Vec, pub kw_defaults: Vec>, } @@ -391,3 +391,28 @@ pub enum StringGroup { values: Vec, }, } + +#[derive(Debug, PartialEq)] +pub enum Varargs { + None, + NoCapture, + Capture(Parameter), +} + +impl Default for Varargs { + fn default() -> Varargs { + Varargs::None + } +} + +impl From>> for Varargs { + fn from(opt: Option>) -> Varargs { + match opt { + Some(inner_opt) => match inner_opt { + Some(param) => Varargs::Capture(param), + None => Varargs::NoCapture, + }, + None => Varargs::None, + } + } +} diff --git a/parser/src/python.lalrpop b/parser/src/python.lalrpop index b9c8672c23..9e5b309b1e 100644 --- a/parser/src/python.lalrpop +++ b/parser/src/python.lalrpop @@ -486,8 +486,8 @@ TypedArgsList: ast::Parameters = { ast::Parameters { args: names, kwonlyargs: kwonlyargs, - vararg: vararg, - kwarg: kwarg, + vararg: vararg.into(), + kwarg: kwarg.into(), defaults: default_elements, kw_defaults: kw_defaults, } @@ -504,8 +504,8 @@ TypedArgsList: ast::Parameters = { ast::Parameters { args: names, kwonlyargs: kwonlyargs, - vararg: vararg, - kwarg: kwarg, + vararg: vararg.into(), + kwarg: kwarg.into(), defaults: default_elements, kw_defaults: kw_defaults, } @@ -515,8 +515,8 @@ TypedArgsList: ast::Parameters = { ast::Parameters { args: vec![], kwonlyargs: kwonlyargs, - vararg: vararg, - kwarg: kwarg, + vararg: vararg.into(), + kwarg: kwarg.into(), defaults: vec![], kw_defaults: kw_defaults, } @@ -525,8 +525,8 @@ TypedArgsList: ast::Parameters = { ast::Parameters { args: vec![], kwonlyargs: vec![], - vararg: None, - kwarg: Some(kw), + vararg: ast::Varargs::None, + kwarg: Some(kw).into(), defaults: vec![], kw_defaults: vec![], } diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index ff6ec873ce..53adef093f 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -18,10 +18,10 @@ pub struct CodeObject { pub instructions: Vec, pub label_map: HashMap, pub locations: Vec, - pub arg_names: Vec, // Names of positional arguments - pub varargs: Option>, // *args or * + pub arg_names: Vec, // Names of positional arguments + pub varargs: Varargs, // *args or * pub kwonlyarg_names: Vec, - pub varkeywords: Option>, // **kwargs or ** + pub varkeywords: Varargs, // **kwargs or ** pub source_path: String, pub first_line_number: usize, pub obj_name: String, // Name of the object that created this code object @@ -237,6 +237,13 @@ pub enum UnaryOperator { Plus, } +#[derive(Debug, Clone, PartialEq)] +pub enum Varargs { + None, + NoCapture, + Capture(String), +} + /* Maintain a stack of blocks on the VM. pub enum BlockType { @@ -248,9 +255,9 @@ pub enum BlockType { impl CodeObject { pub fn new( arg_names: Vec, - varargs: Option>, + varargs: Varargs, kwonlyarg_names: Vec, - varkeywords: Option>, + varkeywords: Varargs, source_path: String, first_line_number: usize, obj_name: String, @@ -401,3 +408,23 @@ impl fmt::Debug for CodeObject { ) } } + +impl From for Varargs { + fn from(varargs: ast::Varargs) -> Varargs { + match varargs { + ast::Varargs::None => Varargs::None, + ast::Varargs::NoCapture => Varargs::NoCapture, + ast::Varargs::Capture(param) => Varargs::Capture(param.arg), + } + } +} + +impl Varargs { + pub fn from_ast_vararg_ref(varargs: &ast::Varargs) -> Varargs { + match varargs { + ast::Varargs::None => Varargs::None, + ast::Varargs::NoCapture => Varargs::NoCapture, + ast::Varargs::Capture(ref param) => Varargs::Capture(param.arg.clone()), + } + } +} diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 08045eb535..2c549ba12a 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -5,7 +5,7 @@ //! https://github.com/python/cpython/blob/master/Python/compile.c //! https://github.com/micropython/micropython/blob/master/py/compile.c -use crate::bytecode::{self, CallType, CodeObject, Instruction}; +use crate::bytecode::{self, CallType, CodeObject, Instruction, Varargs}; use crate::error::CompileError; use crate::obj::objcode; use crate::pyobject::{PyObject, PyObjectRef}; @@ -82,9 +82,9 @@ impl Compiler { let line_number = self.get_source_line_number(); self.code_object_stack.push(CodeObject::new( Vec::new(), - None, + Varargs::None, Vec::new(), - None, + Varargs::None, self.source_path.clone().unwrap(), line_number, obj_name, @@ -445,13 +445,9 @@ impl Compiler { let line_number = self.get_source_line_number(); self.code_object_stack.push(CodeObject::new( args.args.iter().map(|a| a.arg.clone()).collect(), - args.vararg - .as_ref() - .map(|x| x.as_ref().map(|a| a.arg.clone())), + Varargs::from_ast_vararg_ref(&args.vararg), args.kwonlyargs.iter().map(|a| a.arg.clone()).collect(), - args.kwarg - .as_ref() - .map(|x| x.as_ref().map(|a| a.arg.clone())), + Varargs::from_ast_vararg_ref(&args.kwarg), self.source_path.clone().unwrap(), line_number, name.to_string(), @@ -684,9 +680,9 @@ impl Compiler { let line_number = self.get_source_line_number(); self.code_object_stack.push(CodeObject::new( vec![], - None, + Varargs::None, vec![], - None, + Varargs::None, self.source_path.clone().unwrap(), line_number, name.to_string(), @@ -1297,9 +1293,9 @@ impl Compiler { // Create magnificent function : self.code_object_stack.push(CodeObject::new( vec![".0".to_string()], - None, + Varargs::None, vec![], - None, + Varargs::None, self.source_path.clone().unwrap(), line_number, name.clone(), diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 37cc2f677c..8050e70c61 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -401,40 +401,40 @@ impl VirtualMachine { } // Pack other positional arguments in to *args: - if let Some(vararg) = &code_object.varargs { - let mut last_args = vec![]; - for i in n..nargs { - let arg = &args.args[i]; - last_args.push(arg.clone()); - } - let vararg_value = self.ctx.new_tuple(last_args); + match code_object.varargs { + bytecode::Varargs::Capture(ref vararg_name) => { + let mut last_args = vec![]; + for i in n..nargs { + let arg = &args.args[i]; + last_args.push(arg.clone()); + } + let vararg_value = self.ctx.new_tuple(last_args); - // If we have a name (not '*' only) then store it: - if let Some(vararg_name) = vararg { locals.set_item(&self.ctx, vararg_name, vararg_value); } - } else { - // Check the number of positional arguments - if nargs > nexpected_args { - return Err(self.new_type_error(format!( - "Expected {} arguments (got: {})", - nexpected_args, nargs - ))); + bytecode::Varargs::NoCapture => { + // just ignore the rest of the args + } + bytecode::Varargs::None => { + // Check the number of positional arguments + if nargs > nexpected_args { + return Err(self.new_type_error(format!( + "Expected {} arguments (got: {})", + nexpected_args, nargs + ))); + } } } // Do we support `**kwargs` ? - let kwargs = if let Some(kwargs) = &code_object.varkeywords { - let d = self.new_dict(); - - // Store when we have a name: - if let Some(kwargs_name) = kwargs { - locals.set_item(&self.ctx, &kwargs_name, d.clone()); + let kwargs = match code_object.varkeywords { + bytecode::Varargs::Capture(ref kwargs_name) => { + let d = self.new_dict(); + locals.set_item(&self.ctx, kwargs_name, d.clone()); + Some(d) } - - Some(d) - } else { - None + bytecode::Varargs::NoCapture => Some(self.new_dict()), + bytecode::Varargs::None => None, }; // Handle keyword arguments From b30d56df29142bf7cd7351d3ced09429c6b9ae9c Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 19 Mar 2019 20:02:06 -0500 Subject: [PATCH 344/380] Fix some more clippy warnings --- vm/src/obj/objsequence.rs | 1 + wasm/lib/src/browser_module.rs | 11 +++++------ wasm/lib/src/convert.rs | 13 +++---------- wasm/lib/src/vm_class.rs | 2 +- 4 files changed, 10 insertions(+), 17 deletions(-) diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index 893e8cd82d..2fd97d2790 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -78,6 +78,7 @@ pub trait PySliceableSequence { } else if step.is_positive() { let range = self.get_slice_range(start, stop); if range.start < range.end { + #[allow(clippy::range_plus_one)] match step.to_i32() { Some(1) => Ok(self.do_slice(range)), Some(num) => Ok(self.do_stepped_slice(range, num as usize)), diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index 8766d06b44..e30f653917 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -75,8 +75,8 @@ fn browser_fetch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if let Some(headers) = headers { let h = request.headers(); for (key, value) in rustpython_vm::obj::objdict::get_key_value_pairs(&headers) { - let ref key = vm.to_str(&key)?.value; - let ref value = vm.to_str(&value)?.value; + let key = &vm.to_str(&key)?.value; + let value = &vm.to_str(&value)?.value; h.set(key, value) .map_err(|err| convert::js_py_typeerror(vm, err))?; } @@ -163,9 +163,8 @@ pub struct PyPromise { } impl PyValue for PyPromise { - fn class(_vm: &mut VirtualMachine) -> PyObjectRef { - // TODO - unimplemented!() + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.class(BROWSER_NAME, "Promise") } } @@ -183,7 +182,7 @@ pub fn get_promise_value(obj: &PyObjectRef) -> Promise { } pub fn import_promise_type(vm: &mut VirtualMachine) -> PyResult { - match import_module(vm, PathBuf::default(), BROWSER_NAME)?.get_attr("Promise".into()) { + match import_module(vm, PathBuf::default(), BROWSER_NAME)?.get_attr("Promise") { Some(promise) => Ok(promise), None => Err(vm.new_not_implemented_error("No Promise".to_string())), } diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index 42d4675f00..1cf8907681 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -129,10 +129,7 @@ pub fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { } arr.into() } else { - let dumps = rustpython_vm::import::import_module(vm, std::path::PathBuf::default(), "json") - .expect("Couldn't get json module") - .get_attr("dumps".into()) - .expect("Couldn't get json dumps"); + let dumps = vm.class("json", "dumps"); match vm.invoke(dumps, PyFuncArgs::new(vec![py_obj], vec![])) { Ok(value) => { let json = vm.to_pystr(&value).unwrap(); @@ -156,7 +153,7 @@ pub fn object_entries(obj: &Object) -> impl Iterator Result { result .map(|value| py_to_js(vm, value)) - .map_err(|err| py_err_to_js_err(vm, &err).into()) + .map_err(|err| py_err_to_js_err(vm, &err)) } pub fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef { @@ -229,11 +226,7 @@ pub fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef { // Because `JSON.stringify(undefined)` returns undefined vm.get_none() } else { - let loads = rustpython_vm::import::import_module(vm, std::path::PathBuf::default(), "json") - .expect("Couldn't get json module") - .get_attr("loads".into()) - .expect("Couldn't get json dumps"); - + let loads = vm.class("json", "dumps"); let json = match js_sys::JSON::stringify(&js_val) { Ok(json) => String::from(json), Err(_) => return vm.get_none(), diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index dfca86f524..408e2b3cc0 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use std::rc::{Rc, Weak}; use js_sys::{Object, Reflect, SyntaxError, TypeError}; -use wasm_bindgen::{prelude::*, JsCast}; +use wasm_bindgen::prelude::*; use rustpython_vm::compile; use rustpython_vm::frame::{NameProtocol, Scope}; From ff5c203b0e6f2c15d99a56a9dcaa155e2df41c66 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 19 Mar 2019 20:13:03 -0500 Subject: [PATCH 345/380] impl From<&ast::Varargs> for bytecode::Varargs --- vm/src/bytecode.rs | 4 ++-- vm/src/compile.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index 53adef093f..de6f590477 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -419,8 +419,8 @@ impl From for Varargs { } } -impl Varargs { - pub fn from_ast_vararg_ref(varargs: &ast::Varargs) -> Varargs { +impl<'a> From<&'a ast::Varargs> for Varargs { + fn from(varargs: &'a ast::Varargs) -> Varargs { match varargs { ast::Varargs::None => Varargs::None, ast::Varargs::NoCapture => Varargs::NoCapture, diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 2c549ba12a..1742392cdd 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -445,9 +445,9 @@ impl Compiler { let line_number = self.get_source_line_number(); self.code_object_stack.push(CodeObject::new( args.args.iter().map(|a| a.arg.clone()).collect(), - Varargs::from_ast_vararg_ref(&args.vararg), + Varargs::from(&args.vararg), args.kwonlyargs.iter().map(|a| a.arg.clone()).collect(), - Varargs::from_ast_vararg_ref(&args.kwarg), + Varargs::from(&args.kwarg), self.source_path.clone().unwrap(), line_number, name.to_string(), From 33b99b203f8fcb23f82fb442f62cbc3ccd57f203 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 19 Mar 2019 20:34:35 -0500 Subject: [PATCH 346/380] Some minor things --- vm/src/lib.rs | 6 +++++- vm/src/obj/objlist.rs | 4 ++-- vm/src/obj/objtype.rs | 1 - vm/src/pyobject.rs | 6 +++--- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 2783d4c5c5..3892fe2a45 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -6,7 +6,11 @@ //! - Base objects // for methods like vm.to_str(), not the typical use of 'to' as a method prefix -#![allow(clippy::wrong_self_convention, clippy::let_and_return)] +#![allow( + clippy::wrong_self_convention, + clippy::let_and_return, + clippy::implicit_hasher +)] #[macro_use] extern crate bitflags; diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 259e04c78f..f435e6ea8d 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -203,7 +203,7 @@ impl PyListRef { return Ok(index); } } - let needle_str = vm.to_pystr(&needle)?; + let needle_str = &vm.to_str(&needle)?.value; Err(vm.new_value_error(format!("'{}' is not in list", needle_str))) } @@ -240,7 +240,7 @@ impl PyListRef { self.elements.borrow_mut().remove(index); Ok(()) } else { - let needle_str = vm.to_pystr(&needle)?; + let needle_str = &vm.to_str(&needle)?.value; Err(vm.new_value_error(format!("'{}' is not in list", needle_str))) } } diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index ba675d38d3..adb8b236d4 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -331,7 +331,6 @@ fn linearise_mro(mut bases: Vec>) -> Option> { Some(result) } -#[allow(clippy::implicit_hasher)] pub fn new( typ: PyObjectRef, name: &str, diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index c90824ead7..965f1b05af 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -908,7 +908,7 @@ impl DictProtocol for PyObjectRef { } fn get_key_value_pairs(&self) -> Vec<(PyObjectRef, PyObjectRef)> { - if self.payload_is::() { + if self.object_is::() { objdict::get_key_value_pairs(self) } else if let Some(PyModule { ref dict, .. }) = self.payload::() { dict.get_key_value_pairs() @@ -1123,7 +1123,7 @@ impl PyObject { } #[inline] - pub fn payload_is(&self) -> bool { + pub fn object_is(&self) -> bool { self.payload.as_any().is::() } } @@ -1166,7 +1166,7 @@ impl PyObjectPayload for T { impl FromPyObjectRef for PyRef { fn from_pyobj(obj: &PyObjectRef) -> Self { - if obj.payload_is::() { + if obj.object_is::() { PyRef { obj: obj.clone(), _payload: PhantomData, From 01bed2a051c73513986e9c94f67c1ebcb40e8d21 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 19 Mar 2019 20:54:53 -0500 Subject: [PATCH 347/380] Change the WASM example to use rustpython from npm --- wasm/example/package.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/wasm/example/package.json b/wasm/example/package.json index cee4b7702b..5653448b25 100644 --- a/wasm/example/package.json +++ b/wasm/example/package.json @@ -1,10 +1,8 @@ { "name": "rustpython-wasm-example", "version": "1.0.0", - "//1": "`dependencies.rustpython_wasm` would be the version of the npm", - "//2": "library in a real app", "dependencies": { - "rustpython_wasm": "file:../lib/pkg/" + "rustpython_wasm": "0.1.0-pre-alpha.1" }, "devDependencies": { "raw-loader": "1.0.0", @@ -12,7 +10,8 @@ "webpack-cli": "^3.1.2" }, "scripts": { - "build": "webpack" + "build": "webpack", + "dist": "webpack --mode production" }, "license": "MIT" } From ef3b9105ef59644b8d1542d9f3d107ee8d413b5a Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 19 Mar 2019 20:59:48 -0500 Subject: [PATCH 348/380] Rename object_is back to payload_is --- vm/src/pyobject.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 965f1b05af..c90824ead7 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -908,7 +908,7 @@ impl DictProtocol for PyObjectRef { } fn get_key_value_pairs(&self) -> Vec<(PyObjectRef, PyObjectRef)> { - if self.object_is::() { + if self.payload_is::() { objdict::get_key_value_pairs(self) } else if let Some(PyModule { ref dict, .. }) = self.payload::() { dict.get_key_value_pairs() @@ -1123,7 +1123,7 @@ impl PyObject { } #[inline] - pub fn object_is(&self) -> bool { + pub fn payload_is(&self) -> bool { self.payload.as_any().is::() } } @@ -1166,7 +1166,7 @@ impl PyObjectPayload for T { impl FromPyObjectRef for PyRef { fn from_pyobj(obj: &PyObjectRef) -> Self { - if obj.object_is::() { + if obj.payload_is::() { PyRef { obj: obj.clone(), _payload: PhantomData, From b7916cdb53734b3f5fdac90de0bb9146a78552b7 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 19 Mar 2019 22:15:09 -0500 Subject: [PATCH 349/380] Fix varargs errors --- parser/src/parser.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/parser/src/parser.rs b/parser/src/parser.rs index fc783c2aab..a119740ec5 100644 --- a/parser/src/parser.rs +++ b/parser/src/parser.rs @@ -255,8 +255,8 @@ mod tests { } ], kwonlyargs: vec![], - vararg: None, - kwarg: None, + vararg: ast::Varargs::None, + kwarg: ast::Varargs::None, defaults: vec![], kw_defaults: vec![], }, @@ -344,8 +344,8 @@ mod tests { annotation: None, }], kwonlyargs: vec![], - vararg: None, - kwarg: None, + vararg: ast::Varargs::None, + kwarg: ast::Varargs::None, defaults: vec![], kw_defaults: vec![], }, @@ -373,8 +373,8 @@ mod tests { } ], kwonlyargs: vec![], - vararg: None, - kwarg: None, + vararg: ast::Varargs::None, + kwarg: ast::Varargs::None, defaults: vec![ast::Expression::String { value: ast::StringGroup::Constant { value: "default".to_string() From de1bc937e78b4bc65e0a9abfc4a2560624c176a3 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Tue, 19 Mar 2019 22:20:10 -0500 Subject: [PATCH 350/380] Use json straight from `json.rs` --- vm/src/stdlib/json.rs | 28 ++++++++++++++-------------- vm/src/stdlib/mod.rs | 2 +- wasm/lib/src/convert.rs | 33 +++++++++------------------------ 3 files changed, 24 insertions(+), 39 deletions(-) diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index 84b82389ed..5f313c8630 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -188,15 +188,22 @@ impl<'de> Visitor<'de> for PyObjectDeserializer<'de> { } } +pub fn ser_pyobject(vm: &mut VirtualMachine, obj: &PyObjectRef) -> serde_json::Result { + let serializer = PyObjectSerializer { pyobject: obj, vm }; + serde_json::to_string(&serializer) +} + +pub fn de_pyobject(vm: &mut VirtualMachine, s: &str) -> serde_json::Result { + let de = PyObjectDeserializer { vm }; + // TODO: Support deserializing string sub-classes + de.deserialize(&mut serde_json::Deserializer::from_str(s)) +} + /// 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, vm }; - serde_json::to_string(&serializer) - }; - let string = res.map_err(|err| vm.new_type_error(format!("{}", err)))?; + let string = ser_pyobject(vm, obj).map_err(|err| vm.new_type_error(err.to_string()))?; Ok(vm.context().new_str(string)) } @@ -204,15 +211,8 @@ fn json_dumps(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn json_loads(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // TODO: Implement non-trivial deserialization case arg_check!(vm, args, required = [(string, Some(vm.ctx.str_type()))]); - let res = { - let de = PyObjectDeserializer { vm }; - // TODO: Support deserializing string sub-classes - de.deserialize(&mut serde_json::Deserializer::from_str(&objstr::get_value( - &string, - ))) - }; - - res.map_err(|err| { + + de_pyobject(vm, &objstr::get_value(&string)).map_err(|err| { let json_decode_error = vm .sys_module .get_item("modules") diff --git a/vm/src/stdlib/mod.rs b/vm/src/stdlib/mod.rs index c3bb543f52..b813f93f48 100644 --- a/vm/src/stdlib/mod.rs +++ b/vm/src/stdlib/mod.rs @@ -1,6 +1,6 @@ mod ast; mod dis; -mod json; +pub mod json; mod keyword; mod math; mod platform; diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index 42d4675f00..084a84e677 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -111,13 +111,14 @@ pub fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { return func; } - // the browser module might not be injected - if let Ok(promise_type) = browser_module::import_promise_type(vm) { - if objtype::isinstance(&py_obj, &promise_type) { - return browser_module::get_promise_value(&py_obj).into(); - } + } + // the browser module might not be injected + if let Ok(promise_type) = browser_module::import_promise_type(vm) { + if objtype::isinstance(&py_obj, &promise_type) { + return browser_module::get_promise_value(&py_obj).into(); } } + if objtype::isinstance(&py_obj, &vm.ctx.bytes_type()) || objtype::isinstance(&py_obj, &vm.ctx.bytearray_type()) { @@ -129,15 +130,8 @@ pub fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { } arr.into() } else { - let dumps = rustpython_vm::import::import_module(vm, std::path::PathBuf::default(), "json") - .expect("Couldn't get json module") - .get_attr("dumps".into()) - .expect("Couldn't get json dumps"); - match vm.invoke(dumps, PyFuncArgs::new(vec![py_obj], vec![])) { - Ok(value) => { - let json = vm.to_pystr(&value).unwrap(); - js_sys::JSON::parse(&json).unwrap_or(JsValue::UNDEFINED) - } + match rustpython_vm::stdlib::json::ser_pyobject(vm, &py_obj) { + Ok(json) => js_sys::JSON::parse(&json).unwrap_or(JsValue::UNDEFINED), Err(_) => JsValue::UNDEFINED, } } @@ -229,19 +223,10 @@ pub fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef { // Because `JSON.stringify(undefined)` returns undefined vm.get_none() } else { - let loads = rustpython_vm::import::import_module(vm, std::path::PathBuf::default(), "json") - .expect("Couldn't get json module") - .get_attr("loads".into()) - .expect("Couldn't get json dumps"); - 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, PyFuncArgs::new(vec![py_json], vec![])) - // can safely unwrap because we know it's valid JSON - .unwrap() + rustpython_vm::stdlib::json::de_pyobject(vm, &json).unwrap_or_else(|_| vm.get_none()) } } From 4e42bd077c371a53bf987c55fa1a1393b22124d1 Mon Sep 17 00:00:00 2001 From: ben Date: Sat, 16 Mar 2019 17:31:01 +1300 Subject: [PATCH 351/380] Implement __setattr__ based on @nhynes(#540) PR --- vm/src/builtins.rs | 17 ++++++++--------- vm/src/frame.rs | 2 +- vm/src/obj/objobject.rs | 13 ++++++++++++- vm/src/vm.rs | 9 +++++++++ 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 85ce77af06..e225773d66 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -658,15 +658,14 @@ fn builtin_round(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn builtin_setattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(obj, None), (attr, Some(vm.ctx.str_type())), (value, None)] - ); - let name = objstr::get_value(attr); - vm.ctx.set_attr(obj, &name, value.clone()); - Ok(vm.get_none()) +fn builtin_setattr( + obj: PyObjectRef, + attr: PyStringRef, + value: PyObjectRef, + vm: &mut VirtualMachine, +) -> PyResult<()> { + vm.set_attr(&obj, attr.into_object(), value)?; + Ok(()) } // builtin_slice diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 4482e55de3..6f8653f881 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -1146,7 +1146,7 @@ impl Frame { fn store_attr(&self, vm: &mut VirtualMachine, attr_name: &str) -> FrameResult { let parent = self.pop_value(); let value = self.pop_value(); - vm.ctx.set_attr(&parent, attr_name, value); + vm.set_attr(&parent, vm.new_str(attr_name.to_string()), value)?; Ok(None) } diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 7615f4bb17..e4d979db67 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -93,7 +93,17 @@ fn object_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Err(vm.new_type_error("unhashable type".to_string())) } -// TODO: is object the right place for delattr? +fn object_setattr( + obj: PyInstanceRef, + name: PyStringRef, + value: PyObjectRef, + vm: &mut VirtualMachine, +) -> PyResult { + trace!("object.__setattr__({:?}, {}, {:?})", obj, name, value); + vm.ctx.set_attr(obj.as_object(), &name.value, value); + Ok(vm.get_none()) +} + fn object_delattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -167,6 +177,7 @@ pub fn init(context: &PyContext) { 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, "__setattr__", context.new_rustfunc(object_setattr)); context.set_attr(&object, "__delattr__", context.new_rustfunc(object_delattr)); context.set_attr(&object, "__dict__", context.new_property(object_dict)); context.set_attr(&object, "__dir__", context.new_rustfunc(object_dir)); diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 3b3799d0ff..593c15ea93 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -547,6 +547,15 @@ impl VirtualMachine { self.call_method(&obj, "__getattribute__", vec![attr_name]) } + pub fn set_attr( + &mut self, + obj: &PyObjectRef, + attr_name: PyObjectRef, + attr_value: PyObjectRef, + ) -> PyResult { + self.call_method(&obj, "__setattr__", vec![attr_name, attr_value]) + } + pub fn del_attr(&mut self, obj: &PyObjectRef, attr_name: PyObjectRef) -> PyResult { self.call_method(&obj, "__delattr__", vec![attr_name]) } From 63d40edbc644930c3bcac4a2df930d04b8e893d5 Mon Sep 17 00:00:00 2001 From: ben Date: Sat, 16 Mar 2019 20:49:29 +1300 Subject: [PATCH 352/380] Added bunch af attribute has/get/set/delete tests and fixed getattr and hasattr handling of exceptions. --- tests/snippets/attr.py | 84 ++++++++++++++++++++++++++++++++++++++++++ vm/src/builtins.rs | 46 +++++++++++++---------- 2 files changed, 111 insertions(+), 19 deletions(-) create mode 100644 tests/snippets/attr.py diff --git a/tests/snippets/attr.py b/tests/snippets/attr.py new file mode 100644 index 0000000000..439acfa795 --- /dev/null +++ b/tests/snippets/attr.py @@ -0,0 +1,84 @@ +from testutils import assertRaises + + +class A: + pass + + +a = A() +a.b = 10 +assert hasattr(a, 'b') +assert a.b == 10 + +# test override attribute +setattr(a, 'b', 12) +assert a.b == 12 +assert getattr(a, 'b') == 12 + +# test non-existent attribute +with assertRaises(AttributeError): + _ = a.c + +with assertRaises(AttributeError): + getattr(a, 'c') + +assert getattr(a, 'c', 21) == 21 + +# test set attribute +setattr(a, 'c', 20) +assert hasattr(a, 'c') +assert a.c == 20 + +# test delete attribute +delattr(a, 'c') +assert not hasattr(a, 'c') +with assertRaises(AttributeError): + _ = a.c + +# test setting attribute on builtin +with assertRaises(AttributeError): + None.a = 1 + +with assertRaises(AttributeError): + setattr(None, 'a', 2) + + +attrs = {} + + +class CustomLookup: + def __getattr__(self, item): + return "value_{}".format(item) + + def __setattr__(self, key, value): + attrs[key] = value + + +custom = CustomLookup() + +assert custom.attr == "value_attr" + +custom.a = 2 +custom.b = 5 +assert attrs == {'a': 2, 'b': 5} + + +class GetRaise: + def __init__(self, ex): + self.ex = ex + + def __getattr__(self, item): + raise self.ex + + +assert not hasattr(GetRaise(AttributeError()), 'a') +with assertRaises(AttributeError): + getattr(GetRaise(AttributeError()), 'a') +assert getattr(GetRaise(AttributeError()), 'a', 11) == 11 + +with assertRaises(KeyError): + hasattr(GetRaise(KeyError()), 'a') +with assertRaises(KeyError): + getattr(GetRaise(KeyError()), 'a') +with assertRaises(KeyError): + getattr(GetRaise(KeyError()), 'a', 11) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index e225773d66..dccaeee841 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -18,7 +18,7 @@ use crate::obj::objstr::{self, PyStringRef}; use crate::obj::objtype; use crate::frame::Scope; -use crate::function::{Args, PyFuncArgs}; +use crate::function::{Args, OptionalArg, PyFuncArgs}; use crate::pyobject::{ AttributeProtocol, IdProtocol, PyContext, PyObjectRef, PyResult, TypeProtocol, }; @@ -302,30 +302,38 @@ fn builtin_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm.call_method(obj, "__format__", vec![format_spec]) } -fn builtin_getattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(obj, None), (attr, Some(vm.ctx.str_type()))] - ); - vm.get_attribute(obj.clone(), attr.clone()) +fn catch_attr_exception(ex: PyObjectRef, default: T, vm: &mut VirtualMachine) -> PyResult { + if objtype::isinstance(&ex, &vm.ctx.exceptions.attribute_error) { + Ok(default) + } else { + Err(ex) + } +} + +fn builtin_getattr( + obj: PyObjectRef, + attr: PyStringRef, + default: OptionalArg, + vm: &mut VirtualMachine, +) -> PyResult { + let ret = vm.get_attribute(obj.clone(), attr.into_object()); + if let OptionalArg::Present(default) = default { + ret.or_else(|ex| catch_attr_exception(ex, default, vm)) + } else { + ret + } } fn builtin_globals(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { Ok(vm.current_scope().globals.clone()) } -fn builtin_hasattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(obj, None), (attr, Some(vm.ctx.str_type()))] - ); - let has_attr = match vm.get_attribute(obj.clone(), attr.clone()) { - Ok(..) => true, - Err(..) => false, - }; - Ok(vm.context().new_bool(has_attr)) +fn builtin_hasattr(obj: PyObjectRef, attr: PyStringRef, vm: &mut VirtualMachine) -> PyResult { + if let Err(ex) = vm.get_attribute(obj.clone(), attr.into_object()) { + catch_attr_exception(ex, false, vm) + } else { + Ok(true) + } } fn builtin_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From c77b1f3cf5adee9286d70c969115e9b7d5a3c4db Mon Sep 17 00:00:00 2001 From: ben Date: Sun, 17 Mar 2019 11:31:31 +1300 Subject: [PATCH 353/380] Fix bug with __getattr__ --- tests/snippets/attr.py | 14 ++++++++------ tests/snippets/testutils.py | 2 +- vm/src/obj/objobject.rs | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/snippets/attr.py b/tests/snippets/attr.py index 439acfa795..9ee0b071ca 100644 --- a/tests/snippets/attr.py +++ b/tests/snippets/attr.py @@ -35,12 +35,13 @@ class A: with assertRaises(AttributeError): _ = a.c +# TODO Fix # test setting attribute on builtin -with assertRaises(AttributeError): - None.a = 1 - -with assertRaises(AttributeError): - setattr(None, 'a', 2) +# with assertRaises(AttributeError): +# None.a = 1 +# +# with assertRaises(AttributeError): +# setattr(None, 'a', 2) attrs = {} @@ -60,7 +61,8 @@ def __setattr__(self, key, value): custom.a = 2 custom.b = 5 -assert attrs == {'a': 2, 'b': 5} +assert attrs['a'] == 2 +assert attrs['b'] == 5 class GetRaise: diff --git a/tests/snippets/testutils.py b/tests/snippets/testutils.py index 8237ceb621..7dda57b17a 100644 --- a/tests/snippets/testutils.py +++ b/tests/snippets/testutils.py @@ -29,7 +29,7 @@ def __enter__(self): def __exit__(self, exc_type, exc_val, exc_tb): if exc_type is None: - failmsg = '{!s} was not raised'.format(self.expected.__name_) + failmsg = '{!s} was not raised'.format(self.expected.__name__) assert False, failmsg if not issubclass(exc_type, self.expected): return False diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index e4d979db67..56dc80b56a 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -249,7 +249,7 @@ fn object_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } 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, vec![cls, name_str.clone()]) + vm.invoke(getter, vec![obj.clone(), name_str.clone()]) } else { Err(vm.new_attribute_error(format!("{} has no attribute '{}'", obj, name))) } From 48dd507cad1503e2c55ba0d27d5668ebce8e2a50 Mon Sep 17 00:00:00 2001 From: ben Date: Sun, 17 Mar 2019 12:22:31 +1300 Subject: [PATCH 354/380] Remove dict from object --- tests/snippets/attr.py | 11 +++++------ vm/src/obj/objobject.rs | 31 +++++++++++++++++++++---------- vm/src/pyobject.rs | 9 +++++++++ 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/tests/snippets/attr.py b/tests/snippets/attr.py index 9ee0b071ca..cf7162cde9 100644 --- a/tests/snippets/attr.py +++ b/tests/snippets/attr.py @@ -35,14 +35,13 @@ class A: with assertRaises(AttributeError): _ = a.c -# TODO Fix + # test setting attribute on builtin -# with assertRaises(AttributeError): -# None.a = 1 -# -# with assertRaises(AttributeError): -# setattr(None, 'a', 2) +with assertRaises(AttributeError): + object().a = 1 +with assertRaises(AttributeError): + setattr(object(), 'a', 2) attrs = {} diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 56dc80b56a..43addcbc45 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -4,8 +4,8 @@ use super::objtype; use crate::function::PyFuncArgs; use crate::obj::objproperty::PropertyBuilder; use crate::pyobject::{ - AttributeProtocol, DictProtocol, IdProtocol, PyAttributes, PyContext, PyObjectRef, PyRef, - PyResult, PyValue, TypeProtocol, + AttributeProtocol, DictProtocol, IdProtocol, PyAttributes, PyContext, PyObject, PyObjectRef, + PyRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -22,9 +22,12 @@ pub type PyInstanceRef = PyRef; pub fn new_instance(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { // more or less __new__ operator - let type_ref = args.shift(); - let obj = vm.ctx.new_instance(type_ref.clone(), None); - Ok(obj) + let cls = args.shift(); + Ok(if cls.is(&vm.ctx.object) { + PyObject::new_without_dict(PyInstance, cls) + } else { + PyObject::new(PyInstance, cls) + }) } fn object_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -95,13 +98,21 @@ fn object_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn object_setattr( obj: PyInstanceRef, - name: PyStringRef, + attr_name: PyStringRef, value: PyObjectRef, vm: &mut VirtualMachine, -) -> PyResult { - trace!("object.__setattr__({:?}, {}, {:?})", obj, name, value); - vm.ctx.set_attr(obj.as_object(), &name.value, value); - Ok(vm.get_none()) +) -> PyResult<()> { + trace!("object.__setattr__({:?}, {}, {:?})", obj, attr_name, value); + if let Some(ref dict) = obj.as_object().dict { + dict.borrow_mut().insert(attr_name.value.clone(), value); + Ok(()) + } else { + let type_name = objtype::get_type_name(&obj.as_object().typ()); + Err(vm.new_attribute_error(format!( + "'{}' object has no attribute '{}'", + type_name, &attr_name.value + ))) + } } fn object_delattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index afcf41c89a..ef9f51aa96 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1112,6 +1112,15 @@ impl PyObject { .into_ref() } + pub fn new_without_dict(payload: T, typ: PyObjectRef) -> PyObjectRef { + PyObject { + typ, + dict: None, + payload: Box::new(payload), + } + .into_ref() + } + // Move this object into a reference object, transferring ownership. pub fn into_ref(self) -> PyObjectRef { Rc::new(self) From 6c3a402eca7e70eae95299b3468c5906fb343113 Mon Sep 17 00:00:00 2001 From: ben Date: Sun, 17 Mar 2019 12:38:12 +1300 Subject: [PATCH 355/380] Fix setting attributes on modules --- vm/src/obj/objmodule.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/vm/src/obj/objmodule.rs b/vm/src/obj/objmodule.rs index 6ef1cd012c..da7d282486 100644 --- a/vm/src/obj/objmodule.rs +++ b/vm/src/obj/objmodule.rs @@ -1,3 +1,4 @@ +use crate::obj::objstr::PyStringRef; use crate::pyobject::{DictProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; @@ -24,10 +25,15 @@ impl PyModuleRef { .collect(); Ok(vm.ctx.new_list(keys)) } + + fn set_attr(self, attr: PyStringRef, value: PyObjectRef, vm: &mut VirtualMachine) { + self.dict.set_item(&vm.ctx, &attr.value, value) + } } pub fn init(context: &PyContext) { extend_class!(&context, &context.module_type, { - "__dir__" => context.new_rustfunc(PyModuleRef::dir) + "__dir__" => context.new_rustfunc(PyModuleRef::dir), + "__setattr__" => context.new_rustfunc(PyModuleRef::set_attr) }); } From a5050ebafec9812d0a8d871fd10cdb24b36e3b54 Mon Sep 17 00:00:00 2001 From: ben Date: Sun, 17 Mar 2019 14:49:32 +1300 Subject: [PATCH 356/380] Make property return itself if invoke by class binding --- tests/snippets/property.py | 4 ++++ vm/src/obj/objproperty.rs | 14 +++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/tests/snippets/property.py b/tests/snippets/property.py index 50f8e8fc8d..2965224e66 100644 --- a/tests/snippets/property.py +++ b/tests/snippets/property.py @@ -16,6 +16,8 @@ def foo(self): assert f.foo == 100 assert f.foo == 101 +assert type(Fubar.foo) is property + null_property = property() assert type(null_property) is property @@ -45,3 +47,5 @@ def foo(self): assert p1.getter(None).fget == "a" assert p1.setter(None).fset == "b" assert p1.deleter(None).fdel == "c" + +assert p1.__get__(None, object) is p1 diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index e403bb53ab..060bb07fa4 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -6,7 +6,7 @@ use crate::function::IntoPyNativeFunc; use crate::function::OptionalArg; use crate::obj::objstr::PyStringRef; use crate::obj::objtype::PyClassRef; -use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue}; +use crate::pyobject::{IdProtocol, PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; /// Read-only property, doesn't have __set__ or __delete__ @@ -25,7 +25,11 @@ pub type PyReadOnlyPropertyRef = PyRef; impl PyReadOnlyPropertyRef { fn get(self, obj: PyObjectRef, _owner: PyClassRef, vm: &mut VirtualMachine) -> PyResult { - vm.invoke(self.getter.clone(), obj) + if obj.is(&vm.ctx.none) { + Ok(self.into_object()) + } else { + vm.invoke(self.getter.clone(), obj) + } } } @@ -66,7 +70,11 @@ impl PyPropertyRef { fn get(self, obj: PyObjectRef, _owner: PyClassRef, vm: &mut VirtualMachine) -> PyResult { if let Some(getter) = self.getter.as_ref() { - vm.invoke(getter.clone(), obj) + if obj.is(&vm.ctx.none) { + Ok(self.into_object()) + } else { + vm.invoke(getter.clone(), obj) + } } else { Err(vm.new_attribute_error("unreadable attribute".to_string())) } From 08e66b5002f3a3a30db72b350b18c61d367ef1fc Mon Sep 17 00:00:00 2001 From: ben Date: Sun, 17 Mar 2019 19:53:16 +1300 Subject: [PATCH 357/380] Create workaround for properties on None --- vm/src/obj/objnone.rs | 57 ++++++++++++++++++++++++++++++++++++++- vm/src/obj/objproperty.rs | 13 +++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/vm/src/obj/objnone.rs b/vm/src/obj/objnone.rs index fe63ecd984..1c00512199 100644 --- a/vm/src/obj/objnone.rs +++ b/vm/src/obj/objnone.rs @@ -1,6 +1,9 @@ use crate::function::PyFuncArgs; +use crate::obj::objproperty::PyPropertyRef; +use crate::obj::objstr::PyStringRef; use crate::pyobject::{ - IntoPyObject, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, + AttributeProtocol, IntoPyObject, PyContext, PyObjectRef, PyRef, PyResult, PyValue, + TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -39,6 +42,57 @@ impl PyNoneRef { fn bool(self, _vm: &mut VirtualMachine) -> PyResult { Ok(false) } + + fn get_attribute(self, name: PyStringRef, vm: &mut VirtualMachine) -> PyResult { + trace!("None.__getattribute__({:?}, {:?})", self, name); + let cls = self.typ().into_object(); + + // Properties use a comparision with None to determine if they are either invoked by am + // instance binding or a class binding. But if the object itself is None then this detection + // won't work. Instead we call a special function on property that bypasses this check, as + // we are invoking it as a instance binding. + // + // In CPython they instead call the slot tp_descr_get with NULL to indicates it's an + // instance binding. + // https://github.com/python/cpython/blob/master/Objects/typeobject.c#L3281 + fn call_descriptor( + descriptor: PyObjectRef, + get_func: PyObjectRef, + obj: PyObjectRef, + cls: PyObjectRef, + vm: &mut VirtualMachine, + ) -> PyResult { + if let Ok(property) = PyPropertyRef::try_from_object(vm, descriptor.clone()) { + property.instance_binding_get(obj, vm) + } else { + vm.invoke(get_func, vec![descriptor, obj, cls]) + } + } + + if let Some(attr) = cls.get_attr(&name.value) { + let attr_class = attr.typ(); + if attr_class.has_attr("__set__") { + if let Some(get_func) = attr_class.get_attr("__get__") { + return call_descriptor(attr, get_func, self.into_object(), cls.clone(), vm); + } + } + } + + if let Some(obj_attr) = self.as_object().get_attr(&name.value) { + Ok(obj_attr) + } else if let Some(attr) = cls.get_attr(&name.value) { + let attr_class = attr.typ(); + if let Some(get_func) = attr_class.get_attr("__get__") { + call_descriptor(attr, get_func, self.into_object(), cls.clone(), vm) + } else { + Ok(attr) + } + } else if let Some(getter) = cls.get_attr("__getattr__") { + vm.invoke(getter, vec![self.into_object(), name.into_object()]) + } else { + Err(vm.new_attribute_error(format!("{} has no attribute '{}'", self.as_object(), name))) + } + } } fn none_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -55,5 +109,6 @@ pub fn init(context: &PyContext) { "__new__" => context.new_rustfunc(none_new), "__repr__" => context.new_rustfunc(PyNoneRef::repr), "__bool__" => context.new_rustfunc(PyNoneRef::bool), + "__getattribute__" => context.new_rustfunc(PyNoneRef::get_attribute) }); } diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index 060bb07fa4..5369f274d8 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -68,6 +68,19 @@ impl PyPropertyRef { // Descriptor methods + // specialised version that doesn't check for None + pub(crate) fn instance_binding_get( + self, + obj: PyObjectRef, + vm: &mut VirtualMachine, + ) -> PyResult { + if let Some(getter) = self.getter.as_ref() { + vm.invoke(getter.clone(), obj) + } else { + Err(vm.new_attribute_error("unreadable attribute".to_string())) + } + } + fn get(self, obj: PyObjectRef, _owner: PyClassRef, vm: &mut VirtualMachine) -> PyResult { if let Some(getter) = self.getter.as_ref() { if obj.is(&vm.ctx.none) { From 1958e470221393167690f270d2969b237c225a22 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Wed, 20 Mar 2019 09:20:28 +0000 Subject: [PATCH 358/380] Add __class__ cell to method scopes. --- tests/snippets/class.py | 3 +++ vm/src/builtins.rs | 9 ++++++--- vm/src/vm.rs | 11 +++++++++-- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/tests/snippets/class.py b/tests/snippets/class.py index 42144d720b..84ed872460 100644 --- a/tests/snippets/class.py +++ b/tests/snippets/class.py @@ -22,15 +22,18 @@ def __init__(self, x): self.x = x def get_x(self): + assert __class__ is Bar return self.x @classmethod def fubar(cls, x): + assert __class__ is cls assert cls is Bar assert x == 2 @staticmethod def kungfu(x): + assert __class__ is Bar assert x == 3 diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index dccaeee841..432d661a07 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -20,7 +20,7 @@ use crate::obj::objtype; use crate::frame::Scope; use crate::function::{Args, OptionalArg, PyFuncArgs}; use crate::pyobject::{ - AttributeProtocol, IdProtocol, PyContext, PyObjectRef, PyResult, TypeProtocol, + AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -842,7 +842,10 @@ pub fn builtin_build_class_(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> Py let prepare = vm.get_attribute(metaclass.clone(), prepare_name)?; let namespace = vm.invoke(prepare, vec![name_arg.clone(), bases.clone()])?; - vm.invoke_with_locals(function, namespace.clone())?; + let cells = vm.new_dict(); - vm.call_method(&metaclass, "__call__", vec![name_arg, bases, namespace]) + vm.invoke_with_locals(function, cells.clone(), namespace.clone())?; + let class = vm.call_method(&metaclass, "__call__", vec![name_arg, bases, namespace])?; + cells.set_item(&vm.ctx, "__class__", class.clone()); + Ok(class) } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 593c15ea93..47cc162a8f 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -359,14 +359,21 @@ impl VirtualMachine { } } - pub fn invoke_with_locals(&mut self, function: PyObjectRef, locals: PyObjectRef) -> PyResult { + pub fn invoke_with_locals( + &mut self, + function: PyObjectRef, + cells: PyObjectRef, + locals: PyObjectRef, + ) -> PyResult { if let Some(PyFunction { code, scope, defaults: _, }) = &function.payload() { - let scope = scope.child_scope_with_locals(locals); + let scope = scope + .child_scope_with_locals(cells) + .child_scope_with_locals(locals); let frame = self.ctx.new_frame(code.clone(), scope); return self.run_frame_full(frame); } From e35cc813a896cf72d9d4bf1f100d595224529a44 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Wed, 20 Mar 2019 08:58:35 -0500 Subject: [PATCH 359/380] Rename to Unnamed and Named --- parser/src/ast.rs | 8 ++++---- vm/src/bytecode.rs | 12 ++++++------ vm/src/vm.rs | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index b27c231d40..257ceadf2a 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -395,8 +395,8 @@ pub enum StringGroup { #[derive(Debug, PartialEq)] pub enum Varargs { None, - NoCapture, - Capture(Parameter), + Unnamed, + Named(Parameter), } impl Default for Varargs { @@ -409,8 +409,8 @@ impl From>> for Varargs { fn from(opt: Option>) -> Varargs { match opt { Some(inner_opt) => match inner_opt { - Some(param) => Varargs::Capture(param), - None => Varargs::NoCapture, + Some(param) => Varargs::Named(param), + None => Varargs::Unnamed, }, None => Varargs::None, } diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index de6f590477..2fe485b63b 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -240,8 +240,8 @@ pub enum UnaryOperator { #[derive(Debug, Clone, PartialEq)] pub enum Varargs { None, - NoCapture, - Capture(String), + Unnamed, + Named(String), } /* @@ -413,8 +413,8 @@ impl From for Varargs { fn from(varargs: ast::Varargs) -> Varargs { match varargs { ast::Varargs::None => Varargs::None, - ast::Varargs::NoCapture => Varargs::NoCapture, - ast::Varargs::Capture(param) => Varargs::Capture(param.arg), + ast::Varargs::Unnamed => Varargs::Unnamed, + ast::Varargs::Named(param) => Varargs::Named(param.arg), } } } @@ -423,8 +423,8 @@ impl<'a> From<&'a ast::Varargs> for Varargs { fn from(varargs: &'a ast::Varargs) -> Varargs { match varargs { ast::Varargs::None => Varargs::None, - ast::Varargs::NoCapture => Varargs::NoCapture, - ast::Varargs::Capture(ref param) => Varargs::Capture(param.arg.clone()), + ast::Varargs::Unnamed => Varargs::Unnamed, + ast::Varargs::Named(ref param) => Varargs::Named(param.arg.clone()), } } } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 8050e70c61..9a3578f771 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -402,7 +402,7 @@ impl VirtualMachine { // Pack other positional arguments in to *args: match code_object.varargs { - bytecode::Varargs::Capture(ref vararg_name) => { + bytecode::Varargs::Named(ref vararg_name) => { let mut last_args = vec![]; for i in n..nargs { let arg = &args.args[i]; @@ -412,7 +412,7 @@ impl VirtualMachine { locals.set_item(&self.ctx, vararg_name, vararg_value); } - bytecode::Varargs::NoCapture => { + bytecode::Varargs::Unnamed => { // just ignore the rest of the args } bytecode::Varargs::None => { @@ -428,12 +428,12 @@ impl VirtualMachine { // Do we support `**kwargs` ? let kwargs = match code_object.varkeywords { - bytecode::Varargs::Capture(ref kwargs_name) => { + bytecode::Varargs::Named(ref kwargs_name) => { let d = self.new_dict(); locals.set_item(&self.ctx, kwargs_name, d.clone()); Some(d) } - bytecode::Varargs::NoCapture => Some(self.new_dict()), + bytecode::Varargs::Unnamed => Some(self.new_dict()), bytecode::Varargs::None => None, }; From 8c0419e6b4db42c13d1b0da5c9f98bf3516fffe7 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Wed, 20 Mar 2019 20:29:08 +0100 Subject: [PATCH 360/380] Put RefCell around frames member of VirtualMachine. This allows for VirtualMachine to be immutable. --- vm/src/sysmodule.rs | 6 +++--- vm/src/vm.rs | 22 +++++++++++++--------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index e2713d7834..966c6b5a91 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -21,7 +21,7 @@ fn argv(ctx: &PyContext) -> PyObjectRef { fn frame_idx(vm: &mut VirtualMachine, offset: Option<&PyObjectRef>) -> Result { if let Some(int) = offset { if let Some(offset) = objint::get_value(&int).to_usize() { - if offset > vm.frames.len() - 1 { + if offset > vm.frames.borrow().len() - 1 { return Err(vm.new_value_error("call stack is not deep enough".to_string())); } return Ok(offset); @@ -39,8 +39,8 @@ fn getframe(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let idx = frame_idx(vm, offset)?; - let idx = vm.frames.len() - idx - 1; - let frame = &vm.frames[idx]; + let idx = vm.frames.borrow().len() - idx - 1; + let frame = &vm.frames.borrow()[idx]; Ok(frame.clone()) } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index ac97e8e9c3..4ec850eeae 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -6,6 +6,7 @@ extern crate rustpython_parser; +use std::cell::{Ref, RefCell}; use std::collections::hash_map::HashMap; use std::collections::hash_set::HashSet; use std::rc::Rc; @@ -46,7 +47,7 @@ pub struct VirtualMachine { pub sys_module: PyObjectRef, pub stdlib_inits: HashMap, pub ctx: PyContext, - pub frames: Vec, + pub frames: RefCell>, pub wasm_id: Option, } @@ -69,7 +70,7 @@ impl VirtualMachine { sys_module: sysmod, stdlib_inits, ctx, - frames: vec![], + frames: RefCell::new(vec![]), wasm_id: None, } } @@ -87,21 +88,24 @@ impl VirtualMachine { } pub fn run_frame(&mut self, frame: PyObjectRef) -> PyResult { - self.frames.push(frame.clone()); + self.frames.borrow_mut().push(frame.clone()); let frame = objframe::get_value(&frame); let result = frame.run(self); - self.frames.pop(); + self.frames.borrow_mut().pop(); result } - pub fn current_frame(&self) -> &Frame { - let current_frame = &self.frames[self.frames.len() - 1]; - objframe::get_value(current_frame) + pub fn current_frame(&self) -> Ref { + Ref::map(self.frames.borrow(), |frames| { + let index = frames.len() - 1; + let current_frame = &frames[index]; + objframe::get_value(current_frame) + }) } - pub fn current_scope(&self) -> &Scope { + pub fn current_scope(&self) -> Ref { let frame = self.current_frame(); - &frame.scope + Ref::map(frame, |f| &f.scope) } pub fn class(&mut self, module: &str, class: &str) -> PyObjectRef { From 54753b3d1407844a84672ac78b5c74fd312be63e Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Wed, 20 Mar 2019 18:32:08 -0700 Subject: [PATCH 361/380] list, tuple: return NotImplemented for unsupported comparisons --- vm/src/obj/objlist.rs | 37 +++++++++++++++++++++---------------- vm/src/obj/objtuple.rs | 37 +++++++++++++++++++++---------------- 2 files changed, 42 insertions(+), 32 deletions(-) diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index f435e6ea8d..903cd760b4 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -245,57 +245,62 @@ impl PyListRef { } } - fn eq(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn eq(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { if self.as_object().is(&other) { - return Ok(true); + return Ok(vm.new_bool(true)); } if objtype::isinstance(&other, &vm.ctx.list_type()) { let zelf = self.elements.borrow(); let other = get_elements(&other); - Ok(seq_equal(vm, &zelf, &other)?) + let res = seq_equal(vm, &zelf, &other)?; + Ok(vm.new_bool(res)) } else { - Ok(false) + Ok(vm.ctx.not_implemented()) } } - fn lt(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn lt(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.list_type()) { let zelf = self.elements.borrow(); let other = get_elements(&other); - Ok(seq_lt(vm, &zelf, &other)?) + let res = seq_lt(vm, &zelf, &other)?; + Ok(vm.new_bool(res)) } else { - Err(vm.new_type_error(format!("Cannot compare {} and {} using '<'", self, other))) + Ok(vm.ctx.not_implemented()) } } - fn gt(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn gt(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.list_type()) { let zelf = self.elements.borrow(); let other = get_elements(&other); - Ok(seq_gt(vm, &zelf, &other)?) + let res = seq_gt(vm, &zelf, &other)?; + Ok(vm.new_bool(res)) } else { - Err(vm.new_type_error(format!("Cannot compare {} and {} using '>'", self, other))) + Ok(vm.ctx.not_implemented()) } } - fn ge(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn ge(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.list_type()) { let zelf = self.elements.borrow(); let other = get_elements(&other); - Ok(seq_ge(vm, &zelf, &other)?) + let res = seq_ge(vm, &zelf, &other)?; + Ok(vm.new_bool(res)) } else { - Err(vm.new_type_error(format!("Cannot compare {} and {} using '>='", self, other))) + Ok(vm.ctx.not_implemented()) } } - fn le(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn le(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.list_type()) { let zelf = self.elements.borrow(); let other = get_elements(&other); - Ok(seq_le(vm, &zelf, &other)?) + let res = seq_le(vm, &zelf, &other)?; + Ok(vm.new_bool(res)) } else { - Err(vm.new_type_error(format!("Cannot compare {} and {} using '<='", self, other))) + Ok(vm.ctx.not_implemented()) } } } diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 11d6a36947..d1986bce0d 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -46,43 +46,47 @@ impl PyValue for PyTuple { pub type PyTupleRef = PyRef; impl PyTupleRef { - fn lt(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn lt(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.tuple_type()) { let zelf = self.elements.borrow(); let other = get_elements(&other); - Ok(seq_lt(vm, &zelf, &other)?) + let res = seq_lt(vm, &zelf, &other)?; + Ok(vm.new_bool(res)) } else { - Err(vm.new_type_error(format!("Cannot compare {} and {} using '<'", self, other))) + Ok(vm.ctx.not_implemented()) } } - fn gt(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn gt(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.tuple_type()) { let zelf = self.elements.borrow(); let other = get_elements(&other); - Ok(seq_gt(vm, &zelf, &other)?) + let res = seq_gt(vm, &zelf, &other)?; + Ok(vm.new_bool(res)) } else { - Err(vm.new_type_error(format!("Cannot compare {} and {} using '>'", self, other))) + Ok(vm.ctx.not_implemented()) } } - fn ge(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn ge(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.tuple_type()) { let zelf = self.elements.borrow(); let other = get_elements(&other); - Ok(seq_ge(vm, &zelf, &other)?) + let res = seq_ge(vm, &zelf, &other)?; + Ok(vm.new_bool(res)) } else { - Err(vm.new_type_error(format!("Cannot compare {} and {} using '>='", self, other))) + Ok(vm.ctx.not_implemented()) } } - fn le(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn le(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.tuple_type()) { let zelf = self.elements.borrow(); let other = get_elements(&other); - Ok(seq_le(vm, &zelf, &other)?) + let res = seq_le(vm, &zelf, &other)?; + Ok(vm.new_bool(res)) } else { - Err(vm.new_type_error(format!("Cannot compare {} and {} using '<='", self, other))) + Ok(vm.ctx.not_implemented()) } } @@ -93,7 +97,7 @@ impl PyTupleRef { let elements = e1.iter().chain(e2.iter()).cloned().collect(); Ok(vm.ctx.new_tuple(elements)) } else { - Err(vm.new_type_error(format!("Cannot add {} and {}", self, other))) + Ok(vm.ctx.not_implemented()) } } @@ -112,13 +116,14 @@ impl PyTupleRef { Ok(count) } - fn eq(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn eq(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.tuple_type()) { let zelf = &self.elements.borrow(); let other = get_elements(&other); - seq_equal(vm, &zelf, &other) + let res = seq_equal(vm, &zelf, &other)?; + Ok(vm.new_bool(res)) } else { - Ok(false) + Ok(vm.ctx.not_implemented()) } } From 0b1b6682c16eee99c44bfd9654aea42445311c80 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Wed, 20 Mar 2019 18:32:25 -0700 Subject: [PATCH 362/380] Fix Display impl for PyRef --- vm/src/obj/objstr.rs | 9 ++++----- vm/src/obj/objtype.rs | 7 +++++++ vm/src/pyobject.rs | 8 ++++++-- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 8569280ef9..139251f2a0 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -1,3 +1,4 @@ +use std::fmt; use std::hash::{Hash, Hasher}; use std::ops::Range; use std::str::FromStr; @@ -25,11 +26,9 @@ pub struct PyString { pub value: String, } -impl From for PyString { - fn from(t: T) -> PyString { - PyString { - value: t.to_string(), - } +impl fmt::Display for PyString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.value, f) } } diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index adb8b236d4..78c946dbc1 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -1,5 +1,6 @@ use std::cell::RefCell; use std::collections::HashMap; +use std::fmt; use crate::function::PyFuncArgs; use crate::pyobject::{ @@ -20,6 +21,12 @@ pub struct PyClass { pub mro: Vec, } +impl fmt::Display for PyClass { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.name, f) + } +} + pub type PyClassRef = PyRef; impl PyValue for PyClass { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index cd1ca77e66..fa84fc58e4 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -768,9 +768,13 @@ impl IntoPyObject for PyRef { } } -impl fmt::Display for PyRef { +impl fmt::Display for PyRef +where + T: PyValue + fmt::Display, +{ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.obj.fmt(f) + let value: &T = self.obj.payload().expect("unexpected payload for type"); + fmt::Display::fmt(value, f) } } From d024e7ed8d0a57a72a5602782213a5ed5389b774 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Thu, 21 Mar 2019 00:08:36 -0500 Subject: [PATCH 363/380] Switch from an stdlib function to a vm method --- vm/src/stdlib/json.rs | 37 +++++++++++++++++++------------------ vm/src/stdlib/mod.rs | 2 +- vm/src/vm.rs | 8 ++++++++ 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index 5f313c8630..81fb364d48 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -188,22 +188,36 @@ impl<'de> Visitor<'de> for PyObjectDeserializer<'de> { } } -pub fn ser_pyobject(vm: &mut VirtualMachine, obj: &PyObjectRef) -> serde_json::Result { +pub fn ser_pyobject(vm: &mut VirtualMachine, obj: &PyObjectRef) -> PyResult { let serializer = PyObjectSerializer { pyobject: obj, vm }; - serde_json::to_string(&serializer) + serde_json::to_string(&serializer).map_err(|err| vm.new_type_error(err.to_string())) } -pub fn de_pyobject(vm: &mut VirtualMachine, s: &str) -> serde_json::Result { +pub fn de_pyobject(vm: &mut VirtualMachine, s: &str) -> PyResult { let de = PyObjectDeserializer { vm }; // TODO: Support deserializing string sub-classes de.deserialize(&mut serde_json::Deserializer::from_str(s)) + .map_err(|err| { + let json_decode_error = vm + .sys_module + .get_item("modules") + .unwrap() + .get_item("json") + .unwrap() + .get_item("JSONDecodeError") + .unwrap(); + let exc = vm.new_exception(json_decode_error, format!("{}", err)); + 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 + }) } /// 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 string = ser_pyobject(vm, obj).map_err(|err| vm.new_type_error(err.to_string()))?; + let string = ser_pyobject(vm, obj)?; Ok(vm.context().new_str(string)) } @@ -212,20 +226,7 @@ fn json_loads(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // TODO: Implement non-trivial deserialization case arg_check!(vm, args, required = [(string, Some(vm.ctx.str_type()))]); - de_pyobject(vm, &objstr::get_value(&string)).map_err(|err| { - let json_decode_error = vm - .sys_module - .get_item("modules") - .unwrap() - .get_item("json") - .unwrap() - .get_item("JSONDecodeError") - .unwrap(); - let exc = vm.new_exception(json_decode_error, format!("{}", err)); - 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 - }) + de_pyobject(vm, &objstr::get_value(&string)) } pub fn make_module(ctx: &PyContext) -> PyObjectRef { diff --git a/vm/src/stdlib/mod.rs b/vm/src/stdlib/mod.rs index b813f93f48..b0b61119c3 100644 --- a/vm/src/stdlib/mod.rs +++ b/vm/src/stdlib/mod.rs @@ -1,6 +1,6 @@ mod ast; mod dis; -pub mod json; +pub(crate) mod json; mod keyword; mod math; mod platform; diff --git a/vm/src/vm.rs b/vm/src/vm.rs index ac97e8e9c3..f78d657ccf 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -626,6 +626,14 @@ impl VirtualMachine { }) } + pub fn serialize(&mut self, obj: &PyObjectRef) -> PyResult { + crate::stdlib::json::ser_pyobject(self, obj) + } + + pub fn deserialize(&mut self, s: &str) -> PyResult { + crate::stdlib::json::de_pyobject(self, s) + } + pub fn _sub(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_reflection(a, b, "__sub__", "__rsub__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "-")) From ed60b196122807505d43db675c42cadb4a27da3a Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Thu, 21 Mar 2019 00:10:02 -0500 Subject: [PATCH 364/380] Use vm.{,de}serialize in rustpython_wasm --- wasm/lib/src/convert.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index b9c237933e..1113ace878 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -130,7 +130,7 @@ pub fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { } arr.into() } else { - match rustpython_vm::stdlib::json::ser_pyobject(vm, &py_obj) { + match vm.serialize(&py_obj) { Ok(json) => js_sys::JSON::parse(&json).unwrap_or(JsValue::UNDEFINED), Err(_) => JsValue::UNDEFINED, } @@ -227,6 +227,6 @@ pub fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef { Ok(json) => String::from(json), Err(_) => return vm.get_none(), }; - rustpython_vm::stdlib::json::de_pyobject(vm, &json).unwrap_or_else(|_| vm.get_none()) + vm.deserialize(&json).unwrap_or_else(|_| vm.get_none()) } } From a5f6a7b10d8c1e26dcfbad4e0110aa49d94cba1e Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 20 Mar 2019 21:16:10 +1300 Subject: [PATCH 365/380] Implement calling __set__ and __delete__ --- tests/snippets/attr.py | 6 +++++ tests/snippets/property.py | 27 +++++++++++++++++++++ vm/src/obj/objobject.rs | 49 ++++++++++++++++++++++++++------------ 3 files changed, 67 insertions(+), 15 deletions(-) diff --git a/tests/snippets/attr.py b/tests/snippets/attr.py index cf7162cde9..0e9f1eb9bd 100644 --- a/tests/snippets/attr.py +++ b/tests/snippets/attr.py @@ -40,9 +40,15 @@ class A: with assertRaises(AttributeError): object().a = 1 +with assertRaises(AttributeError): + del object().a + with assertRaises(AttributeError): setattr(object(), 'a', 2) +with assertRaises(AttributeError): + delattr(object(), 'a') + attrs = {} diff --git a/tests/snippets/property.py b/tests/snippets/property.py index 2965224e66..1fd5f7e330 100644 --- a/tests/snippets/property.py +++ b/tests/snippets/property.py @@ -19,6 +19,33 @@ def foo(self): assert type(Fubar.foo) is property +class Bar: + def __init__(self): + self.a = 0 + + @property + def foo(self): + return self.a + + @foo.setter + def foo(self, value): + self.a += value + + @foo.deleter + def foo(self): + self.a -= 1 + + +bar = Bar() +assert bar.foo == 0 +bar.foo = 5 +assert bar.a == 5 +del bar.foo +assert bar.a == 4 +del bar.foo +assert bar.a == 3 + + null_property = property() assert type(null_property) is property diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 43addcbc45..f7e91a8d58 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -103,6 +103,17 @@ fn object_setattr( vm: &mut VirtualMachine, ) -> PyResult<()> { trace!("object.__setattr__({:?}, {}, {:?})", obj, attr_name, value); + let cls = obj.as_object().typ(); + + if let Some(attr) = cls.get_attr(&attr_name.value) { + let attr_class = attr.typ(); + if let Some(descriptor) = attr_class.get_attr("__set__") { + return vm + .invoke(descriptor, vec![attr, obj.into_object(), value]) + .map(|_| ()); + } + } + if let Some(ref dict) = obj.as_object().dict { dict.borrow_mut().insert(attr_name.value.clone(), value); Ok(()) @@ -115,23 +126,31 @@ fn object_setattr( } } -fn object_delattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (zelf, Some(vm.ctx.object())), - (attr, Some(vm.ctx.str_type())) - ] - ); +fn object_delattr( + obj: PyInstanceRef, + attr_name: PyStringRef, + vm: &mut VirtualMachine, +) -> PyResult<()> { + let cls = obj.as_object().typ(); - match zelf.dict { - Some(ref dict) => { - let attr_name = objstr::get_value(attr); - dict.borrow_mut().remove(&attr_name); - Ok(vm.get_none()) + if let Some(attr) = cls.get_attr(&attr_name.value) { + let attr_class = attr.typ(); + if let Some(descriptor) = attr_class.get_attr("__delete__") { + return vm + .invoke(descriptor, vec![attr, obj.into_object()]) + .map(|_| ()); } - None => Err(vm.new_type_error("TypeError: no dictionary.".to_string())), + } + + if let Some(ref dict) = obj.as_object().dict { + dict.borrow_mut().remove(&attr_name.value); + Ok(()) + } else { + let type_name = objtype::get_type_name(&obj.as_object().typ()); + Err(vm.new_attribute_error(format!( + "'{}' object has no attribute '{}'", + type_name, &attr_name.value + ))) } } From 4ad8d5ed9b2d0594bbfdcb445cdeb8448e4cd5b1 Mon Sep 17 00:00:00 2001 From: ben Date: Thu, 21 Mar 2019 19:57:35 +1300 Subject: [PATCH 366/380] Make property.__get__ owner parameter optional --- tests/snippets/property.py | 3 +-- vm/src/obj/objproperty.rs | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/tests/snippets/property.py b/tests/snippets/property.py index 1fd5f7e330..c61f131899 100644 --- a/tests/snippets/property.py +++ b/tests/snippets/property.py @@ -51,8 +51,7 @@ def foo(self): p = property(lambda x: x[0]) assert p.__get__((2,), tuple) == 2 -# TODO owner parameter is optional -# assert p.__get__((2,)) == 2 +assert p.__get__((2,)) == 2 with assertRaises(AttributeError): null_property.__get__((), tuple) diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index 5369f274d8..44461c41b8 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -24,7 +24,12 @@ impl PyValue for PyReadOnlyProperty { pub type PyReadOnlyPropertyRef = PyRef; impl PyReadOnlyPropertyRef { - fn get(self, obj: PyObjectRef, _owner: PyClassRef, vm: &mut VirtualMachine) -> PyResult { + fn get( + self, + obj: PyObjectRef, + _owner: OptionalArg, + vm: &mut VirtualMachine, + ) -> PyResult { if obj.is(&vm.ctx.none) { Ok(self.into_object()) } else { @@ -81,7 +86,12 @@ impl PyPropertyRef { } } - fn get(self, obj: PyObjectRef, _owner: PyClassRef, vm: &mut VirtualMachine) -> PyResult { + fn get( + self, + obj: PyObjectRef, + _owner: OptionalArg, + vm: &mut VirtualMachine, + ) -> PyResult { if let Some(getter) = self.getter.as_ref() { if obj.is(&vm.ctx.none) { Ok(self.into_object()) From e66b5078a887a622e48ced73dfaa8391bd1bdea1 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Wed, 20 Mar 2019 14:34:41 +0000 Subject: [PATCH 367/380] Introduce TryIntoRef to make vm.get_attribute more usable. --- vm/src/builtins.rs | 7 +++---- vm/src/exceptions.rs | 8 +++----- vm/src/frame.rs | 6 +++--- vm/src/obj/objstr.rs | 18 ++++++++++++++++-- vm/src/pyobject.rs | 21 +++++++++++++++++++++ vm/src/stdlib/dis.rs | 3 +-- vm/src/vm.rs | 12 ++++++++---- 7 files changed, 55 insertions(+), 20 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index eb06446f42..55aa2a75ec 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -310,7 +310,7 @@ fn builtin_getattr( default: OptionalArg, vm: &mut VirtualMachine, ) -> PyResult { - let ret = vm.get_attribute(obj.clone(), attr.into_object()); + let ret = vm.get_attribute(obj.clone(), attr); if let OptionalArg::Present(default) = default { ret.or_else(|ex| catch_attr_exception(ex, default, vm)) } else { @@ -323,7 +323,7 @@ fn builtin_globals(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { } fn builtin_hasattr(obj: PyObjectRef, attr: PyStringRef, vm: &mut VirtualMachine) -> PyResult { - if let Err(ex) = vm.get_attribute(obj.clone(), attr.into_object()) { + if let Err(ex) = vm.get_attribute(obj.clone(), attr) { catch_attr_exception(ex, false, vm) } else { Ok(true) @@ -832,8 +832,7 @@ pub fn builtin_build_class_(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> Py let bases = vm.context().new_tuple(bases); // Prepare uses full __getattribute__ resolution chain. - let prepare_name = vm.new_str("__prepare__".to_string()); - let prepare = vm.get_attribute(metaclass.clone(), prepare_name)?; + let prepare = vm.get_attribute(metaclass.clone(), "__prepare__")?; let namespace = vm.invoke(prepare, vec![name_arg.clone(), bases.clone()])?; let cells = vm.new_dict(); diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index a4cd9144c7..68f4105c5e 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -1,9 +1,7 @@ use crate::function::PyFuncArgs; use crate::obj::objsequence; use crate::obj::objtype; -use crate::pyobject::{ - create_type, AttributeProtocol, PyContext, PyObjectRef, PyResult, TypeProtocol, -}; +use crate::pyobject::{create_type, PyContext, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; fn exception_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -21,7 +19,7 @@ fn exception_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // Print exception including traceback: pub fn print_exception(vm: &mut VirtualMachine, exc: &PyObjectRef) { - if let Some(tb) = exc.get_attr("__traceback__") { + if let Ok(tb) = vm.get_attribute(exc.clone(), "__traceback__") { println!("Traceback (most recent call last):"); if objtype::isinstance(&tb, &vm.ctx.list_type()) { let mut elements = objsequence::get_elements(&tb).to_vec(); @@ -70,7 +68,7 @@ fn exception_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(exc, Some(vm.ctx.exceptions.exception_type.clone()))] ); let type_name = objtype::get_type_name(&exc.typ()); - let msg = if let Some(m) = exc.get_attr("msg") { + let msg = if let Ok(m) = vm.get_attribute(exc.clone(), "msg") { match vm.to_pystr(&m) { Ok(msg) => msg, _ => "".to_string(), diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 6f8653f881..1588699456 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -245,8 +245,9 @@ impl Frame { &exception, &vm.ctx.exceptions.base_exception_type )); - let traceback_name = vm.new_str("__traceback__".to_string()); - let traceback = vm.get_attribute(exception.clone(), traceback_name).unwrap(); + let traceback = vm + .get_attribute(exception.clone(), "__traceback__") + .unwrap(); trace!("Adding to traceback: {:?} {:?}", traceback, lineno); let pos = vm.ctx.new_tuple(vec![ vm.ctx.new_str(filename.clone()), @@ -1137,7 +1138,6 @@ impl Frame { fn load_attr(&self, vm: &mut VirtualMachine, attr_name: &str) -> FrameResult { let parent = self.pop_value(); - let attr_name = vm.new_str(attr_name.to_string()); let obj = vm.get_attribute(parent, attr_name)?; self.push_value(obj); Ok(None) diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 8569280ef9..d1ffb065bd 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -10,7 +10,7 @@ use crate::format::{FormatParseError, FormatPart, FormatString}; use crate::function::{OptionalArg, PyFuncArgs}; use crate::pyobject::{ IdProtocol, IntoPyObject, PyContext, PyIterable, PyObjectRef, PyRef, PyResult, PyValue, - TryFromObject, TypeProtocol, + TryFromObject, TryIntoRef, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -24,6 +24,7 @@ pub struct PyString { // TODO: shouldn't be public pub value: String, } +pub type PyStringRef = PyRef; impl From for PyString { fn from(t: T) -> PyString { @@ -33,7 +34,20 @@ impl From for PyString { } } -pub type PyStringRef = PyRef; +impl TryIntoRef for String { + fn try_into_ref(self, vm: &mut VirtualMachine) -> PyResult> { + Ok(PyString { value: self }.into_ref(vm)) + } +} + +impl TryIntoRef for &str { + fn try_into_ref(self, vm: &mut VirtualMachine) -> PyResult> { + Ok(PyString { + value: self.to_string(), + } + .into_ref(vm)) + } +} impl PyStringRef { fn add(self, rhs: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index cd1ca77e66..8ee49b57f0 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1044,6 +1044,27 @@ impl TryFromObject for Option { } } +/// Allows coercion of a types into PyRefs, so that we can write functions that can take +/// refs, pyobject refs or basic types. +pub trait TryIntoRef { + fn try_into_ref(self, vm: &mut VirtualMachine) -> PyResult>; +} + +impl TryIntoRef for PyRef { + fn try_into_ref(self, _vm: &mut VirtualMachine) -> PyResult> { + Ok(self) + } +} + +impl TryIntoRef for PyObjectRef +where + T: PyValue, +{ + fn try_into_ref(self, vm: &mut VirtualMachine) -> PyResult> { + TryFromObject::try_from_object(vm, self) + } +} + /// Implemented by any type that can be created from a Python object. /// /// Any type that implements `TryFromObject` is automatically `FromArgs`, and diff --git a/vm/src/stdlib/dis.rs b/vm/src/stdlib/dis.rs index ccc8930ba0..f583cc5a8d 100644 --- a/vm/src/stdlib/dis.rs +++ b/vm/src/stdlib/dis.rs @@ -7,8 +7,7 @@ fn dis_dis(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, None)]); // Method or function: - let code_name = vm.new_str("__code__".to_string()); - if let Ok(co) = vm.get_attribute(obj.clone(), code_name) { + if let Ok(co) = vm.get_attribute(obj.clone(), "__code__") { return dis_disassemble(vm, PyFuncArgs::new(vec![co], vec![])); } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index f78d657ccf..7f9b474d0a 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -24,12 +24,12 @@ use crate::obj::objgenerator; use crate::obj::objiter; use crate::obj::objlist::PyList; use crate::obj::objsequence; -use crate::obj::objstr::PyStringRef; +use crate::obj::objstr::{PyString, PyStringRef}; use crate::obj::objtuple::PyTuple; use crate::obj::objtype; use crate::pyobject::{ AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyObjectRef, PyResult, TryFromObject, - TypeProtocol, + TryIntoRef, TypeProtocol, }; use crate::stdlib; use crate::sysmodule; @@ -549,9 +549,13 @@ impl VirtualMachine { } // get_attribute should be used for full attribute access (usually from user code). - pub fn get_attribute(&mut self, obj: PyObjectRef, attr_name: PyObjectRef) -> PyResult { + pub fn get_attribute(&mut self, obj: PyObjectRef, attr_name: T) -> PyResult + where + T: TryIntoRef, + { + let attr_name = attr_name.try_into_ref(self)?; trace!("vm.__getattribute__: {:?} {:?}", obj, attr_name); - self.call_method(&obj, "__getattribute__", vec![attr_name]) + self.call_method(&obj, "__getattribute__", vec![attr_name.into_object()]) } pub fn set_attr( From 19fc202e1aad16a80a415ec48baaec0fa0d076a0 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Thu, 21 Mar 2019 13:03:51 +0000 Subject: [PATCH 368/380] Classmethod/staticmethod - rewrite using structs and new style methods. --- vm/src/obj/objclassmethod.rs | 67 +++++++++++++++++------------------ vm/src/obj/objstaticmethod.rs | 62 ++++++++++++++++---------------- 2 files changed, 64 insertions(+), 65 deletions(-) diff --git a/vm/src/obj/objclassmethod.rs b/vm/src/obj/objclassmethod.rs index c2322b1836..8a1baad31c 100644 --- a/vm/src/obj/objclassmethod.rs +++ b/vm/src/obj/objclassmethod.rs @@ -1,43 +1,42 @@ -use crate::function::PyFuncArgs; -use crate::pyobject::{AttributeProtocol, PyContext, PyResult, TypeProtocol}; +use super::objtype::PyClassRef; +use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; -pub fn init(context: &PyContext) { - let classmethod_type = &context.classmethod_type; - extend_class!(context, classmethod_type, { - "__get__" => context.new_rustfunc(classmethod_get), - "__new__" => context.new_rustfunc(classmethod_new) - }); +#[derive(Clone, Debug)] +pub struct PyClassMethod { + pub callable: PyObjectRef, } +pub type PyClassMethodRef = PyRef; -fn classmethod_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - trace!("classmethod.__get__ {:?}", args.args); - arg_check!( - vm, - args, - required = [ - (cls, Some(vm.ctx.classmethod_type())), - (_inst, None), - (owner, None) - ] - ); - match cls.get_attr("function") { - Some(function) => { - let py_obj = owner.clone(); - let py_method = vm.ctx.new_bound_method(function, py_obj); - Ok(py_method) - } - None => Err(vm.new_attribute_error( - "Attribute Error: classmethod must have 'function' attribute".to_string(), - )), +impl PyValue for PyClassMethod { + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.classmethod_type() } } -fn classmethod_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - trace!("classmethod.__new__ {:?}", args.args); - arg_check!(vm, args, required = [(cls, None), (callable, None)]); +impl PyClassMethodRef { + fn new( + cls: PyClassRef, + callable: PyObjectRef, + vm: &mut VirtualMachine, + ) -> PyResult { + PyClassMethod { + callable: callable.clone(), + } + .into_ref_with_type(vm, cls) + } + + fn get(self, _inst: PyObjectRef, owner: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + Ok(vm + .ctx + .new_bound_method(self.callable.clone(), owner.clone())) + } +} - let py_obj = vm.ctx.new_instance(cls.clone(), None); - vm.ctx.set_attr(&py_obj, "function", callable.clone()); - Ok(py_obj) +pub fn init(context: &PyContext) { + let classmethod_type = &context.classmethod_type; + extend_class!(context, classmethod_type, { + "__get__" => context.new_rustfunc(PyClassMethodRef::get), + "__new__" => context.new_rustfunc(PyClassMethodRef::new) + }); } diff --git a/vm/src/obj/objstaticmethod.rs b/vm/src/obj/objstaticmethod.rs index 9d1a96ed2a..78307bcc3e 100644 --- a/vm/src/obj/objstaticmethod.rs +++ b/vm/src/obj/objstaticmethod.rs @@ -1,40 +1,40 @@ -use crate::function::PyFuncArgs; -use crate::pyobject::{AttributeProtocol, PyContext, PyResult, TypeProtocol}; +use super::objtype::PyClassRef; +use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; -pub fn init(context: &PyContext) { - let staticmethod_type = &context.staticmethod_type; - extend_class!(context, staticmethod_type, { - "__get__" => context.new_rustfunc(staticmethod_get), - "__new__" => context.new_rustfunc(staticmethod_new), - }); +#[derive(Clone, Debug)] +pub struct PyStaticMethod { + pub callable: PyObjectRef, } +pub type PyStaticMethodRef = PyRef; -// `staticmethod` methods. -fn staticmethod_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - trace!("staticmethod.__get__ {:?}", args.args); - arg_check!( - vm, - args, - required = [ - (cls, Some(vm.ctx.staticmethod_type())), - (_inst, None), - (_owner, None) - ] - ); - match cls.get_attr("function") { - Some(function) => Ok(function), - None => Err(vm.new_attribute_error( - "Attribute Error: staticmethod must have 'function' attribute".to_string(), - )), +impl PyValue for PyStaticMethod { + fn class(vm: &mut VirtualMachine) -> PyObjectRef { + vm.ctx.staticmethod_type() } } -fn staticmethod_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - trace!("staticmethod.__new__ {:?}", args.args); - arg_check!(vm, args, required = [(cls, None), (callable, None)]); +impl PyStaticMethodRef { + fn new( + cls: PyClassRef, + callable: PyObjectRef, + vm: &mut VirtualMachine, + ) -> PyResult { + PyStaticMethod { + callable: callable.clone(), + } + .into_ref_with_type(vm, cls) + } + + fn get(self, _inst: PyObjectRef, _owner: PyObjectRef, _vm: &mut VirtualMachine) -> PyResult { + Ok(self.callable.clone()) + } +} - let py_obj = vm.ctx.new_instance(cls.clone(), None); - vm.ctx.set_attr(&py_obj, "function", callable.clone()); - Ok(py_obj) +pub fn init(context: &PyContext) { + let staticmethod_type = &context.staticmethod_type; + extend_class!(context, staticmethod_type, { + "__get__" => context.new_rustfunc(PyStaticMethodRef::get), + "__new__" => context.new_rustfunc(PyStaticMethodRef::new), + }); } From 88d6926f786874060b378110329181817572c157 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Thu, 21 Mar 2019 20:25:16 -0500 Subject: [PATCH 369/380] Make the REPL handle continuation better --- src/main.rs | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index 1b92a5ee46..a7dffb02d7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -159,6 +159,14 @@ fn get_history_path() -> PathBuf { xdg_dirs.place_cache_file("repl_history.txt").unwrap() } +fn get_prompt(vm: &mut VirtualMachine, prompt_name: &str) -> String { + vm.sys_module + .get_attr(prompt_name) + .as_ref() + .map(objstr::get_value) + .unwrap_or_else(String::new) +} + fn run_shell(vm: &mut VirtualMachine) -> PyResult { println!( "Welcome to the magnificent Rust Python {} interpreter", @@ -176,25 +184,36 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult { println!("No previous history."); } - let ps1 = &objstr::get_value(&vm.sys_module.get_attr("ps1").unwrap()); - let ps2 = &objstr::get_value(&vm.sys_module.get_attr("ps2").unwrap()); - let mut prompt = ps1; + let mut prompt = get_prompt(vm, "ps1"); + + let mut continuing = false; loop { - match repl.readline(prompt) { + if !continuing { + prompt = get_prompt(vm, "ps1"); + } + match repl.readline(&prompt) { Ok(line) => { debug!("You entered {:?}", line); input.push_str(&line); - input.push_str("\n"); + input.push('\n'); repl.add_history_entry(line.trim_end()); + if continuing { + if line.is_empty() { + continuing = false; + } else { + continue; + } + } + match shell_exec(vm, &input, vars.clone()) { Err(CompileError::Parse(ParseError::EOF(_))) => { - prompt = ps2; + prompt = get_prompt(vm, "ps2"); + continuing = true; continue; } _ => { - prompt = ps1; input = String::new(); } } @@ -202,7 +221,8 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult { Err(ReadlineError::Interrupted) => { // TODO: Raise a real KeyboardInterrupt exception println!("^C"); - break; + continuing = false; + continue; } Err(ReadlineError::Eof) => { break; From a9051ab462404a702ba12f1ba4b2ff7220971b8d Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Fri, 22 Mar 2019 01:48:14 -0500 Subject: [PATCH 370/380] Convert all &mut VirtualMachine to &VirtualMachine --- derive/src/lib.rs | 2 +- src/main.rs | 26 +++--- vm/src/builtins.rs | 90 ++++++++++---------- vm/src/dictdatatype.rs | 14 ++-- vm/src/eval.rs | 2 +- vm/src/exceptions.rs | 6 +- vm/src/frame.rs | 60 ++++++-------- vm/src/function.rs | 26 +++--- vm/src/import.rs | 14 +--- vm/src/obj/objbool.rs | 12 +-- vm/src/obj/objbuiltinfunc.rs | 2 +- vm/src/obj/objbytearray.rs | 36 ++++---- vm/src/obj/objbytes.rs | 22 ++--- vm/src/obj/objclassmethod.rs | 4 +- vm/src/obj/objcode.rs | 22 ++--- vm/src/obj/objcomplex.rs | 22 ++--- vm/src/obj/objdict.rs | 32 ++++---- vm/src/obj/objellipsis.rs | 4 +- vm/src/obj/objenumerate.rs | 6 +- vm/src/obj/objfilter.rs | 6 +- vm/src/obj/objfloat.rs | 50 +++++------ vm/src/obj/objframe.rs | 8 +- vm/src/obj/objfunction.rs | 8 +- vm/src/obj/objgenerator.rs | 12 +-- vm/src/obj/objint.rs | 82 +++++++++--------- vm/src/obj/objiter.rs | 22 +++-- vm/src/obj/objlist.rs | 60 +++++++------- vm/src/obj/objmap.rs | 6 +- vm/src/obj/objmemory.rs | 4 +- vm/src/obj/objmodule.rs | 6 +- vm/src/obj/objnone.rs | 16 ++-- vm/src/obj/objobject.rs | 44 +++++----- vm/src/obj/objproperty.rs | 32 ++++---- vm/src/obj/objrange.rs | 28 +++---- vm/src/obj/objsequence.rs | 18 ++-- vm/src/obj/objset.rs | 78 +++++++++--------- vm/src/obj/objslice.rs | 12 +-- vm/src/obj/objstaticmethod.rs | 4 +- vm/src/obj/objstr.rs | 143 ++++++++++++++------------------ vm/src/obj/objsuper.rs | 6 +- vm/src/obj/objtuple.rs | 34 ++++---- vm/src/obj/objtype.rs | 24 +++--- vm/src/obj/objweakref.rs | 6 +- vm/src/obj/objzip.rs | 6 +- vm/src/pyobject.rs | 44 +++++----- vm/src/stdlib/ast.rs | 2 +- vm/src/stdlib/dis.rs | 4 +- vm/src/stdlib/io.rs | 36 ++++---- vm/src/stdlib/json.rs | 8 +- vm/src/stdlib/keyword.rs | 2 +- vm/src/stdlib/math.rs | 30 +++---- vm/src/stdlib/os.rs | 6 +- vm/src/stdlib/platform.rs | 6 +- vm/src/stdlib/pystruct.rs | 50 +++++------ vm/src/stdlib/random.rs | 8 +- vm/src/stdlib/re.rs | 26 +++--- vm/src/stdlib/socket.rs | 35 ++++---- vm/src/stdlib/time_module.rs | 4 +- vm/src/stdlib/tokenize.rs | 2 +- vm/src/stdlib/types.rs | 2 +- vm/src/sysmodule.rs | 8 +- vm/src/vm.rs | 146 ++++++++++++++++----------------- wasm/lib/src/browser_module.rs | 40 +++++---- wasm/lib/src/convert.rs | 20 ++--- wasm/lib/src/vm_class.rs | 132 +++++++++-------------------- wasm/lib/src/wasm_builtins.rs | 4 +- 66 files changed, 814 insertions(+), 918 deletions(-) diff --git a/derive/src/lib.rs b/derive/src/lib.rs index f52ca3e555..ad743e72fe 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -38,7 +38,7 @@ fn impl_from_args(input: &DeriveInput) -> TokenStream2 { quote! { impl crate::function::FromArgs for #name { fn from_args( - vm: &mut crate::vm::VirtualMachine, + vm: &crate::vm::VirtualMachine, args: &mut crate::function::PyFuncArgs ) -> Result { Ok(#name { #(#fields)* }) diff --git a/src/main.rs b/src/main.rs index 1b92a5ee46..2c50819dad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -51,26 +51,26 @@ fn main() { .get_matches(); // Construct vm: - let mut vm = VirtualMachine::new(); + let vm = VirtualMachine::new(); // Figure out if a -c option was given: let result = if let Some(command) = matches.value_of("c") { - run_command(&mut vm, command.to_string()) + run_command(&vm, command.to_string()) } else if let Some(module) = matches.value_of("m") { - run_module(&mut vm, module) + run_module(&vm, module) } else { // Figure out if a script was passed: match matches.value_of("script") { - None => run_shell(&mut vm), - Some(filename) => run_script(&mut vm, filename), + None => run_shell(&vm), + Some(filename) => run_script(&vm, filename), } }; // See if any exception leaked out: - handle_exception(&mut vm, result); + handle_exception(&vm, result); } -fn _run_string(vm: &mut VirtualMachine, source: &str, source_path: String) -> PyResult { +fn _run_string(vm: &VirtualMachine, source: &str, source_path: String) -> PyResult { let code_obj = compile::compile( source, &compile::Mode::Exec, @@ -86,14 +86,14 @@ fn _run_string(vm: &mut VirtualMachine, source: &str, source_path: String) -> Py vm.run_code_obj(code_obj, vars) } -fn handle_exception(vm: &mut VirtualMachine, result: PyResult) { +fn handle_exception(vm: &VirtualMachine, result: PyResult) { if let Err(err) = result { print_exception(vm, &err); std::process::exit(1); } } -fn run_command(vm: &mut VirtualMachine, mut source: String) -> PyResult { +fn run_command(vm: &VirtualMachine, mut source: String) -> PyResult { debug!("Running command {}", source); // This works around https://github.com/RustPython/RustPython/issues/17 @@ -101,13 +101,13 @@ fn run_command(vm: &mut VirtualMachine, mut source: String) -> PyResult { _run_string(vm, &source, "".to_string()) } -fn run_module(vm: &mut VirtualMachine, module: &str) -> PyResult { +fn run_module(vm: &VirtualMachine, module: &str) -> PyResult { debug!("Running module {}", module); let current_path = PathBuf::from("."); import::import_module(vm, current_path, module) } -fn run_script(vm: &mut VirtualMachine, script_file: &str) -> PyResult { +fn run_script(vm: &VirtualMachine, script_file: &str) -> PyResult { debug!("Running file {}", script_file); // Parse an ast from it: let file_path = Path::new(script_file); @@ -120,7 +120,7 @@ fn run_script(vm: &mut VirtualMachine, script_file: &str) -> PyResult { } } -fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: Scope) -> Result<(), CompileError> { +fn shell_exec(vm: &VirtualMachine, source: &str, scope: Scope) -> Result<(), CompileError> { match compile::compile( source, &compile::Mode::Single, @@ -159,7 +159,7 @@ fn get_history_path() -> PathBuf { xdg_dirs.place_cache_file("repl_history.txt").unwrap() } -fn run_shell(vm: &mut VirtualMachine) -> PyResult { +fn run_shell(vm: &VirtualMachine) -> PyResult { println!( "Welcome to the magnificent Rust Python {} interpreter", crate_version!() diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 55aa2a75ec..68e933db3e 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -27,7 +27,7 @@ use crate::vm::VirtualMachine; #[cfg(not(target_arch = "wasm32"))] use crate::stdlib::io::io_open; -fn get_locals(vm: &mut VirtualMachine) -> PyObjectRef { +fn get_locals(vm: &VirtualMachine) -> PyObjectRef { let d = vm.new_dict(); // TODO: implement dict_iter_items? let locals = vm.get_locals(); @@ -38,11 +38,11 @@ fn get_locals(vm: &mut VirtualMachine) -> PyObjectRef { d } -fn dir_locals(vm: &mut VirtualMachine) -> PyObjectRef { +fn dir_locals(vm: &VirtualMachine) -> PyObjectRef { get_locals(vm) } -fn builtin_abs(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_abs(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(x, None)]); match vm.get_method(x.clone(), "__abs__") { Ok(attrib) => vm.invoke(attrib, PyFuncArgs::new(vec![], vec![])), @@ -50,7 +50,7 @@ fn builtin_abs(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn builtin_all(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_all(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(iterable, None)]); let items = vm.extract_elements(iterable)?; for item in items { @@ -62,7 +62,7 @@ fn builtin_all(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_bool(true)) } -fn builtin_any(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_any(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(iterable, None)]); let iterator = objiter::get_iter(vm, iterable)?; @@ -78,7 +78,7 @@ fn builtin_any(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // builtin_ascii -fn builtin_bin(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_bin(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(number, Some(vm.ctx.int_type()))]); let n = objint::get_value(number); @@ -93,13 +93,13 @@ fn builtin_bin(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // builtin_breakpoint -fn builtin_callable(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_callable(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, None)]); let is_callable = obj.typ().has_attr("__call__"); Ok(vm.new_bool(is_callable)) } -fn builtin_chr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_chr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(i, Some(vm.ctx.int_type()))]); let code_point = objint::get_value(i).to_u32().unwrap(); @@ -112,7 +112,7 @@ fn builtin_chr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_str(txt)) } -fn builtin_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_compile(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -149,7 +149,7 @@ fn builtin_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }) } -fn builtin_delattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_delattr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -158,7 +158,7 @@ fn builtin_delattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm.del_attr(obj, attr.clone()) } -fn builtin_dir(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_dir(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { if args.args.is_empty() { Ok(dir_locals(vm)) } else { @@ -169,7 +169,7 @@ fn builtin_dir(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn builtin_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_divmod(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(x, None), (y, None)]); match vm.get_method(x.clone(), "__divmod__") { Ok(attrib) => vm.invoke(attrib, vec![y.clone()]), @@ -179,7 +179,7 @@ fn builtin_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { /// Implements `eval`. /// See also: https://docs.python.org/3/library/functions.html#eval -fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_eval(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -213,7 +213,7 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { /// Implements `exec` /// https://docs.python.org/3/library/functions.html#exec -fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_exec(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -246,7 +246,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn make_scope( - vm: &mut VirtualMachine, + vm: &VirtualMachine, globals: Option<&PyObjectRef>, locals: Option<&PyObjectRef>, ) -> PyResult { @@ -283,7 +283,7 @@ fn make_scope( Ok(Scope::new(locals, globals)) } -fn builtin_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_format(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -296,7 +296,7 @@ fn builtin_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm.call_method(obj, "__format__", vec![format_spec]) } -fn catch_attr_exception(ex: PyObjectRef, default: T, vm: &mut VirtualMachine) -> PyResult { +fn catch_attr_exception(ex: PyObjectRef, default: T, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&ex, &vm.ctx.exceptions.attribute_error) { Ok(default) } else { @@ -308,7 +308,7 @@ fn builtin_getattr( obj: PyObjectRef, attr: PyStringRef, default: OptionalArg, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> PyResult { let ret = vm.get_attribute(obj.clone(), attr); if let OptionalArg::Present(default) = default { @@ -318,11 +318,11 @@ fn builtin_getattr( } } -fn builtin_globals(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { +fn builtin_globals(vm: &VirtualMachine, _args: PyFuncArgs) -> PyResult { Ok(vm.current_scope().globals.clone()) } -fn builtin_hasattr(obj: PyObjectRef, attr: PyStringRef, vm: &mut VirtualMachine) -> PyResult { +fn builtin_hasattr(obj: PyObjectRef, attr: PyStringRef, vm: &VirtualMachine) -> PyResult { if let Err(ex) = vm.get_attribute(obj.clone(), attr) { catch_attr_exception(ex, false, vm) } else { @@ -330,7 +330,7 @@ fn builtin_hasattr(obj: PyObjectRef, attr: PyStringRef, vm: &mut VirtualMachine) } } -fn builtin_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_hash(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, None)]); vm.call_method(obj, "__hash__", vec![]) @@ -338,7 +338,7 @@ fn builtin_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // builtin_help -fn builtin_hex(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_hex(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(number, Some(vm.ctx.int_type()))]); let n = objint::get_value(number); @@ -351,7 +351,7 @@ fn builtin_hex(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_str(s)) } -fn builtin_id(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_id(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, None)]); Ok(vm.context().new_int(obj.get_id())) @@ -359,7 +359,7 @@ fn builtin_id(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // builtin_input -fn builtin_isinstance(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_isinstance(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -370,7 +370,7 @@ fn builtin_isinstance(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_bool(isinstance)) } -fn builtin_issubclass(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_issubclass(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -381,12 +381,12 @@ fn builtin_issubclass(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.context().new_bool(issubclass)) } -fn builtin_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_iter(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(iter_target, None)]); objiter::get_iter(vm, iter_target) } -fn builtin_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_len(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, None)]); let len_method_name = "__len__"; match vm.get_method(obj.clone(), len_method_name) { @@ -399,12 +399,12 @@ fn builtin_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn builtin_locals(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_locals(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args); Ok(vm.get_locals()) } -fn builtin_max(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_max(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { let candidates = if args.args.len() > 1 { args.args.clone() } else if args.args.len() == 1 { @@ -453,7 +453,7 @@ fn builtin_max(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(x) } -fn builtin_min(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_min(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { let candidates = if args.args.len() > 1 { args.args.clone() } else if args.args.len() == 1 { @@ -501,7 +501,7 @@ fn builtin_min(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(x) } -fn builtin_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_next(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -524,7 +524,7 @@ fn builtin_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn builtin_oct(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_oct(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(number, Some(vm.ctx.int_type()))]); let n = objint::get_value(number); @@ -537,7 +537,7 @@ fn builtin_oct(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_str(s)) } -fn builtin_ord(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_ord(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(string, Some(vm.ctx.str_type()))]); let string = objstr::get_value(string); let string_len = string.chars().count(); @@ -555,7 +555,7 @@ fn builtin_ord(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn builtin_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_pow(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -591,11 +591,7 @@ pub struct PrintOptions { flush: bool, } -pub fn builtin_print( - objects: Args, - options: PrintOptions, - vm: &mut VirtualMachine, -) -> PyResult<()> { +pub fn builtin_print(objects: Args, options: PrintOptions, vm: &VirtualMachine) -> PyResult<()> { let stdout = io::stdout(); let mut stdout_lock = stdout.lock(); let mut first = true; @@ -624,11 +620,11 @@ pub fn builtin_print( Ok(()) } -fn builtin_repr(obj: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { +fn builtin_repr(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { vm.to_repr(&obj) } -fn builtin_reversed(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_reversed(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, None)]); match vm.get_method(obj.clone(), "__reversed__") { @@ -642,7 +638,7 @@ fn builtin_reversed(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } // builtin_reversed -fn builtin_round(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_round(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -664,7 +660,7 @@ fn builtin_setattr( obj: PyObjectRef, attr: PyStringRef, value: PyObjectRef, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> PyResult<()> { vm.set_attr(&obj, attr.into_object(), value)?; Ok(()) @@ -672,7 +668,7 @@ fn builtin_setattr( // builtin_slice -fn builtin_sorted(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { +fn builtin_sorted(vm: &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); @@ -682,7 +678,7 @@ fn builtin_sorted(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { Ok(lst) } -fn builtin_sum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_sum(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(iterable, None)]); let items = vm.extract_elements(iterable)?; @@ -695,7 +691,7 @@ fn builtin_sum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } // Should be renamed to builtin___import__? -fn builtin_import(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn builtin_import(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -815,7 +811,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { py_mod } -pub fn builtin_build_class_(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { +pub fn builtin_build_class_(vm: &VirtualMachine, mut args: PyFuncArgs) -> PyResult { let function = args.shift(); let name_arg = args.shift(); let bases = args.args.clone(); diff --git a/vm/src/dictdatatype.rs b/vm/src/dictdatatype.rs index df6b3bb5a8..436923ea95 100644 --- a/vm/src/dictdatatype.rs +++ b/vm/src/dictdatatype.rs @@ -33,7 +33,7 @@ impl Dict { /// Store a key pub fn insert( &mut self, - vm: &mut VirtualMachine, + vm: &VirtualMachine, key: &PyObjectRef, value: PyObjectRef, ) -> PyResult<()> { @@ -66,7 +66,7 @@ impl Dict { } } - pub fn contains(&self, vm: &mut VirtualMachine, key: &PyObjectRef) -> PyResult { + pub fn contains(&self, vm: &VirtualMachine, key: &PyObjectRef) -> PyResult { if let LookupResult::Existing(_index) = self.lookup(vm, key)? { Ok(true) } else { @@ -75,7 +75,7 @@ impl Dict { } /// Retrieve a key - pub fn get(&self, vm: &mut VirtualMachine, key: &PyObjectRef) -> PyResult { + pub fn get(&self, vm: &VirtualMachine, key: &PyObjectRef) -> PyResult { if let LookupResult::Existing(index) = self.lookup(vm, key)? { if let Some(entry) = &self.entries[index] { Ok(entry.value.clone()) @@ -89,7 +89,7 @@ impl Dict { } /// Delete a key - pub fn delete(&mut self, vm: &mut VirtualMachine, key: &PyObjectRef) -> PyResult<()> { + pub fn delete(&mut self, vm: &VirtualMachine, key: &PyObjectRef) -> PyResult<()> { if let LookupResult::Existing(index) = self.lookup(vm, key)? { self.entries[index] = None; self.size -= 1; @@ -118,7 +118,7 @@ impl Dict { } /// Lookup the index for the given key. - fn lookup(&self, vm: &mut VirtualMachine, key: &PyObjectRef) -> PyResult { + fn lookup(&self, vm: &VirtualMachine, key: &PyObjectRef) -> PyResult { let hash_value = calc_hash(vm, key)?; let perturb = hash_value; let mut hash_index: usize = hash_value; @@ -169,14 +169,14 @@ enum LookupResult { Existing(usize), // Existing record, index into entries } -fn calc_hash(vm: &mut VirtualMachine, key: &PyObjectRef) -> PyResult { +fn calc_hash(vm: &VirtualMachine, key: &PyObjectRef) -> PyResult { let hash = vm.call_method(key, "__hash__", vec![])?; Ok(objint::get_value(&hash).to_usize().unwrap()) } /// Invoke __eq__ on two keys fn do_eq( - vm: &mut VirtualMachine, + vm: &VirtualMachine, key1: &PyObjectRef, key2: &PyObjectRef, ) -> Result { diff --git a/vm/src/eval.rs b/vm/src/eval.rs index 7f44cde89b..202286eb13 100644 --- a/vm/src/eval.rs +++ b/vm/src/eval.rs @@ -7,7 +7,7 @@ use crate::frame::Scope; use crate::pyobject::PyResult; use crate::vm::VirtualMachine; -pub fn eval(vm: &mut VirtualMachine, source: &str, scope: Scope, source_path: &str) -> PyResult { +pub fn eval(vm: &VirtualMachine, source: &str, scope: Scope, source_path: &str) -> PyResult { match compile::compile( source, &compile::Mode::Eval, diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 68f4105c5e..d37f174717 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -4,7 +4,7 @@ use crate::obj::objtype; use crate::pyobject::{create_type, PyContext, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; -fn exception_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn exception_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { let zelf = args.args[0].clone(); let msg = if args.args.len() > 1 { args.args[1].clone() @@ -18,7 +18,7 @@ fn exception_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } // Print exception including traceback: -pub fn print_exception(vm: &mut VirtualMachine, exc: &PyObjectRef) { +pub fn print_exception(vm: &VirtualMachine, exc: &PyObjectRef) { if let Ok(tb) = vm.get_attribute(exc.clone(), "__traceback__") { println!("Traceback (most recent call last):"); if objtype::isinstance(&tb, &vm.ctx.list_type()) { @@ -61,7 +61,7 @@ pub fn print_exception(vm: &mut VirtualMachine, exc: &PyObjectRef) { } } -fn exception_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn exception_str(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 1588699456..8cd0177ff5 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -185,7 +185,7 @@ pub struct Frame { } impl PyValue for Frame { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.frame_type() } } @@ -223,7 +223,7 @@ impl Frame { } } - pub fn run(&self, vm: &mut VirtualMachine) -> Result { + pub fn run(&self, vm: &VirtualMachine) -> Result { let filename = &self.code.source_path.to_string(); // This is the name of the object being run: @@ -276,7 +276,7 @@ impl Frame { } // Execute a single instruction: - fn execute_instruction(&self, vm: &mut VirtualMachine) -> FrameResult { + fn execute_instruction(&self, vm: &VirtualMachine) -> FrameResult { let instruction = self.fetch_instruction(); { trace!("======="); @@ -780,7 +780,7 @@ impl Frame { fn get_elements( &self, - vm: &mut VirtualMachine, + vm: &VirtualMachine, size: usize, unpack: bool, ) -> Result, PyObjectRef> { @@ -799,12 +799,7 @@ impl Frame { } } - fn import( - &self, - vm: &mut VirtualMachine, - module: &str, - symbol: &Option, - ) -> FrameResult { + fn import(&self, vm: &VirtualMachine, module: &str, symbol: &Option) -> FrameResult { let module = vm.import(module)?; // If we're importing a symbol, look it up and use it, otherwise construct a module and return @@ -825,7 +820,7 @@ impl Frame { Ok(None) } - fn import_star(&self, vm: &mut VirtualMachine, module: &str) -> FrameResult { + fn import_star(&self, vm: &VirtualMachine, module: &str) -> FrameResult { let module = vm.import(module)?; // Grab all the names from the module and put them in the context @@ -836,7 +831,7 @@ impl Frame { } // Unwind all blocks: - fn unwind_blocks(&self, vm: &mut VirtualMachine) -> Option { + fn unwind_blocks(&self, vm: &VirtualMachine) -> Option { while let Some(block) = self.pop_block() { match block.typ { BlockType::Loop { .. } => {} @@ -860,7 +855,7 @@ impl Frame { None } - fn unwind_loop(&self, vm: &mut VirtualMachine) -> Block { + fn unwind_loop(&self, vm: &VirtualMachine) -> Block { loop { let block = self.current_block().expect("not in a loop"); match block.typ { @@ -882,7 +877,7 @@ impl Frame { } } - fn unwind_exception(&self, vm: &mut VirtualMachine, exc: PyObjectRef) -> Option { + fn unwind_exception(&self, vm: &VirtualMachine, exc: PyObjectRef) -> Option { // unwind block stack on exception and find any handlers: while let Some(block) = self.pop_block() { match block.typ { @@ -927,7 +922,7 @@ impl Frame { fn with_exit( &self, - vm: &mut VirtualMachine, + vm: &VirtualMachine, context_manager: &PyObjectRef, exc: Option, ) -> PyResult { @@ -950,18 +945,18 @@ impl Frame { vm.call_method(context_manager, "__exit__", args) } - fn store_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult { + fn store_name(&self, vm: &VirtualMachine, name: &str) -> FrameResult { let obj = self.pop_value(); self.scope.store_name(&vm, name, obj); Ok(None) } - fn delete_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult { + fn delete_name(&self, vm: &VirtualMachine, name: &str) -> FrameResult { self.scope.delete_name(vm, name); Ok(None) } - fn load_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult { + fn load_name(&self, vm: &VirtualMachine, name: &str) -> FrameResult { match self.scope.load_name(&vm, name) { Some(value) => { self.push_value(value); @@ -976,11 +971,11 @@ impl Frame { } } - fn subscript(&self, vm: &mut VirtualMachine, a: PyObjectRef, b: PyObjectRef) -> PyResult { + fn subscript(&self, vm: &VirtualMachine, a: PyObjectRef, b: PyObjectRef) -> PyResult { vm.call_method(&a, "__getitem__", vec![b]) } - fn execute_store_subscript(&self, vm: &mut VirtualMachine) -> FrameResult { + fn execute_store_subscript(&self, vm: &VirtualMachine) -> FrameResult { let idx = self.pop_value(); let obj = self.pop_value(); let value = self.pop_value(); @@ -988,7 +983,7 @@ impl Frame { Ok(None) } - fn execute_delete_subscript(&self, vm: &mut VirtualMachine) -> FrameResult { + fn execute_delete_subscript(&self, vm: &VirtualMachine) -> FrameResult { let idx = self.pop_value(); let obj = self.pop_value(); vm.call_method(&obj, "__delitem__", vec![idx])?; @@ -1003,7 +998,7 @@ impl Frame { fn execute_binop( &self, - vm: &mut VirtualMachine, + vm: &VirtualMachine, op: &bytecode::BinaryOperator, inplace: bool, ) -> FrameResult { @@ -1045,7 +1040,7 @@ impl Frame { Ok(None) } - fn execute_unop(&self, vm: &mut VirtualMachine, op: &bytecode::UnaryOperator) -> FrameResult { + fn execute_unop(&self, vm: &VirtualMachine, op: &bytecode::UnaryOperator) -> FrameResult { let a = self.pop_value(); let value = match *op { bytecode::UnaryOperator::Minus => vm.call_method(&a, "__neg__", vec![])?, @@ -1067,7 +1062,7 @@ impl Frame { // https://docs.python.org/3/reference/expressions.html#membership-test-operations fn _membership( &self, - vm: &mut VirtualMachine, + vm: &VirtualMachine, needle: PyObjectRef, haystack: &PyObjectRef, ) -> PyResult { @@ -1076,7 +1071,7 @@ impl Frame { // not implemented. } - fn _in(&self, vm: &mut VirtualMachine, needle: PyObjectRef, haystack: PyObjectRef) -> PyResult { + fn _in(&self, vm: &VirtualMachine, needle: PyObjectRef, haystack: PyObjectRef) -> PyResult { match self._membership(vm, needle, &haystack) { Ok(found) => Ok(found), Err(_) => Err(vm.new_type_error(format!( @@ -1086,12 +1081,7 @@ impl Frame { } } - fn _not_in( - &self, - vm: &mut VirtualMachine, - needle: PyObjectRef, - haystack: PyObjectRef, - ) -> PyResult { + fn _not_in(&self, vm: &VirtualMachine, needle: PyObjectRef, haystack: PyObjectRef) -> PyResult { match self._membership(vm, needle, &haystack) { Ok(found) => Ok(vm.ctx.new_bool(!objbool::get_value(&found))), Err(_) => Err(vm.new_type_error(format!( @@ -1114,7 +1104,7 @@ impl Frame { fn execute_compare( &self, - vm: &mut VirtualMachine, + vm: &VirtualMachine, op: &bytecode::ComparisonOperator, ) -> FrameResult { let b = self.pop_value(); @@ -1136,21 +1126,21 @@ impl Frame { Ok(None) } - fn load_attr(&self, vm: &mut VirtualMachine, attr_name: &str) -> FrameResult { + fn load_attr(&self, vm: &VirtualMachine, attr_name: &str) -> FrameResult { let parent = self.pop_value(); let obj = vm.get_attribute(parent, attr_name)?; self.push_value(obj); Ok(None) } - fn store_attr(&self, vm: &mut VirtualMachine, attr_name: &str) -> FrameResult { + fn store_attr(&self, vm: &VirtualMachine, attr_name: &str) -> FrameResult { let parent = self.pop_value(); let value = self.pop_value(); vm.set_attr(&parent, vm.new_str(attr_name.to_string()), value)?; Ok(None) } - fn delete_attr(&self, vm: &mut VirtualMachine, attr_name: &str) -> FrameResult { + fn delete_attr(&self, vm: &VirtualMachine, attr_name: &str) -> FrameResult { let parent = self.pop_value(); let name = vm.ctx.new_str(attr_name.to_string()); vm.del_attr(&parent, name)?; diff --git a/vm/src/function.rs b/vm/src/function.rs index 8ce7ad12a3..c5ec06cb1c 100644 --- a/vm/src/function.rs +++ b/vm/src/function.rs @@ -79,7 +79,7 @@ impl PyFuncArgs { &self, key: &str, ty: PyObjectRef, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> Result, PyObjectRef> { match self.get_optional_kwarg(key) { Some(kwarg) => { @@ -131,7 +131,7 @@ impl PyFuncArgs { /// /// If the given `FromArgs` includes any conversions, exceptions raised /// during the conversion will halt the binding and return the error. - fn bind(mut self, vm: &mut VirtualMachine) -> PyResult { + fn bind(mut self, vm: &VirtualMachine) -> PyResult { let given_args = self.args.len(); let bound = match T::from_args(vm, &mut self) { Ok(args) => args, @@ -203,7 +203,7 @@ pub trait FromArgs: Sized { } /// Extracts this item from the next argument(s). - fn from_args(vm: &mut VirtualMachine, args: &mut PyFuncArgs) -> Result; + fn from_args(vm: &VirtualMachine, args: &mut PyFuncArgs) -> Result; } /// A map of keyword arguments to their values. @@ -226,7 +226,7 @@ impl FromArgs for KwArgs where T: TryFromObject, { - fn from_args(vm: &mut VirtualMachine, args: &mut PyFuncArgs) -> Result { + fn from_args(vm: &VirtualMachine, args: &mut PyFuncArgs) -> Result { let mut kwargs = HashMap::new(); for (name, value) in args.remaining_keyword() { kwargs.insert(name, T::try_from_object(vm, value)?); @@ -249,7 +249,7 @@ impl FromArgs for Args where T: TryFromObject, { - fn from_args(vm: &mut VirtualMachine, args: &mut PyFuncArgs) -> Result { + fn from_args(vm: &VirtualMachine, args: &mut PyFuncArgs) -> Result { let mut varargs = Vec::new(); while let Some(value) = args.next_positional() { varargs.push(T::try_from_object(vm, value)?); @@ -275,7 +275,7 @@ where 1..=1 } - fn from_args(vm: &mut VirtualMachine, args: &mut PyFuncArgs) -> Result { + fn from_args(vm: &VirtualMachine, args: &mut PyFuncArgs) -> Result { if let Some(value) = args.next_positional() { Ok(T::try_from_object(vm, value)?) } else { @@ -309,7 +309,7 @@ where 0..=1 } - fn from_args(vm: &mut VirtualMachine, args: &mut PyFuncArgs) -> Result { + fn from_args(vm: &VirtualMachine, args: &mut PyFuncArgs) -> Result { if let Some(value) = args.next_positional() { Ok(Present(T::try_from_object(vm, value)?)) } else { @@ -321,7 +321,7 @@ where // For functions that accept no arguments. Implemented explicitly instead of via // macro below to avoid unused warnings. impl FromArgs for () { - fn from_args(_vm: &mut VirtualMachine, _args: &mut PyFuncArgs) -> Result { + fn from_args(_vm: &VirtualMachine, _args: &mut PyFuncArgs) -> Result { Ok(()) } } @@ -349,7 +349,7 @@ macro_rules! tuple_from_py_func_args { min..=max } - fn from_args(vm: &mut VirtualMachine, args: &mut PyFuncArgs) -> Result { + fn from_args(vm: &VirtualMachine, args: &mut PyFuncArgs) -> Result { Ok(($($T::from_args(vm, args)?,)+)) } } @@ -366,14 +366,14 @@ tuple_from_py_func_args!(A, B, C, D); tuple_from_py_func_args!(A, B, C, D, E); /// A built-in Python function. -pub type PyNativeFunc = Box PyResult + 'static>; +pub type PyNativeFunc = Box PyResult + 'static>; /// Implemented by types that are or can generate built-in functions. /// /// For example, any function that: /// /// - Accepts a sequence of types that implement `FromArgs`, followed by a -/// `&mut VirtualMachine` +/// `&VirtualMachine` /// - Returns some type that implements `IntoPyObject` /// /// will generate a `PyNativeFunc` that performs the appropriate type and arity @@ -388,7 +388,7 @@ pub trait IntoPyNativeFunc { impl IntoPyNativeFunc for F where - F: Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult + 'static, + F: Fn(&VirtualMachine, PyFuncArgs) -> PyResult + 'static, { fn into_func(self) -> PyNativeFunc { Box::new(self) @@ -409,7 +409,7 @@ macro_rules! into_py_native_func_tuple { ($(($n:tt, $T:ident)),*) => { impl IntoPyNativeFunc<($($T,)*), R> for F where - F: Fn($($T,)* &mut VirtualMachine) -> R + 'static, + F: Fn($($T,)* &VirtualMachine) -> R + 'static, $($T: FromArgs,)* ($($T,)*): FromArgs, R: IntoPyObject, diff --git a/vm/src/import.rs b/vm/src/import.rs index a36168c287..ea5a125ca9 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -12,13 +12,9 @@ use crate::pyobject::{AttributeProtocol, DictProtocol, PyResult}; use crate::util; use crate::vm::VirtualMachine; -fn import_uncached_module( - vm: &mut VirtualMachine, - current_path: PathBuf, - module: &str, -) -> PyResult { +fn import_uncached_module(vm: &VirtualMachine, current_path: PathBuf, module: &str) -> PyResult { // Check for Rust-native modules - if let Some(module) = vm.stdlib_inits.get(module) { + if let Some(module) = vm.stdlib_inits.borrow().get(module) { return Ok(module(&vm.ctx).clone()); } @@ -48,11 +44,7 @@ fn import_uncached_module( Ok(vm.ctx.new_module(module, attrs)) } -pub fn import_module( - vm: &mut VirtualMachine, - current_path: PathBuf, - module_name: &str, -) -> PyResult { +pub fn import_module(vm: &VirtualMachine, current_path: PathBuf, module_name: &str) -> PyResult { // First, see if we already loaded the module: let sys_modules = vm.sys_module.get_attr("modules").unwrap(); if let Some(module) = sys_modules.get_item(module_name) { diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index 1a5b10d015..4feb1300bd 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -15,18 +15,18 @@ use super::objtuple::PyTuple; use super::objtype; impl IntoPyObject for bool { - fn into_pyobject(self, vm: &mut VirtualMachine) -> PyResult { + fn into_pyobject(self, vm: &VirtualMachine) -> PyResult { Ok(vm.ctx.new_bool(self)) } } impl TryFromObject for bool { - fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { + fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { boolval(vm, obj) } } -pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { +pub fn boolval(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { if let Some(s) = obj.payload::() { return Ok(!s.value.is_empty()); } @@ -70,7 +70,7 @@ The class bool is a subclass of the class int, and cannot be subclassed."; context.set_attr(&bool_type, "__doc__", context.new_str(bool_doc.to_string())); } -pub fn not(vm: &mut VirtualMachine, obj: &PyObjectRef) -> PyResult { +pub fn not(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult { if objtype::isinstance(obj, &vm.ctx.bool_type()) { let value = get_value(obj); Ok(vm.ctx.new_bool(!value)) @@ -84,7 +84,7 @@ pub fn get_value(obj: &PyObjectRef) -> bool { !obj.payload::().unwrap().value.is_zero() } -fn bool_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result { +fn bool_repr(vm: &VirtualMachine, args: PyFuncArgs) -> Result { arg_check!(vm, args, required = [(obj, Some(vm.ctx.bool_type()))]); let v = get_value(obj); let s = if v { @@ -95,7 +95,7 @@ fn bool_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result PyResult { +fn bool_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, diff --git a/vm/src/obj/objbuiltinfunc.rs b/vm/src/obj/objbuiltinfunc.rs index ca181fea4d..259c94b627 100644 --- a/vm/src/obj/objbuiltinfunc.rs +++ b/vm/src/obj/objbuiltinfunc.rs @@ -10,7 +10,7 @@ pub struct PyBuiltinFunction { } impl PyValue for PyBuiltinFunction { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.builtin_function_or_method_type() } } diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index 745d07e59f..c7038fe9ac 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -29,7 +29,7 @@ impl PyByteArray { } impl PyValue for PyByteArray { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.bytearray_type() } } @@ -147,7 +147,7 @@ pub fn init(context: &PyContext) { fn bytearray_new( cls: PyClassRef, val_option: OptionalArg, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> PyResult { // Create bytes data: let value = if let OptionalArg::Present(ival) = val_option { @@ -169,14 +169,14 @@ fn bytearray_new( PyByteArray::new(value).into_ref_with_type(vm, cls.clone()) } -fn bytesarray_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn bytesarray_len(vm: &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 { +fn bytearray_eq(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -191,31 +191,31 @@ fn bytearray_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bool(result)) } -fn bytearray_isalnum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn bytearray_isalnum(vm: &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.is_empty() && bytes.iter().all(|x| char::from(*x).is_alphanumeric()))) } -fn bytearray_isalpha(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn bytearray_isalpha(vm: &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.is_empty() && bytes.iter().all(|x| char::from(*x).is_alphabetic()))) } -fn bytearray_isascii(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn bytearray_isascii(vm: &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.is_empty() && bytes.iter().all(|x| char::from(*x).is_ascii()))) } -fn bytearray_isdigit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn bytearray_isdigit(vm: &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.is_empty() && bytes.iter().all(|x| char::from(*x).is_digit(10)))) } -fn bytearray_islower(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn bytearray_islower(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); let bytes = get_value(zelf); Ok(vm.new_bool( @@ -227,13 +227,13 @@ fn bytearray_islower(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { )) } -fn bytearray_isspace(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn bytearray_isspace(vm: &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.is_empty() && bytes.iter().all(|x| char::from(*x).is_whitespace()))) } -fn bytearray_isupper(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn bytearray_isupper(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); let bytes = get_value(zelf); Ok(vm.new_bool( @@ -245,7 +245,7 @@ fn bytearray_isupper(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { )) } -fn bytearray_istitle(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn bytearray_istitle(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); let bytes = get_value(zelf); @@ -284,7 +284,7 @@ fn is_cased(c: char) -> bool { } /* -fn bytearray_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn bytearray_getitem(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -308,7 +308,7 @@ fn bytearray_to_hex(bytearray: &[u8]) -> String { }) } -fn bytearray_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn bytearray_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, Some(vm.ctx.bytearray_type()))]); let value = get_value(obj); let data = @@ -316,13 +316,13 @@ fn bytearray_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_str(format!("bytearray(b'{}')", data))) } -fn bytearray_clear(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn bytearray_clear(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); get_mut_value(zelf).clear(); Ok(vm.get_none()) } -fn bytearray_pop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn bytearray_pop(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, Some(vm.ctx.bytearray_type()))]); let mut value = get_mut_value(obj); @@ -333,13 +333,13 @@ fn bytearray_pop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn bytearray_lower(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn bytearray_lower(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, Some(vm.ctx.bytearray_type()))]); let value = get_value(obj).to_vec().to_ascii_lowercase(); Ok(vm.ctx.new_bytearray(value)) } -fn bytearray_upper(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn bytearray_upper(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, Some(vm.ctx.bytearray_type()))]); let value = get_value(obj).to_vec().to_ascii_uppercase(); Ok(vm.ctx.new_bytearray(value)) diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 38222360fc..dc1f930a46 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -34,7 +34,7 @@ impl Deref for PyBytes { } impl PyValue for PyBytes { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.bytes_type() } } @@ -76,7 +76,7 @@ pub fn init(context: &PyContext) { fn bytes_new( cls: PyClassRef, val_option: OptionalArg, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> PyResult { // Create bytes data: let value = if let OptionalArg::Present(ival) = val_option { @@ -95,7 +95,7 @@ fn bytes_new( PyBytes::new(value).into_ref_with_type(vm, cls) } -fn bytes_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn bytes_eq(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -110,7 +110,7 @@ fn bytes_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bool(result)) } -fn bytes_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn bytes_ge(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -125,7 +125,7 @@ fn bytes_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bool(result)) } -fn bytes_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn bytes_gt(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -140,7 +140,7 @@ fn bytes_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bool(result)) } -fn bytes_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn bytes_le(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -155,7 +155,7 @@ fn bytes_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bool(result)) } -fn bytes_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn bytes_lt(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -170,14 +170,14 @@ fn bytes_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bool(result)) } -fn bytes_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn bytes_len(vm: &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 { +fn bytes_hash(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytes_type()))]); let data = get_value(zelf); let mut hasher = std::collections::hash_map::DefaultHasher::new(); @@ -190,14 +190,14 @@ pub fn get_value<'a>(obj: &'a PyObjectRef) -> impl Deref> + 'a &obj.payload::().unwrap().value } -fn bytes_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn bytes_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, Some(vm.ctx.bytes_type()))]); let value = get_value(obj); let data = String::from_utf8(value.to_vec()).unwrap(); Ok(vm.new_str(format!("b'{}'", data))) } -fn bytes_iter(obj: PyBytesRef, _vm: &mut VirtualMachine) -> PyIteratorValue { +fn bytes_iter(obj: PyBytesRef, _vm: &VirtualMachine) -> PyIteratorValue { PyIteratorValue { position: Cell::new(0), iterated_obj: obj.into_object(), diff --git a/vm/src/obj/objclassmethod.rs b/vm/src/obj/objclassmethod.rs index c2322b1836..846c399281 100644 --- a/vm/src/obj/objclassmethod.rs +++ b/vm/src/obj/objclassmethod.rs @@ -10,7 +10,7 @@ pub fn init(context: &PyContext) { }); } -fn classmethod_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn classmethod_get(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("classmethod.__get__ {:?}", args.args); arg_check!( vm, @@ -33,7 +33,7 @@ fn classmethod_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn classmethod_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn classmethod_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("classmethod.__new__ {:?}", args.args); arg_check!(vm, args, required = [(cls, None), (callable, None)]); diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index 2b0a4b9354..21df7f2cc1 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -26,7 +26,7 @@ impl fmt::Debug for PyCode { } impl PyValue for PyCode { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.code_type() } } @@ -39,7 +39,7 @@ pub fn init(context: &PyContext) { for (name, f) in &[ ( "co_argcount", - code_co_argcount as fn(&mut VirtualMachine, PyFuncArgs) -> PyResult, + code_co_argcount as fn(&VirtualMachine, PyFuncArgs) -> PyResult, ), ("co_consts", code_co_consts), ("co_filename", code_co_filename), @@ -59,12 +59,12 @@ pub fn get_value(obj: &PyObjectRef) -> bytecode::CodeObject { } } -fn code_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn code_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(_cls, None)]); Err(vm.new_type_error("Cannot directly create code object".to_string())) } -fn code_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn code_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(o, Some(vm.ctx.code_type()))]); let code = get_value(o); @@ -78,33 +78,33 @@ fn code_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_str(repr)) } -fn member_code_obj(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn member_code_obj(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.code_type()))]); Ok(get_value(zelf)) } -fn code_co_argcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn code_co_argcount(vm: &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_filename(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn code_co_filename(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { let code_obj = member_code_obj(vm, args)?; let source_path = code_obj.source_path; Ok(vm.new_str(source_path)) } -fn code_co_firstlineno(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn code_co_firstlineno(vm: &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 { +fn code_co_kwonlyargcount(vm: &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_consts(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn code_co_consts(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { let code_obj = member_code_obj(vm, args)?; let consts = code_obj .get_constants() @@ -113,7 +113,7 @@ fn code_co_consts(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_tuple(consts)) } -fn code_co_name(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn code_co_name(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { let code_obj = member_code_obj(vm, args)?; Ok(vm.new_str(code_obj.obj_name)) } diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index ab07b60ba9..7443306512 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -16,7 +16,7 @@ pub struct PyComplex { type PyComplexRef = PyRef; impl PyValue for PyComplex { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.complex_type() } } @@ -71,7 +71,7 @@ fn complex_new( cls: PyClassRef, real: OptionalArg, imag: OptionalArg, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> PyResult { let real = match real { OptionalArg::Missing => 0.0, @@ -87,26 +87,26 @@ fn complex_new( PyComplex { value }.into_ref_with_type(vm, cls) } -fn complex_real(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn complex_real(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.complex_type()))]); let Complex64 { re, .. } = get_value(zelf); Ok(vm.ctx.new_float(re)) } -fn complex_imag(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn complex_imag(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.complex_type()))]); let Complex64 { im, .. } = get_value(zelf); Ok(vm.ctx.new_float(im)) } -fn complex_abs(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn complex_abs(vm: &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 { +fn complex_add(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -126,7 +126,7 @@ fn complex_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn complex_radd(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn complex_radd(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -145,14 +145,14 @@ fn complex_radd(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn complex_conjugate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn complex_conjugate(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(i, Some(vm.ctx.complex_type()))]); let v1 = get_value(i); Ok(vm.ctx.new_complex(v1.conj())) } -fn complex_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn complex_eq(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -177,12 +177,12 @@ fn complex_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bool(result)) } -fn complex_neg(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn complex_neg(vm: &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 { +fn complex_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, Some(vm.ctx.complex_type()))]); let v = get_value(obj); let repr = if v.re == 0. { diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 8219ab71f5..ad134f5aeb 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -30,7 +30,7 @@ impl fmt::Debug for PyDict { } impl PyValue for PyDict { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.dict_type() } } @@ -45,7 +45,7 @@ pub fn get_mut_elements<'a>(obj: &'a PyObjectRef) -> impl DerefMut PyAttributes { attrs } -pub fn attributes_to_py_dict(vm: &mut VirtualMachine, attributes: PyAttributes) -> PyResult { +pub fn attributes_to_py_dict(vm: &VirtualMachine, attributes: PyAttributes) -> PyResult { let dict = vm.ctx.new_dict(); for (key, value) in attributes { let key = vm.ctx.new_str(key); @@ -133,7 +133,7 @@ pub fn attributes_to_py_dict(vm: &mut VirtualMachine, attributes: PyAttributes) } // Python dict methods: -fn dict_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn dict_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -149,7 +149,7 @@ fn dict_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } else { let iter = objiter::get_iter(vm, dict_obj)?; loop { - fn err(vm: &mut VirtualMachine) -> PyObjectRef { + fn err(vm: &VirtualMachine) -> PyObjectRef { vm.new_type_error("Iterator must have exactly two elements".to_string()) } let element = match objiter::get_next_object(vm, &iter)? { @@ -174,11 +174,11 @@ fn dict_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } impl PyDictRef { - fn len(self, _vm: &mut VirtualMachine) -> usize { + fn len(self, _vm: &VirtualMachine) -> usize { self.entries.borrow().len() } - fn repr(self, vm: &mut VirtualMachine) -> PyResult { + fn repr(self, vm: &VirtualMachine) -> PyResult { let s = if let Some(_guard) = ReprGuard::enter(self.as_object()) { let elements = get_key_value_pairs(self.as_object()); let mut str_parts = vec![]; @@ -195,11 +195,11 @@ impl PyDictRef { Ok(vm.new_str(s)) } - fn contains(self, key: PyStringRef, _vm: &mut VirtualMachine) -> bool { + fn contains(self, key: PyStringRef, _vm: &VirtualMachine) -> bool { self.entries.borrow().contains_key(&key.value) } - fn delitem(self, key: PyStringRef, vm: &mut VirtualMachine) -> PyResult<()> { + fn delitem(self, key: PyStringRef, vm: &VirtualMachine) -> PyResult<()> { let key = &key.value; // Delete the item: let mut elements = self.entries.borrow_mut(); @@ -209,12 +209,12 @@ impl PyDictRef { } } - fn clear(self, _vm: &mut VirtualMachine) { + fn clear(self, _vm: &VirtualMachine) { self.entries.borrow_mut().clear() } /// When iterating over a dictionary, we iterate over the keys of it. - fn iter(self, vm: &mut VirtualMachine) -> PyIteratorValue { + fn iter(self, vm: &VirtualMachine) -> PyIteratorValue { let keys = self .entries .borrow() @@ -229,7 +229,7 @@ impl PyDictRef { } } - fn values(self, vm: &mut VirtualMachine) -> PyIteratorValue { + fn values(self, vm: &VirtualMachine) -> PyIteratorValue { let values = self .entries .borrow() @@ -244,7 +244,7 @@ impl PyDictRef { } } - fn items(self, vm: &mut VirtualMachine) -> PyIteratorValue { + fn items(self, vm: &VirtualMachine) -> PyIteratorValue { let items = self .entries .borrow() @@ -259,12 +259,12 @@ impl PyDictRef { } } - fn setitem(self, needle: PyObjectRef, value: PyObjectRef, _vm: &mut VirtualMachine) { + fn setitem(self, needle: PyObjectRef, value: PyObjectRef, _vm: &VirtualMachine) { let mut elements = self.entries.borrow_mut(); set_item_in_content(&mut elements, &needle, &value) } - fn getitem(self, key: PyStringRef, vm: &mut VirtualMachine) -> PyResult { + fn getitem(self, key: PyStringRef, vm: &VirtualMachine) -> PyResult { let key = &key.value; // What we are looking for: @@ -280,7 +280,7 @@ impl PyDictRef { self, key: PyStringRef, default: OptionalArg, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> PyObjectRef { // What we are looking for: let key = &key.value; diff --git a/vm/src/obj/objellipsis.rs b/vm/src/obj/objellipsis.rs index a0ed8f6441..03c9d69e8a 100644 --- a/vm/src/obj/objellipsis.rs +++ b/vm/src/obj/objellipsis.rs @@ -12,12 +12,12 @@ pub fn init(context: &PyContext) { ); } -fn ellipsis_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn ellipsis_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(_cls, None)]); Ok(vm.ctx.ellipsis()) } -fn ellipsis_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn ellipsis_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(_cls, None)]); Ok(vm.new_str("Ellipsis".to_string())) } diff --git a/vm/src/obj/objenumerate.rs b/vm/src/obj/objenumerate.rs index fb7ae7c716..5a66b7b5e2 100644 --- a/vm/src/obj/objenumerate.rs +++ b/vm/src/obj/objenumerate.rs @@ -20,7 +20,7 @@ pub struct PyEnumerate { type PyEnumerateRef = PyRef; impl PyValue for PyEnumerate { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.enumerate_type() } } @@ -29,7 +29,7 @@ fn enumerate_new( cls: PyClassRef, iterable: PyObjectRef, start: OptionalArg, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> PyResult { let counter = match start { OptionalArg::Present(start) => start.value.clone(), @@ -44,7 +44,7 @@ fn enumerate_new( .into_ref_with_type(vm, cls) } -fn enumerate_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn enumerate_next(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, diff --git a/vm/src/obj/objfilter.rs b/vm/src/obj/objfilter.rs index da329c809a..d83f70cc5c 100644 --- a/vm/src/obj/objfilter.rs +++ b/vm/src/obj/objfilter.rs @@ -14,12 +14,12 @@ pub struct PyFilter { } impl PyValue for PyFilter { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.filter_type() } } -fn filter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn filter_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -35,7 +35,7 @@ fn filter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { )) } -fn filter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn filter_next(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(filter, Some(vm.ctx.filter_type()))]); if let Some(PyFilter { diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 7c5389a58b..849b31d408 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -16,13 +16,13 @@ pub struct PyFloat { } impl PyValue for PyFloat { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.float_type() } } impl IntoPyObject for f64 { - fn into_pyobject(self, vm: &mut VirtualMachine) -> PyResult { + fn into_pyobject(self, vm: &VirtualMachine) -> PyResult { Ok(vm.ctx.new_float(self)) } } @@ -36,7 +36,7 @@ impl From for PyFloat { pub type PyFloatRef = PyRef; impl PyFloatRef { - fn eq(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn eq(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { let value = self.value; let result = if objtype::isinstance(&other, &vm.ctx.float_type()) { let other = get_value(&other); @@ -55,7 +55,7 @@ impl PyFloatRef { vm.ctx.new_bool(result) } - fn lt(self, i2: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn lt(self, i2: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { let v1 = self.value; if objtype::isinstance(&i2, &vm.ctx.float_type()) { vm.ctx.new_bool(v1 < get_value(&i2)) @@ -67,7 +67,7 @@ impl PyFloatRef { } } - fn le(self, i2: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn le(self, i2: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { let v1 = self.value; if objtype::isinstance(&i2, &vm.ctx.float_type()) { vm.ctx.new_bool(v1 <= get_value(&i2)) @@ -79,7 +79,7 @@ impl PyFloatRef { } } - fn gt(self, i2: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn gt(self, i2: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { let v1 = self.value; if objtype::isinstance(&i2, &vm.ctx.float_type()) { vm.ctx.new_bool(v1 > get_value(&i2)) @@ -91,7 +91,7 @@ impl PyFloatRef { } } - fn ge(self, i2: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn ge(self, i2: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { let v1 = self.value; if objtype::isinstance(&i2, &vm.ctx.float_type()) { vm.ctx.new_bool(v1 >= get_value(&i2)) @@ -103,11 +103,11 @@ impl PyFloatRef { } } - fn abs(self, _vm: &mut VirtualMachine) -> f64 { + fn abs(self, _vm: &VirtualMachine) -> f64 { self.value.abs() } - fn add(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn add(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { let v1 = self.value; if objtype::isinstance(&other, &vm.ctx.float_type()) { vm.ctx.new_float(v1 + get_value(&other)) @@ -119,7 +119,7 @@ impl PyFloatRef { } } - fn divmod(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn divmod(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.float_type()) || objtype::isinstance(&other, &vm.ctx.int_type()) { @@ -131,7 +131,7 @@ impl PyFloatRef { } } - fn floordiv(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn floordiv(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { let v1 = self.value; let v2 = if objtype::isinstance(&other, &vm.ctx.float_type) { get_value(&other) @@ -150,7 +150,7 @@ impl PyFloatRef { } } - fn new_float(cls: PyObjectRef, arg: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn new_float(cls: PyObjectRef, arg: PyObjectRef, vm: &VirtualMachine) -> PyResult { let value = if objtype::isinstance(&arg, &vm.ctx.float_type()) { get_value(&arg) } else if objtype::isinstance(&arg, &vm.ctx.int_type()) { @@ -191,7 +191,7 @@ impl PyFloatRef { Ok(PyObject::new(PyFloat { value }, cls.clone())) } - fn mod_(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn mod_(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { let v1 = self.value; let v2 = if objtype::isinstance(&other, &vm.ctx.float_type) { get_value(&other) @@ -210,11 +210,11 @@ impl PyFloatRef { } } - fn neg(self, _vm: &mut VirtualMachine) -> f64 { + fn neg(self, _vm: &VirtualMachine) -> f64 { -self.value } - fn pow(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn pow(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { let v1 = self.value; if objtype::isinstance(&other, &vm.ctx.float_type()) { vm.ctx.new_float(v1.powf(get_value(&other))) @@ -226,7 +226,7 @@ impl PyFloatRef { } } - fn sub(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn sub(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { let v1 = self.value; if objtype::isinstance(&other, &vm.ctx.float_type()) { Ok(vm.ctx.new_float(v1 - get_value(&other))) @@ -239,7 +239,7 @@ impl PyFloatRef { } } - fn rsub(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn rsub(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { let v1 = self.value; if objtype::isinstance(&other, &vm.ctx.float_type()) { Ok(vm.ctx.new_float(get_value(&other) - v1)) @@ -252,11 +252,11 @@ impl PyFloatRef { } } - fn repr(self, _vm: &mut VirtualMachine) -> String { + fn repr(self, _vm: &VirtualMachine) -> String { self.value.to_string() } - fn truediv(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn truediv(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { let v1 = self.value; let v2 = if objtype::isinstance(&other, &vm.ctx.float_type) { get_value(&other) @@ -275,7 +275,7 @@ impl PyFloatRef { } } - fn rtruediv(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn rtruediv(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { let v1 = self.value; let v2 = if objtype::isinstance(&other, &vm.ctx.float_type) { get_value(&other) @@ -294,7 +294,7 @@ impl PyFloatRef { } } - fn mul(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn mul(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { let v1 = self.value; if objtype::isinstance(&other, &vm.ctx.float_type) { Ok(vm.ctx.new_float(v1 * get_value(&other))) @@ -307,16 +307,16 @@ impl PyFloatRef { } } - fn is_integer(self, _vm: &mut VirtualMachine) -> bool { + fn is_integer(self, _vm: &VirtualMachine) -> bool { let v = self.value; (v - v.round()).abs() < std::f64::EPSILON } - fn real(self, _vm: &mut VirtualMachine) -> Self { + fn real(self, _vm: &VirtualMachine) -> Self { self } - fn as_integer_ratio(self, vm: &mut VirtualMachine) -> PyResult { + fn as_integer_ratio(self, vm: &VirtualMachine) -> PyResult { let value = self.value; if value.is_infinite() { return Err( @@ -339,7 +339,7 @@ pub fn get_value(obj: &PyObjectRef) -> f64 { obj.payload::().unwrap().value } -pub fn make_float(vm: &mut VirtualMachine, obj: &PyObjectRef) -> PyResult { +pub fn make_float(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult { if objtype::isinstance(obj, &vm.ctx.float_type()) { Ok(get_value(obj)) } else if let Ok(method) = vm.get_method(obj.clone(), "__float__") { diff --git a/vm/src/obj/objframe.rs b/vm/src/obj/objframe.rs index c3ebad300d..2d179fa24d 100644 --- a/vm/src/obj/objframe.rs +++ b/vm/src/obj/objframe.rs @@ -15,24 +15,24 @@ pub fn init(context: &PyContext) { context.set_attr(&frame_type, "f_code", context.new_property(frame_fcode)); } -fn frame_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn frame_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(_cls, None)]); Err(vm.new_type_error("Cannot directly create frame object".to_string())) } -fn frame_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn frame_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(_frame, Some(vm.ctx.frame_type()))]); let repr = "".to_string(); Ok(vm.new_str(repr)) } -fn frame_flocals(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn frame_flocals(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(frame, Some(vm.ctx.frame_type()))]); let frame = get_value(frame); Ok(frame.scope.get_locals().clone()) } -fn frame_fcode(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn frame_fcode(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(frame, Some(vm.ctx.frame_type()))]); Ok(vm.ctx.new_code_object(get_value(frame).code.clone())) } diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index f2c0bbafe9..c4c480d67b 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -22,7 +22,7 @@ impl PyFunction { } impl PyValue for PyFunction { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.function_type() } } @@ -41,7 +41,7 @@ impl PyMethod { } impl PyValue for PyMethod { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.bound_method_type() } } @@ -59,7 +59,7 @@ pub fn init(context: &PyContext) { }); } -fn bind_method(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn bind_method(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -73,7 +73,7 @@ fn bind_method(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn function_code(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn function_code(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { match args.args[0].payload() { Some(PyFunction { ref code, .. }) => Ok(code.clone()), None => Err(vm.new_type_error("no code".to_string())), diff --git a/vm/src/obj/objgenerator.rs b/vm/src/obj/objgenerator.rs index 2fe08c1387..208bcb43f9 100644 --- a/vm/src/obj/objgenerator.rs +++ b/vm/src/obj/objgenerator.rs @@ -14,7 +14,7 @@ pub struct PyGenerator { type PyGeneratorRef = PyRef; impl PyValue for PyGenerator { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.generator_type() } } @@ -38,22 +38,22 @@ pub fn init(context: &PyContext) { ); } -pub fn new_generator(frame: PyObjectRef, vm: &mut VirtualMachine) -> PyGeneratorRef { +pub fn new_generator(frame: PyObjectRef, vm: &VirtualMachine) -> PyGeneratorRef { PyGenerator { frame }.into_ref(vm) } -fn generator_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn generator_iter(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(o, Some(vm.ctx.generator_type()))]); Ok(o.clone()) } -fn generator_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn generator_next(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(o, Some(vm.ctx.generator_type()))]); let value = vm.get_none(); send(vm, o, &value) } -fn generator_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn generator_send(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -62,7 +62,7 @@ fn generator_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { send(vm, o, value) } -fn send(vm: &mut VirtualMachine, gen: &PyObjectRef, value: &PyObjectRef) -> PyResult { +fn send(vm: &VirtualMachine, gen: &PyObjectRef, value: &PyObjectRef) -> PyResult { if let Some(PyGenerator { ref frame }) = gen.payload() { if let Some(frame) = frame.payload::() { frame.push_value(value.clone()); diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index d1b1388cea..553042ed5f 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -31,13 +31,13 @@ impl PyInt { } impl IntoPyObject for BigInt { - fn into_pyobject(self, vm: &mut VirtualMachine) -> PyResult { + fn into_pyobject(self, vm: &VirtualMachine) -> PyResult { Ok(vm.ctx.new_int(self)) } } impl PyValue for PyInt { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.int_type() } } @@ -45,7 +45,7 @@ impl PyValue for PyInt { macro_rules! impl_into_pyobject_int { ($($t:ty)*) => {$( impl IntoPyObject for $t { - fn into_pyobject(self, vm: &mut VirtualMachine) -> PyResult { + fn into_pyobject(self, vm: &VirtualMachine) -> PyResult { Ok(vm.ctx.new_int(self)) } } @@ -57,7 +57,7 @@ impl_into_pyobject_int!(isize i8 i16 i32 i64 usize u8 u16 u32 u64) ; macro_rules! impl_try_from_object_int { ($(($t:ty, $to_prim:ident),)*) => {$( impl TryFromObject for $t { - fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { + fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { match PyRef::::try_from_object(vm, obj)?.value.$to_prim() { Some(value) => Ok(value), None => Err( @@ -86,11 +86,11 @@ impl_try_from_object_int!( ); impl PyIntRef { - fn pass_value(self, _vm: &mut VirtualMachine) -> Self { + fn pass_value(self, _vm: &VirtualMachine) -> Self { self } - fn eq(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn eq(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { if objtype::isinstance(&other, &vm.ctx.int_type()) { vm.ctx.new_bool(self.value == *get_value(&other)) } else { @@ -98,7 +98,7 @@ impl PyIntRef { } } - fn ne(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn ne(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { if objtype::isinstance(&other, &vm.ctx.int_type()) { vm.ctx.new_bool(self.value != *get_value(&other)) } else { @@ -106,7 +106,7 @@ impl PyIntRef { } } - fn lt(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn lt(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { if objtype::isinstance(&other, &vm.ctx.int_type()) { vm.ctx.new_bool(self.value < *get_value(&other)) } else { @@ -114,7 +114,7 @@ impl PyIntRef { } } - fn le(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn le(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { if objtype::isinstance(&other, &vm.ctx.int_type()) { vm.ctx.new_bool(self.value <= *get_value(&other)) } else { @@ -122,7 +122,7 @@ impl PyIntRef { } } - fn gt(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn gt(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { if objtype::isinstance(&other, &vm.ctx.int_type()) { vm.ctx.new_bool(self.value > *get_value(&other)) } else { @@ -130,7 +130,7 @@ impl PyIntRef { } } - fn ge(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn ge(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { if objtype::isinstance(&other, &vm.ctx.int_type()) { vm.ctx.new_bool(self.value >= *get_value(&other)) } else { @@ -138,7 +138,7 @@ impl PyIntRef { } } - fn add(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn add(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { if objtype::isinstance(&other, &vm.ctx.int_type()) { vm.ctx.new_int((&self.value) + get_value(&other)) } else { @@ -146,7 +146,7 @@ impl PyIntRef { } } - fn sub(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn sub(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { if objtype::isinstance(&other, &vm.ctx.int_type()) { vm.ctx.new_int((&self.value) - get_value(&other)) } else { @@ -154,7 +154,7 @@ impl PyIntRef { } } - fn rsub(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn rsub(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { if objtype::isinstance(&other, &vm.ctx.int_type()) { vm.ctx.new_int(get_value(&other) - (&self.value)) } else { @@ -162,7 +162,7 @@ impl PyIntRef { } } - fn mul(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn mul(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { if objtype::isinstance(&other, &vm.ctx.int_type()) { vm.ctx.new_int((&self.value) * get_value(&other)) } else { @@ -170,7 +170,7 @@ impl PyIntRef { } } - fn truediv(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn truediv(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.int_type()) { div_ints(vm, &self.value, &get_value(&other)) } else { @@ -178,7 +178,7 @@ impl PyIntRef { } } - fn rtruediv(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn rtruediv(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.int_type()) { div_ints(vm, &get_value(&other), &self.value) } else { @@ -186,7 +186,7 @@ impl PyIntRef { } } - fn floordiv(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn floordiv(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.int_type()) { let v2 = get_value(&other); if *v2 != BigInt::zero() { @@ -199,7 +199,7 @@ impl PyIntRef { } } - fn lshift(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn lshift(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if !objtype::isinstance(&other, &vm.ctx.int_type()) { return Err(vm.new_type_error(format!( "unsupported operand type(s) for << '{}' and '{}'", @@ -222,7 +222,7 @@ impl PyIntRef { } } - fn rshift(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn rshift(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if !objtype::isinstance(&other, &vm.ctx.int_type()) { return Err(vm.new_type_error(format!( "unsupported operand type(s) for >> '{}' and '{}'", @@ -245,7 +245,7 @@ impl PyIntRef { } } - fn xor(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn xor(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { if objtype::isinstance(&other, &vm.ctx.int_type()) { vm.ctx.new_int((&self.value) ^ get_value(&other)) } else { @@ -253,7 +253,7 @@ impl PyIntRef { } } - fn rxor(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn rxor(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { if objtype::isinstance(&other, &vm.ctx.int_type()) { vm.ctx.new_int(get_value(&other) ^ (&self.value)) } else { @@ -261,7 +261,7 @@ impl PyIntRef { } } - fn or(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn or(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { if objtype::isinstance(&other, &vm.ctx.int_type()) { vm.ctx.new_int((&self.value) | get_value(&other)) } else { @@ -269,7 +269,7 @@ impl PyIntRef { } } - fn and(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn and(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { if objtype::isinstance(&other, &vm.ctx.int_type()) { let v2 = get_value(&other); vm.ctx.new_int((&self.value) & v2) @@ -278,7 +278,7 @@ impl PyIntRef { } } - fn pow(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn pow(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { if objtype::isinstance(&other, &vm.ctx.int_type()) { let v2 = get_value(&other).to_u32().unwrap(); vm.ctx.new_int(self.value.pow(v2)) @@ -290,7 +290,7 @@ impl PyIntRef { } } - fn mod_(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn mod_(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.int_type()) { let v2 = get_value(&other); if *v2 != BigInt::zero() { @@ -303,7 +303,7 @@ impl PyIntRef { } } - fn divmod(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn divmod(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.int_type()) { let v2 = get_value(&other); if *v2 != BigInt::zero() { @@ -319,37 +319,37 @@ impl PyIntRef { } } - fn neg(self, _vm: &mut VirtualMachine) -> BigInt { + fn neg(self, _vm: &VirtualMachine) -> BigInt { -(&self.value) } - fn hash(self, _vm: &mut VirtualMachine) -> u64 { + fn hash(self, _vm: &VirtualMachine) -> u64 { let mut hasher = std::collections::hash_map::DefaultHasher::new(); self.value.hash(&mut hasher); hasher.finish() } - fn abs(self, _vm: &mut VirtualMachine) -> BigInt { + fn abs(self, _vm: &VirtualMachine) -> BigInt { self.value.abs() } - fn round(self, _precision: OptionalArg, _vm: &mut VirtualMachine) -> Self { + fn round(self, _precision: OptionalArg, _vm: &VirtualMachine) -> Self { self } - fn float(self, _vm: &mut VirtualMachine) -> f64 { + fn float(self, _vm: &VirtualMachine) -> f64 { self.value.to_f64().unwrap() } - fn invert(self, _vm: &mut VirtualMachine) -> BigInt { + fn invert(self, _vm: &VirtualMachine) -> BigInt { !(&self.value) } - fn repr(self, _vm: &mut VirtualMachine) -> String { + fn repr(self, _vm: &VirtualMachine) -> String { self.value.to_string() } - fn format(self, spec: PyRef, vm: &mut VirtualMachine) -> PyResult { + fn format(self, spec: PyRef, vm: &VirtualMachine) -> PyResult { let format_spec = FormatSpec::parse(&spec.value); match format_spec.format_int(&self.value) { Ok(string) => Ok(string), @@ -357,20 +357,20 @@ impl PyIntRef { } } - fn bool(self, _vm: &mut VirtualMachine) -> bool { + fn bool(self, _vm: &VirtualMachine) -> bool { !self.value.is_zero() } - fn bit_length(self, _vm: &mut VirtualMachine) -> usize { + fn bit_length(self, _vm: &VirtualMachine) -> usize { self.value.bits() } - fn imag(self, _vm: &mut VirtualMachine) -> usize { + fn imag(self, _vm: &VirtualMachine) -> usize { 0 } } -fn int_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn int_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -393,7 +393,7 @@ fn int_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } // Casting function: -pub fn to_int(vm: &mut VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult { +pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult { let val = if objtype::isinstance(obj, &vm.ctx.int_type()) { get_value(obj).clone() } else if objtype::isinstance(obj, &vm.ctx.float_type()) { @@ -426,7 +426,7 @@ pub fn get_value(obj: &PyObjectRef) -> &BigInt { } #[inline] -fn div_ints(vm: &mut VirtualMachine, i1: &BigInt, i2: &BigInt) -> PyResult { +fn div_ints(vm: &VirtualMachine, i1: &BigInt, i2: &BigInt) -> PyResult { if i2.is_zero() { return Err(vm.new_zero_division_error("integer division by zero".to_string())); } diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 4c527776d7..1b7709d748 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -18,14 +18,14 @@ use super::objtype; * in the vm when a for loop is entered. Next, it is used when the builtin * function 'iter' is called. */ -pub fn get_iter(vm: &mut VirtualMachine, iter_target: &PyObjectRef) -> PyResult { +pub fn get_iter(vm: &VirtualMachine, iter_target: &PyObjectRef) -> PyResult { vm.call_method(iter_target, "__iter__", vec![]) // let type_str = objstr::get_value(&vm.to_str(iter_target.typ()).unwrap()); // let type_error = vm.new_type_error(format!("Cannot iterate over {}", type_str)); // return Err(type_error); } -pub fn call_next(vm: &mut VirtualMachine, iter_obj: &PyObjectRef) -> PyResult { +pub fn call_next(vm: &VirtualMachine, iter_obj: &PyObjectRef) -> PyResult { vm.call_method(iter_obj, "__next__", vec![]) } @@ -33,7 +33,7 @@ pub fn call_next(vm: &mut VirtualMachine, iter_obj: &PyObjectRef) -> PyResult { * Helper function to retrieve the next object (or none) from an iterator. */ pub fn get_next_object( - vm: &mut VirtualMachine, + vm: &VirtualMachine, iter_obj: &PyObjectRef, ) -> PyResult> { let next_obj: PyResult = call_next(vm, iter_obj); @@ -52,7 +52,7 @@ pub fn get_next_object( } /* Retrieve all elements from an iterator */ -pub fn get_all(vm: &mut VirtualMachine, iter_obj: &PyObjectRef) -> PyResult> { +pub fn get_all(vm: &VirtualMachine, iter_obj: &PyObjectRef) -> PyResult> { let mut elements = vec![]; loop { let element = get_next_object(vm, iter_obj)?; @@ -64,12 +64,12 @@ pub fn get_all(vm: &mut VirtualMachine, iter_obj: &PyObjectRef) -> PyResult PyObjectRef { +pub fn new_stop_iteration(vm: &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 { +fn contains(vm: &VirtualMachine, args: PyFuncArgs, iter_type: PyObjectRef) -> PyResult { arg_check!( vm, args, @@ -93,9 +93,7 @@ fn contains(vm: &mut VirtualMachine, args: PyFuncArgs, iter_type: PyObjectRef) - 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()) - } + move |vm: &VirtualMachine, args: PyFuncArgs| contains(vm, args, cloned_iter_type.clone()) }; context.set_attr( &iter_type, @@ -104,7 +102,7 @@ pub fn iter_type_init(context: &PyContext, iter_type: &PyObjectRef) { ); let iter_func = { let cloned_iter_type = iter_type.clone(); - move |vm: &mut VirtualMachine, args: PyFuncArgs| { + move |vm: &VirtualMachine, args: PyFuncArgs| { arg_check!( vm, args, @@ -118,13 +116,13 @@ pub fn iter_type_init(context: &PyContext, iter_type: &PyObjectRef) { } // Sequence iterator: -fn iter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn iter_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(iter_target, None)]); get_iter(vm, iter_target) } -fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn iter_next(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(iter, Some(vm.ctx.iter_type()))]); if let Some(PyIteratorValue { diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 903cd760b4..1b3a9b4fa9 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -40,7 +40,7 @@ impl From> for PyList { } impl PyValue for PyList { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.list_type() } } @@ -48,17 +48,17 @@ impl PyValue for PyList { pub type PyListRef = PyRef; impl PyListRef { - pub fn append(self, x: PyObjectRef, _vm: &mut VirtualMachine) { + pub fn append(self, x: PyObjectRef, _vm: &VirtualMachine) { self.elements.borrow_mut().push(x); } - fn extend(self, x: PyObjectRef, vm: &mut VirtualMachine) -> PyResult<()> { + fn extend(self, x: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { let mut new_elements = vm.extract_elements(&x)?; self.elements.borrow_mut().append(&mut new_elements); Ok(()) } - fn insert(self, position: isize, element: PyObjectRef, _vm: &mut VirtualMachine) { + fn insert(self, position: isize, element: PyObjectRef, _vm: &VirtualMachine) { let mut vec = self.elements.borrow_mut(); let vec_len = vec.len().to_isize().unwrap(); // This unbounded position can be < 0 or > vec.len() @@ -72,7 +72,7 @@ impl PyListRef { vec.insert(position, element.clone()); } - fn add(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn add(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.list_type()) { let e1 = self.elements.borrow(); let e2 = get_elements(&other); @@ -83,7 +83,7 @@ impl PyListRef { } } - fn iadd(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn iadd(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.list_type()) { self.elements .borrow_mut() @@ -94,23 +94,23 @@ impl PyListRef { } } - fn clear(self, _vm: &mut VirtualMachine) { + fn clear(self, _vm: &VirtualMachine) { self.elements.borrow_mut().clear(); } - fn copy(self, vm: &mut VirtualMachine) -> PyObjectRef { + fn copy(self, vm: &VirtualMachine) -> PyObjectRef { vm.ctx.new_list(self.elements.borrow().clone()) } - fn len(self, _vm: &mut VirtualMachine) -> usize { + fn len(self, _vm: &VirtualMachine) -> usize { self.elements.borrow().len() } - fn reverse(self, _vm: &mut VirtualMachine) { + fn reverse(self, _vm: &VirtualMachine) { self.elements.borrow_mut().reverse(); } - fn getitem(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn getitem(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { get_item( vm, self.as_object(), @@ -119,14 +119,14 @@ impl PyListRef { ) } - fn iter(self, _vm: &mut VirtualMachine) -> PyIteratorValue { + fn iter(self, _vm: &VirtualMachine) -> PyIteratorValue { PyIteratorValue { position: Cell::new(0), iterated_obj: self.into_object(), } } - fn setitem(self, key: PyObjectRef, value: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn setitem(self, key: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult { let mut elements = self.elements.borrow_mut(); if objtype::isinstance(&key, &vm.ctx.int_type()) { @@ -145,7 +145,7 @@ impl PyListRef { } } - fn repr(self, vm: &mut VirtualMachine) -> PyResult { + fn repr(self, vm: &VirtualMachine) -> PyResult { let s = if let Some(_guard) = ReprGuard::enter(self.as_object()) { let mut str_parts = vec![]; for elem in self.elements.borrow().iter() { @@ -159,12 +159,12 @@ impl PyListRef { Ok(s) } - fn mul(self, counter: isize, vm: &mut VirtualMachine) -> PyObjectRef { + fn mul(self, counter: isize, vm: &VirtualMachine) -> PyObjectRef { let new_elements = seq_mul(&self.elements.borrow(), counter); vm.ctx.new_list(new_elements) } - fn count(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn count(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { let mut count: usize = 0; for element in self.elements.borrow().iter() { if needle.is(element) { @@ -179,7 +179,7 @@ impl PyListRef { Ok(count) } - fn contains(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn contains(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { for element in self.elements.borrow().iter() { if needle.is(element) { return Ok(true); @@ -193,7 +193,7 @@ impl PyListRef { Ok(false) } - fn index(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn index(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { for (index, element) in self.elements.borrow().iter().enumerate() { if needle.is(element) { return Ok(index); @@ -207,7 +207,7 @@ impl PyListRef { Err(vm.new_value_error(format!("'{}' is not in list", needle_str))) } - fn pop(self, i: OptionalArg, vm: &mut VirtualMachine) -> PyResult { + fn pop(self, i: OptionalArg, vm: &VirtualMachine) -> PyResult { let mut i = i.into_option().unwrap_or(-1); let mut elements = self.elements.borrow_mut(); if i < 0 { @@ -222,7 +222,7 @@ impl PyListRef { } } - fn remove(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult<()> { + fn remove(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { let mut ri: Option = None; for (index, element) in self.elements.borrow().iter().enumerate() { if needle.is(element) { @@ -245,7 +245,7 @@ impl PyListRef { } } - fn eq(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn eq(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if self.as_object().is(&other) { return Ok(vm.new_bool(true)); } @@ -260,7 +260,7 @@ impl PyListRef { } } - fn lt(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn lt(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.list_type()) { let zelf = self.elements.borrow(); let other = get_elements(&other); @@ -271,7 +271,7 @@ impl PyListRef { } } - fn gt(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn gt(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.list_type()) { let zelf = self.elements.borrow(); let other = get_elements(&other); @@ -282,7 +282,7 @@ impl PyListRef { } } - fn ge(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn ge(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.list_type()) { let zelf = self.elements.borrow(); let other = get_elements(&other); @@ -293,7 +293,7 @@ impl PyListRef { } } - fn le(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn le(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.list_type()) { let zelf = self.elements.borrow(); let other = get_elements(&other); @@ -308,7 +308,7 @@ impl PyListRef { fn list_new( cls: PyRef, iterable: OptionalArg, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> PyResult { if !objtype::issubclass(cls.as_object(), &vm.ctx.list_type()) { return Err(vm.new_type_error(format!("{} is not a subtype of list", cls))); @@ -324,7 +324,7 @@ fn list_new( } fn quicksort( - vm: &mut VirtualMachine, + vm: &VirtualMachine, keys: &mut [PyObjectRef], values: &mut [PyObjectRef], ) -> PyResult<()> { @@ -338,7 +338,7 @@ fn quicksort( } fn partition( - vm: &mut VirtualMachine, + vm: &VirtualMachine, keys: &mut [PyObjectRef], values: &mut [PyObjectRef], ) -> PyResult { @@ -365,7 +365,7 @@ fn partition( } fn do_sort( - vm: &mut VirtualMachine, + vm: &VirtualMachine, values: &mut Vec, key_func: Option, reverse: bool, @@ -388,7 +388,7 @@ fn do_sort( Ok(()) } -fn list_sort(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn list_sort(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(list, Some(vm.ctx.list_type()))]); let key_func = args.get_optional_kwarg("key"); let reverse = args.get_optional_kwarg("reverse"); diff --git a/vm/src/obj/objmap.rs b/vm/src/obj/objmap.rs index 029d3bae91..74aabe1072 100644 --- a/vm/src/obj/objmap.rs +++ b/vm/src/obj/objmap.rs @@ -13,7 +13,7 @@ pub struct PyMap { type PyMapRef = PyRef; impl PyValue for PyMap { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.map_type() } } @@ -22,7 +22,7 @@ fn map_new( cls: PyClassRef, function: PyObjectRef, iterables: Args, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> PyResult { let iterators = iterables .into_iter() @@ -35,7 +35,7 @@ fn map_new( .into_ref_with_type(vm, cls.clone()) } -fn map_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn map_next(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(map, Some(vm.ctx.map_type()))]); if let Some(PyMap { diff --git a/vm/src/obj/objmemory.rs b/vm/src/obj/objmemory.rs index 5386faaf2e..aa67985514 100644 --- a/vm/src/obj/objmemory.rs +++ b/vm/src/obj/objmemory.rs @@ -8,12 +8,12 @@ pub struct PyMemoryView { } impl PyValue for PyMemoryView { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.memoryview_type() } } -pub fn new_memory_view(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +pub fn new_memory_view(vm: &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( diff --git a/vm/src/obj/objmodule.rs b/vm/src/obj/objmodule.rs index da7d282486..fe6e3fca1d 100644 --- a/vm/src/obj/objmodule.rs +++ b/vm/src/obj/objmodule.rs @@ -10,13 +10,13 @@ pub struct PyModule { pub type PyModuleRef = PyRef; impl PyValue for PyModule { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.module_type() } } impl PyModuleRef { - fn dir(self: PyModuleRef, vm: &mut VirtualMachine) -> PyResult { + fn dir(self: PyModuleRef, vm: &VirtualMachine) -> PyResult { let keys = self .dict .get_key_value_pairs() @@ -26,7 +26,7 @@ impl PyModuleRef { Ok(vm.ctx.new_list(keys)) } - fn set_attr(self, attr: PyStringRef, value: PyObjectRef, vm: &mut VirtualMachine) { + fn set_attr(self, attr: PyStringRef, value: PyObjectRef, vm: &VirtualMachine) { self.dict.set_item(&vm.ctx, &attr.value, value) } } diff --git a/vm/src/obj/objnone.rs b/vm/src/obj/objnone.rs index 1c00512199..6bee900312 100644 --- a/vm/src/obj/objnone.rs +++ b/vm/src/obj/objnone.rs @@ -12,7 +12,7 @@ pub struct PyNone; pub type PyNoneRef = PyRef; impl PyValue for PyNone { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.none().typ() } } @@ -20,13 +20,13 @@ impl PyValue for PyNone { // This allows a built-in function to not return a value, mapping to // Python's behavior of returning `None` in this situation. impl IntoPyObject for () { - fn into_pyobject(self, vm: &mut VirtualMachine) -> PyResult { + fn into_pyobject(self, vm: &VirtualMachine) -> PyResult { Ok(vm.ctx.none()) } } impl IntoPyObject for Option { - fn into_pyobject(self, vm: &mut VirtualMachine) -> PyResult { + fn into_pyobject(self, vm: &VirtualMachine) -> PyResult { match self { Some(x) => x.into_pyobject(vm), None => Ok(vm.ctx.none()), @@ -35,15 +35,15 @@ impl IntoPyObject for Option { } impl PyNoneRef { - fn repr(self, _vm: &mut VirtualMachine) -> PyResult { + fn repr(self, _vm: &VirtualMachine) -> PyResult { Ok("None".to_string()) } - fn bool(self, _vm: &mut VirtualMachine) -> PyResult { + fn bool(self, _vm: &VirtualMachine) -> PyResult { Ok(false) } - fn get_attribute(self, name: PyStringRef, vm: &mut VirtualMachine) -> PyResult { + fn get_attribute(self, name: PyStringRef, vm: &VirtualMachine) -> PyResult { trace!("None.__getattribute__({:?}, {:?})", self, name); let cls = self.typ().into_object(); @@ -60,7 +60,7 @@ impl PyNoneRef { get_func: PyObjectRef, obj: PyObjectRef, cls: PyObjectRef, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> PyResult { if let Ok(property) = PyPropertyRef::try_from_object(vm, descriptor.clone()) { property.instance_binding_get(obj, vm) @@ -95,7 +95,7 @@ impl PyNoneRef { } } -fn none_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn none_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index f7e91a8d58..86c5469022 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -13,14 +13,14 @@ use crate::vm::VirtualMachine; pub struct PyInstance; impl PyValue for PyInstance { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.object() } } pub type PyInstanceRef = PyRef; -pub fn new_instance(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { +pub fn new_instance(vm: &VirtualMachine, mut args: PyFuncArgs) -> PyResult { // more or less __new__ operator let cls = args.shift(); Ok(if cls.is(&vm.ctx.object) { @@ -30,7 +30,7 @@ pub fn new_instance(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { }) } -fn object_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn object_eq(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -39,7 +39,7 @@ fn object_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.not_implemented()) } -fn object_ne(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn object_ne(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -49,7 +49,7 @@ fn object_ne(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.not_implemented()) } -fn object_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn object_lt(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -59,7 +59,7 @@ fn object_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.not_implemented()) } -fn object_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn object_le(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -69,7 +69,7 @@ fn object_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.not_implemented()) } -fn object_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn object_gt(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -79,7 +79,7 @@ fn object_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.not_implemented()) } -fn object_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn object_ge(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -89,7 +89,7 @@ fn object_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.not_implemented()) } -fn object_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn object_hash(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(_zelf, Some(vm.ctx.object()))]); // For now default to non hashable @@ -100,7 +100,7 @@ fn object_setattr( obj: PyInstanceRef, attr_name: PyStringRef, value: PyObjectRef, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> PyResult<()> { trace!("object.__setattr__({:?}, {}, {:?})", obj, attr_name, value); let cls = obj.as_object().typ(); @@ -126,11 +126,7 @@ fn object_setattr( } } -fn object_delattr( - obj: PyInstanceRef, - attr_name: PyStringRef, - vm: &mut VirtualMachine, -) -> PyResult<()> { +fn object_delattr(obj: PyInstanceRef, attr_name: PyStringRef, vm: &VirtualMachine) -> PyResult<()> { let cls = obj.as_object().typ(); if let Some(attr) = cls.get_attr(&attr_name.value) { @@ -154,19 +150,19 @@ fn object_delattr( } } -fn object_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn object_str(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.object()))]); vm.call_method(zelf, "__repr__", vec![]) } -fn object_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn object_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, Some(vm.ctx.object()))]); let type_name = objtype::get_type_name(&obj.typ()); let address = obj.get_id(); Ok(vm.new_str(format!("<{} object at 0x{:x}>", type_name, address))) } -pub fn object_dir(obj: PyObjectRef, vm: &mut VirtualMachine) -> PyList { +pub fn object_dir(obj: PyObjectRef, vm: &VirtualMachine) -> PyList { let attributes = get_attributes(&obj); let attributes: Vec = attributes .keys() @@ -178,7 +174,7 @@ pub fn object_dir(obj: PyObjectRef, vm: &mut VirtualMachine) -> PyList { fn object_format( obj: PyObjectRef, format_spec: PyStringRef, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> PyResult { if format_spec.value.is_empty() { vm.to_str(&obj) @@ -223,24 +219,24 @@ pub fn init(context: &PyContext) { context.set_attr(&object, "__doc__", context.new_str(object_doc.to_string())); } -fn object_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { +fn object_init(vm: &VirtualMachine, _args: PyFuncArgs) -> PyResult { Ok(vm.ctx.none()) } -fn object_class(obj: PyObjectRef, _vm: &mut VirtualMachine) -> PyObjectRef { +fn object_class(obj: PyObjectRef, _vm: &VirtualMachine) -> PyObjectRef { obj.typ() } fn object_class_setter( instance: PyObjectRef, _value: PyObjectRef, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> PyResult { let type_repr = vm.to_pystr(&instance.typ())?; Err(vm.new_type_error(format!("can't change class of type '{}'", type_repr))) } -fn object_dict(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn object_dict(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { if let Some(ref dict) = args.args[0].dict { let new_dict = vm.new_dict(); for (attr, value) in dict.borrow().iter() { @@ -252,7 +248,7 @@ fn object_dict(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn object_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn object_getattribute(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index 44461c41b8..d1791f2485 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -16,7 +16,7 @@ pub struct PyReadOnlyProperty { } impl PyValue for PyReadOnlyProperty { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.readonly_property_type() } } @@ -28,7 +28,7 @@ impl PyReadOnlyPropertyRef { self, obj: PyObjectRef, _owner: OptionalArg, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> PyResult { if obj.is(&vm.ctx.none) { Ok(self.into_object()) @@ -47,7 +47,7 @@ pub struct PyProperty { } impl PyValue for PyProperty { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.property_type() } } @@ -61,7 +61,7 @@ impl PyPropertyRef { fset: OptionalArg, fdel: OptionalArg, _doc: OptionalArg, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> PyResult { PyProperty { getter: fget.into_option(), @@ -74,11 +74,7 @@ impl PyPropertyRef { // Descriptor methods // specialised version that doesn't check for None - pub(crate) fn instance_binding_get( - self, - obj: PyObjectRef, - vm: &mut VirtualMachine, - ) -> PyResult { + pub(crate) fn instance_binding_get(self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { if let Some(getter) = self.getter.as_ref() { vm.invoke(getter.clone(), obj) } else { @@ -90,7 +86,7 @@ impl PyPropertyRef { self, obj: PyObjectRef, _owner: OptionalArg, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> PyResult { if let Some(getter) = self.getter.as_ref() { if obj.is(&vm.ctx.none) { @@ -103,7 +99,7 @@ impl PyPropertyRef { } } - fn set(self, obj: PyObjectRef, value: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn set(self, obj: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult { if let Some(setter) = self.setter.as_ref() { vm.invoke(setter.clone(), vec![obj, value]) } else { @@ -111,7 +107,7 @@ impl PyPropertyRef { } } - fn delete(self, obj: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn delete(self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { if let Some(deleter) = self.deleter.as_ref() { vm.invoke(deleter.clone(), obj) } else { @@ -121,21 +117,21 @@ impl PyPropertyRef { // Access functions - fn fget(self, _vm: &mut VirtualMachine) -> Option { + fn fget(self, _vm: &VirtualMachine) -> Option { self.getter.clone() } - fn fset(self, _vm: &mut VirtualMachine) -> Option { + fn fset(self, _vm: &VirtualMachine) -> Option { self.setter.clone() } - fn fdel(self, _vm: &mut VirtualMachine) -> Option { + fn fdel(self, _vm: &VirtualMachine) -> Option { self.deleter.clone() } // Python builder functions - fn getter(self, getter: Option, vm: &mut VirtualMachine) -> PyResult { + fn getter(self, getter: Option, vm: &VirtualMachine) -> PyResult { PyProperty { getter: getter.or_else(|| self.getter.clone()), setter: self.setter.clone(), @@ -144,7 +140,7 @@ impl PyPropertyRef { .into_ref_with_type(vm, self.typ()) } - fn setter(self, setter: Option, vm: &mut VirtualMachine) -> PyResult { + fn setter(self, setter: Option, vm: &VirtualMachine) -> PyResult { PyProperty { getter: self.getter.clone(), setter: setter.or_else(|| self.setter.clone()), @@ -153,7 +149,7 @@ impl PyPropertyRef { .into_ref_with_type(vm, self.typ()) } - fn deleter(self, deleter: Option, vm: &mut VirtualMachine) -> PyResult { + fn deleter(self, deleter: Option, vm: &VirtualMachine) -> PyResult { PyProperty { getter: self.getter.clone(), setter: self.setter.clone(), diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 49f1a9e988..0730762a84 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -25,7 +25,7 @@ pub struct PyRange { } impl PyValue for PyRange { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.range_type() } } @@ -196,7 +196,7 @@ pub fn init(context: &PyContext) { context.set_attr(&range_type, "step", context.new_property(range_step)); } -fn range_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn range_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -232,7 +232,7 @@ fn range_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn range_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn range_iter(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(range, Some(vm.ctx.range_type()))]); Ok(PyObject::new( @@ -244,7 +244,7 @@ fn range_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { )) } -fn range_reversed(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn range_reversed(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); let range = get_value(zelf).reversed(); @@ -258,7 +258,7 @@ fn range_reversed(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { )) } -fn range_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn range_len(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); if let Some(len) = get_value(zelf).try_len() { @@ -268,7 +268,7 @@ fn range_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn range_getitem(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -328,7 +328,7 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn range_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn range_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); let repr = get_value(zelf).repr(); @@ -336,7 +336,7 @@ fn range_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_str(repr)) } -fn range_bool(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn range_bool(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); let len = get_value(zelf).len(); @@ -344,7 +344,7 @@ fn range_bool(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bool(len > 0)) } -fn range_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn range_contains(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -362,7 +362,7 @@ fn range_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bool(result)) } -fn range_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn range_index(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -383,7 +383,7 @@ fn range_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn range_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn range_count(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -399,17 +399,17 @@ fn range_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn range_start(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn range_start(vm: &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 { +fn range_stop(vm: &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 { +fn range_step(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); Ok(vm.ctx.new_int(get_value(zelf).step)) } diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index 2fd97d2790..ad9c7bf361 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -61,11 +61,7 @@ pub trait PySliceableSequence { start..stop } - fn get_slice_items( - &self, - vm: &mut VirtualMachine, - slice: &PyObjectRef, - ) -> Result + fn get_slice_items(&self, vm: &VirtualMachine, slice: &PyObjectRef) -> Result where Self: Sized, { @@ -142,7 +138,7 @@ impl PySliceableSequence for Vec { } pub fn get_item( - vm: &mut VirtualMachine, + vm: &VirtualMachine, sequence: &PyObjectRef, elements: &[PyObjectRef], subscript: PyObjectRef, @@ -186,7 +182,7 @@ pub fn get_item( } pub fn seq_equal( - vm: &mut VirtualMachine, + vm: &VirtualMachine, zelf: &[PyObjectRef], other: &[PyObjectRef], ) -> Result { @@ -207,7 +203,7 @@ pub fn seq_equal( } pub fn seq_lt( - vm: &mut VirtualMachine, + vm: &VirtualMachine, zelf: &[PyObjectRef], other: &[PyObjectRef], ) -> Result { @@ -247,7 +243,7 @@ pub fn seq_lt( } pub fn seq_gt( - vm: &mut VirtualMachine, + vm: &VirtualMachine, zelf: &[PyObjectRef], other: &[PyObjectRef], ) -> Result { @@ -286,7 +282,7 @@ pub fn seq_gt( } pub fn seq_ge( - vm: &mut VirtualMachine, + vm: &VirtualMachine, zelf: &[PyObjectRef], other: &[PyObjectRef], ) -> Result { @@ -294,7 +290,7 @@ pub fn seq_ge( } pub fn seq_le( - vm: &mut VirtualMachine, + vm: &VirtualMachine, zelf: &[PyObjectRef], other: &[PyObjectRef], ) -> Result { diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 2b46261468..b11c21aa39 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -32,7 +32,7 @@ impl fmt::Debug for PySet { } impl PyValue for PySet { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.set_type() } } @@ -42,10 +42,10 @@ pub fn get_elements(obj: &PyObjectRef) -> HashMap { } fn perform_action_with_hash( - vm: &mut VirtualMachine, + vm: &VirtualMachine, elements: &mut HashMap, item: &PyObjectRef, - f: &Fn(&mut VirtualMachine, &mut HashMap, u64, &PyObjectRef) -> PyResult, + f: &Fn(&VirtualMachine, &mut HashMap, u64, &PyObjectRef) -> PyResult, ) -> PyResult { let hash: PyObjectRef = vm.call_method(item, "__hash__", vec![])?; @@ -57,12 +57,12 @@ fn perform_action_with_hash( } fn insert_into_set( - vm: &mut VirtualMachine, + vm: &VirtualMachine, elements: &mut HashMap, item: &PyObjectRef, ) -> PyResult { fn insert( - vm: &mut VirtualMachine, + vm: &VirtualMachine, elements: &mut HashMap, key: u64, value: &PyObjectRef, @@ -73,7 +73,7 @@ fn insert_into_set( perform_action_with_hash(vm, elements, item, &insert) } -fn set_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn set_add(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("set.add called with: {:?}", args); arg_check!( vm, @@ -86,7 +86,7 @@ fn set_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn set_remove(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn set_remove(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("set.remove called with: {:?}", args); arg_check!( vm, @@ -96,7 +96,7 @@ fn set_remove(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match s.payload::() { Some(set) => { fn remove( - vm: &mut VirtualMachine, + vm: &VirtualMachine, elements: &mut HashMap, key: u64, value: &PyObjectRef, @@ -115,7 +115,7 @@ fn set_remove(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn set_discard(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn set_discard(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("set.discard called with: {:?}", args); arg_check!( vm, @@ -125,7 +125,7 @@ fn set_discard(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match s.payload::() { Some(set) => { fn discard( - vm: &mut VirtualMachine, + vm: &VirtualMachine, elements: &mut HashMap, key: u64, _value: &PyObjectRef, @@ -139,7 +139,7 @@ fn set_discard(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn set_clear(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn set_clear(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("set.clear called"); arg_check!(vm, args, required = [(s, Some(vm.ctx.set_type()))]); match s.payload::() { @@ -155,7 +155,7 @@ fn set_clear(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn set_new( cls: PyClassRef, iterable: OptionalArg, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> PyResult { if !objtype::issubclass(cls.as_object(), &vm.ctx.set_type()) { return Err(vm.new_type_error(format!("{} is not a subtype of set", cls))); @@ -179,14 +179,14 @@ fn set_new( .into_ref_with_type(vm, cls) } -fn set_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn set_len(vm: &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())) } -fn set_copy(obj: PySetRef, _vm: &mut VirtualMachine) -> PySet { +fn set_copy(obj: PySetRef, _vm: &VirtualMachine) -> PySet { trace!("set.copy called with: {:?}", obj); let elements = obj.elements.borrow().clone(); PySet { @@ -194,7 +194,7 @@ fn set_copy(obj: PySetRef, _vm: &mut VirtualMachine) -> PySet { } } -fn set_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn set_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(o, Some(vm.ctx.set_type()))]); let elements = get_elements(o); @@ -214,7 +214,7 @@ fn set_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_str(s)) } -pub fn set_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +pub fn set_contains(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -234,7 +234,7 @@ pub fn set_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_bool(false)) } -fn set_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn set_eq(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { set_compare_inner( vm, args, @@ -243,7 +243,7 @@ fn set_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ) } -fn set_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn set_ge(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { set_compare_inner( vm, args, @@ -252,7 +252,7 @@ fn set_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ) } -fn set_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn set_gt(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { set_compare_inner( vm, args, @@ -261,7 +261,7 @@ fn set_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ) } -fn set_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn set_le(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { set_compare_inner( vm, args, @@ -270,7 +270,7 @@ fn set_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ) } -fn set_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn set_lt(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { set_compare_inner( vm, args, @@ -280,7 +280,7 @@ fn set_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn set_compare_inner( - vm: &mut VirtualMachine, + vm: &VirtualMachine, args: PyFuncArgs, size_func: &Fn(usize, usize) -> bool, swap: bool, @@ -327,7 +327,7 @@ fn set_compare_inner( Ok(vm.new_bool(true)) } -fn set_union(zelf: PySetRef, other: PySetRef, _vm: &mut VirtualMachine) -> PySet { +fn set_union(zelf: PySetRef, other: PySetRef, _vm: &VirtualMachine) -> PySet { let mut elements = zelf.elements.borrow().clone(); elements.extend(other.elements.borrow().clone()); @@ -336,18 +336,18 @@ fn set_union(zelf: PySetRef, other: PySetRef, _vm: &mut VirtualMachine) -> PySet } } -fn set_intersection(zelf: PySetRef, other: PySetRef, vm: &mut VirtualMachine) -> PyResult { +fn set_intersection(zelf: PySetRef, other: PySetRef, vm: &VirtualMachine) -> PyResult { set_combine_inner(zelf, other, vm, SetCombineOperation::Intersection) } -fn set_difference(zelf: PySetRef, other: PySetRef, vm: &mut VirtualMachine) -> PyResult { +fn set_difference(zelf: PySetRef, other: PySetRef, vm: &VirtualMachine) -> PyResult { set_combine_inner(zelf, other, vm, SetCombineOperation::Difference) } fn set_symmetric_difference( zelf: PySetRef, other: PySetRef, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> PyResult { let mut elements = HashMap::new(); @@ -378,7 +378,7 @@ enum SetCombineOperation { fn set_combine_inner( zelf: PySetRef, other: PySetRef, - vm: &mut VirtualMachine, + vm: &VirtualMachine, op: SetCombineOperation, ) -> PyResult { let mut elements = HashMap::new(); @@ -399,7 +399,7 @@ fn set_combine_inner( }) } -fn set_pop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn set_pop(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.set_type()))]); match s.payload::() { @@ -414,12 +414,12 @@ fn set_pop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn set_update(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn set_update(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { set_ior(vm, args)?; Ok(vm.get_none()) } -fn set_ior(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn set_ior(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -438,26 +438,26 @@ fn set_ior(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(zelf.clone()) } -fn set_intersection_update(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn set_intersection_update(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { set_combine_update_inner(vm, args, SetCombineOperation::Intersection)?; Ok(vm.get_none()) } -fn set_iand(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn set_iand(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { set_combine_update_inner(vm, args, SetCombineOperation::Intersection) } -fn set_difference_update(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn set_difference_update(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { set_combine_update_inner(vm, args, SetCombineOperation::Difference)?; Ok(vm.get_none()) } -fn set_isub(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn set_isub(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { set_combine_update_inner(vm, args, SetCombineOperation::Difference) } fn set_combine_update_inner( - vm: &mut VirtualMachine, + vm: &VirtualMachine, args: PyFuncArgs, op: SetCombineOperation, ) -> PyResult { @@ -486,12 +486,12 @@ fn set_combine_update_inner( Ok(zelf.clone()) } -fn set_symmetric_difference_update(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn set_symmetric_difference_update(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { set_ixor(vm, args)?; Ok(vm.get_none()) } -fn set_ixor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn set_ixor(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -518,7 +518,7 @@ fn set_ixor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(zelf.clone()) } -fn set_iter(zelf: PySetRef, vm: &mut VirtualMachine) -> PyIteratorValue { +fn set_iter(zelf: PySetRef, vm: &VirtualMachine) -> PyIteratorValue { let items = zelf.elements.borrow().values().cloned().collect(); let set_list = vm.ctx.new_list(items); PyIteratorValue { @@ -527,7 +527,7 @@ fn set_iter(zelf: PySetRef, vm: &mut VirtualMachine) -> PyIteratorValue { } } -fn frozenset_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn frozenset_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(o, Some(vm.ctx.frozenset_type()))]); let elements = get_elements(o); diff --git a/vm/src/obj/objslice.rs b/vm/src/obj/objslice.rs index 8d65bc331e..263490565d 100644 --- a/vm/src/obj/objslice.rs +++ b/vm/src/obj/objslice.rs @@ -15,12 +15,12 @@ pub struct PySlice { } impl PyValue for PySlice { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.slice_type() } } -fn slice_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn slice_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { no_kwargs!(vm, args); let (cls, start, stop, step): ( &PyObjectRef, @@ -64,7 +64,7 @@ fn slice_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { )) } -fn get_property_value(vm: &mut VirtualMachine, value: &Option) -> PyResult { +fn get_property_value(vm: &VirtualMachine, value: &Option) -> PyResult { if let Some(value) = value { Ok(vm.ctx.new_int(value.clone())) } else { @@ -72,7 +72,7 @@ fn get_property_value(vm: &mut VirtualMachine, value: &Option) -> PyResu } } -fn slice_start(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn slice_start(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(slice, Some(vm.ctx.slice_type()))]); if let Some(PySlice { start, .. }) = &slice.payload() { get_property_value(vm, start) @@ -81,7 +81,7 @@ fn slice_start(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn slice_stop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn slice_stop(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(slice, Some(vm.ctx.slice_type()))]); if let Some(PySlice { stop, .. }) = &slice.payload() { get_property_value(vm, stop) @@ -90,7 +90,7 @@ fn slice_stop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn slice_step(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn slice_step(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(slice, Some(vm.ctx.slice_type()))]); if let Some(PySlice { step, .. }) = &slice.payload() { get_property_value(vm, step) diff --git a/vm/src/obj/objstaticmethod.rs b/vm/src/obj/objstaticmethod.rs index 9d1a96ed2a..807398de29 100644 --- a/vm/src/obj/objstaticmethod.rs +++ b/vm/src/obj/objstaticmethod.rs @@ -11,7 +11,7 @@ pub fn init(context: &PyContext) { } // `staticmethod` methods. -fn staticmethod_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn staticmethod_get(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("staticmethod.__get__ {:?}", args.args); arg_check!( vm, @@ -30,7 +30,7 @@ fn staticmethod_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn staticmethod_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn staticmethod_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("staticmethod.__new__ {:?}", args.args); arg_check!(vm, args, required = [(cls, None), (callable, None)]); diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 51e29768cb..826396925c 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -34,13 +34,13 @@ impl fmt::Display for PyString { } impl TryIntoRef for String { - fn try_into_ref(self, vm: &mut VirtualMachine) -> PyResult> { + fn try_into_ref(self, vm: &VirtualMachine) -> PyResult> { Ok(PyString { value: self }.into_ref(vm)) } } impl TryIntoRef for &str { - fn try_into_ref(self, vm: &mut VirtualMachine) -> PyResult> { + fn try_into_ref(self, vm: &VirtualMachine) -> PyResult> { Ok(PyString { value: self.to_string(), } @@ -49,7 +49,7 @@ impl TryIntoRef for &str { } impl PyStringRef { - fn add(self, rhs: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn add(self, rhs: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&rhs, &vm.ctx.str_type()) { Ok(format!("{}{}", self.value, get_value(&rhs))) } else { @@ -57,7 +57,7 @@ impl PyStringRef { } } - fn eq(self, rhs: PyObjectRef, vm: &mut VirtualMachine) -> bool { + fn eq(self, rhs: PyObjectRef, vm: &VirtualMachine) -> bool { if objtype::isinstance(&rhs, &vm.ctx.str_type()) { self.value == get_value(&rhs) } else { @@ -65,15 +65,15 @@ impl PyStringRef { } } - fn contains(self, needle: PyStringRef, _vm: &mut VirtualMachine) -> bool { + fn contains(self, needle: PyStringRef, _vm: &VirtualMachine) -> bool { self.value.contains(&needle.value) } - fn getitem(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn getitem(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { subscript(vm, &self.value, needle) } - fn gt(self, rhs: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn gt(self, rhs: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&rhs, &vm.ctx.str_type()) { Ok(self.value > get_value(&rhs)) } else { @@ -81,7 +81,7 @@ impl PyStringRef { } } - fn ge(self, rhs: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn ge(self, rhs: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&rhs, &vm.ctx.str_type()) { Ok(self.value >= get_value(&rhs)) } else { @@ -89,7 +89,7 @@ impl PyStringRef { } } - fn lt(self, rhs: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn lt(self, rhs: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&rhs, &vm.ctx.str_type()) { Ok(self.value < get_value(&rhs)) } else { @@ -97,7 +97,7 @@ impl PyStringRef { } } - fn le(self, rhs: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn le(self, rhs: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&rhs, &vm.ctx.str_type()) { Ok(self.value <= get_value(&rhs)) } else { @@ -105,17 +105,17 @@ impl PyStringRef { } } - fn hash(self, _vm: &mut VirtualMachine) -> usize { + fn hash(self, _vm: &VirtualMachine) -> usize { let mut hasher = std::collections::hash_map::DefaultHasher::new(); self.value.hash(&mut hasher); hasher.finish() as usize } - fn len(self, _vm: &mut VirtualMachine) -> usize { + fn len(self, _vm: &VirtualMachine) -> usize { self.value.chars().count() } - fn mul(self, val: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn mul(self, val: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&val, &vm.ctx.int_type()) { let value = &self.value; let multiplier = objint::get_value(&val).to_i32().unwrap(); @@ -129,11 +129,11 @@ impl PyStringRef { } } - fn str(self, _vm: &mut VirtualMachine) -> PyStringRef { + fn str(self, _vm: &VirtualMachine) -> PyStringRef { self } - fn repr(self, _vm: &mut VirtualMachine) -> String { + fn repr(self, _vm: &VirtualMachine) -> String { let value = &self.value; let quote_char = if count_char(value, '\'') > count_char(value, '"') { '"' @@ -163,20 +163,20 @@ impl PyStringRef { formatted } - fn lower(self, _vm: &mut VirtualMachine) -> String { + fn lower(self, _vm: &VirtualMachine) -> String { self.value.to_lowercase() } // casefold is much more aggressive than lower - fn casefold(self, _vm: &mut VirtualMachine) -> String { + fn casefold(self, _vm: &VirtualMachine) -> String { caseless::default_case_fold_str(&self.value) } - fn upper(self, _vm: &mut VirtualMachine) -> String { + fn upper(self, _vm: &VirtualMachine) -> String { self.value.to_uppercase() } - fn capitalize(self, _vm: &mut VirtualMachine) -> String { + fn capitalize(self, _vm: &VirtualMachine) -> String { let (first_part, lower_str) = self.value.split_at(1); format!("{}{}", first_part.to_uppercase(), lower_str) } @@ -185,7 +185,7 @@ impl PyStringRef { self, pattern: OptionalArg, num: OptionalArg, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> PyObjectRef { let value = &self.value; let pattern = match pattern { @@ -206,7 +206,7 @@ impl PyStringRef { self, pattern: OptionalArg, num: OptionalArg, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> PyObjectRef { let value = &self.value; let pattern = match pattern { @@ -223,15 +223,15 @@ impl PyStringRef { vm.ctx.new_list(elements) } - fn strip(self, _vm: &mut VirtualMachine) -> String { + fn strip(self, _vm: &VirtualMachine) -> String { self.value.trim().to_string() } - fn lstrip(self, _vm: &mut VirtualMachine) -> String { + fn lstrip(self, _vm: &VirtualMachine) -> String { self.value.trim_start().to_string() } - fn rstrip(self, _vm: &mut VirtualMachine) -> String { + fn rstrip(self, _vm: &VirtualMachine) -> String { self.value.trim_end().to_string() } @@ -240,7 +240,7 @@ impl PyStringRef { suffix: PyStringRef, start: OptionalArg, end: OptionalArg, - _vm: &mut VirtualMachine, + _vm: &VirtualMachine, ) -> bool { if let Some((start, end)) = adjust_indices(start, end, self.value.len()) { self.value[start..end].ends_with(&suffix.value) @@ -254,7 +254,7 @@ impl PyStringRef { prefix: PyStringRef, start: OptionalArg, end: OptionalArg, - _vm: &mut VirtualMachine, + _vm: &VirtualMachine, ) -> bool { if let Some((start, end)) = adjust_indices(start, end, self.value.len()) { self.value[start..end].starts_with(&prefix.value) @@ -263,15 +263,15 @@ impl PyStringRef { } } - fn isalnum(self, _vm: &mut VirtualMachine) -> bool { + fn isalnum(self, _vm: &VirtualMachine) -> bool { !self.value.is_empty() && self.value.chars().all(char::is_alphanumeric) } - fn isnumeric(self, _vm: &mut VirtualMachine) -> bool { + fn isnumeric(self, _vm: &VirtualMachine) -> bool { !self.value.is_empty() && self.value.chars().all(char::is_numeric) } - fn isdigit(self, _vm: &mut VirtualMachine) -> bool { + fn isdigit(self, _vm: &VirtualMachine) -> bool { // python's isdigit also checks if exponents are digits, these are the unicodes for exponents let valid_unicodes: [u16; 10] = [ 0x2070, 0x00B9, 0x00B2, 0x00B3, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079, @@ -287,7 +287,7 @@ impl PyStringRef { } } - fn isdecimal(self, _vm: &mut VirtualMachine) -> bool { + fn isdecimal(self, _vm: &VirtualMachine) -> bool { if self.value.is_empty() { false } else { @@ -295,11 +295,11 @@ impl PyStringRef { } } - fn title(self, _vm: &mut VirtualMachine) -> String { + fn title(self, _vm: &VirtualMachine) -> String { make_title(&self.value) } - fn swapcase(self, _vm: &mut VirtualMachine) -> String { + fn swapcase(self, _vm: &VirtualMachine) -> String { let mut swapped_str = String::with_capacity(self.value.len()); for c in self.value.chars() { // to_uppercase returns an iterator, to_ascii_uppercase returns the char @@ -314,7 +314,7 @@ impl PyStringRef { swapped_str } - fn isalpha(self, _vm: &mut VirtualMachine) -> bool { + fn isalpha(self, _vm: &VirtualMachine) -> bool { !self.value.is_empty() && self.value.chars().all(char::is_alphanumeric) } @@ -323,7 +323,7 @@ impl PyStringRef { old: Self, new: Self, num: OptionalArg, - _vm: &mut VirtualMachine, + _vm: &VirtualMachine, ) -> String { match num.into_option() { Some(num) => self.value.replacen(&old.value, &new.value, num), @@ -333,11 +333,11 @@ impl PyStringRef { // cpython's isspace ignores whitespace, including \t and \n, etc, unless the whole string is empty // which is why isspace is using is_ascii_whitespace. Same for isupper & islower - fn isspace(self, _vm: &mut VirtualMachine) -> bool { + fn isspace(self, _vm: &VirtualMachine) -> bool { !self.value.is_empty() && self.value.chars().all(|c| c.is_ascii_whitespace()) } - fn isupper(self, _vm: &mut VirtualMachine) -> bool { + fn isupper(self, _vm: &VirtualMachine) -> bool { !self.value.is_empty() && self .value @@ -346,7 +346,7 @@ impl PyStringRef { .all(char::is_uppercase) } - fn islower(self, _vm: &mut VirtualMachine) -> bool { + fn islower(self, _vm: &VirtualMachine) -> bool { !self.value.is_empty() && self .value @@ -355,12 +355,12 @@ impl PyStringRef { .all(char::is_lowercase) } - fn isascii(self, _vm: &mut VirtualMachine) -> bool { + fn isascii(self, _vm: &VirtualMachine) -> bool { !self.value.is_empty() && self.value.chars().all(|c| c.is_ascii()) } // doesn't implement keep new line delimiter just yet - fn splitlines(self, vm: &mut VirtualMachine) -> PyObjectRef { + fn splitlines(self, vm: &VirtualMachine) -> PyObjectRef { let elements = self .value .split('\n') @@ -369,7 +369,7 @@ impl PyStringRef { vm.ctx.new_list(elements) } - fn join(self, iterable: PyIterable, vm: &mut VirtualMachine) -> PyResult { + fn join(self, iterable: PyIterable, vm: &VirtualMachine) -> PyResult { let mut joined = String::new(); for (idx, elem) in iterable.iter(vm)?.enumerate() { @@ -388,7 +388,7 @@ impl PyStringRef { sub: Self, start: OptionalArg, end: OptionalArg, - _vm: &mut VirtualMachine, + _vm: &VirtualMachine, ) -> isize { let value = &self.value; if let Some((start, end)) = adjust_indices(start, end, value.len()) { @@ -406,7 +406,7 @@ impl PyStringRef { sub: Self, start: OptionalArg, end: OptionalArg, - _vm: &mut VirtualMachine, + _vm: &VirtualMachine, ) -> isize { let value = &self.value; if let Some((start, end)) = adjust_indices(start, end, value.len()) { @@ -424,7 +424,7 @@ impl PyStringRef { sub: Self, start: OptionalArg, end: OptionalArg, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> PyResult { let value = &self.value; if let Some((start, end)) = adjust_indices(start, end, value.len()) { @@ -442,7 +442,7 @@ impl PyStringRef { sub: Self, start: OptionalArg, end: OptionalArg, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> PyResult { let value = &self.value; if let Some((start, end)) = adjust_indices(start, end, value.len()) { @@ -455,7 +455,7 @@ impl PyStringRef { } } - fn partition(self, sub: PyStringRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn partition(self, sub: PyStringRef, vm: &VirtualMachine) -> PyObjectRef { let value = &self.value; let sub = &sub.value; let mut new_tup = Vec::new(); @@ -473,7 +473,7 @@ impl PyStringRef { vm.ctx.new_tuple(new_tup) } - fn rpartition(self, sub: PyStringRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn rpartition(self, sub: PyStringRef, vm: &VirtualMachine) -> PyObjectRef { let value = &self.value; let sub = &sub.value; let mut new_tup = Vec::new(); @@ -492,7 +492,7 @@ impl PyStringRef { vm.ctx.new_tuple(new_tup) } - fn istitle(self, _vm: &mut VirtualMachine) -> bool { + fn istitle(self, _vm: &VirtualMachine) -> bool { if self.value.is_empty() { false } else { @@ -505,7 +505,7 @@ impl PyStringRef { sub: Self, start: OptionalArg, end: OptionalArg, - _vm: &mut VirtualMachine, + _vm: &VirtualMachine, ) -> usize { let value = &self.value; if let Some((start, end)) = adjust_indices(start, end, value.len()) { @@ -515,7 +515,7 @@ impl PyStringRef { } } - fn zfill(self, len: usize, _vm: &mut VirtualMachine) -> String { + fn zfill(self, len: usize, _vm: &VirtualMachine) -> String { let value = &self.value; if len <= value.len() { value.to_string() @@ -524,7 +524,7 @@ impl PyStringRef { } } - fn get_fill_char<'a>(rep: &'a OptionalArg, vm: &mut VirtualMachine) -> PyResult<&'a str> { + fn get_fill_char<'a>(rep: &'a OptionalArg, vm: &VirtualMachine) -> PyResult<&'a str> { let rep_str = match rep { OptionalArg::Present(ref st) => &st.value, OptionalArg::Missing => " ", @@ -538,34 +538,19 @@ impl PyStringRef { } } - fn ljust( - self, - len: usize, - rep: OptionalArg, - vm: &mut VirtualMachine, - ) -> PyResult { + fn ljust(self, len: usize, rep: OptionalArg, vm: &VirtualMachine) -> PyResult { let value = &self.value; let rep_char = PyStringRef::get_fill_char(&rep, vm)?; Ok(format!("{}{}", value, rep_char.repeat(len))) } - fn rjust( - self, - len: usize, - rep: OptionalArg, - vm: &mut VirtualMachine, - ) -> PyResult { + fn rjust(self, len: usize, rep: OptionalArg, vm: &VirtualMachine) -> PyResult { let value = &self.value; let rep_char = PyStringRef::get_fill_char(&rep, vm)?; Ok(format!("{}{}", rep_char.repeat(len), value)) } - fn center( - self, - len: usize, - rep: OptionalArg, - vm: &mut VirtualMachine, - ) -> PyResult { + fn center(self, len: usize, rep: OptionalArg, vm: &VirtualMachine) -> PyResult { let value = &self.value; let rep_char = PyStringRef::get_fill_char(&rep, vm)?; let left_buff: usize = (len - value.len()) / 2; @@ -578,7 +563,7 @@ impl PyStringRef { )) } - fn expandtabs(self, tab_stop: OptionalArg, _vm: &mut VirtualMachine) -> String { + fn expandtabs(self, tab_stop: OptionalArg, _vm: &VirtualMachine) -> String { let tab_stop = tab_stop.into_option().unwrap_or(8 as usize); let mut expanded_str = String::new(); let mut tab_size = tab_stop; @@ -601,7 +586,7 @@ impl PyStringRef { expanded_str } - fn isidentifier(self, _vm: &mut VirtualMachine) -> bool { + fn isidentifier(self, _vm: &VirtualMachine) -> bool { let value = &self.value; // a string is not an identifier if it has whitespace or starts with a number if !value.chars().any(|c| c.is_ascii_whitespace()) @@ -620,13 +605,13 @@ impl PyStringRef { } impl PyValue for PyString { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.str_type() } } impl IntoPyObject for String { - fn into_pyobject(self, vm: &mut VirtualMachine) -> PyResult { + fn into_pyobject(self, vm: &VirtualMachine) -> PyResult { Ok(vm.ctx.new_str(self)) } } @@ -713,7 +698,7 @@ fn count_char(s: &str, c: char) -> usize { s.chars().filter(|x| *x == c).count() } -fn str_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn str_format(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { if args.args.is_empty() { return Err( vm.new_type_error("descriptor 'format' of 'str' object needs an argument".to_string()) @@ -741,11 +726,7 @@ fn str_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn call_object_format( - vm: &mut VirtualMachine, - argument: PyObjectRef, - format_spec: &str, -) -> PyResult { +fn call_object_format(vm: &VirtualMachine, argument: PyObjectRef, format_spec: &str) -> PyResult { 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()) { @@ -757,7 +738,7 @@ fn call_object_format( } fn perform_format( - vm: &mut VirtualMachine, + vm: &VirtualMachine, format_string: &FormatString, arguments: &PyFuncArgs, ) -> PyResult { @@ -814,7 +795,7 @@ fn perform_format( fn str_new( cls: PyClassRef, object: OptionalArg, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> PyResult { let string = match object { OptionalArg::Present(ref input) => vm.to_str(input)?.into_object(), @@ -890,7 +871,7 @@ fn to_graphemes>(value: S) -> Vec { .collect() } -pub fn subscript(vm: &mut VirtualMachine, value: &str, b: PyObjectRef) -> PyResult { +pub fn subscript(vm: &VirtualMachine, value: &str, b: PyObjectRef) -> PyResult { if objtype::isinstance(&b, &vm.ctx.int_type()) { match objint::get_value(&b).to_i32() { Some(pos) => { diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index 594624213d..390e591833 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -21,7 +21,7 @@ pub struct PySuper { } impl PyValue for PySuper { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.super_type() } } @@ -56,7 +56,7 @@ pub fn init(context: &PyContext) { ); } -fn super_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn super_getattribute(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -81,7 +81,7 @@ fn super_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn super_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn super_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("super.__new__ {:?}", args.args); arg_check!( vm, diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index d1986bce0d..7438e1c90a 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -38,7 +38,7 @@ impl From> for PyTuple { } impl PyValue for PyTuple { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.tuple_type() } } @@ -46,7 +46,7 @@ impl PyValue for PyTuple { pub type PyTupleRef = PyRef; impl PyTupleRef { - fn lt(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn lt(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.tuple_type()) { let zelf = self.elements.borrow(); let other = get_elements(&other); @@ -57,7 +57,7 @@ impl PyTupleRef { } } - fn gt(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn gt(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.tuple_type()) { let zelf = self.elements.borrow(); let other = get_elements(&other); @@ -68,7 +68,7 @@ impl PyTupleRef { } } - fn ge(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn ge(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.tuple_type()) { let zelf = self.elements.borrow(); let other = get_elements(&other); @@ -79,7 +79,7 @@ impl PyTupleRef { } } - fn le(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn le(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.tuple_type()) { let zelf = self.elements.borrow(); let other = get_elements(&other); @@ -90,7 +90,7 @@ impl PyTupleRef { } } - fn add(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn add(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.tuple_type()) { let e1 = self.elements.borrow(); let e2 = get_elements(&other); @@ -101,7 +101,7 @@ impl PyTupleRef { } } - fn count(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn count(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { let mut count: usize = 0; for element in self.elements.borrow().iter() { if element.is(&needle) { @@ -116,7 +116,7 @@ impl PyTupleRef { Ok(count) } - fn eq(self, other: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn eq(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.tuple_type()) { let zelf = &self.elements.borrow(); let other = get_elements(&other); @@ -127,7 +127,7 @@ impl PyTupleRef { } } - fn hash(self, vm: &mut VirtualMachine) -> PyResult { + fn hash(self, vm: &VirtualMachine) -> PyResult { let mut hasher = std::collections::hash_map::DefaultHasher::new(); for element in self.elements.borrow().iter() { let hash_result = vm.call_method(element, "__hash__", vec![])?; @@ -137,18 +137,18 @@ impl PyTupleRef { Ok(hasher.finish()) } - fn iter(self, _vm: &mut VirtualMachine) -> PyIteratorValue { + fn iter(self, _vm: &VirtualMachine) -> PyIteratorValue { PyIteratorValue { position: Cell::new(0), iterated_obj: self.into_object(), } } - fn len(self, _vm: &mut VirtualMachine) -> usize { + fn len(self, _vm: &VirtualMachine) -> usize { self.elements.borrow().len() } - fn repr(self, vm: &mut VirtualMachine) -> PyResult { + fn repr(self, vm: &VirtualMachine) -> PyResult { let s = if let Some(_guard) = ReprGuard::enter(self.as_object()) { let mut str_parts = vec![]; for elem in self.elements.borrow().iter() { @@ -167,12 +167,12 @@ impl PyTupleRef { Ok(s) } - fn mul(self, counter: isize, vm: &mut VirtualMachine) -> PyObjectRef { + fn mul(self, counter: isize, vm: &VirtualMachine) -> PyObjectRef { let new_elements = seq_mul(&self.elements.borrow(), counter); vm.ctx.new_tuple(new_elements) } - fn getitem(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn getitem(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { get_item( vm, self.as_object(), @@ -181,7 +181,7 @@ impl PyTupleRef { ) } - fn index(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn index(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { for (index, element) in self.elements.borrow().iter().enumerate() { if element.is(&needle) { return Ok(index); @@ -194,7 +194,7 @@ impl PyTupleRef { Err(vm.new_value_error("tuple.index(x): x not in tuple".to_string())) } - fn contains(self, needle: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn contains(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { for element in self.elements.borrow().iter() { if element.is(&needle) { return Ok(true); @@ -211,7 +211,7 @@ impl PyTupleRef { fn tuple_new( cls: PyClassRef, iterable: OptionalArg, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> PyResult { if !objtype::issubclass(cls.as_object(), &vm.ctx.tuple_type()) { return Err(vm.new_type_error(format!("{} is not a subtype of tuple", cls))); diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 78c946dbc1..d5648f07be 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -30,7 +30,7 @@ impl fmt::Display for PyClass { pub type PyClassRef = PyRef; impl PyValue for PyClass { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.type_type() } } @@ -81,17 +81,17 @@ impl PyClassRef { } } - fn mro(self, _vm: &mut VirtualMachine) -> PyTuple { + fn mro(self, _vm: &VirtualMachine) -> PyTuple { let elements: Vec = _mro(&self).iter().map(|x| x.as_object().clone()).collect(); PyTuple::from(elements) } - fn set_mro(self, _value: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn set_mro(self, _value: PyObjectRef, vm: &VirtualMachine) -> PyResult { Err(vm.new_attribute_error("read-only attribute".to_string())) } - fn dir(self, vm: &mut VirtualMachine) -> PyList { + fn dir(self, vm: &VirtualMachine) -> PyList { let attributes = get_attributes(self); let attributes: Vec = attributes .keys() @@ -100,19 +100,19 @@ impl PyClassRef { PyList::from(attributes) } - fn instance_check(self, obj: PyObjectRef, _vm: &mut VirtualMachine) -> bool { + fn instance_check(self, obj: PyObjectRef, _vm: &VirtualMachine) -> bool { isinstance(&obj, self.as_object()) } - fn subclass_check(self, subclass: PyObjectRef, _vm: &mut VirtualMachine) -> bool { + fn subclass_check(self, subclass: PyObjectRef, _vm: &VirtualMachine) -> bool { issubclass(&subclass, self.as_object()) } - fn repr(self, _vm: &mut VirtualMachine) -> String { + fn repr(self, _vm: &VirtualMachine) -> String { format!("", self.name) } - fn prepare(_name: PyStringRef, _bases: PyObjectRef, vm: &mut VirtualMachine) -> PyObjectRef { + fn prepare(_name: PyStringRef, _bases: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { vm.new_dict() } } @@ -170,7 +170,7 @@ pub fn get_type_name(typ: &PyObjectRef) -> String { } } -pub fn type_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +pub fn type_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { debug!("type.__new__ {:?}", args); if args.args.len() == 2 { arg_check!( @@ -197,7 +197,7 @@ pub fn type_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn type_new_class( - vm: &mut VirtualMachine, + vm: &VirtualMachine, typ: &PyObjectRef, name: &PyObjectRef, bases: &PyObjectRef, @@ -218,7 +218,7 @@ pub fn type_new_class( ) } -pub fn type_call(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { +pub fn type_call(vm: &VirtualMachine, mut args: PyFuncArgs) -> PyResult { debug!("type_call: {:?}", args); let cls = args.shift(); let new = cls.get_attr("__new__").unwrap(); @@ -234,7 +234,7 @@ pub fn type_call(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { Ok(obj) } -pub fn type_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +pub fn type_getattribute(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, diff --git a/vm/src/obj/objweakref.rs b/vm/src/obj/objweakref.rs index 69d7027370..e6dd1e1e69 100644 --- a/vm/src/obj/objweakref.rs +++ b/vm/src/obj/objweakref.rs @@ -23,7 +23,7 @@ impl PyWeak { } impl PyValue for PyWeak { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.weakref_type() } } @@ -32,11 +32,11 @@ pub type PyWeakRef = PyRef; impl PyWeakRef { // TODO callbacks - fn create(cls: PyClassRef, referent: PyObjectRef, vm: &mut VirtualMachine) -> PyResult { + fn create(cls: PyClassRef, referent: PyObjectRef, vm: &VirtualMachine) -> PyResult { PyWeak::downgrade(referent).into_ref_with_type(vm, cls) } - fn call(self, vm: &mut VirtualMachine) -> PyObjectRef { + fn call(self, vm: &VirtualMachine) -> PyObjectRef { self.referent.upgrade().unwrap_or_else(|| vm.get_none()) } } diff --git a/vm/src/obj/objzip.rs b/vm/src/obj/objzip.rs index 8250337e0a..0a3c5453a4 100644 --- a/vm/src/obj/objzip.rs +++ b/vm/src/obj/objzip.rs @@ -10,12 +10,12 @@ pub struct PyZip { } impl PyValue for PyZip { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.zip_type() } } -fn zip_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn zip_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { no_kwargs!(vm, args); let cls = &args.args[0]; let iterables = &args.args[1..]; @@ -26,7 +26,7 @@ fn zip_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(PyObject::new(PyZip { iterators }, cls.clone())) } -fn zip_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn zip_next(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zip, Some(vm.ctx.zip_type()))]); if let Some(PyZip { ref iterators }) = zip.payload() { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 5780cf6201..d6e84b653b 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -165,7 +165,7 @@ pub fn create_type(name: &str, type_type: &PyObjectRef, base: &PyObjectRef) -> P pub struct PyNotImplemented; impl PyValue for PyNotImplemented { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.not_implemented().typ() } } @@ -174,7 +174,7 @@ impl PyValue for PyNotImplemented { pub struct PyEllipsis; impl PyValue for PyEllipsis { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.ellipsis_type.clone() } } @@ -660,7 +660,7 @@ impl PyContext { }; } - pub fn unwrap_constant(&mut self, value: &bytecode::Constant) -> PyObjectRef { + pub fn unwrap_constant(&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), @@ -744,7 +744,7 @@ impl TryFromObject for PyRef where T: PyValue, { - fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { + fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { if objtype::isinstance(&obj, &T::class(vm)) { Ok(PyRef { obj, @@ -763,7 +763,7 @@ where } impl IntoPyObject for PyRef { - fn into_pyobject(self, _vm: &mut VirtualMachine) -> PyResult { + fn into_pyobject(self, _vm: &VirtualMachine) -> PyResult { Ok(self.obj) } } @@ -976,7 +976,7 @@ impl PyIterable { /// /// This operation may fail if an exception is raised while invoking the /// `__iter__` method of the iterable object. - pub fn iter<'a>(&self, vm: &'a mut VirtualMachine) -> PyResult> { + pub fn iter<'a>(&self, vm: &'a VirtualMachine) -> PyResult> { let iter_obj = vm.invoke( self.method.clone(), PyFuncArgs { @@ -994,7 +994,7 @@ impl PyIterable { } pub struct PyIterator<'a, T> { - vm: &'a mut VirtualMachine, + vm: &'a VirtualMachine, obj: PyObjectRef, _item: std::marker::PhantomData, } @@ -1024,7 +1024,7 @@ impl TryFromObject for PyIterable where T: TryFromObject, { - fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { + fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { Ok(PyIterable { method: vm.get_method(obj, "__iter__")?, _item: std::marker::PhantomData, @@ -1033,13 +1033,13 @@ where } impl TryFromObject for PyObjectRef { - fn try_from_object(_vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { + fn try_from_object(_vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { Ok(obj) } } impl TryFromObject for Option { - fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { + fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { if vm.get_none().is(&obj) { Ok(None) } else { @@ -1051,11 +1051,11 @@ impl TryFromObject for Option { /// Allows coercion of a types into PyRefs, so that we can write functions that can take /// refs, pyobject refs or basic types. pub trait TryIntoRef { - fn try_into_ref(self, vm: &mut VirtualMachine) -> PyResult>; + fn try_into_ref(self, vm: &VirtualMachine) -> PyResult>; } impl TryIntoRef for PyRef { - fn try_into_ref(self, _vm: &mut VirtualMachine) -> PyResult> { + fn try_into_ref(self, _vm: &VirtualMachine) -> PyResult> { Ok(self) } } @@ -1064,7 +1064,7 @@ impl TryIntoRef for PyObjectRef where T: PyValue, { - fn try_into_ref(self, vm: &mut VirtualMachine) -> PyResult> { + fn try_into_ref(self, vm: &VirtualMachine) -> PyResult> { TryFromObject::try_from_object(vm, self) } } @@ -1075,7 +1075,7 @@ where /// so can be accepted as a argument to a built-in function. pub trait TryFromObject: Sized { /// Attempt to convert a Python object to a value of this type. - fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult; + fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult; } /// Implemented by any type that can be returned from a built-in Python function. @@ -1084,11 +1084,11 @@ pub trait TryFromObject: Sized { /// and should be implemented by many primitive Rust types, allowing a built-in /// function to simply return a `bool` or a `usize` for example. pub trait IntoPyObject { - fn into_pyobject(self, vm: &mut VirtualMachine) -> PyResult; + fn into_pyobject(self, vm: &VirtualMachine) -> PyResult; } impl IntoPyObject for PyObjectRef { - fn into_pyobject(self, _vm: &mut VirtualMachine) -> PyResult { + fn into_pyobject(self, _vm: &VirtualMachine) -> PyResult { Ok(self) } } @@ -1097,7 +1097,7 @@ impl IntoPyObject for PyResult where T: IntoPyObject, { - fn into_pyobject(self, vm: &mut VirtualMachine) -> PyResult { + fn into_pyobject(self, vm: &VirtualMachine) -> PyResult { self.and_then(|res| T::into_pyobject(res, vm)) } } @@ -1108,7 +1108,7 @@ impl IntoPyObject for T where T: PyValue + Sized, { - fn into_pyobject(self, vm: &mut VirtualMachine) -> PyResult { + fn into_pyobject(self, vm: &VirtualMachine) -> PyResult { Ok(PyObject::new(self, T::class(vm))) } } @@ -1122,7 +1122,7 @@ pub struct PyIteratorValue { } impl PyValue for PyIteratorValue { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.iter_type() } } @@ -1163,16 +1163,16 @@ impl PyObject { } pub trait PyValue: fmt::Debug + Sized + 'static { - fn class(vm: &mut VirtualMachine) -> PyObjectRef; + fn class(vm: &VirtualMachine) -> PyObjectRef; - fn into_ref(self, vm: &mut VirtualMachine) -> PyRef { + fn into_ref(self, vm: &VirtualMachine) -> PyRef { PyRef { obj: PyObject::new(self, Self::class(vm)), _payload: PhantomData, } } - fn into_ref_with_type(self, vm: &mut VirtualMachine, cls: PyClassRef) -> PyResult> { + fn into_ref_with_type(self, vm: &VirtualMachine, cls: PyClassRef) -> PyResult> { let class = Self::class(vm); if objtype::issubclass(&cls.obj, &class) { Ok(PyRef { diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index c3c7c3903a..a3010a5199 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -612,7 +612,7 @@ fn string_to_ast(ctx: &PyContext, string: &ast::StringGroup) -> PyObjectRef { } } -fn ast_parse(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn ast_parse(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(source, Some(vm.ctx.str_type()))]); let source_string = objstr::get_value(source); diff --git a/vm/src/stdlib/dis.rs b/vm/src/stdlib/dis.rs index f583cc5a8d..04209d8b3b 100644 --- a/vm/src/stdlib/dis.rs +++ b/vm/src/stdlib/dis.rs @@ -3,7 +3,7 @@ use crate::obj::objcode; use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; -fn dis_dis(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn dis_dis(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, None)]); // Method or function: @@ -14,7 +14,7 @@ fn dis_dis(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { dis_disassemble(vm, PyFuncArgs::new(vec![obj.clone()], vec![])) } -fn dis_disassemble(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn dis_disassemble(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(co, Some(vm.ctx.code_type()))]); let code = objcode::get_value(co); diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index aba31198aa..fcd7dffb8c 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -43,23 +43,23 @@ struct PyStringIO { type PyStringIORef = PyRef; impl PyValue for PyStringIO { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.class("io", "StringIO") } } impl PyStringIORef { - fn write(self, data: objstr::PyStringRef, _vm: &mut VirtualMachine) { + fn write(self, data: objstr::PyStringRef, _vm: &VirtualMachine) { let data = data.value.clone(); self.data.borrow_mut().push_str(&data); } - fn getvalue(self, _vm: &mut VirtualMachine) -> String { + fn getvalue(self, _vm: &VirtualMachine) -> String { self.data.borrow().clone() } } -fn string_io_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn string_io_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(cls, None)]); Ok(PyObject::new( @@ -70,23 +70,23 @@ fn string_io_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { )) } -fn bytes_io_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { +fn bytes_io_init(vm: &VirtualMachine, _args: PyFuncArgs) -> PyResult { // TODO Ok(vm.get_none()) } -fn bytes_io_getvalue(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn bytes_io_getvalue(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args); // TODO Ok(vm.get_none()) } -fn io_base_cm_enter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn io_base_cm_enter(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(instance, None)]); Ok(instance.clone()) } -fn io_base_cm_exit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn io_base_cm_exit(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -101,13 +101,13 @@ fn io_base_cm_exit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.get_none()) } -fn buffered_io_base_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn buffered_io_base_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { 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 { +fn buffered_reader_read(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(buffered, None)]); let buff_size = 8 * 1024; let buffer = vm.ctx.new_bytearray(vec![0; buff_size]); @@ -137,7 +137,7 @@ 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 { +fn file_io_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -163,7 +163,7 @@ fn file_io_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn file_io_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn file_io_read(vm: &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(objstr::get_value(&py_name)) { @@ -187,7 +187,7 @@ fn file_io_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bytes(bytes)) } -fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn file_io_readinto(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(file_io, None), (obj, None)]); if !obj.readonly() { @@ -223,7 +223,7 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.get_none()) } -fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn file_io_write(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -257,7 +257,7 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn buffered_writer_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn buffered_writer_write(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -270,7 +270,7 @@ fn buffered_writer_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult vm.call_method(&raw, "write", vec![obj.clone()]) } -fn text_io_wrapper_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn text_io_wrapper_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -281,7 +281,7 @@ 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 { +fn text_io_base_read(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(text_io_base, None)]); let raw = vm.ctx.get_attr(&text_io_base, "buffer").unwrap(); @@ -297,7 +297,7 @@ fn text_io_base_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +pub fn io_open(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index 81fb364d48..da373a955c 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -188,12 +188,12 @@ impl<'de> Visitor<'de> for PyObjectDeserializer<'de> { } } -pub fn ser_pyobject(vm: &mut VirtualMachine, obj: &PyObjectRef) -> PyResult { +pub fn ser_pyobject(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult { let serializer = PyObjectSerializer { pyobject: obj, vm }; serde_json::to_string(&serializer).map_err(|err| vm.new_type_error(err.to_string())) } -pub fn de_pyobject(vm: &mut VirtualMachine, s: &str) -> PyResult { +pub fn de_pyobject(vm: &VirtualMachine, s: &str) -> PyResult { let de = PyObjectDeserializer { vm }; // TODO: Support deserializing string sub-classes de.deserialize(&mut serde_json::Deserializer::from_str(s)) @@ -214,7 +214,7 @@ pub fn de_pyobject(vm: &mut VirtualMachine, s: &str) -> PyResult { } /// Implement json.dumps -fn json_dumps(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn json_dumps(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { // TODO: Implement non-trivial serialisation case arg_check!(vm, args, required = [(obj, None)]); let string = ser_pyobject(vm, obj)?; @@ -222,7 +222,7 @@ fn json_dumps(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } /// Implement json.loads -fn json_loads(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn json_loads(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { // TODO: Implement non-trivial deserialization case arg_check!(vm, args, required = [(string, Some(vm.ctx.str_type()))]); diff --git a/vm/src/stdlib/keyword.rs b/vm/src/stdlib/keyword.rs index ff192cffb6..fe27399c55 100644 --- a/vm/src/stdlib/keyword.rs +++ b/vm/src/stdlib/keyword.rs @@ -9,7 +9,7 @@ use crate::obj::objstr; use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; -fn keyword_iskeyword(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn keyword_iskeyword(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); let s = objstr::get_value(s); let keywords = lexer::get_keywords(); diff --git a/vm/src/stdlib/math.rs b/vm/src/stdlib/math.rs index a8f035625b..5af599cb7d 100644 --- a/vm/src/stdlib/math.rs +++ b/vm/src/stdlib/math.rs @@ -14,7 +14,7 @@ use crate::vm::VirtualMachine; // Helper macro: macro_rules! make_math_func { ( $fname:ident, $fun:ident ) => { - fn $fname(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + fn $fname(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(value, None)]); let value = objfloat::make_float(vm, value)?; let value = value.$fun(); @@ -27,19 +27,19 @@ macro_rules! make_math_func { // Number theory functions: make_math_func!(math_fabs, abs); -fn math_isfinite(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn math_isfinite(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { 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 { +fn math_isinf(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { 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 { +fn math_isnan(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(value, None)]); let value = objfloat::make_float(vm, value)?.is_nan(); Ok(vm.ctx.new_bool(value)) @@ -49,7 +49,7 @@ fn math_isnan(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { make_math_func!(math_exp, exp); make_math_func!(math_expm1, exp_m1); -fn math_log(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn math_log(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(x, None)], optional = [(base, None)]); let x = objfloat::make_float(vm, x)?; match base { @@ -61,7 +61,7 @@ fn math_log(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn math_log1p(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn math_log1p(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(x, None)]); let x = objfloat::make_float(vm, x)?; Ok(vm.ctx.new_float((x + 1.0).ln())) @@ -70,7 +70,7 @@ fn math_log1p(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { make_math_func!(math_log2, log2); make_math_func!(math_log10, log10); -fn math_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn math_pow(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(x, None), (y, None)]); let x = objfloat::make_float(vm, x)?; let y = objfloat::make_float(vm, y)?; @@ -84,7 +84,7 @@ make_math_func!(math_acos, acos); make_math_func!(math_asin, asin); make_math_func!(math_atan, atan); -fn math_atan2(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn math_atan2(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(y, None), (x, None)]); let y = objfloat::make_float(vm, y)?; let x = objfloat::make_float(vm, x)?; @@ -93,7 +93,7 @@ fn math_atan2(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { make_math_func!(math_cos, cos); -fn math_hypot(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn math_hypot(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(x, None), (y, None)]); let x = objfloat::make_float(vm, x)?; let y = objfloat::make_float(vm, y)?; @@ -103,13 +103,13 @@ fn math_hypot(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { make_math_func!(math_sin, sin); make_math_func!(math_tan, tan); -fn math_degrees(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn math_degrees(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { 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 { +fn math_radians(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { 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))) @@ -124,7 +124,7 @@ make_math_func!(math_sinh, sinh); make_math_func!(math_tanh, tanh); // Special functions: -fn math_erf(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn math_erf(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(value, None)]); let x = objfloat::make_float(vm, value)?; @@ -135,7 +135,7 @@ fn math_erf(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn math_erfc(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn math_erfc(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(value, None)]); let x = objfloat::make_float(vm, value)?; @@ -146,7 +146,7 @@ fn math_erfc(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn math_gamma(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn math_gamma(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(value, None)]); let x = objfloat::make_float(vm, value)?; @@ -159,7 +159,7 @@ fn math_gamma(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn math_lgamma(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn math_lgamma(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(value, None)]); let x = objfloat::make_float(vm, value)?; diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 0bb616ff01..42b4160ca8 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -50,7 +50,7 @@ pub fn raw_file_number(handle: File) -> i64 { unimplemented!(); } -pub fn os_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +pub fn os_close(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(fileno, Some(vm.ctx.int_type()))]); let raw_fileno = objint::get_value(&fileno); @@ -63,7 +63,7 @@ pub fn os_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.get_none()) } -pub fn os_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +pub fn os_open(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -96,7 +96,7 @@ 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 { +fn os_error(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, diff --git a/vm/src/stdlib/platform.rs b/vm/src/stdlib/platform.rs index f22b8be0e2..a87383a723 100644 --- a/vm/src/stdlib/platform.rs +++ b/vm/src/stdlib/platform.rs @@ -10,18 +10,18 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { }) } -fn platform_python_implementation(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn platform_python_implementation(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args); Ok(vm.new_str("RustPython".to_string())) } -fn platform_python_version(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn platform_python_version(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args); // TODO: fetch version from somewhere. Ok(vm.new_str("4.0.0".to_string())) } -fn platform_python_compiler(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn platform_python_compiler(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args); let version = rustc_version_runtime::version_meta(); Ok(vm.new_str(format!("rustc {}", version.semver))) diff --git a/vm/src/stdlib/pystruct.rs b/vm/src/stdlib/pystruct.rs index 5da654b691..9dadc520a9 100644 --- a/vm/src/stdlib/pystruct.rs +++ b/vm/src/stdlib/pystruct.rs @@ -43,23 +43,23 @@ fn parse_format_string(fmt: String) -> Vec { codes } -fn get_int(vm: &mut VirtualMachine, arg: &PyObjectRef) -> PyResult { +fn get_int(vm: &VirtualMachine, arg: &PyObjectRef) -> PyResult { objint::to_int(vm, arg, 10) } -fn pack_i8(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { +fn pack_i8(vm: &VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { let v = get_int(vm, arg)?.to_i8().unwrap(); data.write_i8(v).unwrap(); Ok(()) } -fn pack_u8(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { +fn pack_u8(vm: &VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { let v = get_int(vm, arg)?.to_u8().unwrap(); data.write_u8(v).unwrap(); Ok(()) } -fn pack_bool(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { +fn pack_bool(vm: &VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { if objtype::isinstance(&arg, &vm.ctx.bool_type()) { let v = if objbool::get_value(arg) { 1 } else { 0 }; data.write_u8(v).unwrap(); @@ -69,43 +69,43 @@ fn pack_bool(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> Py } } -fn pack_i16(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { +fn pack_i16(vm: &VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { let v = get_int(vm, arg)?.to_i16().unwrap(); data.write_i16::(v).unwrap(); Ok(()) } -fn pack_u16(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { +fn pack_u16(vm: &VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { let v = get_int(vm, arg)?.to_u16().unwrap(); data.write_u16::(v).unwrap(); Ok(()) } -fn pack_i32(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { +fn pack_i32(vm: &VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { let v = get_int(vm, arg)?.to_i32().unwrap(); data.write_i32::(v).unwrap(); Ok(()) } -fn pack_u32(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { +fn pack_u32(vm: &VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { let v = get_int(vm, arg)?.to_u32().unwrap(); data.write_u32::(v).unwrap(); Ok(()) } -fn pack_i64(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { +fn pack_i64(vm: &VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { let v = get_int(vm, arg)?.to_i64().unwrap(); data.write_i64::(v).unwrap(); Ok(()) } -fn pack_u64(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { +fn pack_u64(vm: &VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { let v = get_int(vm, arg)?.to_u64().unwrap(); data.write_u64::(v).unwrap(); Ok(()) } -fn pack_f32(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { +fn pack_f32(vm: &VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { if objtype::isinstance(&arg, &vm.ctx.float_type()) { let v = objfloat::get_value(arg) as f32; data.write_f32::(v).unwrap(); @@ -115,7 +115,7 @@ fn pack_f32(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyR } } -fn pack_f64(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { +fn pack_f64(vm: &VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> { if objtype::isinstance(&arg, &vm.ctx.float_type()) { let v = objfloat::get_value(arg) as f64; data.write_f64::(v).unwrap(); @@ -125,7 +125,7 @@ fn pack_f64(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyR } } -fn struct_pack(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn struct_pack(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { if args.args.is_empty() { Err(vm.new_type_error(format!( "Expected at least 1 argument (got: {})", @@ -178,84 +178,84 @@ fn struct_pack(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn unpack_i8(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { +fn unpack_i8(vm: &VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_i8() { Err(err) => panic!("Error in reading {:?}", err), Ok(v) => Ok(vm.ctx.new_int(v)), } } -fn unpack_u8(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { +fn unpack_u8(vm: &VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_u8() { Err(err) => panic!("Error in reading {:?}", err), Ok(v) => Ok(vm.ctx.new_int(v)), } } -fn unpack_bool(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { +fn unpack_bool(vm: &VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_u8() { Err(err) => panic!("Error in reading {:?}", err), Ok(v) => Ok(vm.ctx.new_bool(v > 0)), } } -fn unpack_i16(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { +fn unpack_i16(vm: &VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_i16::() { Err(err) => panic!("Error in reading {:?}", err), Ok(v) => Ok(vm.ctx.new_int(v)), } } -fn unpack_u16(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { +fn unpack_u16(vm: &VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_u16::() { Err(err) => panic!("Error in reading {:?}", err), Ok(v) => Ok(vm.ctx.new_int(v)), } } -fn unpack_i32(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { +fn unpack_i32(vm: &VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_i32::() { Err(err) => panic!("Error in reading {:?}", err), Ok(v) => Ok(vm.ctx.new_int(v)), } } -fn unpack_u32(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { +fn unpack_u32(vm: &VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_u32::() { Err(err) => panic!("Error in reading {:?}", err), Ok(v) => Ok(vm.ctx.new_int(v)), } } -fn unpack_i64(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { +fn unpack_i64(vm: &VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_i64::() { Err(err) => panic!("Error in reading {:?}", err), Ok(v) => Ok(vm.ctx.new_int(v)), } } -fn unpack_u64(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { +fn unpack_u64(vm: &VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_u64::() { Err(err) => panic!("Error in reading {:?}", err), Ok(v) => Ok(vm.ctx.new_int(v)), } } -fn unpack_f32(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { +fn unpack_f32(vm: &VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_f32::() { Err(err) => panic!("Error in reading {:?}", err), Ok(v) => Ok(vm.ctx.new_float(f64::from(v))), } } -fn unpack_f64(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { +fn unpack_f64(vm: &VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_f64::() { Err(err) => panic!("Error in reading {:?}", err), Ok(v) => Ok(vm.ctx.new_float(v)), } } -fn struct_unpack(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn struct_unpack(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, diff --git a/vm/src/stdlib/random.rs b/vm/src/stdlib/random.rs index 2ad1e51c0b..82c1f410c3 100644 --- a/vm/src/stdlib/random.rs +++ b/vm/src/stdlib/random.rs @@ -16,12 +16,12 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { }) } -fn random_gauss(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn random_gauss(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { // TODO: is this the same? random_normalvariate(vm, args) } -fn random_normalvariate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn random_normalvariate(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -38,7 +38,7 @@ fn random_normalvariate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(py_value) } -fn random_random(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn random_random(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args); let value = rand::random::(); let py_value = vm.ctx.new_float(value); @@ -47,7 +47,7 @@ fn random_random(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { /* * TODO: enable this function: -fn random_weibullvariate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn random_weibullvariate(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(alpha, Some(vm.ctx.float_type())), (beta, Some(vm.ctx.float_type()))]); let alpha = objfloat::get_value(alpha); let beta = objfloat::get_value(beta); diff --git a/vm/src/stdlib/re.rs b/vm/src/stdlib/re.rs index eda5be75f1..6bc5fb7eae 100644 --- a/vm/src/stdlib/re.rs +++ b/vm/src/stdlib/re.rs @@ -18,7 +18,7 @@ use crate::pyobject::{ use crate::vm::VirtualMachine; impl PyValue for Regex { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.import("re").unwrap().get_attr("Pattern").unwrap() } } @@ -47,7 +47,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { /// Implement re.match /// See also: /// https://docs.python.org/3/library/re.html#re.match -fn re_match(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn re_match(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -65,7 +65,7 @@ fn re_match(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { /// Implement re.search /// See also: /// https://docs.python.org/3/library/re.html#re.search -fn re_search(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn re_search(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -82,19 +82,19 @@ fn re_search(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { do_search(vm, ®ex, search_text) } -fn do_match(vm: &mut VirtualMachine, regex: &Regex, search_text: String) -> PyResult { +fn do_match(vm: &VirtualMachine, regex: &Regex, search_text: String) -> PyResult { // TODO: implement match! do_search(vm, regex, search_text) } -fn do_search(vm: &mut VirtualMachine, regex: &Regex, search_text: String) -> PyResult { +fn do_search(vm: &VirtualMachine, regex: &Regex, search_text: String) -> PyResult { match regex.find(&search_text) { None => Ok(vm.get_none()), Some(result) => create_match(vm, &result), } } -fn make_regex(vm: &mut VirtualMachine, pattern: &PyObjectRef) -> PyResult { +fn make_regex(vm: &VirtualMachine, pattern: &PyObjectRef) -> PyResult { let pattern_str = objstr::get_value(pattern); match Regex::new(&pattern_str) { @@ -111,13 +111,13 @@ struct PyMatch { } impl PyValue for PyMatch { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.class("re", "Match") } } /// Take a found regular expression and convert it to proper match object. -fn create_match(vm: &mut VirtualMachine, match_value: &Match) -> PyResult { +fn create_match(vm: &VirtualMachine, match_value: &Match) -> PyResult { // Return match object: // TODO: implement match object // TODO: how to refer to match object defined in this @@ -138,7 +138,7 @@ fn create_match(vm: &mut VirtualMachine, match_value: &Match) -> PyResult { /// Compile a regular expression into a Pattern object. /// See also: /// https://docs.python.org/3/library/re.html#re.compile -fn re_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn re_compile(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -153,7 +153,7 @@ fn re_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(PyObject::new(regex, pattern_class.clone())) } -fn pattern_match(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn pattern_match(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -165,7 +165,7 @@ fn pattern_match(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { do_match(vm, ®ex, search_text) } -fn pattern_search(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn pattern_search(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -179,14 +179,14 @@ fn pattern_search(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { /// Returns start of match /// see: https://docs.python.org/3/library/re.html#re.Match.start -fn match_start(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn match_start(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, None)]); // TODO: implement groups let m = get_match(zelf); Ok(vm.new_int(m.start)) } -fn match_end(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn match_end(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, None)]); // TODO: implement groups let m = get_match(zelf); diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index f09a52f552..4e75cfa417 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -23,7 +23,7 @@ enum AddressFamily { } impl AddressFamily { - fn from_i32(vm: &mut VirtualMachine, value: i32) -> Result { + fn from_i32(vm: &VirtualMachine, value: i32) -> Result { match value { 1 => Ok(AddressFamily::Unix), 2 => Ok(AddressFamily::Inet), @@ -40,7 +40,7 @@ enum SocketKind { } impl SocketKind { - fn from_i32(vm: &mut VirtualMachine, value: i32) -> Result { + fn from_i32(vm: &VirtualMachine, value: i32) -> Result { match value { 1 => Ok(SocketKind::Stream), 2 => Ok(SocketKind::Dgram), @@ -118,7 +118,7 @@ pub struct Socket { } impl PyValue for Socket { - fn class(_vm: &mut VirtualMachine) -> PyObjectRef { + fn class(_vm: &VirtualMachine) -> PyObjectRef { // TODO unimplemented!() } @@ -138,7 +138,7 @@ fn get_socket<'a>(obj: &'a PyObjectRef) -> impl Deref + 'a { obj.payload::().unwrap() } -fn socket_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn socket_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -159,7 +159,7 @@ fn socket_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { )) } -fn socket_connect(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn socket_connect(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -194,7 +194,7 @@ fn socket_connect(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn socket_bind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn socket_bind(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -229,10 +229,7 @@ fn socket_bind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn get_address_string( - vm: &mut VirtualMachine, - address: &PyObjectRef, -) -> Result { +fn get_address_string(vm: &VirtualMachine, address: &PyObjectRef) -> Result { let args = PyFuncArgs { args: get_elements(address).to_vec(), kwargs: vec![], @@ -253,7 +250,7 @@ fn get_address_string( )) } -fn socket_listen(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn socket_listen(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -262,7 +259,7 @@ fn socket_listen(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.get_none()) } -fn socket_accept(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn socket_accept(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, None)]); let socket = get_socket(zelf); @@ -290,7 +287,7 @@ fn socket_accept(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_tuple(vec![sock_obj, addr_tuple])) } -fn socket_recv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn socket_recv(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -309,7 +306,7 @@ fn socket_recv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bytes(buffer)) } -fn socket_recvfrom(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn socket_recvfrom(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -334,7 +331,7 @@ fn socket_recvfrom(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_tuple(vec![vm.ctx.new_bytes(buffer), addr_tuple])) } -fn socket_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn socket_send(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -352,7 +349,7 @@ fn socket_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.get_none()) } -fn socket_sendto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn socket_sendto(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -393,7 +390,7 @@ fn socket_sendto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn socket_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn socket_close(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, None)]); let socket = get_socket(zelf); @@ -401,7 +398,7 @@ fn socket_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.get_none()) } -fn socket_getsockname(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn socket_getsockname(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, None)]); let socket = get_socket(zelf); @@ -416,7 +413,7 @@ fn socket_getsockname(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn get_addr_tuple(vm: &mut VirtualMachine, addr: SocketAddr) -> PyResult { +fn get_addr_tuple(vm: &VirtualMachine, addr: SocketAddr) -> PyResult { let port = vm.ctx.new_int(addr.port()); let ip = vm.ctx.new_str(addr.ip().to_string()); diff --git a/vm/src/stdlib/time_module.rs b/vm/src/stdlib/time_module.rs index c43ae037ef..dfe3ab8e35 100644 --- a/vm/src/stdlib/time_module.rs +++ b/vm/src/stdlib/time_module.rs @@ -8,7 +8,7 @@ use crate::obj::objfloat; use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; -fn time_sleep(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn time_sleep(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(seconds, Some(vm.ctx.float_type()))]); let seconds = objfloat::get_value(seconds); let secs: u64 = seconds.trunc() as u64; @@ -22,7 +22,7 @@ fn duration_to_f64(d: Duration) -> f64 { (d.as_secs() as f64) + (f64::from(d.subsec_nanos()) / 1e9) } -fn time_time(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn time_time(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args); let x = match SystemTime::now().duration_since(UNIX_EPOCH) { Ok(v) => duration_to_f64(v), diff --git a/vm/src/stdlib/tokenize.rs b/vm/src/stdlib/tokenize.rs index 1027e1f030..0aa5df3d2a 100644 --- a/vm/src/stdlib/tokenize.rs +++ b/vm/src/stdlib/tokenize.rs @@ -11,7 +11,7 @@ use crate::obj::objstr; use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; -fn tokenize_tokenize(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn tokenize_tokenize(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(readline, Some(vm.ctx.str_type()))]); let source = objstr::get_value(readline); diff --git a/vm/src/stdlib/types.rs b/vm/src/stdlib/types.rs index 9768f8dc13..279cdbdf9e 100644 --- a/vm/src/stdlib/types.rs +++ b/vm/src/stdlib/types.rs @@ -7,7 +7,7 @@ use crate::obj::objtype; use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; use crate::VirtualMachine; -fn types_new_class(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn types_new_class(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index 966c6b5a91..7a0234d0d8 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -18,7 +18,7 @@ fn argv(ctx: &PyContext) -> PyObjectRef { ctx.new_list(argv) } -fn frame_idx(vm: &mut VirtualMachine, offset: Option<&PyObjectRef>) -> Result { +fn frame_idx(vm: &VirtualMachine, offset: Option<&PyObjectRef>) -> Result { if let Some(int) = offset { if let Some(offset) = objint::get_value(&int).to_usize() { if offset > vm.frames.borrow().len() - 1 { @@ -30,7 +30,7 @@ fn frame_idx(vm: &mut VirtualMachine, offset: Option<&PyObjectRef>) -> Result PyResult { +fn getframe(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -44,13 +44,13 @@ fn getframe(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(frame.clone()) } -fn sys_getrefcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn sys_getrefcount(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(object, None)]); let size = Rc::strong_count(&object); Ok(vm.ctx.new_int(size)) } -fn sys_getsizeof(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn sys_getsizeof(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(object, None)]); // TODO: implement default optional argument. let size = mem::size_of_val(&object); diff --git a/vm/src/vm.rs b/vm/src/vm.rs index ea39f336d9..f50ab3221d 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -45,7 +45,7 @@ use num_bigint::BigInt; pub struct VirtualMachine { pub builtins: PyObjectRef, pub sys_module: PyObjectRef, - pub stdlib_inits: HashMap, + pub stdlib_inits: RefCell>, pub ctx: PyContext, pub frames: RefCell>, pub wasm_id: Option, @@ -64,7 +64,7 @@ impl VirtualMachine { let modules = sysmod.get_attr("modules").unwrap(); modules.set_item(&ctx, "builtins", builtins.clone()); - let stdlib_inits = stdlib::get_module_inits(); + let stdlib_inits = RefCell::new(stdlib::get_module_inits()); VirtualMachine { builtins, sys_module: sysmod, @@ -75,19 +75,19 @@ impl VirtualMachine { } } - pub fn run_code_obj(&mut self, code: PyObjectRef, scope: Scope) -> PyResult { + pub fn run_code_obj(&self, code: PyObjectRef, scope: Scope) -> PyResult { let frame = self.ctx.new_frame(code, scope); self.run_frame_full(frame) } - pub fn run_frame_full(&mut self, frame: PyObjectRef) -> PyResult { + pub fn run_frame_full(&self, frame: PyObjectRef) -> PyResult { match self.run_frame(frame)? { ExecutionResult::Return(value) => Ok(value), _ => panic!("Got unexpected result from function"), } } - pub fn run_frame(&mut self, frame: PyObjectRef) -> PyResult { + pub fn run_frame(&self, frame: PyObjectRef) -> PyResult { self.frames.borrow_mut().push(frame.clone()); let frame = objframe::get_value(&frame); let result = frame.run(self); @@ -108,7 +108,7 @@ impl VirtualMachine { Ref::map(frame, |f| &f.scope) } - pub fn class(&mut self, module: &str, class: &str) -> PyObjectRef { + pub fn class(&self, module: &str, class: &str) -> PyObjectRef { self.import(module) .unwrap_or_else(|_| panic!("unable to import {}", module)) .get_attr(class) @@ -134,13 +134,13 @@ impl VirtualMachine { self.ctx.new_dict() } - pub fn new_empty_exception(&mut self, exc_type: PyObjectRef) -> PyResult { + pub fn new_empty_exception(&self, exc_type: PyObjectRef) -> PyResult { info!("New exception created: no msg"); let args = PyFuncArgs::default(); self.invoke(exc_type, args) } - pub fn new_exception(&mut self, exc_type: PyObjectRef, msg: String) -> PyObjectRef { + pub fn new_exception(&self, exc_type: PyObjectRef, msg: String) -> PyObjectRef { // TODO: exc_type may be user-defined exception, so we should return PyResult // TODO: maybe there is a clearer way to create an instance: info!("New exception created: {}", msg); @@ -151,18 +151,18 @@ impl VirtualMachine { self.invoke(exc_type, args).unwrap() } - pub fn new_attribute_error(&mut self, msg: String) -> PyObjectRef { + pub fn new_attribute_error(&self, msg: String) -> PyObjectRef { let attribute_error = self.ctx.exceptions.attribute_error.clone(); self.new_exception(attribute_error, msg) } - pub fn new_type_error(&mut self, msg: String) -> PyObjectRef { + pub fn new_type_error(&self, msg: String) -> PyObjectRef { let type_error = self.ctx.exceptions.type_error.clone(); self.new_exception(type_error, msg) } pub fn new_unsupported_operand_error( - &mut self, + &self, a: PyObjectRef, b: PyObjectRef, op: &str, @@ -175,39 +175,39 @@ impl VirtualMachine { )) } - pub fn new_os_error(&mut self, msg: String) -> PyObjectRef { + pub fn new_os_error(&self, msg: String) -> PyObjectRef { let os_error = self.ctx.exceptions.os_error.clone(); self.new_exception(os_error, msg) } /// Create a new python ValueError object. Useful for raising errors from /// python functions implemented in rust. - pub fn new_value_error(&mut self, msg: String) -> PyObjectRef { + pub fn new_value_error(&self, msg: String) -> PyObjectRef { let value_error = self.ctx.exceptions.value_error.clone(); self.new_exception(value_error, msg) } - pub fn new_key_error(&mut self, msg: String) -> PyObjectRef { + pub fn new_key_error(&self, msg: String) -> PyObjectRef { let key_error = self.ctx.exceptions.key_error.clone(); self.new_exception(key_error, msg) } - pub fn new_index_error(&mut self, msg: String) -> PyObjectRef { + pub fn new_index_error(&self, msg: String) -> PyObjectRef { let index_error = self.ctx.exceptions.index_error.clone(); self.new_exception(index_error, msg) } - pub fn new_not_implemented_error(&mut self, msg: String) -> PyObjectRef { + pub fn new_not_implemented_error(&self, msg: String) -> PyObjectRef { let not_implemented_error = self.ctx.exceptions.not_implemented_error.clone(); self.new_exception(not_implemented_error, msg) } - pub fn new_zero_division_error(&mut self, msg: String) -> PyObjectRef { + pub fn new_zero_division_error(&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_overflow_error(&mut self, msg: String) -> PyObjectRef { + pub fn new_overflow_error(&self, msg: String) -> PyObjectRef { let overflow_error = self.ctx.exceptions.overflow_error.clone(); self.new_exception(overflow_error, msg) } @@ -233,22 +233,22 @@ impl VirtualMachine { } // Container of the virtual machine state: - pub fn to_str(&mut self, obj: &PyObjectRef) -> PyResult { + pub fn to_str(&self, obj: &PyObjectRef) -> PyResult { let str = self.call_method(&obj, "__str__", vec![])?; TryFromObject::try_from_object(self, str) } - pub fn to_pystr(&mut self, obj: &PyObjectRef) -> Result { + pub fn to_pystr(&self, obj: &PyObjectRef) -> Result { let py_str_obj = self.to_str(obj)?; Ok(py_str_obj.value.clone()) } - pub fn to_repr(&mut self, obj: &PyObjectRef) -> PyResult { + pub fn to_repr(&self, obj: &PyObjectRef) -> PyResult { let repr = self.call_method(obj, "__repr__", vec![])?; TryFromObject::try_from_object(self, repr) } - pub fn import(&mut self, module: &str) -> PyResult { + pub fn import(&self, module: &str) -> PyResult { let builtins_import = self.builtins.get_item("__import__"); match builtins_import { Some(func) => self.invoke(func, vec![self.ctx.new_str(module.to_string())]), @@ -261,7 +261,7 @@ impl VirtualMachine { /// Determines if `obj` is an instance of `cls`, either directly, indirectly or virtually via /// the __instancecheck__ magic method. - pub fn isinstance(&mut self, obj: &PyObjectRef, cls: &PyObjectRef) -> PyResult { + pub fn isinstance(&self, obj: &PyObjectRef, cls: &PyObjectRef) -> PyResult { // cpython first does an exact check on the type, although documentation doesn't state that // https://github.com/python/cpython/blob/a24107b04c1277e3c1105f98aff5bfa3a98b33a0/Objects/abstract.c#L2408 if Rc::ptr_eq(&obj.typ(), cls) { @@ -274,12 +274,12 @@ impl VirtualMachine { /// Determines if `subclass` is a subclass of `cls`, either directly, indirectly or virtually /// via the __subclasscheck__ magic method. - pub fn issubclass(&mut self, subclass: &PyObjectRef, cls: &PyObjectRef) -> PyResult { + pub fn issubclass(&self, subclass: &PyObjectRef, cls: &PyObjectRef) -> PyResult { let ret = self.call_method(cls, "__subclasscheck__", vec![subclass.clone()])?; objbool::boolval(self, ret) } - pub fn call_get_descriptor(&mut self, attr: PyObjectRef, obj: PyObjectRef) -> PyResult { + pub fn call_get_descriptor(&self, attr: PyObjectRef, obj: PyObjectRef) -> PyResult { let attr_class = attr.typ(); if let Some(descriptor) = attr_class.get_attr("__get__") { let cls = obj.typ(); @@ -289,7 +289,7 @@ impl VirtualMachine { } } - pub fn call_method(&mut self, obj: &PyObjectRef, method_name: &str, args: T) -> PyResult + pub fn call_method(&self, obj: &PyObjectRef, method_name: &str, args: T) -> PyResult where T: Into, { @@ -311,7 +311,7 @@ impl VirtualMachine { } } - pub fn invoke(&mut self, func_ref: PyObjectRef, args: T) -> PyResult + pub fn invoke(&self, func_ref: PyObjectRef, args: T) -> PyResult where T: Into, { @@ -342,7 +342,7 @@ impl VirtualMachine { } fn invoke_python_function( - &mut self, + &self, code: &PyObjectRef, scope: &Scope, defaults: &PyObjectRef, @@ -364,7 +364,7 @@ impl VirtualMachine { } pub fn invoke_with_locals( - &mut self, + &self, function: PyObjectRef, cells: PyObjectRef, locals: PyObjectRef, @@ -388,7 +388,7 @@ impl VirtualMachine { } fn fill_locals_from_args( - &mut self, + &self, code_object: &bytecode::CodeObject, locals: &PyObjectRef, args: PyFuncArgs, @@ -539,7 +539,7 @@ impl VirtualMachine { Ok(()) } - pub fn extract_elements(&mut self, value: &PyObjectRef) -> PyResult> { + pub fn extract_elements(&self, value: &PyObjectRef) -> PyResult> { // Extract elements from item, if possible: let elements = if objtype::isinstance(value, &self.ctx.tuple_type()) || objtype::isinstance(value, &self.ctx.list_type()) @@ -553,7 +553,7 @@ impl VirtualMachine { } // get_attribute should be used for full attribute access (usually from user code). - pub fn get_attribute(&mut self, obj: PyObjectRef, attr_name: T) -> PyResult + pub fn get_attribute(&self, obj: PyObjectRef, attr_name: T) -> PyResult where T: TryIntoRef, { @@ -563,7 +563,7 @@ impl VirtualMachine { } pub fn set_attr( - &mut self, + &self, obj: &PyObjectRef, attr_name: PyObjectRef, attr_value: PyObjectRef, @@ -571,13 +571,13 @@ impl VirtualMachine { self.call_method(&obj, "__setattr__", vec![attr_name, attr_value]) } - pub fn del_attr(&mut self, obj: &PyObjectRef, attr_name: PyObjectRef) -> PyResult { + pub fn del_attr(&self, obj: &PyObjectRef, attr_name: PyObjectRef) -> PyResult { self.call_method(&obj, "__delattr__", vec![attr_name]) } // get_method should be used for internal access to magic methods (by-passing // the full getattribute look-up. - pub fn get_method(&mut self, obj: PyObjectRef, method_name: &str) -> PyResult { + pub fn get_method(&self, obj: PyObjectRef, method_name: &str) -> PyResult { let cls = obj.typ(); match cls.get_attr(method_name) { Some(method) => self.call_get_descriptor(method, obj.clone()), @@ -590,14 +590,14 @@ impl VirtualMachine { /// Otherwise, or if the result is the special `NotImplemented` built-in constant, /// calls `unsupported` to determine fallback value. pub fn call_or_unsupported( - &mut self, + &self, obj: PyObjectRef, arg: PyObjectRef, method: &str, unsupported: F, ) -> PyResult where - F: Fn(&mut VirtualMachine, PyObjectRef, PyObjectRef) -> PyResult, + F: Fn(&VirtualMachine, PyObjectRef, PyObjectRef) -> PyResult, { if let Ok(method) = self.get_method(obj.clone(), method) { let result = self.invoke(method, vec![arg.clone()])?; @@ -620,12 +620,12 @@ impl VirtualMachine { /// 2. If above is not implemented, calls `__rand__` with `rhs` and `lhs`. /// 3. If above is not implemented, invokes `unsupported` for the result. pub fn call_or_reflection( - &mut self, + &self, lhs: PyObjectRef, rhs: PyObjectRef, default: &str, reflection: &str, - unsupported: fn(&mut VirtualMachine, PyObjectRef, PyObjectRef) -> PyResult, + unsupported: fn(&VirtualMachine, PyObjectRef, PyObjectRef) -> PyResult, ) -> PyResult { // Try to call the default method self.call_or_unsupported(lhs, rhs, default, move |vm, lhs, rhs| { @@ -634,21 +634,21 @@ impl VirtualMachine { }) } - pub fn serialize(&mut self, obj: &PyObjectRef) -> PyResult { + pub fn serialize(&self, obj: &PyObjectRef) -> PyResult { crate::stdlib::json::ser_pyobject(self, obj) } - pub fn deserialize(&mut self, s: &str) -> PyResult { + pub fn deserialize(&self, s: &str) -> PyResult { crate::stdlib::json::de_pyobject(self, s) } - pub fn _sub(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _sub(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_reflection(a, b, "__sub__", "__rsub__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "-")) }) } - pub fn _isub(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _isub(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_unsupported(a, b, "__isub__", |vm, a, b| { vm.call_or_reflection(a, b, "__sub__", "__rsub__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "-=")) @@ -656,13 +656,13 @@ impl VirtualMachine { }) } - pub fn _add(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _add(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_reflection(a, b, "__add__", "__radd__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "+")) }) } - pub fn _iadd(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _iadd(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_unsupported(a, b, "__iadd__", |vm, a, b| { vm.call_or_reflection(a, b, "__add__", "__radd__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "+=")) @@ -670,13 +670,13 @@ impl VirtualMachine { }) } - pub fn _mul(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _mul(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_reflection(a, b, "__mul__", "__rmul__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "*")) }) } - pub fn _imul(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _imul(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_unsupported(a, b, "__imul__", |vm, a, b| { vm.call_or_reflection(a, b, "__mul__", "__rmul__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "*=")) @@ -684,13 +684,13 @@ impl VirtualMachine { }) } - pub fn _matmul(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _matmul(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_reflection(a, b, "__matmul__", "__rmatmul__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "@")) }) } - pub fn _imatmul(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _imatmul(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_unsupported(a, b, "__imatmul__", |vm, a, b| { vm.call_or_reflection(a, b, "__matmul__", "__rmatmul__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "@=")) @@ -698,13 +698,13 @@ impl VirtualMachine { }) } - pub fn _truediv(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _truediv(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_reflection(a, b, "__truediv__", "__rtruediv__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "/")) }) } - pub fn _itruediv(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _itruediv(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_unsupported(a, b, "__itruediv__", |vm, a, b| { vm.call_or_reflection(a, b, "__truediv__", "__rtruediv__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "/=")) @@ -712,13 +712,13 @@ impl VirtualMachine { }) } - pub fn _floordiv(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _floordiv(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_reflection(a, b, "__floordiv__", "__rfloordiv__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "//")) }) } - pub fn _ifloordiv(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _ifloordiv(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_unsupported(a, b, "__ifloordiv__", |vm, a, b| { vm.call_or_reflection(a, b, "__floordiv__", "__rfloordiv__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "//=")) @@ -726,13 +726,13 @@ impl VirtualMachine { }) } - pub fn _pow(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _pow(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_reflection(a, b, "__pow__", "__rpow__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "**")) }) } - pub fn _ipow(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _ipow(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_unsupported(a, b, "__ipow__", |vm, a, b| { vm.call_or_reflection(a, b, "__pow__", "__rpow__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "**=")) @@ -740,13 +740,13 @@ impl VirtualMachine { }) } - pub fn _mod(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _mod(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_reflection(a, b, "__mod__", "__rmod__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "%")) }) } - pub fn _imod(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _imod(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_unsupported(a, b, "__imod__", |vm, a, b| { vm.call_or_reflection(a, b, "__mod__", "__rmod__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "%=")) @@ -754,13 +754,13 @@ impl VirtualMachine { }) } - pub fn _lshift(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _lshift(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_reflection(a, b, "__lshift__", "__rlshift__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "<<")) }) } - pub fn _ilshift(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _ilshift(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_unsupported(a, b, "__ilshift__", |vm, a, b| { vm.call_or_reflection(a, b, "__lshift__", "__rlshift__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "<<=")) @@ -768,13 +768,13 @@ impl VirtualMachine { }) } - pub fn _rshift(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _rshift(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_reflection(a, b, "__rshift__", "__rrshift__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, ">>")) }) } - pub fn _irshift(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _irshift(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_unsupported(a, b, "__irshift__", |vm, a, b| { vm.call_or_reflection(a, b, "__rshift__", "__rrshift__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, ">>=")) @@ -782,13 +782,13 @@ impl VirtualMachine { }) } - pub fn _xor(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _xor(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_reflection(a, b, "__xor__", "__rxor__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "^")) }) } - pub fn _ixor(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _ixor(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_unsupported(a, b, "__ixor__", |vm, a, b| { vm.call_or_reflection(a, b, "__xor__", "__rxor__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "^=")) @@ -796,13 +796,13 @@ impl VirtualMachine { }) } - pub fn _or(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _or(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_reflection(a, b, "__or__", "__ror__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "|")) }) } - pub fn _ior(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _ior(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_unsupported(a, b, "__ior__", |vm, a, b| { vm.call_or_reflection(a, b, "__or__", "__ror__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "|=")) @@ -810,13 +810,13 @@ impl VirtualMachine { }) } - pub fn _and(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _and(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_reflection(a, b, "__and__", "__rand__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "&")) }) } - pub fn _iand(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _iand(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_unsupported(a, b, "__iand__", |vm, a, b| { vm.call_or_reflection(a, b, "__and__", "__rand__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "&=")) @@ -824,38 +824,38 @@ impl VirtualMachine { }) } - pub fn _eq(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _eq(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_reflection(a, b, "__eq__", "__eq__", |vm, a, b| { Ok(vm.new_bool(a.is(&b))) }) } - pub fn _ne(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _ne(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_reflection(a, b, "__ne__", "__ne__", |vm, a, b| { let eq = vm._eq(a, b)?; objbool::not(vm, &eq) }) } - pub fn _lt(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + pub fn _lt(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_reflection(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 { + pub fn _le(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_reflection(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 { + pub fn _gt(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_reflection(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 { + pub fn _ge(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_reflection(a, b, "__ge__", "__le__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, ">=")) }) diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index e30f653917..7fc2a2476e 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -24,7 +24,7 @@ enum FetchResponseFormat { } impl FetchResponseFormat { - fn from_str(vm: &mut VirtualMachine, s: &str) -> Result { + fn from_str(vm: &VirtualMachine, s: &str) -> Result { match s { "json" => Ok(FetchResponseFormat::Json), "text" => Ok(FetchResponseFormat::Text), @@ -41,7 +41,7 @@ impl FetchResponseFormat { } } -fn browser_fetch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn browser_fetch(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(url, Some(vm.ctx.str_type()))]); let promise_type = import_promise_type(vm)?; @@ -104,7 +104,7 @@ fn browser_fetch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(PyPromise::new_obj(promise_type, future_to_promise(future))) } -fn browser_request_animation_frame(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn browser_request_animation_frame(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(func, Some(vm.ctx.function_type()))]); use std::{cell::RefCell, rc::Rc}; @@ -117,12 +117,13 @@ fn browser_request_animation_frame(vm: &mut VirtualMachine, args: PyFuncArgs) -> let func = func.clone(); - let acc_vm = AccessibleVM::from_vm(vm); + let acc_vm = AccessibleVM::from(vm); *g.borrow_mut() = Some(Closure::wrap(Box::new(move |time: f64| { - let vm = &mut acc_vm + let stored_vm = acc_vm .upgrade() .expect("that the vm is valid from inside of request_animation_frame"); + let vm = &stored_vm.vm; let func = func.clone(); let args = vec![vm.ctx.new_float(time)]; let _ = vm.invoke(func, args); @@ -140,7 +141,7 @@ fn browser_request_animation_frame(vm: &mut VirtualMachine, args: PyFuncArgs) -> Ok(vm.ctx.new_int(id)) } -fn browser_cancel_animation_frame(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn browser_cancel_animation_frame(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(id, Some(vm.ctx.int_type()))]); let id = objint::get_value(id).to_i32().ok_or_else(|| { @@ -163,7 +164,7 @@ pub struct PyPromise { } impl PyValue for PyPromise { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.class(BROWSER_NAME, "Promise") } } @@ -181,14 +182,14 @@ pub fn get_promise_value(obj: &PyObjectRef) -> Promise { panic!("Inner error getting promise") } -pub fn import_promise_type(vm: &mut VirtualMachine) -> PyResult { +pub fn import_promise_type(vm: &VirtualMachine) -> PyResult { match import_module(vm, PathBuf::default(), BROWSER_NAME)?.get_attr("Promise") { Some(promise) => Ok(promise), None => Err(vm.new_not_implemented_error("No Promise".to_string())), } } -fn promise_then(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn promise_then(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { let promise_type = import_promise_type(vm)?; arg_check!( vm, @@ -203,14 +204,15 @@ fn promise_then(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let on_fulfill = on_fulfill.clone(); let on_reject = on_reject.cloned(); - let acc_vm = AccessibleVM::from_vm(vm); + let acc_vm = AccessibleVM::from(vm); let promise = get_promise_value(zelf); let ret_future = JsFuture::from(promise).then(move |res| { - let vm = &mut acc_vm + let stored_vm = &acc_vm .upgrade() .expect("that the vm is valid when the promise resolves"); + let vm = &stored_vm.vm; let ret = match res { Ok(val) => { let val = convert::js_to_py(vm, val); @@ -233,7 +235,7 @@ fn promise_then(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(PyPromise::new_obj(promise_type, ret_promise)) } -fn promise_catch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn promise_catch(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { let promise_type = import_promise_type(vm)?; arg_check!( vm, @@ -246,16 +248,17 @@ fn promise_catch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let on_reject = on_reject.clone(); - let acc_vm = AccessibleVM::from_vm(vm); + let acc_vm = AccessibleVM::from(vm); let promise = get_promise_value(zelf); let ret_future = JsFuture::from(promise).then(move |res| match res { Ok(val) => Ok(val), Err(err) => { - let vm = &mut acc_vm + let stored_vm = acc_vm .upgrade() .expect("that the vm is valid when the promise resolves"); + let vm = &stored_vm.vm; let err = convert::js_to_py(vm, err); let res = vm.invoke(on_reject, PyFuncArgs::new(vec![err], vec![])); convert::pyresult_to_jsresult(vm, res) @@ -267,7 +270,7 @@ fn promise_catch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(PyPromise::new_obj(promise_type, ret_promise)) } -fn browser_alert(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn browser_alert(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(message, Some(vm.ctx.str_type()))]); window() @@ -277,7 +280,7 @@ fn browser_alert(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.get_none()) } -fn browser_confirm(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn browser_confirm(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(message, Some(vm.ctx.str_type()))]); let result = window() @@ -287,7 +290,7 @@ fn browser_confirm(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_bool(result)) } -fn browser_prompt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn browser_prompt(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, @@ -331,7 +334,8 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { }) } -pub fn setup_browser_module(vm: &mut VirtualMachine) { +pub fn setup_browser_module(vm: &VirtualMachine) { vm.stdlib_inits + .borrow_mut() .insert(BROWSER_NAME.to_string(), Box::new(make_module)); } diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index 1113ace878..122ff1763e 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -10,7 +10,7 @@ use rustpython_vm::VirtualMachine; use crate::browser_module; use crate::vm_class::{AccessibleVM, WASMVirtualMachine}; -pub fn py_err_to_js_err(vm: &mut VirtualMachine, py_err: &PyObjectRef) -> JsValue { +pub fn py_err_to_js_err(vm: &VirtualMachine, py_err: &PyObjectRef) -> JsValue { macro_rules! map_exceptions { ($py_exc:ident, $msg:expr, { $($py_exc_ty:expr => $js_err_new:expr),*$(,)? }) => { $(if objtype::isinstance($py_exc, $py_exc_ty) { @@ -57,12 +57,12 @@ pub fn py_err_to_js_err(vm: &mut VirtualMachine, py_err: &PyObjectRef) -> JsValu js_err } -pub fn js_py_typeerror(vm: &mut VirtualMachine, js_err: JsValue) -> PyObjectRef { +pub fn js_py_typeerror(vm: &VirtualMachine, js_err: JsValue) -> PyObjectRef { let msg = js_err.unchecked_into::().to_string(); vm.new_type_error(msg.into()) } -pub fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { +pub fn py_to_js(vm: &VirtualMachine, py_obj: PyObjectRef) -> JsValue { if let Some(ref wasm_id) = vm.wasm_id { if objtype::isinstance(&py_obj, &vm.ctx.function_type()) { let wasm_vm = WASMVirtualMachine { @@ -81,9 +81,10 @@ pub fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { } }; let acc_vm = AccessibleVM::from(wasm_vm.clone()); - let vm = &mut acc_vm + let stored_vm = acc_vm .upgrade() .expect("acc. VM to be invalid when WASM vm is valid"); + let vm = &stored_vm.vm; let mut py_func_args = PyFuncArgs::default(); if let Some(ref args) = args { for arg in args.values() { @@ -147,13 +148,13 @@ pub fn object_entries(obj: &Object) -> impl Iterator Result { +pub fn pyresult_to_jsresult(vm: &VirtualMachine, result: PyResult) -> Result { result .map(|value| py_to_js(vm, value)) .map_err(|err| py_err_to_js_err(vm, &err)) } -pub fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef { +pub fn js_to_py(vm: &VirtualMachine, js_val: JsValue) -> PyObjectRef { if js_val.is_object() { if let Some(promise) = js_val.dyn_ref::() { // the browser module might not be injected @@ -193,8 +194,8 @@ pub fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef { } } 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 { + vm.ctx + .new_rustfunc(move |vm: &VirtualMachine, args: PyFuncArgs| -> PyResult { let func = func.clone(); let this = Object::new(); for (k, v) in args.kwargs { @@ -208,8 +209,7 @@ pub fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef { 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, diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index 408e2b3cc0..3b97ccfb44 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -21,10 +21,10 @@ impl HeldRcInner for T {} pub(crate) struct StoredVirtualMachine { pub vm: VirtualMachine, - pub scope: Scope, + pub scope: RefCell, /// you can put a Rc in here, keep it as a Weak, and it'll be held only for /// as long as the StoredVM is alive - held_rcs: Vec>, + held_rcs: RefCell>>, } impl StoredVirtualMachine { @@ -37,8 +37,8 @@ impl StoredVirtualMachine { vm.wasm_id = Some(id); StoredVirtualMachine { vm, - scope, - held_rcs: vec![], + scope: RefCell::new(scope), + held_rcs: RefCell::new(Vec::new()), } } } @@ -47,9 +47,9 @@ impl StoredVirtualMachine { // probably gets compiled down to a normal-ish static varible, like Atomic* types do: // https://rustwasm.github.io/2018/10/24/multithreading-rust-and-wasm.html#atomic-instructions thread_local! { - static STORED_VMS: RefCell>>> = + static STORED_VMS: RefCell>> = RefCell::default(); - static ACTIVE_VMS: RefCell> = RefCell::default(); + static ACTIVE_VMS: RefCell> = RefCell::default(); } #[wasm_bindgen(js_name = vmStore)] @@ -63,7 +63,7 @@ impl VMStore { if !vms.contains_key(&id) { let stored_vm = StoredVirtualMachine::new(id.clone(), inject_browser_module.unwrap_or(true)); - vms.insert(id.clone(), Rc::new(RefCell::new(stored_vm))); + vms.insert(id.clone(), Rc::new(stored_vm)); } }); WASMVirtualMachine { id } @@ -110,8 +110,8 @@ impl VMStore { } #[derive(Clone)] -pub struct AccessibleVM { - weak: Weak>, +pub(crate) struct AccessibleVM { + weak: Weak, id: String, } @@ -122,35 +122,8 @@ impl AccessibleVM { AccessibleVM { weak, id } } - pub fn from_vm(vm: &VirtualMachine) -> AccessibleVM { - AccessibleVM::from_id( - vm.wasm_id - .clone() - .expect("VM passed to from_vm to have wasm_id be Some()"), - ) - } - - pub fn upgrade(&self) -> Option { - let vm_cell = self.weak.upgrade()?; - let top_level = match vm_cell.try_borrow_mut() { - Ok(mut vm) => { - ACTIVE_VMS.with(|cell| { - cell.borrow_mut().insert(self.id.clone(), &mut vm.vm); - }); - true - } - Err(_) => false, - }; - Some(ACTIVE_VMS.with(|cell| { - let vms = cell.borrow(); - let ptr = vms.get(&self.id).expect("id to be in ACTIVE_VMS"); - let vm = unsafe { &mut **ptr }; - AccessibleVMPtr { - id: self.id.clone(), - top_level, - inner: vm, - } - })) + pub fn upgrade(&self) -> Option> { + self.weak.upgrade() } } @@ -164,31 +137,13 @@ impl From<&WASMVirtualMachine> for AccessibleVM { AccessibleVM::from_id(vm.id.clone()) } } - -pub struct AccessibleVMPtr<'a> { - id: String, - top_level: bool, - inner: &'a mut VirtualMachine, -} - -impl std::ops::Deref for AccessibleVMPtr<'_> { - type Target = VirtualMachine; - fn deref(&self) -> &VirtualMachine { - &self.inner - } -} -impl std::ops::DerefMut for AccessibleVMPtr<'_> { - fn deref_mut(&mut self) -> &mut VirtualMachine { - &mut self.inner - } -} - -impl Drop for AccessibleVMPtr<'_> { - fn drop(&mut self) { - if self.top_level { - // remove the (now invalid) pointer from the map - ACTIVE_VMS.with(|cell| cell.borrow_mut().remove(&self.id)); - } +impl From<&VirtualMachine> for AccessibleVM { + fn from(vm: &VirtualMachine) -> AccessibleVM { + AccessibleVM::from_id( + vm.wasm_id + .clone() + .expect("VM passed to from::() to have wasm_id be Some()"), + ) } } @@ -202,19 +157,18 @@ pub struct WASMVirtualMachine { impl WASMVirtualMachine { pub(crate) fn with_unchecked(&self, f: F) -> R where - F: FnOnce(&mut StoredVirtualMachine) -> R, + F: FnOnce(&StoredVirtualMachine) -> R, { let stored_vm = STORED_VMS.with(|cell| { let mut vms = cell.borrow_mut(); vms.get_mut(&self.id).unwrap().clone() }); - let mut stored_vm = stored_vm.borrow_mut(); - f(&mut stored_vm) + f(&stored_vm) } pub(crate) fn with(&self, f: F) -> Result where - F: FnOnce(&mut StoredVirtualMachine) -> R, + F: FnOnce(&StoredVirtualMachine) -> R, { self.assert_valid()?; Ok(self.with_unchecked(f)) @@ -230,7 +184,7 @@ impl WASMVirtualMachine { ) -> Result, JsValue> { self.with(|stored_vm| { let weak = Rc::downgrade(&rc); - stored_vm.held_rcs.push(rc); + stored_vm.held_rcs.borrow_mut().push(rc); weak }) } @@ -256,23 +210,21 @@ impl WASMVirtualMachine { pub fn add_to_scope(&self, name: String, value: JsValue) -> Result<(), JsValue> { self.with( move |StoredVirtualMachine { - ref mut vm, - ref mut scope, - .. + ref vm, ref scope, .. }| { let value = convert::js_to_py(vm, value); - scope.store_name(&vm, &name, value); + scope.borrow_mut().store_name(&vm, &name, value); }, ) } #[wasm_bindgen(js_name = setStdout)] pub fn set_stdout(&self, stdout: JsValue) -> Result<(), JsValue> { - self.with(move |StoredVirtualMachine { ref mut vm, .. }| { + self.with(move |StoredVirtualMachine { ref vm, .. }| { fn error() -> JsValue { TypeError::new("Unknown stdout option, please pass a function or 'console'").into() } - let print_fn: Box PyResult> = + let print_fn: Box PyResult> = if let Some(s) = stdout.as_string() { match s.as_str() { "console" => Box::new(wasm_builtins::builtin_print_console), @@ -280,18 +232,16 @@ impl WASMVirtualMachine { } } else if stdout.is_function() { let func = js_sys::Function::from(stdout); - Box::new( - move |vm: &mut VirtualMachine, args: PyFuncArgs| -> PyResult { - func.call1( - &JsValue::UNDEFINED, - &wasm_builtins::format_print_args(vm, args)?.into(), - ) - .map_err(|err| convert::js_to_py(vm, err))?; - Ok(vm.get_none()) - }, - ) + Box::new(move |vm: &VirtualMachine, args: PyFuncArgs| -> PyResult { + func.call1( + &JsValue::UNDEFINED, + &wasm_builtins::format_print_args(vm, args)?.into(), + ) + .map_err(|err| convert::js_to_py(vm, err))?; + Ok(vm.get_none()) + }) } else if stdout.is_undefined() || stdout.is_null() { - fn noop(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { + fn noop(vm: &VirtualMachine, _args: PyFuncArgs) -> PyResult { Ok(vm.get_none()) } Box::new(noop) @@ -306,7 +256,7 @@ impl WASMVirtualMachine { #[wasm_bindgen(js_name = injectModule)] pub fn inject_module(&self, name: String, module: Object) -> Result<(), JsValue> { - self.with(|StoredVirtualMachine { ref mut vm, .. }| { + self.with(|StoredVirtualMachine { ref vm, .. }| { let mut module_items: HashMap = HashMap::new(); for entry in convert::object_entries(&module) { let (key, value) = entry?; @@ -324,7 +274,9 @@ impl WASMVirtualMachine { py_mod }; - vm.stdlib_inits.insert(mod_name, Box::new(stdlib_init_fn)); + vm.stdlib_inits + .borrow_mut() + .insert(mod_name, Box::new(stdlib_init_fn)); Ok(()) })? @@ -334,9 +286,7 @@ impl WASMVirtualMachine { self.assert_valid()?; self.with_unchecked( |StoredVirtualMachine { - ref mut vm, - ref mut scope, - .. + ref vm, ref scope, .. }| { source.push('\n'); let code = @@ -378,7 +328,7 @@ impl WASMVirtualMachine { } js_err })?; - let result = vm.run_code_obj(code, scope.clone()); + let result = vm.run_code_obj(code, scope.borrow().clone()); convert::pyresult_to_jsresult(vm, result) }, ) diff --git a/wasm/lib/src/wasm_builtins.rs b/wasm/lib/src/wasm_builtins.rs index cc3435bbaa..d5bc830dfe 100644 --- a/wasm/lib/src/wasm_builtins.rs +++ b/wasm/lib/src/wasm_builtins.rs @@ -16,7 +16,7 @@ pub(crate) fn window() -> web_sys::Window { web_sys::window().expect("Window to be available") } -pub fn format_print_args(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result { +pub fn format_print_args(vm: &VirtualMachine, args: PyFuncArgs) -> Result { // Handle 'sep' kwarg: let sep_arg = args .get_optional_kwarg("sep") @@ -68,7 +68,7 @@ pub fn format_print_args(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result PyResult { +pub fn builtin_print_console(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { let arr = Array::new(); for arg in args.args { arr.push(&vm.to_pystr(&arg)?.into()); From 243826679c5b700daf3c35ce0880d0136a331e44 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Fri, 22 Mar 2019 01:51:44 -0500 Subject: [PATCH 371/380] Remove unnecessary &mut --- 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 3b97ccfb44..9d80854f73 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -32,7 +32,7 @@ impl StoredVirtualMachine { let mut vm = VirtualMachine::new(); let scope = vm.ctx.new_scope(); if inject_browser_module { - setup_browser_module(&mut vm); + setup_browser_module(&vm); } vm.wasm_id = Some(id); StoredVirtualMachine { From 2c93c79ac8c127b9390520f07095759476b36417 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Fri, 22 Mar 2019 01:53:55 -0500 Subject: [PATCH 372/380] Immutable prompt variable --- src/main.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index a7dffb02d7..87ed80868e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -184,14 +184,14 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult { println!("No previous history."); } - let mut prompt = get_prompt(vm, "ps1"); - let mut continuing = false; loop { - if !continuing { - prompt = get_prompt(vm, "ps1"); - } + let prompt = if continuing { + get_prompt(vm, "ps2") + } else { + get_prompt(vm, "ps1") + }; match repl.readline(&prompt) { Ok(line) => { debug!("You entered {:?}", line); @@ -209,7 +209,6 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult { match shell_exec(vm, &input, vars.clone()) { Err(CompileError::Parse(ParseError::EOF(_))) => { - prompt = get_prompt(vm, "ps2"); continuing = true; continue; } From 23ee751880669c5810cb42e0b3c912b304b340a6 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Thu, 21 Mar 2019 13:06:45 +0000 Subject: [PATCH 373/380] Use vm.get_attribute to access attributes of modules. --- src/main.rs | 14 ++++---------- vm/src/frame.rs | 15 ++++++--------- vm/src/import.rs | 12 ++++++++---- vm/src/stdlib/io.rs | 9 ++++----- vm/src/stdlib/re.rs | 6 ++---- vm/src/sysmodule.rs | 3 ++- vm/src/vm.rs | 15 ++++++--------- 7 files changed, 32 insertions(+), 42 deletions(-) diff --git a/src/main.rs b/src/main.rs index 1b92a5ee46..1ff1b7ef42 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,14 +10,8 @@ extern crate rustyline; use clap::{App, Arg}; use rustpython_parser::error::ParseError; use rustpython_vm::{ - compile, - error::CompileError, - frame::Scope, - import, - obj::objstr, - print_exception, - pyobject::{AttributeProtocol, PyResult}, - util, VirtualMachine, + compile, error::CompileError, frame::Scope, import, obj::objstr, print_exception, + pyobject::PyResult, util, VirtualMachine, }; use rustyline::{error::ReadlineError, Editor}; use std::path::{Path, PathBuf}; @@ -176,8 +170,8 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult { println!("No previous history."); } - let ps1 = &objstr::get_value(&vm.sys_module.get_attr("ps1").unwrap()); - let ps2 = &objstr::get_value(&vm.sys_module.get_attr("ps2").unwrap()); + let ps1 = &objstr::get_value(&vm.get_attribute(vm.sys_module.clone(), "ps1").unwrap()); + let ps2 = &objstr::get_value(&vm.get_attribute(vm.sys_module.clone(), "ps2").unwrap()); let mut prompt = ps1; loop { diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 1588699456..576d988fb5 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -21,8 +21,8 @@ use crate::obj::objslice::PySlice; use crate::obj::objstr; use crate::obj::objtype; use crate::pyobject::{ - AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyObjectRef, PyResult, PyValue, - TryFromObject, TypeProtocol, + DictProtocol, IdProtocol, PyContext, PyObjectRef, PyResult, PyValue, TryFromObject, + TypeProtocol, }; use crate::vm::VirtualMachine; @@ -810,13 +810,10 @@ impl Frame { // 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_attr(symbol).map_or_else( - || { - let import_error = vm.context().exceptions.import_error.clone(); - Err(vm.new_exception(import_error, format!("cannot import name '{}'", symbol))) - }, - Ok, - ), + Some(symbol) => vm.get_attribute(module, symbol.as_str()).map_err(|_| { + let import_error = vm.context().exceptions.import_error.clone(); + vm.new_exception(import_error, format!("cannot import name '{}'", symbol)) + }), None => Ok(module), }; diff --git a/vm/src/import.rs b/vm/src/import.rs index a36168c287..0b27eb72c9 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -8,7 +8,7 @@ use std::path::PathBuf; use crate::compile; use crate::frame::Scope; use crate::obj::{objsequence, objstr}; -use crate::pyobject::{AttributeProtocol, DictProtocol, PyResult}; +use crate::pyobject::{DictProtocol, PyResult}; use crate::util; use crate::vm::VirtualMachine; @@ -54,7 +54,7 @@ pub fn import_module( module_name: &str, ) -> PyResult { // First, see if we already loaded the module: - let sys_modules = vm.sys_module.get_attr("modules").unwrap(); + let sys_modules = vm.get_attribute(vm.sys_module.clone(), "modules")?; if let Some(module) = sys_modules.get_item(module_name) { return Ok(module); } @@ -63,8 +63,12 @@ pub fn import_module( Ok(module) } -fn find_source(vm: &VirtualMachine, current_path: PathBuf, name: &str) -> Result { - let sys_path = vm.sys_module.get_attr("path").unwrap(); +fn find_source( + vm: &mut VirtualMachine, + current_path: PathBuf, + name: &str, +) -> Result { + let sys_path = vm.get_attribute(vm.sys_module.clone(), "path").unwrap(); let mut paths: Vec = objsequence::get_elements(&sys_path) .iter() .map(|item| PathBuf::from(objstr::get_value(item))) diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index aba31198aa..5ba8197fbf 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -20,8 +20,7 @@ use crate::obj::objbytes; use crate::obj::objint; use crate::obj::objstr; use crate::pyobject::{ - AttributeProtocol, BufferProtocol, PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue, - TypeProtocol, + BufferProtocol, PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -165,7 +164,7 @@ fn file_io_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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 py_name = vm.get_attribute(file_io.clone(), "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())), @@ -200,7 +199,7 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let py_length = vm.call_method(obj, "__len__", PyFuncArgs::default())?; let length = objint::get_value(&py_length).to_u64().unwrap(); - let file_no = file_io.get_attr("fileno").unwrap(); + let file_no = vm.get_attribute(file_io.clone(), "fileno")?; let raw_fd = objint::get_value(&file_no).to_i64().unwrap(); //extract unix file descriptor. @@ -230,7 +229,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("fileno").unwrap(); + let file_no = vm.get_attribute(file_io.clone(), "fileno")?; let raw_fd = objint::get_value(&file_no).to_i64().unwrap(); //unsafe block - creates file handle from the UNIX file descriptor diff --git a/vm/src/stdlib/re.rs b/vm/src/stdlib/re.rs index eda5be75f1..8fc7e359ed 100644 --- a/vm/src/stdlib/re.rs +++ b/vm/src/stdlib/re.rs @@ -12,14 +12,12 @@ use regex::{Match, Regex}; use crate::function::PyFuncArgs; use crate::import; use crate::obj::objstr; -use crate::pyobject::{ - AttributeProtocol, PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, -}; +use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; impl PyValue for Regex { fn class(vm: &mut VirtualMachine) -> PyObjectRef { - vm.import("re").unwrap().get_attr("Pattern").unwrap() + vm.class("re", "Pattern") } } diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index e2713d7834..6bfa14eb3e 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -57,7 +57,7 @@ fn sys_getsizeof(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_int(size)) } -pub fn make_module(ctx: &PyContext) -> PyObjectRef { +pub fn make_module(ctx: &PyContext, builtins: PyObjectRef) -> PyObjectRef { let path_list = match env::var_os("PYTHONPATH") { Some(paths) => env::split_paths(&paths) .map(|path| { @@ -156,6 +156,7 @@ settrace() -- set the global debug tracing function }); modules.set_item(&ctx, sys_name, sys_mod.clone()); + modules.set_item(&ctx, "builtins", builtins); ctx.set_attr(&sys_mod, "modules", modules); sys_mod diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 7f9b474d0a..594b596381 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -57,11 +57,7 @@ impl VirtualMachine { // Hard-core modules: let builtins = builtins::make_module(&ctx); - let sysmod = sysmodule::make_module(&ctx); - - // Add builtins as builtins module: - let modules = sysmod.get_attr("modules").unwrap(); - modules.set_item(&ctx, "builtins", builtins.clone()); + let sysmod = sysmodule::make_module(&ctx, builtins.clone()); let stdlib_inits = stdlib::get_module_inits(); VirtualMachine { @@ -105,10 +101,11 @@ impl VirtualMachine { } pub fn class(&mut self, module: &str, class: &str) -> PyObjectRef { - self.import(module) - .unwrap_or_else(|_| panic!("unable to import {}", module)) - .get_attr(class) - .unwrap_or_else(|| panic!("module {} has no class {}", module, class)) + let module = self + .import(module) + .unwrap_or_else(|_| panic!("unable to import {}", module)); + self.get_attribute(module.clone(), class) + .unwrap_or_else(|_| panic!("module {} has no class {}", module, class)) } /// Create a new python string object. From 79666c255a227f94fa9a78bc2c2c374c33a8e2f8 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Fri, 22 Mar 2019 07:24:06 -0500 Subject: [PATCH 374/380] Fix some issues wrt mutability --- vm/src/import.rs | 2 +- vm/src/obj/objclassmethod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vm/src/import.rs b/vm/src/import.rs index 24eebf67c7..8e8fe33849 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -56,7 +56,7 @@ pub fn import_module(vm: &VirtualMachine, current_path: PathBuf, module_name: &s } fn find_source( - vm: &mut VirtualMachine, + vm: &VirtualMachine, current_path: PathBuf, name: &str, ) -> Result { diff --git a/vm/src/obj/objclassmethod.rs b/vm/src/obj/objclassmethod.rs index 6ad560603c..725f508e98 100644 --- a/vm/src/obj/objclassmethod.rs +++ b/vm/src/obj/objclassmethod.rs @@ -9,7 +9,7 @@ pub struct PyClassMethod { pub type PyClassMethodRef = PyRef; impl PyValue for PyClassMethod { - fn class(vm: &mut VirtualMachine) -> PyObjectRef { + fn class(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.classmethod_type() } } From 36ff4e37d360fe8dad35409b25e65aef34fe53d2 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Fri, 22 Mar 2019 07:29:14 -0500 Subject: [PATCH 375/380] Use vm.get_attribute --- src/main.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index 87ed80868e..4393946273 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,14 +10,8 @@ extern crate rustyline; use clap::{App, Arg}; use rustpython_parser::error::ParseError; use rustpython_vm::{ - compile, - error::CompileError, - frame::Scope, - import, - obj::objstr, - print_exception, - pyobject::{AttributeProtocol, PyResult}, - util, VirtualMachine, + compile, error::CompileError, frame::Scope, import, obj::objstr, print_exception, + pyobject::PyResult, util, VirtualMachine, }; use rustyline::{error::ReadlineError, Editor}; use std::path::{Path, PathBuf}; @@ -160,8 +154,8 @@ fn get_history_path() -> PathBuf { } fn get_prompt(vm: &mut VirtualMachine, prompt_name: &str) -> String { - vm.sys_module - .get_attr(prompt_name) + vm.get_attribute(vm.sys_module.clone(), prompt_name) + .ok() .as_ref() .map(objstr::get_value) .unwrap_or_else(String::new) From 474577becaad269af5099ec3aebe78ffcc65175d Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Fri, 22 Mar 2019 16:23:15 +0200 Subject: [PATCH 376/380] Fix format --- vm/src/import.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/vm/src/import.rs b/vm/src/import.rs index 8e8fe33849..ed5a6d094b 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -55,11 +55,7 @@ pub fn import_module(vm: &VirtualMachine, current_path: PathBuf, module_name: &s Ok(module) } -fn find_source( - vm: &VirtualMachine, - current_path: PathBuf, - name: &str, -) -> Result { +fn find_source(vm: &VirtualMachine, current_path: PathBuf, name: &str) -> Result { let sys_path = vm.get_attribute(vm.sys_module.clone(), "path").unwrap(); let mut paths: Vec = objsequence::get_elements(&sys_path) .iter() From 6230a25c4bfbd14b4b65017f6fe0818d2aa46c64 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Tue, 19 Mar 2019 22:50:58 +0200 Subject: [PATCH 377/380] Use first argument in super --- tests/snippets/class.py | 13 +++++++++++++ vm/src/obj/objsuper.rs | 42 ++++++++++++++++++++++++++--------------- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/tests/snippets/class.py b/tests/snippets/class.py index 84ed872460..ee766f38f8 100644 --- a/tests/snippets/class.py +++ b/tests/snippets/class.py @@ -75,6 +75,19 @@ def test1(self): assert c.test() == 100 assert c.test1() == 200 +class Me(): + + def test(me): + return 100 + +class Me2(Me): + + def test(me): + return super().test() + +me = Me2() +assert me.test() == 100 + a = super(bool, True) assert isinstance(a, super) assert type(a) is super diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index 390e591833..ebb9e8d950 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -7,6 +7,7 @@ https://github.com/python/cpython/blob/50b48572d9a90c5bb36e2bef6179548ea927a35a/ */ use crate::function::PyFuncArgs; +use crate::obj::objstr; use crate::obj::objtype::PyClass; use crate::pyobject::{ DictProtocol, PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, @@ -75,7 +76,11 @@ fn super_getattribute(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { return Ok(vm.ctx.new_bound_method(item, inst.clone())); } } - Err(vm.new_attribute_error(format!("{} has no attribute '{}'", inst, name_str))) + Err(vm.new_attribute_error(format!( + "{} has no attribute '{}'", + inst, + objstr::get_value(name_str) + ))) } _ => panic!("not Class"), } @@ -94,14 +99,31 @@ fn super_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { return Err(vm.new_type_error(format!("{:?} is not a subtype of super", cls))); } + // Get the bound object: + let py_obj = if let Some(obj) = py_obj { + obj.clone() + } else { + let frame = vm.current_frame(); + if let Some(first_arg) = frame.code.arg_names.get(0) { + match vm.get_locals().get_item(first_arg) { + Some(obj) => obj.clone(), + _ => { + return Err(vm + .new_type_error(format!("super arguement {} was not supplied", first_arg))); + } + } + } else { + return Err(vm.new_type_error( + "super must be called with 1 argument or from inside class method".to_string(), + )); + } + }; + // Get the type: let py_type = if let Some(ty) = py_type { ty.clone() } else { - match vm.get_locals().get_item("self") { - Some(obj) => obj.typ().clone(), - _ => panic!("No self"), - } + py_obj.typ().clone() }; // Check type argument: @@ -113,16 +135,6 @@ fn super_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { ))); } - // Get the bound object: - let py_obj = if let Some(obj) = py_obj { - obj.clone() - } else { - match vm.get_locals().get_item("self") { - Some(obj) => obj, - _ => panic!("No self"), - } - }; - // Check obj type: if !(objtype::isinstance(&py_obj, &py_type) || objtype::issubclass(&py_obj, &py_type)) { return Err(vm.new_type_error( From 84e89d37e2a3b03f6c7a9a8a5ada99a2fc577b23 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Thu, 21 Mar 2019 18:56:18 +0200 Subject: [PATCH 378/380] Use __class__ cell in super --- tests/snippets/class.py | 14 +++++++++++ vm/src/frame.rs | 28 ++++++++++++++-------- vm/src/obj/objsuper.rs | 51 ++++++++++++++++++++++++++--------------- 3 files changed, 65 insertions(+), 28 deletions(-) diff --git a/tests/snippets/class.py b/tests/snippets/class.py index ee766f38f8..35d043a6d4 100644 --- a/tests/snippets/class.py +++ b/tests/snippets/class.py @@ -85,6 +85,20 @@ class Me2(Me): def test(me): return super().test() +class A(): + def f(self): + pass + +class B(A): + def f(self): + super().f() + +class C(B): + def f(self): + super().f() + +C().f() + me = Me2() assert me.test() == 100 diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 3a6809f0ea..e257ea21fa 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -106,6 +106,20 @@ impl Scope { } } + pub fn get(&self, name: &str) -> Option { + for dict in self.locals.iter() { + if let Some(value) = dict.get_item(name) { + return Some(value); + } + } + + if let Some(value) = self.globals.get_item(name) { + return Some(value); + } + + None + } + pub fn get_only_locals(&self) -> Option { self.locals.iter().next().cloned() } @@ -130,17 +144,11 @@ pub trait NameProtocol { impl NameProtocol for Scope { fn load_name(&self, vm: &VirtualMachine, name: &str) -> Option { - for dict in self.locals.iter() { - if let Some(value) = dict.get_item(name) { - return Some(value); - } - } - - if let Some(value) = self.globals.get_item(name) { - return Some(value); + if let Some(value) = self.get(name) { + Some(value) + } else { + vm.builtins.get_item(name) } - - vm.builtins.get_item(name) } fn store_name(&self, vm: &VirtualMachine, key: &str, value: PyObjectRef) { diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index ebb9e8d950..38e9cf0e7c 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -19,6 +19,7 @@ use super::objtype; #[derive(Debug)] pub struct PySuper { obj: PyObjectRef, + typ: PyObjectRef, } impl PyValue for PySuper { @@ -68,8 +69,9 @@ fn super_getattribute(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { ); let inst = super_obj.payload::().unwrap().obj.clone(); + let typ = super_obj.payload::().unwrap().typ.clone(); - match inst.typ().payload::() { + match typ.payload::() { Some(PyClass { ref mro, .. }) => { for class in mro { if let Ok(item) = vm.get_attribute(class.as_object().clone(), name_str.clone()) { @@ -99,6 +101,29 @@ fn super_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { return Err(vm.new_type_error(format!("{:?} is not a subtype of super", cls))); } + // Get the type: + let py_type = if let Some(ty) = py_type { + ty.clone() + } else { + match vm.current_scope().get("__class__") { + Some(obj) => obj.clone(), + _ => { + return Err(vm.new_type_error( + "super must be called with 1 argument or from inside class method".to_string(), + )); + } + } + }; + + // Check type argument: + if !objtype::isinstance(&py_type, &vm.get_type()) { + let type_name = objtype::get_type_name(&py_type.typ()); + return Err(vm.new_type_error(format!( + "super() argument 1 must be type, not {}", + type_name + ))); + } + // Get the bound object: let py_obj = if let Some(obj) = py_obj { obj.clone() @@ -119,22 +144,6 @@ fn super_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { } }; - // Get the type: - let py_type = if let Some(ty) = py_type { - ty.clone() - } else { - py_obj.typ().clone() - }; - - // Check type argument: - if !objtype::isinstance(&py_type, &vm.get_type()) { - let type_name = objtype::get_type_name(&py_type.typ()); - return Err(vm.new_type_error(format!( - "super() argument 1 must be type, not {}", - type_name - ))); - } - // Check obj type: if !(objtype::isinstance(&py_obj, &py_type) || objtype::issubclass(&py_obj, &py_type)) { return Err(vm.new_type_error( @@ -142,5 +151,11 @@ fn super_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { )); } - Ok(PyObject::new(PySuper { obj: py_obj }, cls.clone())) + Ok(PyObject::new( + PySuper { + obj: py_obj, + typ: py_type, + }, + cls.clone(), + )) } From 2c8657c3b37a8ceed583ff6a014168b3fcc82fb6 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Fri, 22 Mar 2019 10:45:58 +0200 Subject: [PATCH 379/380] Add load_cell to NameProtocol --- vm/src/frame.rs | 38 ++++++++++++++++++++------------------ vm/src/obj/objsuper.rs | 3 ++- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index e257ea21fa..0015557fec 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -106,20 +106,6 @@ impl Scope { } } - pub fn get(&self, name: &str) -> Option { - for dict in self.locals.iter() { - if let Some(value) = dict.get_item(name) { - return Some(value); - } - } - - if let Some(value) = self.globals.get_item(name) { - return Some(value); - } - - None - } - pub fn get_only_locals(&self) -> Option { self.locals.iter().next().cloned() } @@ -140,15 +126,31 @@ pub trait NameProtocol { fn load_name(&self, vm: &VirtualMachine, name: &str) -> Option; fn store_name(&self, vm: &VirtualMachine, name: &str, value: PyObjectRef); fn delete_name(&self, vm: &VirtualMachine, name: &str); + fn load_cell(&self, vm: &VirtualMachine, name: &str) -> Option; } impl NameProtocol for Scope { fn load_name(&self, vm: &VirtualMachine, name: &str) -> Option { - if let Some(value) = self.get(name) { - Some(value) - } else { - vm.builtins.get_item(name) + for dict in self.locals.iter() { + if let Some(value) = dict.get_item(name) { + return Some(value); + } + } + + if let Some(value) = self.globals.get_item(name) { + return Some(value); } + + vm.builtins.get_item(name) + } + + fn load_cell(&self, _vm: &VirtualMachine, name: &str) -> Option { + for dict in self.locals.iter().skip(1) { + if let Some(value) = dict.get_item(name) { + return Some(value); + } + } + None } fn store_name(&self, vm: &VirtualMachine, key: &str, value: PyObjectRef) { diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index 38e9cf0e7c..2b2be02414 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -6,6 +6,7 @@ https://github.com/python/cpython/blob/50b48572d9a90c5bb36e2bef6179548ea927a35a/ */ +use crate::frame::NameProtocol; use crate::function::PyFuncArgs; use crate::obj::objstr; use crate::obj::objtype::PyClass; @@ -105,7 +106,7 @@ fn super_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { let py_type = if let Some(ty) = py_type { ty.clone() } else { - match vm.current_scope().get("__class__") { + match vm.current_scope().load_cell(vm, "__class__") { Some(obj) => obj.clone(), _ => { return Err(vm.new_type_error( From 8f6d8a78de8fc7daa93ae78f7cb58331963275e4 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Fri, 22 Mar 2019 13:14:08 -0500 Subject: [PATCH 380/380] Keepalive WASM webpack build --- wasm/demo/webpack.config.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/wasm/demo/webpack.config.js b/wasm/demo/webpack.config.js index 1f13b99fa9..7ae9ecffda 100644 --- a/wasm/demo/webpack.config.js +++ b/wasm/demo/webpack.config.js @@ -4,6 +4,8 @@ const WasmPackPlugin = require('@wasm-tool/wasm-pack-plugin'); const path = require('path'); const fs = require('fs'); +const interval = setInterval(() => console.log('keepalive'), 1000 * 60 * 5); + module.exports = { entry: './src/index.js', output: { @@ -40,6 +42,13 @@ module.exports = { }), new WasmPackPlugin({ crateDirectory: path.join(__dirname, '../lib') - }) + }), + { + apply(compiler) { + compiler.hooks.done.tap('clearInterval', () => { + clearInterval(interval); + }); + } + } ] };