From 430bc4bac2217c3fe5dc6c06e6eda28272cb93d6 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Sat, 23 Mar 2019 09:35:53 -0500 Subject: [PATCH 01/51] Fix vm.import when there are no frames on stack --- vm/src/builtins.rs | 11 ++++++++--- vm/src/obj/objsuper.rs | 8 +++++--- vm/src/vm.rs | 30 +++++++++++++++++++----------- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 6d3d512a79..a81273e60c 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -700,9 +700,14 @@ fn builtin_import(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { ] ); let current_path = { - let mut source_pathbuf = PathBuf::from(&vm.current_frame().code.source_path); - source_pathbuf.pop(); - source_pathbuf + match vm.current_frame() { + Some(frame) => { + let mut source_pathbuf = PathBuf::from(&frame.code.source_path); + source_pathbuf.pop(); + source_pathbuf + } + None => PathBuf::new(), + } }; import_module(vm, current_path, &objstr::get_value(name)) diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index 2b2be02414..43161ae6b7 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -129,13 +129,15 @@ fn super_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { let py_obj = if let Some(obj) = py_obj { obj.clone() } else { - let frame = vm.current_frame(); + let frame = vm.current_frame().expect("no current frame for super()"); 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))); + return Err(vm.new_type_error(format!( + "super arguement {} was not supplied", + first_arg + ))); } } } else { diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 029588d130..05a115732b 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -91,19 +91,28 @@ impl VirtualMachine { result } - 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_frame(&self) -> Option> { + let frames = self.frames.borrow(); + if frames.is_empty() { + None + } else { + Some(Ref::map(self.frames.borrow(), |frames| { + objframe::get_value(frames.last().unwrap()) + })) + } } pub fn current_scope(&self) -> Ref { - let frame = self.current_frame(); + let frame = self + .current_frame() + .expect("called current_scope but no frames on the stack"); Ref::map(frame, |f| &f.scope) } + pub fn try_class(&self, module: &str, class: &str) -> PyResult { + self.get_attribute(self.import(module)?, class) + } + pub fn class(&self, module: &str, class: &str) -> PyObjectRef { let module = self .import(module) @@ -246,10 +255,9 @@ impl VirtualMachine { } 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())]), - None => Err(self.new_exception( + match self.get_attribute(self.builtins.clone(), "__import__") { + Ok(func) => self.invoke(func, vec![self.ctx.new_str(module.to_string())]), + Err(_) => Err(self.new_exception( self.ctx.exceptions.import_error.clone(), "__import__ not found".to_string(), )), From a830d8e131bf5d4f5debbc054814bbbbe4777535 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Sat, 23 Mar 2019 09:36:15 -0500 Subject: [PATCH 02/51] Add PyDocument and PyElement --- vm/src/function.rs | 1 + wasm/lib/src/browser_module.rs | 113 ++++++++++++++++++++++++++------- wasm/lib/src/convert.rs | 4 +- wasm/lib/src/vm_class.rs | 1 - 4 files changed, 94 insertions(+), 25 deletions(-) diff --git a/vm/src/function.rs b/vm/src/function.rs index 7159427e56..3093b8c5f5 100644 --- a/vm/src/function.rs +++ b/vm/src/function.rs @@ -304,6 +304,7 @@ pub enum OptionalArg { } impl OptionalArg { + #[inline] pub fn into_option(self) -> Option { match self { Present(value) => Some(value), diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index 7fc2a2476e..13f1404ab2 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -1,5 +1,3 @@ -use std::path::PathBuf; - use futures::Future; use js_sys::Promise; use num_traits::cast::ToPrimitive; @@ -7,11 +5,13 @@ 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::function::{OptionalArg, PyFuncArgs}; +use rustpython_vm::obj::{ + objint, + objstr::{self, PyStringRef}, +}; use rustpython_vm::pyobject::{ - AttributeProtocol, PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, + PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, }; use rustpython_vm::VirtualMachine; @@ -44,7 +44,7 @@ impl FetchResponseFormat { 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)?; + let promise_type = vm.try_class("browser", "Promise")?; let response_format = args.get_optional_kwarg_with_type("response_format", vm.ctx.str_type(), vm)?; @@ -165,7 +165,7 @@ pub struct PyPromise { impl PyValue for PyPromise { fn class(vm: &VirtualMachine) -> PyObjectRef { - vm.class(BROWSER_NAME, "Promise") + vm.class("browser", "Promise") } } @@ -182,15 +182,8 @@ pub fn get_promise_value(obj: &PyObjectRef) -> Promise { panic!("Inner error getting promise") } -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: &VirtualMachine, args: PyFuncArgs) -> PyResult { - let promise_type = import_promise_type(vm)?; + let promise_type = vm.try_class("browser", "Promise")?; arg_check!( vm, args, @@ -236,7 +229,7 @@ fn promise_then(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { } fn promise_catch(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - let promise_type = import_promise_type(vm)?; + let promise_type = vm.try_class("browser", "Promise")?; arg_check!( vm, args, @@ -270,6 +263,65 @@ fn promise_catch(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(PyPromise::new_obj(promise_type, ret_promise)) } +#[derive(Debug)] +struct PyDocument { + doc: web_sys::Document, +} +type PyDocumentRef = PyRef; + +impl PyValue for PyDocument { + fn class(vm: &VirtualMachine) -> PyObjectRef { + vm.class("browser", "Document") + } +} + +fn document_query(zelf: PyDocumentRef, query: PyStringRef, vm: &VirtualMachine) -> PyResult { + let elem = zelf + .doc + .query_selector(&query.value) + .map_err(|err| convert::js_py_typeerror(vm, err))?; + let elem = match elem { + Some(elem) => PyElement { elem }.into_ref(vm).into_object(), + None => vm.get_none(), + }; + Ok(elem) +} + +#[derive(Debug)] +struct PyElement { + elem: web_sys::Element, +} +type PyElementRef = PyRef; + +impl PyValue for PyElement { + fn class(vm: &VirtualMachine) -> PyObjectRef { + vm.class("browser", "Element") + } +} + +fn elem_get_attr( + zelf: PyElementRef, + attr: PyStringRef, + default: OptionalArg, + vm: &VirtualMachine, +) -> PyObjectRef { + match zelf.elem.get_attribute(&attr.value) { + Some(s) => vm.new_str(s), + None => default.into_option().unwrap_or_else(|| vm.get_none()), + } +} + +fn elem_set_attr( + zelf: PyElementRef, + attr: PyStringRef, + value: PyStringRef, + vm: &VirtualMachine, +) -> PyResult<()> { + zelf.elem + .set_attribute(&attr.value, &value.value) + .map_err(|err| convert::js_to_py(vm, err)) +} + fn browser_alert(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(message, Some(vm.ctx.str_type()))]); @@ -315,19 +367,36 @@ fn browser_prompt(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(result) } -const BROWSER_NAME: &str = "browser"; - 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) + "catch" => ctx.new_rustfunc(promise_catch), + }); + + let document_class = py_class!(ctx, "Document", ctx.object(), { + "query" => ctx.new_rustfunc(document_query), + }); + + let document = PyObject::new( + PyDocument { + doc: window().document().expect("Document missing from window"), + }, + document_class.clone(), + ); + + let element = py_class!(ctx, "Element", ctx.object(), { + "get_attr" => ctx.new_rustfunc(elem_get_attr), + "set_attr" => ctx.new_rustfunc(elem_set_attr), }); - py_module!(ctx, BROWSER_NAME, { + py_module!(ctx, "browser", { "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, + "Document" => document_class, + "document" => document, + "Element" => element, "alert" => ctx.new_rustfunc(browser_alert), "confirm" => ctx.new_rustfunc(browser_confirm), "prompt" => ctx.new_rustfunc(browser_prompt), @@ -337,5 +406,5 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { pub fn setup_browser_module(vm: &VirtualMachine) { vm.stdlib_inits .borrow_mut() - .insert(BROWSER_NAME.to_string(), Box::new(make_module)); + .insert("browser".to_string(), Box::new(make_module)); } diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index 122ff1763e..b7f9048e91 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -114,7 +114,7 @@ pub fn py_to_js(vm: &VirtualMachine, py_obj: PyObjectRef) -> JsValue { } } // the browser module might not be injected - if let Ok(promise_type) = browser_module::import_promise_type(vm) { + if let Ok(promise_type) = vm.try_class("browser", "Promise") { if objtype::isinstance(&py_obj, &promise_type) { return browser_module::get_promise_value(&py_obj).into(); } @@ -158,7 +158,7 @@ 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 - if let Ok(promise_type) = browser_module::import_promise_type(vm) { + if let Ok(promise_type) = vm.try_class("browser", "Promise") { return browser_module::PyPromise::new_obj(promise_type, promise.clone()); } } diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index 9d80854f73..77822147a8 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -49,7 +49,6 @@ impl StoredVirtualMachine { thread_local! { static STORED_VMS: RefCell>> = RefCell::default(); - static ACTIVE_VMS: RefCell> = RefCell::default(); } #[wasm_bindgen(js_name = vmStore)] From 2626a800ab486c74ae3a28e19a0ac1757e19fe64 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sat, 23 Mar 2019 11:04:40 +0200 Subject: [PATCH 03/51] Support frozenset --- tests/snippets/set.py | 85 ++++++++++++++++++ vm/src/obj/objset.rs | 204 ++++++++++++++++++++++++++++++------------ 2 files changed, 233 insertions(+), 56 deletions(-) diff --git a/tests/snippets/set.py b/tests/snippets/set.py index a7a20b0efb..9e49dff89e 100644 --- a/tests/snippets/set.py +++ b/tests/snippets/set.py @@ -144,3 +144,88 @@ def __hash__(self): assert a == set([1,2,4,5]) with assertRaises(TypeError): a ^= 1 + +# frozen set + +assert frozenset([1,2]) == frozenset([1,2]) +assert not frozenset([1,2,3]) == frozenset([1,2]) + +assert frozenset([1,2,3]) >= frozenset([1,2]) +assert frozenset([1,2]) >= frozenset([1,2]) +assert not frozenset([1,3]) >= frozenset([1,2]) + +assert frozenset([1,2,3]).issuperset(frozenset([1,2])) +assert frozenset([1,2]).issuperset(frozenset([1,2])) +assert not frozenset([1,3]).issuperset(frozenset([1,2])) + +assert frozenset([1,2,3]) > frozenset([1,2]) +assert not frozenset([1,2]) > frozenset([1,2]) +assert not frozenset([1,3]) > frozenset([1,2]) + +assert frozenset([1,2]) <= frozenset([1,2,3]) +assert frozenset([1,2]) <= frozenset([1,2]) +assert not frozenset([1,3]) <= frozenset([1,2]) + +assert frozenset([1,2]).issubset(frozenset([1,2,3])) +assert frozenset([1,2]).issubset(frozenset([1,2])) +assert not frozenset([1,3]).issubset(frozenset([1,2])) + +assert frozenset([1,2]) < frozenset([1,2,3]) +assert not frozenset([1,2]) < frozenset([1,2]) +assert not frozenset([1,3]) < frozenset([1,2]) + +a = frozenset([1, 2, 3]) +assert len(a) == 3 +b = a.copy() +assert b == a + +assert frozenset([1,2,3]).union(frozenset([4,5])) == frozenset([1,2,3,4,5]) +assert frozenset([1,2,3]).union(frozenset([1,2,3,4,5])) == frozenset([1,2,3,4,5]) + +assert frozenset([1,2,3]) | frozenset([4,5]) == frozenset([1,2,3,4,5]) +assert frozenset([1,2,3]) | frozenset([1,2,3,4,5]) == frozenset([1,2,3,4,5]) + +assert frozenset([1,2,3]).intersection(frozenset([1,2])) == frozenset([1,2]) +assert frozenset([1,2,3]).intersection(frozenset([5,6])) == frozenset([]) + +assert frozenset([1,2,3]) & frozenset([4,5]) == frozenset([]) +assert frozenset([1,2,3]) & frozenset([1,2,3,4,5]) == frozenset([1,2,3]) + +assert frozenset([1,2,3]).difference(frozenset([1,2])) == frozenset([3]) +assert frozenset([1,2,3]).difference(frozenset([5,6])) == frozenset([1,2,3]) + +assert frozenset([1,2,3]) - frozenset([4,5]) == frozenset([1,2,3]) +assert frozenset([1,2,3]) - frozenset([1,2,3,4,5]) == frozenset([]) + +assert frozenset([1,2,3]).symmetric_difference(frozenset([1,2])) == frozenset([3]) +assert frozenset([1,2,3]).symmetric_difference(frozenset([5,6])) == frozenset([1,2,3,5,6]) + +assert frozenset([1,2,3]) ^ frozenset([4,5]) == frozenset([1,2,3,4,5]) +assert frozenset([1,2,3]) ^ frozenset([1,2,3,4,5]) == frozenset([4,5]) + +assert_raises(TypeError, lambda: frozenset([[]])) + +# set and frozen set +assert frozenset([1,2,3]).union(set([4,5])) == frozenset([1,2,3,4,5]) +assert set([1,2,3]).union(frozenset([4,5])) == set([1,2,3,4,5]) + +assert frozenset([1,2,3]) | set([4,5]) == frozenset([1,2,3,4,5]) +assert set([1,2,3]) | frozenset([4,5]) == set([1,2,3,4,5]) + +assert frozenset([1,2,3]).intersection(set([5,6])) == frozenset([]) +assert set([1,2,3]).intersection(frozenset([5,6])) == set([]) + +assert frozenset([1,2,3]) & set([1,2,3,4,5]) == frozenset([1,2,3]) +assert set([1,2,3]) & frozenset([1,2,3,4,5]) == set([1,2,3]) + +assert frozenset([1,2,3]).difference(set([5,6])) == frozenset([1,2,3]) +assert set([1,2,3]).difference(frozenset([5,6])) == set([1,2,3]) + +assert frozenset([1,2,3]) - set([4,5]) == frozenset([1,2,3]) +assert set([1,2,3]) - frozenset([4,5]) == frozenset([1,2,3]) + +assert frozenset([1,2,3]).symmetric_difference(set([1,2])) == frozenset([3]) +assert set([1,2,3]).symmetric_difference(frozenset([1,2])) == set([3]) + +assert frozenset([1,2,3]) ^ set([4,5]) == frozenset([1,2,3,4,5]) +assert set([1,2,3]) ^ frozenset([4,5]) == set([1,2,3,4,5]) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 33a4455318..8bb338d407 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -9,13 +9,14 @@ use std::hash::{Hash, Hasher}; use crate::function::{OptionalArg, PyFuncArgs}; use crate::pyobject::{ - PyContext, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, + PyContext, PyIteratorValue, PyObject, 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::PyClassRef; #[derive(Default)] @@ -24,6 +25,12 @@ pub struct PySet { } pub type PySetRef = PyRef; +#[derive(Default)] +pub struct PyFrozenSet { + elements: RefCell>, +} +pub type PyFrozenSetRef = PyRef; + impl fmt::Debug for PySet { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // TODO: implement more detailed, non-recursive Debug formatter @@ -31,14 +38,69 @@ impl fmt::Debug for PySet { } } +impl fmt::Debug for PyFrozenSet { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // TODO: implement more detailed, non-recursive Debug formatter + f.write_str("frozenset") + } +} + impl PyValue for PySet { fn class(vm: &VirtualMachine) -> PyClassRef { vm.ctx.set_type() } } +impl PyValue for PyFrozenSet { + fn class(vm: &VirtualMachine) -> PyClassRef { + vm.ctx.frozenset_type() + } +} + pub fn get_elements(obj: &PyObjectRef) -> HashMap { - obj.payload::().unwrap().elements.borrow().clone() + if let Some(set) = obj.payload::() { + return set.elements.borrow().clone(); + } else if let Some(frozenset) = obj.payload::() { + return frozenset.elements.borrow().clone(); + } + panic!("Not frozenset or set"); +} + +macro_rules! validate_set_or_frozenset { + ( $vm:ident, $obj:expr ) => { + if !(objtype::issubclass(&$obj, &$vm.ctx.set_type()) + || objtype::issubclass(&$obj, &$vm.ctx.frozenset_type())) + { + return Err($vm.new_type_error(format!( + "{} is not a subtype of set or frozenset a", + $obj.typ() + ))); + } + }; +} + +fn create_set( + vm: &VirtualMachine, + elements: HashMap, + cls: PyClassRef, +) -> PyResult { + if objtype::issubclass(&cls, &vm.ctx.set_type()) { + Ok(PyObject::new( + PySet { + elements: RefCell::new(elements), + }, + PySet::class(vm).into_object(), + )) + } else if objtype::issubclass(&cls, &vm.ctx.frozenset_type()) { + Ok(PyObject::new( + PyFrozenSet { + elements: RefCell::new(elements), + }, + PyFrozenSet::class(vm).into_object(), + )) + } else { + Err(vm.new_type_error(format!("{} is not a subtype of set or frozenset", cls))) + } } fn perform_action_with_hash( @@ -152,11 +214,9 @@ fn set_clear(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { } /* Create a new object of sub-type of set */ -fn set_new( - cls: PyClassRef, - iterable: OptionalArg, - vm: &VirtualMachine, -) -> PyResult { +fn set_new(cls: PyClassRef, iterable: OptionalArg, vm: &VirtualMachine) -> PyResult { + validate_set_or_frozenset!(vm, cls); + let elements: HashMap = match iterable { OptionalArg::Missing => HashMap::new(), OptionalArg::Present(iterable) => { @@ -169,25 +229,22 @@ fn set_new( } }; - PySet { - elements: RefCell::new(elements), - } - .into_ref_with_type(vm, cls) + create_set(vm, elements, cls) } 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()))]); + arg_check!(vm, args, required = [(s, None)]); + validate_set_or_frozenset!(vm, s.type_pyref()); let elements = get_elements(s); Ok(vm.context().new_int(elements.len())) } -fn set_copy(obj: PySetRef, _vm: &VirtualMachine) -> PySet { +fn set_copy(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { trace!("set.copy called with: {:?}", obj); - let elements = obj.elements.borrow().clone(); - PySet { - elements: RefCell::new(elements), - } + validate_set_or_frozenset!(vm, obj.type_pyref()); + let elements = get_elements(&obj).clone(); + create_set(vm, elements, obj.type_pyref()) } fn set_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -211,11 +268,8 @@ fn set_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn set_contains(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(set, Some(vm.ctx.set_type())), (needle, None)] - ); + arg_check!(vm, args, required = [(set, None), (needle, None)]); + validate_set_or_frozenset!(vm, &set.type_pyref()); for element in get_elements(set).iter() { match vm._eq(needle.clone(), element.1.clone()) { Ok(value) => { @@ -281,14 +335,10 @@ fn set_compare_inner( size_func: &Fn(usize, usize) -> bool, swap: bool, ) -> PyResult { - arg_check!( - vm, - args, - required = [ - (zelf, Some(vm.ctx.set_type())), - (other, Some(vm.ctx.set_type())) - ] - ); + arg_check!(vm, args, required = [(zelf, None), (other, None)]); + + validate_set_or_frozenset!(vm, zelf.type_pyref()); + validate_set_or_frozenset!(vm, other.type_pyref()); let get_zelf = |swap: bool| -> &PyObjectRef { if swap { @@ -323,47 +373,48 @@ fn set_compare_inner( Ok(vm.new_bool(true)) } -fn set_union(zelf: PySetRef, other: PySetRef, _vm: &VirtualMachine) -> PySet { - let mut elements = zelf.elements.borrow().clone(); - elements.extend(other.elements.borrow().clone()); +fn set_union(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { + validate_set_or_frozenset!(vm, zelf.type_pyref()); + validate_set_or_frozenset!(vm, other.type_pyref()); - PySet { - elements: RefCell::new(elements), - } + let mut elements = get_elements(&zelf).clone(); + elements.extend(get_elements(&other).clone()); + + create_set(vm, elements, zelf.type_pyref()) } -fn set_intersection(zelf: PySetRef, other: PySetRef, vm: &VirtualMachine) -> PyResult { +fn set_intersection(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { set_combine_inner(zelf, other, vm, SetCombineOperation::Intersection) } -fn set_difference(zelf: PySetRef, other: PySetRef, vm: &VirtualMachine) -> PyResult { +fn set_difference(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { set_combine_inner(zelf, other, vm, SetCombineOperation::Difference) } fn set_symmetric_difference( - zelf: PySetRef, - other: PySetRef, + zelf: PyObjectRef, + other: PyObjectRef, vm: &VirtualMachine, -) -> PyResult { +) -> PyResult { + validate_set_or_frozenset!(vm, zelf.type_pyref()); + validate_set_or_frozenset!(vm, other.type_pyref()); let mut elements = HashMap::new(); - for element in zelf.elements.borrow().iter() { - let value = vm.call_method(other.as_object(), "__contains__", vec![element.1.clone()])?; + for element in get_elements(&zelf).iter() { + let value = vm.call_method(&other, "__contains__", vec![element.1.clone()])?; if !objbool::get_value(&value) { elements.insert(element.0.clone(), element.1.clone()); } } - for element in other.elements.borrow().iter() { - let value = vm.call_method(zelf.as_object(), "__contains__", vec![element.1.clone()])?; + for element in get_elements(&other).iter() { + let value = vm.call_method(&zelf, "__contains__", vec![element.1.clone()])?; if !objbool::get_value(&value) { elements.insert(element.0.clone(), element.1.clone()); } } - Ok(PySet { - elements: RefCell::new(elements), - }) + create_set(vm, elements, zelf.type_pyref()) } enum SetCombineOperation { @@ -372,15 +423,17 @@ enum SetCombineOperation { } fn set_combine_inner( - zelf: PySetRef, - other: PySetRef, + zelf: PyObjectRef, + other: PyObjectRef, vm: &VirtualMachine, op: SetCombineOperation, -) -> PyResult { +) -> PyResult { + validate_set_or_frozenset!(vm, zelf.type_pyref()); + validate_set_or_frozenset!(vm, other.type_pyref()); let mut elements = HashMap::new(); - for element in zelf.elements.borrow().iter() { - let value = vm.call_method(other.as_object(), "__contains__", vec![element.1.clone()])?; + for element in get_elements(&zelf).iter() { + let value = vm.call_method(&other, "__contains__", vec![element.1.clone()])?; let should_add = match op { SetCombineOperation::Intersection => objbool::get_value(&value), SetCombineOperation::Difference => !objbool::get_value(&value), @@ -390,9 +443,7 @@ fn set_combine_inner( } } - Ok(PySet { - elements: RefCell::new(elements), - }) + create_set(vm, elements, zelf.type_pyref()) } fn set_pop(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -614,6 +665,46 @@ pub fn init(context: &PyContext) { frozenset(iterable) -> frozenset object\n\n\ Build an immutable unordered collection of unique elements."; + context.set_attr(frozenset_type, "__new__", context.new_rustfunc(set_new)); + context.set_attr(frozenset_type, "__eq__", context.new_rustfunc(set_eq)); + context.set_attr(frozenset_type, "__ge__", context.new_rustfunc(set_ge)); + context.set_attr(frozenset_type, "__gt__", context.new_rustfunc(set_gt)); + context.set_attr(frozenset_type, "__le__", context.new_rustfunc(set_le)); + context.set_attr(frozenset_type, "__lt__", context.new_rustfunc(set_lt)); + context.set_attr(frozenset_type, "issubset", context.new_rustfunc(set_le)); + context.set_attr(frozenset_type, "issuperset", context.new_rustfunc(set_ge)); + context.set_attr(frozenset_type, "union", context.new_rustfunc(set_union)); + context.set_attr(frozenset_type, "__or__", context.new_rustfunc(set_union)); + context.set_attr( + frozenset_type, + "intersection", + context.new_rustfunc(set_intersection), + ); + context.set_attr( + frozenset_type, + "__and__", + context.new_rustfunc(set_intersection), + ); + context.set_attr( + frozenset_type, + "difference", + context.new_rustfunc(set_difference), + ); + context.set_attr( + frozenset_type, + "__sub__", + context.new_rustfunc(set_difference), + ); + context.set_attr( + frozenset_type, + "symmetric_difference", + context.new_rustfunc(set_symmetric_difference), + ); + context.set_attr( + frozenset_type, + "__xor__", + context.new_rustfunc(set_symmetric_difference), + ); context.set_attr( frozenset_type, "__contains__", @@ -630,4 +721,5 @@ pub fn init(context: &PyContext) { "__repr__", context.new_rustfunc(frozenset_repr), ); + context.set_attr(frozenset_type, "copy", context.new_rustfunc(set_copy)); } From dc05d5f94b43985245e5af0a505d0d0ce4e2a350 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sat, 23 Mar 2019 11:06:18 +0200 Subject: [PATCH 04/51] Remove Refcell around PyFrozenSet elements --- vm/src/obj/objset.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 8bb338d407..1d69d3bdef 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -27,7 +27,7 @@ pub type PySetRef = PyRef; #[derive(Default)] pub struct PyFrozenSet { - elements: RefCell>, + elements: HashMap, } pub type PyFrozenSetRef = PyRef; @@ -61,7 +61,7 @@ pub fn get_elements(obj: &PyObjectRef) -> HashMap { if let Some(set) = obj.payload::() { return set.elements.borrow().clone(); } else if let Some(frozenset) = obj.payload::() { - return frozenset.elements.borrow().clone(); + return frozenset.elements.clone(); } panic!("Not frozenset or set"); } @@ -93,9 +93,7 @@ fn create_set( )) } else if objtype::issubclass(&cls, &vm.ctx.frozenset_type()) { Ok(PyObject::new( - PyFrozenSet { - elements: RefCell::new(elements), - }, + PyFrozenSet { elements: elements }, PyFrozenSet::class(vm).into_object(), )) } else { From 0aabe88c5dc1aceea51a579e3bb9c5551bd17949 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Sat, 23 Mar 2019 14:04:14 -0500 Subject: [PATCH 05/51] Put `browser` class methods into `impl` blocks --- vm/src/vm.rs | 7 +- wasm/lib/src/browser_module.rs | 235 +++++++++++++++------------------ wasm/lib/src/convert.rs | 14 +- 3 files changed, 121 insertions(+), 135 deletions(-) diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 76b06ddfcd..9cf4b98e09 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -111,7 +111,10 @@ impl VirtualMachine { } pub fn try_class(&self, module: &str, class: &str) -> PyResult { - let class = self.get_attribute(self.import(module)?, class)?.downcast().expect("not a class"); + let class = self + .get_attribute(self.import(module)?, class)? + .downcast() + .expect("not a class"); Ok(class) } @@ -122,7 +125,7 @@ impl VirtualMachine { let class = self .get_attribute(module.clone(), class) .unwrap_or_else(|_| panic!("module {} has no class {}", module, class)); - class.downcast().expect("not a class"); + class.downcast().expect("not a class") } /// Create a new python string object. diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index 0be52c43d6..ce2d3d6c14 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -7,6 +7,7 @@ use wasm_bindgen_futures::{future_to_promise, JsFuture}; use rustpython_vm::function::{OptionalArg, PyFuncArgs}; use rustpython_vm::obj::{ + objfunction::PyFunction, objint, objstr::{self, PyStringRef}, objtype::PyClassRef, @@ -45,8 +46,6 @@ impl FetchResponseFormat { fn browser_fetch(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(url, Some(vm.ctx.str_type()))]); - let promise_type = vm.try_class("browser", "Promise")?; - 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)?; @@ -102,7 +101,9 @@ fn browser_fetch(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { }) .and_then(JsFuture::from); - Ok(PyPromise::new_obj(promise_type, future_to_promise(future))) + Ok(PyPromise::new(future_to_promise(future)) + .into_ref(vm) + .into_object()) } fn browser_request_animation_frame(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -163,6 +164,7 @@ fn browser_cancel_animation_frame(vm: &VirtualMachine, args: PyFuncArgs) -> PyRe pub struct PyPromise { value: Promise, } +pub type PyPromiseRef = PyRef; impl PyValue for PyPromise { fn class(vm: &VirtualMachine) -> PyClassRef { @@ -171,156 +173,135 @@ impl PyValue for PyPromise { } impl PyPromise { - pub fn new_obj(promise_type: PyClassRef, value: Promise) -> PyObjectRef { - PyObject::new(PyPromise { value }, promise_type.into_object()) + pub fn new(value: Promise) -> PyPromise { + PyPromise { value } } -} - -pub fn get_promise_value(obj: &PyObjectRef) -> Promise { - if let Some(promise) = obj.payload::() { - return promise.value.clone(); + pub fn value(&self) -> Promise { + self.value.clone() } - panic!("Inner error getting promise") -} -fn promise_then(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - let promise_type = vm.try_class("browser", "Promise")?; - 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); - - let promise = get_promise_value(zelf); + fn then( + zelf: PyPromiseRef, + on_fulfill: PyRef, + on_reject: OptionalArg>, + vm: &VirtualMachine, + ) -> PyResult { + let acc_vm = AccessibleVM::from(vm); - let ret_future = JsFuture::from(promise).then(move |res| { - 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); - 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); + let ret_future = JsFuture::from(zelf.value.clone()).then(move |res| { + 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); + vm.invoke(on_fulfill.into_object(), PyFuncArgs::new(vec![val], vec![])) } - } - }; - convert::pyresult_to_jsresult(vm, ret) - }); - - let ret_promise = future_to_promise(ret_future); - - Ok(PyPromise::new_obj(promise_type, ret_promise)) -} - -fn promise_catch(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - let promise_type = vm.try_class("browser", "Promise")?; - arg_check!( - vm, - args, - required = [ - (zelf, Some(promise_type.clone())), - (on_reject, Some(vm.ctx.function_type())) - ] - ); + Err(err) => { + if let OptionalArg::Present(on_reject) = on_reject { + let err = convert::js_to_py(vm, err); + vm.invoke(on_reject.into_object(), PyFuncArgs::new(vec![err], vec![])) + } else { + return Err(err); + } + } + }; + convert::pyresult_to_jsresult(vm, ret) + }); - let on_reject = on_reject.clone(); + let ret_promise = future_to_promise(ret_future); - let acc_vm = AccessibleVM::from(vm); + Ok(PyPromise::new(ret_promise).into_ref(vm)) + } - let promise = get_promise_value(zelf); + fn catch( + zelf: PyPromiseRef, + on_reject: PyRef, + vm: &VirtualMachine, + ) -> PyResult { + let acc_vm = AccessibleVM::from(vm); - let ret_future = JsFuture::from(promise).then(move |res| match res { - Ok(val) => Ok(val), - Err(err) => { - 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) - } - }); + let ret_future = JsFuture::from(zelf.value.clone()).then(move |res| match res { + Ok(val) => Ok(val), + Err(err) => { + 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.into_object(), PyFuncArgs::new(vec![err], vec![])); + convert::pyresult_to_jsresult(vm, res) + } + }); - let ret_promise = future_to_promise(ret_future); + let ret_promise = future_to_promise(ret_future); - Ok(PyPromise::new_obj(promise_type, ret_promise)) + Ok(PyPromise::new(ret_promise).into_ref(vm)) + } } #[derive(Debug)] -struct PyDocument { +struct Document { doc: web_sys::Document, } -type PyDocumentRef = PyRef; +type DocumentRef = PyRef; -impl PyValue for PyDocument { - fn class(vm: &VirtualMachine) -> PyObjectRef { +impl PyValue for Document { + fn class(vm: &VirtualMachine) -> PyClassRef { vm.class("browser", "Document") } } -fn document_query(zelf: PyDocumentRef, query: PyStringRef, vm: &VirtualMachine) -> PyResult { - let elem = zelf - .doc - .query_selector(&query.value) - .map_err(|err| convert::js_py_typeerror(vm, err))?; - let elem = match elem { - Some(elem) => PyElement { elem }.into_ref(vm).into_object(), - None => vm.get_none(), - }; - Ok(elem) +impl Document { + fn query(zelf: DocumentRef, query: PyStringRef, vm: &VirtualMachine) -> PyResult { + let elem = zelf + .doc + .query_selector(&query.value) + .map_err(|err| convert::js_py_typeerror(vm, err))?; + let elem = match elem { + Some(elem) => Element { elem }.into_ref(vm).into_object(), + None => vm.get_none(), + }; + Ok(elem) + } } #[derive(Debug)] -struct PyElement { +struct Element { elem: web_sys::Element, } -type PyElementRef = PyRef; +type ElementRef = PyRef; -impl PyValue for PyElement { - fn class(vm: &VirtualMachine) -> PyObjectRef { +impl PyValue for Element { + fn class(vm: &VirtualMachine) -> PyClassRef { vm.class("browser", "Element") } } -fn elem_get_attr( - zelf: PyElementRef, - attr: PyStringRef, - default: OptionalArg, - vm: &VirtualMachine, -) -> PyObjectRef { - match zelf.elem.get_attribute(&attr.value) { - Some(s) => vm.new_str(s), - None => default.into_option().unwrap_or_else(|| vm.get_none()), +impl Element { + fn get_attr( + zelf: ElementRef, + attr: PyStringRef, + default: OptionalArg, + vm: &VirtualMachine, + ) -> PyObjectRef { + match zelf.elem.get_attribute(&attr.value) { + Some(s) => vm.new_str(s), + None => default.into_option().unwrap_or_else(|| vm.get_none()), + } } -} -fn elem_set_attr( - zelf: PyElementRef, - attr: PyStringRef, - value: PyStringRef, - vm: &VirtualMachine, -) -> PyResult<()> { - zelf.elem - .set_attribute(&attr.value, &value.value) - .map_err(|err| convert::js_to_py(vm, err)) + fn set_attr( + zelf: ElementRef, + attr: PyStringRef, + value: PyStringRef, + vm: &VirtualMachine, + ) -> PyResult<()> { + zelf.elem + .set_attribute(&attr.value, &value.value) + .map_err(|err| convert::js_to_py(vm, err)) + } } fn browser_alert(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -370,24 +351,24 @@ fn browser_prompt(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { 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), + "then" => ctx.new_rustfunc(PyPromise::then), + "catch" => ctx.new_rustfunc(PyPromise::catch), }); let document_class = py_class!(ctx, "Document", ctx.object(), { - "query" => ctx.new_rustfunc(document_query), + "query" => ctx.new_rustfunc(Document::query), }); let document = PyObject::new( - PyDocument { + Document { doc: window().document().expect("Document missing from window"), }, - document_class.clone(), + document_class.clone().into_object(), ); let element = py_class!(ctx, "Element", ctx.object(), { - "get_attr" => ctx.new_rustfunc(elem_get_attr), - "set_attr" => ctx.new_rustfunc(elem_set_attr), + "get_attr" => ctx.new_rustfunc(Element::get_attr), + "set_attr" => ctx.new_rustfunc(Element::set_attr), }); py_module!(ctx, "browser", { diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index 9c9e9f902e..ecdb4b1102 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -4,7 +4,7 @@ use wasm_bindgen::{closure::Closure, prelude::*, JsCast}; use rustpython_vm::function::PyFuncArgs; use rustpython_vm::obj::{objbytes, objint, objsequence, objtype}; -use rustpython_vm::pyobject::{DictProtocol, PyObjectRef, PyResult}; +use rustpython_vm::pyobject::{DictProtocol, PyObjectRef, PyResult, PyValue}; use rustpython_vm::VirtualMachine; use crate::browser_module; @@ -115,9 +115,9 @@ pub fn py_to_js(vm: &VirtualMachine, py_obj: PyObjectRef) -> JsValue { } } // the browser module might not be injected - if let Ok(promise_type) = vm.try_class("browser", "Promise") { - if objtype::isinstance(&py_obj, &promise_type) { - return browser_module::get_promise_value(&py_obj).into(); + if vm.try_class("browser", "Promise").is_ok() { + if let Some(py_prom) = py_obj.payload::() { + return py_prom.value().into(); } } @@ -159,8 +159,10 @@ 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 - if let Ok(promise_type) = vm.try_class("browser", "Promise") { - return browser_module::PyPromise::new_obj(promise_type, promise.clone()); + if vm.try_class("browser", "Promise").is_ok() { + return browser_module::PyPromise::new(promise.clone()) + .into_ref(vm) + .into_object(); } } if Array::is_array(&js_val) { From 590b659548a090d08bb51bca46a4457cc560e9a8 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sat, 23 Mar 2019 23:24:57 +0200 Subject: [PATCH 06/51] Change validate_set_or_frozenset to a function --- vm/src/obj/objset.rs | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 1d69d3bdef..300dc9bca9 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -66,17 +66,13 @@ pub fn get_elements(obj: &PyObjectRef) -> HashMap { panic!("Not frozenset or set"); } -macro_rules! validate_set_or_frozenset { - ( $vm:ident, $obj:expr ) => { - if !(objtype::issubclass(&$obj, &$vm.ctx.set_type()) - || objtype::issubclass(&$obj, &$vm.ctx.frozenset_type())) - { - return Err($vm.new_type_error(format!( - "{} is not a subtype of set or frozenset a", - $obj.typ() - ))); - } - }; +fn validate_set_or_frozenset(vm: &VirtualMachine, cls: PyClassRef) -> PyResult<()> { + if !(objtype::issubclass(&cls, &vm.ctx.set_type()) + || objtype::issubclass(&cls, &vm.ctx.frozenset_type())) + { + return Err(vm.new_type_error(format!("{} is not a subtype of set or frozenset", cls))); + } + Ok(()) } fn create_set( @@ -213,7 +209,7 @@ fn set_clear(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { /* Create a new object of sub-type of set */ fn set_new(cls: PyClassRef, iterable: OptionalArg, vm: &VirtualMachine) -> PyResult { - validate_set_or_frozenset!(vm, cls); + validate_set_or_frozenset(vm, cls.clone())?; let elements: HashMap = match iterable { OptionalArg::Missing => HashMap::new(), @@ -227,20 +223,20 @@ fn set_new(cls: PyClassRef, iterable: OptionalArg, vm: &VirtualMach } }; - create_set(vm, elements, cls) + create_set(vm, elements, cls.clone()) } fn set_len(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("set.len called with: {:?}", args); arg_check!(vm, args, required = [(s, None)]); - validate_set_or_frozenset!(vm, s.type_pyref()); + validate_set_or_frozenset(vm, s.type_pyref())?; let elements = get_elements(s); Ok(vm.context().new_int(elements.len())) } fn set_copy(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { trace!("set.copy called with: {:?}", obj); - validate_set_or_frozenset!(vm, obj.type_pyref()); + validate_set_or_frozenset(vm, obj.type_pyref())?; let elements = get_elements(&obj).clone(); create_set(vm, elements, obj.type_pyref()) } @@ -267,7 +263,7 @@ fn set_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn set_contains(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(set, None), (needle, None)]); - validate_set_or_frozenset!(vm, &set.type_pyref()); + validate_set_or_frozenset(vm, set.type_pyref())?; for element in get_elements(set).iter() { match vm._eq(needle.clone(), element.1.clone()) { Ok(value) => { @@ -335,8 +331,8 @@ fn set_compare_inner( ) -> PyResult { arg_check!(vm, args, required = [(zelf, None), (other, None)]); - validate_set_or_frozenset!(vm, zelf.type_pyref()); - validate_set_or_frozenset!(vm, other.type_pyref()); + validate_set_or_frozenset(vm, zelf.type_pyref())?; + validate_set_or_frozenset(vm, other.type_pyref())?; let get_zelf = |swap: bool| -> &PyObjectRef { if swap { @@ -372,8 +368,8 @@ fn set_compare_inner( } fn set_union(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - validate_set_or_frozenset!(vm, zelf.type_pyref()); - validate_set_or_frozenset!(vm, other.type_pyref()); + validate_set_or_frozenset(vm, zelf.type_pyref())?; + validate_set_or_frozenset(vm, other.type_pyref())?; let mut elements = get_elements(&zelf).clone(); elements.extend(get_elements(&other).clone()); @@ -394,8 +390,8 @@ fn set_symmetric_difference( other: PyObjectRef, vm: &VirtualMachine, ) -> PyResult { - validate_set_or_frozenset!(vm, zelf.type_pyref()); - validate_set_or_frozenset!(vm, other.type_pyref()); + validate_set_or_frozenset(vm, zelf.type_pyref())?; + validate_set_or_frozenset(vm, other.type_pyref())?; let mut elements = HashMap::new(); for element in get_elements(&zelf).iter() { @@ -426,8 +422,8 @@ fn set_combine_inner( vm: &VirtualMachine, op: SetCombineOperation, ) -> PyResult { - validate_set_or_frozenset!(vm, zelf.type_pyref()); - validate_set_or_frozenset!(vm, other.type_pyref()); + validate_set_or_frozenset(vm, zelf.type_pyref())?; + validate_set_or_frozenset(vm, other.type_pyref())?; let mut elements = HashMap::new(); for element in get_elements(&zelf).iter() { From 6fa059fd6c00eb4095c834a39dcfe43965b9a1b6 Mon Sep 17 00:00:00 2001 From: ben Date: Sat, 23 Mar 2019 18:52:53 +1300 Subject: [PATCH 07/51] Make PyObject.typ a PyClassRef --- src/main.rs | 22 +++------- vm/src/builtins.rs | 56 +++++++++++-------------- vm/src/compile.rs | 14 +++---- vm/src/eval.rs | 7 +--- vm/src/function.rs | 7 ++++ vm/src/import.rs | 2 +- vm/src/obj/objcode.rs | 4 +- vm/src/obj/objfilter.rs | 33 ++++++++------- vm/src/obj/objfloat.rs | 6 +-- vm/src/obj/objmemory.rs | 21 +++++----- vm/src/obj/objobject.rs | 4 +- vm/src/obj/objproperty.rs | 4 +- vm/src/obj/objsequence.rs | 4 +- vm/src/obj/objslice.rs | 6 ++- vm/src/obj/objtype.rs | 31 ++++---------- vm/src/obj/objzip.rs | 17 ++++---- vm/src/pyobject.rs | 86 +++++++++++++++------------------------ vm/src/stdlib/re.rs | 49 +++++++--------------- vm/src/vm.rs | 5 ++- wasm/lib/src/vm_class.rs | 3 +- 20 files changed, 154 insertions(+), 227 deletions(-) diff --git a/src/main.rs b/src/main.rs index a0683ec3ed..70adf10080 100644 --- a/src/main.rs +++ b/src/main.rs @@ -65,16 +65,11 @@ fn main() { } fn _run_string(vm: &VirtualMachine, source: &str, source_path: String) -> PyResult { - let code_obj = compile::compile( - source, - &compile::Mode::Exec, - source_path, - vm.ctx.code_type(), - ) - .map_err(|err| { - let syntax_error = vm.context().exceptions.syntax_error.clone(); - vm.new_exception(syntax_error, err.to_string()) - })?; + let code_obj = + compile::compile(vm, source, &compile::Mode::Exec, source_path).map_err(|err| { + let syntax_error = vm.context().exceptions.syntax_error.clone(); + vm.new_exception(syntax_error, err.to_string()) + })?; // trace!("Code object: {:?}", code_obj.borrow()); let vars = vm.ctx.new_scope(); // Keep track of local variables vm.run_code_obj(code_obj, vars) @@ -115,12 +110,7 @@ fn run_script(vm: &VirtualMachine, script_file: &str) -> PyResult { } fn shell_exec(vm: &VirtualMachine, source: &str, scope: Scope) -> Result<(), CompileError> { - match compile::compile( - source, - &compile::Mode::Single, - "".to_string(), - vm.ctx.code_type(), - ) { + match compile::compile(vm, source, &compile::Mode::Single, "".to_string()) { Ok(code) => { if let Err(err) = vm.run_code_obj(code, scope) { print_exception(vm, &err); diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 5ac00561c6..c81ee79e92 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -24,6 +24,7 @@ use crate::pyobject::{ }; use crate::vm::VirtualMachine; +use crate::obj::objcode::PyCodeRef; #[cfg(not(target_arch = "wasm32"))] use crate::stdlib::io::io_open; @@ -112,22 +113,17 @@ fn builtin_chr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_str(txt)) } -fn builtin_compile(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (source, None), - (filename, Some(vm.ctx.str_type())), - (mode, Some(vm.ctx.str_type())) - ] - ); - let source = objstr::get_value(source); +fn builtin_compile( + source: PyStringRef, + filename: PyStringRef, + mode: PyStringRef, + vm: &VirtualMachine, +) -> PyResult { // TODO: fix this newline bug: - let source = format!("{}\n", source); + let source = format!("{}\n", &source.value); let mode = { - let mode = objstr::get_value(mode); + let mode = &mode.value; if mode == "exec" { compile::Mode::Exec } else if mode == "eval" { @@ -141,9 +137,7 @@ fn builtin_compile(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { } }; - let filename = objstr::get_value(filename); - - compile::compile(&source, &mode, filename, vm.ctx.code_type()).map_err(|err| { + compile::compile(vm, &source, &mode, filename.value.to_string()).map_err(|err| { let syntax_error = vm.context().exceptions.syntax_error.clone(); vm.new_exception(syntax_error, err.to_string()) }) @@ -190,25 +184,23 @@ fn builtin_eval(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { let scope = make_scope(vm, globals, locals)?; // Determine code object: - let code_obj = if objtype::isinstance(source, &vm.ctx.code_type()) { - source.clone() + let code_obj = if let Ok(code_obj) = PyCodeRef::try_from_object(vm, source.clone()) { + code_obj } 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: let source = format!("{}\n", source); - compile::compile(&source, &mode, "".to_string(), vm.ctx.code_type()).map_err( - |err| { - let syntax_error = vm.context().exceptions.syntax_error.clone(); - vm.new_exception(syntax_error, err.to_string()) - }, - )? + compile::compile(vm, &source, &mode, "".to_string()).map_err(|err| { + let syntax_error = vm.context().exceptions.syntax_error.clone(); + vm.new_exception(syntax_error, err.to_string()) + })? } else { return Err(vm.new_type_error("code argument must be str or code object".to_string())); }; // Run the source: - vm.run_code_obj(code_obj.clone(), scope) + vm.run_code_obj(code_obj, scope) } /// Implements `exec` @@ -229,14 +221,12 @@ fn builtin_exec(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { let source = objstr::get_value(source); // TODO: fix this newline bug: let source = format!("{}\n", source); - compile::compile(&source, &mode, "".to_string(), vm.ctx.code_type()).map_err( - |err| { - let syntax_error = vm.context().exceptions.syntax_error.clone(); - vm.new_exception(syntax_error, err.to_string()) - }, - )? - } else if objtype::isinstance(source, &vm.ctx.code_type()) { - source.clone() + compile::compile(vm, &source, &mode, "".to_string()).map_err(|err| { + let syntax_error = vm.context().exceptions.syntax_error.clone(); + vm.new_exception(syntax_error, err.to_string()) + })? + } else if let Ok(code_obj) = PyCodeRef::try_from_object(vm, source.clone()) { + code_obj } else { return Err(vm.new_type_error("source argument must be str or code object".to_string())); }; diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 46101cdf46..00bafe7278 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -8,8 +8,9 @@ use crate::bytecode::{self, CallType, CodeObject, Instruction, Varargs}; use crate::error::CompileError; use crate::obj::objcode; -use crate::obj::objtype::PyClassRef; -use crate::pyobject::{PyObject, PyObjectRef}; +use crate::obj::objcode::PyCodeRef; +use crate::pyobject::PyValue; +use crate::VirtualMachine; use num_complex::Complex64; use rustpython_parser::{ast, parser}; @@ -24,11 +25,11 @@ struct Compiler { /// Compile a given sourcecode into a bytecode object. pub fn compile( + vm: &VirtualMachine, source: &str, mode: &Mode, source_path: String, - code_type: PyClassRef, -) -> Result { +) -> Result { let mut compiler = Compiler::new(); compiler.source_path = Some(source_path); compiler.push_new_code_object("".to_string()); @@ -50,10 +51,7 @@ pub fn compile( let code = compiler.pop_code_object(); trace!("Compilation completed: {:?}", code); - Ok(PyObject::new( - objcode::PyCode::new(code), - code_type.into_object(), - )) + Ok(objcode::PyCode::new(code).into_ref(vm)) } pub enum Mode { diff --git a/vm/src/eval.rs b/vm/src/eval.rs index 202286eb13..6c7b3ca549 100644 --- a/vm/src/eval.rs +++ b/vm/src/eval.rs @@ -8,12 +8,7 @@ use crate::pyobject::PyResult; use crate::vm::VirtualMachine; pub fn eval(vm: &VirtualMachine, source: &str, scope: Scope, source_path: &str) -> PyResult { - match compile::compile( - source, - &compile::Mode::Eval, - source_path.to_string(), - vm.ctx.code_type(), - ) { + match compile::compile(vm, source, &compile::Mode::Eval, source_path.to_string()) { Ok(bytecode) => { debug!("Code object: {:?}", bytecode); vm.run_code_obj(bytecode, scope) diff --git a/vm/src/function.rs b/vm/src/function.rs index a7581d3f25..17e7678233 100644 --- a/vm/src/function.rs +++ b/vm/src/function.rs @@ -305,6 +305,13 @@ pub enum OptionalArg { } impl OptionalArg { + pub fn is_present(&self) -> bool { + match self { + Present(_) => true, + Missing => false, + } + } + pub fn into_option(self) -> Option { match self { Present(value) => Some(value), diff --git a/vm/src/import.rs b/vm/src/import.rs index ed5a6d094b..936b896805 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -27,10 +27,10 @@ fn import_uncached_module(vm: &VirtualMachine, current_path: PathBuf, module: &s let source = util::read_file(file_path.as_path()) .map_err(|e| vm.new_exception(import_error.clone(), e.description().to_string()))?; let code_obj = compile::compile( + vm, &source, &compile::Mode::Exec, file_path.to_str().unwrap().to_string(), - vm.ctx.code_type(), ) .map_err(|err| { let syntax_error = vm.context().exceptions.syntax_error.clone(); diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index fda5603f00..7fe54bf513 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -7,9 +7,11 @@ use std::fmt; use crate::bytecode; use crate::function::PyFuncArgs; use crate::obj::objtype::PyClassRef; -use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyResult, PyValue, TypeProtocol}; +use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; +pub type PyCodeRef = PyRef; + pub struct PyCode { code: bytecode::CodeObject, } diff --git a/vm/src/obj/objfilter.rs b/vm/src/obj/objfilter.rs index 6fcf84fb4d..e547d2c43e 100644 --- a/vm/src/obj/objfilter.rs +++ b/vm/src/obj/objfilter.rs @@ -1,13 +1,13 @@ use crate::function::PyFuncArgs; -use crate::pyobject::{ - IdProtocol, PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, -}; +use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; // Required for arg_check! to use isinstance use super::objbool; use super::objiter; use crate::obj::objtype::PyClassRef; +pub type PyFilterRef = PyRef; + #[derive(Debug)] pub struct PyFilter { predicate: PyObjectRef, @@ -20,20 +20,19 @@ impl PyValue for PyFilter { } } -fn filter_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(cls, None), (function, None), (iterable, None)] - ); - let iterator = objiter::get_iter(vm, iterable)?; - Ok(PyObject::new( - PyFilter { - predicate: function.clone(), - iterator, - }, - cls.clone(), - )) +fn filter_new( + cls: PyClassRef, + function: PyObjectRef, + iterable: PyObjectRef, + vm: &VirtualMachine, +) -> PyResult { + let iterator = objiter::get_iter(vm, &iterable)?; + + PyFilter { + predicate: function.clone(), + iterator, + } + .into_ref_with_type(vm, cls) } fn filter_next(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 1abeabba2a..958d368a89 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::obj::objtype::PyClassRef; use crate::pyobject::{ - IntoPyObject, PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, + IntoPyObject, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; use num_bigint::ToBigInt; @@ -155,7 +155,7 @@ impl PyFloatRef { } } - fn new_float(cls: PyObjectRef, arg: PyObjectRef, vm: &VirtualMachine) -> PyResult { + fn new_float(cls: PyClassRef, 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()) { @@ -193,7 +193,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(PyFloat { value }, cls.clone())) + PyFloat { value }.into_ref_with_type(vm, cls) } fn mod_(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { diff --git a/vm/src/obj/objmemory.rs b/vm/src/obj/objmemory.rs index c5926c4abd..7f53fb5ae6 100644 --- a/vm/src/obj/objmemory.rs +++ b/vm/src/obj/objmemory.rs @@ -1,8 +1,9 @@ -use crate::function::PyFuncArgs; use crate::obj::objtype::PyClassRef; -use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol}; +use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; +pub type PyMemoryViewRef = PyRef; + #[derive(Debug)] pub struct PyMemoryView { obj: PyObjectRef, @@ -14,15 +15,13 @@ impl PyValue for PyMemoryView { } } -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( - PyMemoryView { - obj: bytes_object.clone(), - }, - cls.clone(), - )) +pub fn new_memory_view( + cls: PyClassRef, + bytes_object: PyObjectRef, + vm: &VirtualMachine, +) -> PyResult { + vm.ctx.set_attr(&cls, "obj", bytes_object.clone()); + PyMemoryView { obj: bytes_object }.into_ref_with_type(vm, cls) } pub fn init(ctx: &PyContext) { diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index e0ff9b20cd..84ba73fe68 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -7,7 +7,7 @@ use crate::obj::objproperty::PropertyBuilder; use crate::obj::objtype::PyClassRef; use crate::pyobject::{ DictProtocol, IdProtocol, PyAttributes, PyContext, PyObject, PyObjectRef, PyResult, PyValue, - TypeProtocol, + TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -22,7 +22,7 @@ impl PyValue for PyInstance { pub fn new_instance(vm: &VirtualMachine, mut args: PyFuncArgs) -> PyResult { // more or less __new__ operator - let cls = args.shift(); + let cls = PyClassRef::try_from_object(vm, args.shift())?; Ok(if cls.is(&vm.ctx.object) { PyObject::new_without_dict(PyInstance, cls) } else { diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index 7a9dd8447e..c5459a9fb2 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -200,7 +200,7 @@ impl<'a> PropertyBuilder<'a> { deleter: None, }; - PyObject::new(payload, self.ctx.property_type().into_object()) + PyObject::new(payload, self.ctx.property_type()) } else { let payload = PyReadOnlyProperty { getter: self.getter.expect( @@ -208,7 +208,7 @@ impl<'a> PropertyBuilder<'a> { ), }; - PyObject::new(payload, self.ctx.readonly_property_type().into_object()) + PyObject::new(payload, self.ctx.readonly_property_type()) } } } diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index ad9c7bf361..83d0f9f9f8 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -163,12 +163,12 @@ pub fn get_item( if sequence.payload::().is_some() { Ok(PyObject::new( PyList::from(elements.to_vec().get_slice_items(vm, &subscript)?), - sequence.typ(), + sequence.type_pyref(), )) } else if sequence.payload::().is_some() { Ok(PyObject::new( PyTuple::from(elements.to_vec().get_slice_items(vm, &subscript)?), - sequence.typ(), + sequence.type_pyref(), )) } else { panic!("sequence get_item called for non-sequence") diff --git a/vm/src/obj/objslice.rs b/vm/src/obj/objslice.rs index 4a7124fdfd..9df435290f 100644 --- a/vm/src/obj/objslice.rs +++ b/vm/src/obj/objslice.rs @@ -1,7 +1,9 @@ use num_bigint::BigInt; use crate::function::PyFuncArgs; -use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol}; +use crate::pyobject::{ + FromPyObjectRef, PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, +}; use crate::vm::VirtualMachine; use super::objint; @@ -61,7 +63,7 @@ fn slice_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { stop: stop.map(|x| objint::get_value(x).clone()), step: step.map(|x| objint::get_value(x).clone()), }, - cls.clone(), + PyClassRef::from_pyobj(&cls), )) } diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 81f04603bb..1b53226c9d 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -241,11 +241,12 @@ pub fn type_new_class( bases.push(vm.ctx.object()); let name = objstr::get_value(name); new( - typ.clone(), + PyClassRef::from_pyobj(typ), &name, bases, objdict::py_dict_to_attributes(dict), ) + .map(|x| x.into_object()) } pub fn type_call(class: PyClassRef, args: Args, kwargs: KwArgs, vm: &VirtualMachine) -> PyResult { @@ -365,14 +366,14 @@ fn linearise_mro(mut bases: Vec>) -> Option> { } pub fn new( - typ: PyObjectRef, + typ: PyClassRef, name: &str, bases: Vec, dict: HashMap, -) -> PyResult { +) -> PyResult { let mros = bases.into_iter().map(|x| _mro(&x)).collect(); let mro = linearise_mro(mros).unwrap(); - Ok(PyObject { + let new_type = PyObject { payload: PyClass { name: String::from(name), mro, @@ -380,7 +381,8 @@ pub fn new( dict: Some(RefCell::new(dict)), typ, } - .into_ref()) + .into_ref(); + Ok(PyClassRef::from_pyobj(&new_type)) } #[cfg(test)] @@ -401,23 +403,8 @@ mod tests { let object: PyClassRef = context.object.clone(); let type_type = &context.type_type; - let a = new( - type_type.clone().into_object(), - "A", - vec![object.clone()], - HashMap::new(), - ) - .unwrap(); - let b = new( - type_type.clone().into_object(), - "B", - vec![object.clone()], - HashMap::new(), - ) - .unwrap(); - - let a: PyClassRef = a.downcast().unwrap(); - let b: PyClassRef = b.downcast().unwrap(); + let a = new(type_type.clone(), "A", vec![object.clone()], HashMap::new()).unwrap(); + let b = new(type_type.clone(), "B", vec![object.clone()], HashMap::new()).unwrap(); assert_eq!( map_ids(linearise_mro(vec![ diff --git a/vm/src/obj/objzip.rs b/vm/src/obj/objzip.rs index 63df96f62f..e628cb218a 100644 --- a/vm/src/obj/objzip.rs +++ b/vm/src/obj/objzip.rs @@ -1,10 +1,12 @@ -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 crate::obj::objtype::PyClassRef; +pub type PyZipRef = PyRef; + #[derive(Debug)] pub struct PyZip { iterators: Vec, @@ -16,15 +18,12 @@ impl PyValue for PyZip { } } -fn zip_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - no_kwargs!(vm, args); - let cls = &args.args[0]; - let iterables = &args.args[1..]; +fn zip_new(cls: PyClassRef, iterables: Args, vm: &VirtualMachine) -> PyResult { let iterators = iterables - .iter() - .map(|iterable| objiter::get_iter(vm, iterable)) + .into_iter() + .map(|iterable| objiter::get_iter(vm, &iterable)) .collect::, _>>()?; - Ok(PyObject::new(PyZip { iterators }, cls.clone())) + PyZip { iterators }.into_ref_with_type(vm, cls) } fn zip_next(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 41a1ca6f34..deeb4f370c 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -152,14 +152,7 @@ pub struct PyContext { pub fn create_type(name: &str, type_type: &PyClassRef, base: &PyClassRef) -> PyClassRef { let dict = PyAttributes::new(); - let new_type = objtype::new( - type_type.clone().into_object(), - name, - vec![base.clone()], - dict, - ) - .unwrap(); - new_type.downcast().unwrap() + objtype::new(type_type.clone(), name, vec![base.clone()], dict).unwrap() } pub type PyNotImplementedRef = PyRef; @@ -212,13 +205,14 @@ fn init_type_hierarchy() -> (PyClassRef, PyClassRef) { 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; + + let type_type = PyClassRef::from_pyobj(&type_type); + let object_type = PyClassRef::from_pyobj(&object_type); + ptr::write(&mut (*object_type_ptr).typ, type_type.clone()); ptr::write(&mut (*type_type_ptr).typ, type_type.clone()); - ( - type_type.downcast().unwrap(), - object_type.downcast().unwrap(), - ) + (type_type, object_type) } } @@ -265,7 +259,7 @@ impl PyContext { fn create_object(payload: T, cls: &PyClassRef) -> PyRef { PyRef { - obj: PyObject::new(payload, cls.clone().into_object()), + obj: PyObject::new(payload, cls.clone()), _payload: PhantomData, } } @@ -520,33 +514,27 @@ impl PyContext { } pub fn new_int>(&self, i: T) -> PyObjectRef { - PyObject::new(PyInt::new(i), self.int_type().into_object()) + PyObject::new(PyInt::new(i), self.int_type()) } pub fn new_float(&self, value: f64) -> PyObjectRef { - PyObject::new(PyFloat::from(value), self.float_type().into_object()) + PyObject::new(PyFloat::from(value), self.float_type()) } pub fn new_complex(&self, value: Complex64) -> PyObjectRef { - PyObject::new(PyComplex::from(value), self.complex_type().into_object()) + PyObject::new(PyComplex::from(value), self.complex_type()) } pub fn new_str(&self, s: String) -> PyObjectRef { - PyObject::new(objstr::PyString { value: s }, self.str_type().into_object()) + PyObject::new(objstr::PyString { value: s }, self.str_type()) } pub fn new_bytes(&self, data: Vec) -> PyObjectRef { - PyObject::new( - objbytes::PyBytes::new(data), - self.bytes_type().into_object(), - ) + PyObject::new(objbytes::PyBytes::new(data), self.bytes_type()) } pub fn new_bytearray(&self, data: Vec) -> PyObjectRef { - PyObject::new( - objbytearray::PyByteArray::new(data), - self.bytearray_type().into_object(), - ) + PyObject::new(objbytearray::PyByteArray::new(data), self.bytearray_type()) } pub fn new_bool(&self, b: bool) -> PyObjectRef { @@ -558,32 +546,25 @@ impl PyContext { } pub fn new_tuple(&self, elements: Vec) -> PyObjectRef { - PyObject::new(PyTuple::from(elements), self.tuple_type().into_object()) + PyObject::new(PyTuple::from(elements), self.tuple_type()) } pub fn new_list(&self, elements: Vec) -> PyObjectRef { - PyObject::new(PyList::from(elements), self.list_type().into_object()) + 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(PySet::default(), self.set_type().into_object()) + PyObject::new(PySet::default(), self.set_type()) } pub fn new_dict(&self) -> PyObjectRef { - PyObject::new(PyDict::default(), self.dict_type().into_object()) + PyObject::new(PyDict::default(), self.dict_type()) } pub fn new_class(&self, name: &str, base: PyClassRef) -> PyClassRef { - let typ = objtype::new( - self.type_type().into_object(), - name, - vec![base], - PyAttributes::new(), - ) - .unwrap(); - typ.downcast().unwrap() + objtype::new(self.type_type(), name, vec![base], PyAttributes::new()).unwrap() } pub fn new_scope(&self) -> Scope { @@ -596,7 +577,7 @@ impl PyContext { name: name.to_string(), dict, }, - self.module_type.clone().into_object(), + self.module_type.clone(), ) } @@ -606,12 +587,12 @@ impl PyContext { { PyObject::new( PyBuiltinFunction::new(f.into_func()), - self.builtin_function_or_method_type().into_object(), + self.builtin_function_or_method_type(), ) } pub fn new_frame(&self, code: PyObjectRef, scope: Scope) -> PyObjectRef { - PyObject::new(Frame::new(code, scope), self.frame_type().into_object()) + PyObject::new(Frame::new(code, scope), self.frame_type()) } pub fn new_property(&self, f: F) -> PyObjectRef @@ -622,7 +603,7 @@ impl PyContext { } pub fn new_code_object(&self, code: bytecode::CodeObject) -> PyObjectRef { - PyObject::new(objcode::PyCode::new(code), self.code_type().into_object()) + PyObject::new(objcode::PyCode::new(code), self.code_type()) } pub fn new_function( @@ -633,21 +614,18 @@ impl PyContext { ) -> PyObjectRef { PyObject::new( PyFunction::new(code_obj, scope, defaults), - self.function_type().into_object(), + self.function_type(), ) } pub fn new_bound_method(&self, function: PyObjectRef, object: PyObjectRef) -> PyObjectRef { - PyObject::new( - PyMethod::new(object, function), - self.bound_method_type().into_object(), - ) + PyObject::new(PyMethod::new(object, function), self.bound_method_type()) } pub fn new_instance(&self, class: PyClassRef, dict: Option) -> PyObjectRef { let dict = dict.unwrap_or_default(); PyObject { - typ: class.into_object(), + typ: class, dict: Some(RefCell::new(dict)), payload: objobject::PyInstance, } @@ -716,7 +694,7 @@ pub struct PyObject where T: ?Sized + PyObjectPayload, { - pub typ: PyObjectRef, + pub typ: PyClassRef, pub dict: Option>, // __dict__ member pub payload: T, } @@ -896,7 +874,7 @@ where T: ?Sized + PyObjectPayload, { fn type_ref(&self) -> &PyObjectRef { - &self.typ + self.typ.as_object() } } @@ -1124,7 +1102,7 @@ where T: PyValue + Sized, { fn into_pyobject(self, vm: &VirtualMachine) -> PyResult { - Ok(PyObject::new(self, T::class(vm).into_object())) + Ok(PyObject::new(self, T::class(vm))) } } @@ -1146,7 +1124,7 @@ impl PyObject where T: Sized + PyObjectPayload, { - pub fn new(payload: T, typ: PyObjectRef) -> PyObjectRef { + pub fn new(payload: T, typ: PyClassRef) -> PyObjectRef { PyObject { typ, dict: Some(RefCell::new(PyAttributes::new())), @@ -1155,7 +1133,7 @@ where .into_ref() } - pub fn new_without_dict(payload: T, typ: PyObjectRef) -> PyObjectRef { + pub fn new_without_dict(payload: T, typ: PyClassRef) -> PyObjectRef { PyObject { typ, dict: None, @@ -1187,7 +1165,7 @@ pub trait PyValue: fmt::Debug + Sized + 'static { fn into_ref(self, vm: &VirtualMachine) -> PyRef { PyRef { - obj: PyObject::new(self, Self::class(vm).into_object()), + obj: PyObject::new(self, Self::class(vm)), _payload: PhantomData, } } @@ -1196,7 +1174,7 @@ pub trait PyValue: fmt::Debug + Sized + 'static { let class = Self::class(vm); if objtype::issubclass(&cls, &class) { Ok(PyRef { - obj: PyObject::new(self, cls.obj), + obj: PyObject::new(self, cls), _payload: PhantomData, }) } else { diff --git a/vm/src/stdlib/re.rs b/vm/src/stdlib/re.rs index 57db3f0a80..4dab1b0c3f 100644 --- a/vm/src/stdlib/re.rs +++ b/vm/src/stdlib/re.rs @@ -4,16 +4,13 @@ * This module fits the python re interface onto the rust regular expression * system. */ - -use std::path::PathBuf; - use regex::{Match, Regex}; use crate::function::PyFuncArgs; -use crate::import; use crate::obj::objstr; +use crate::obj::objstr::PyStringRef; use crate::obj::objtype::PyClassRef; -use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol}; +use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; impl PyValue for Regex { @@ -55,7 +52,8 @@ fn re_match(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { (string, Some(vm.ctx.str_type())) ] ); - let regex = make_regex(vm, pattern)?; + let pattern_str = objstr::get_value(&pattern); + let regex = make_regex(vm, &pattern_str)?; let search_text = objstr::get_value(string); do_match(vm, ®ex, search_text) @@ -74,8 +72,8 @@ fn re_search(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { ] ); - // let pattern_str = objstr::get_value(&pattern); - let regex = make_regex(vm, pattern)?; + let pattern_str = objstr::get_value(&pattern); + let regex = make_regex(vm, &pattern_str)?; let search_text = objstr::get_value(string); do_search(vm, ®ex, search_text) @@ -93,10 +91,8 @@ fn do_search(vm: &VirtualMachine, regex: &Regex, search_text: String) -> PyResul } } -fn make_regex(vm: &VirtualMachine, pattern: &PyObjectRef) -> PyResult { - let pattern_str = objstr::get_value(pattern); - - match Regex::new(&pattern_str) { +fn make_regex(vm: &VirtualMachine, pattern: &str) -> PyResult { + match Regex::new(pattern) { Ok(regex) => Ok(regex), Err(err) => Err(vm.new_value_error(format!("Error in regex: {:?}", err))), } @@ -117,39 +113,24 @@ impl PyValue for PyMatch { /// Take a found regular expression and convert it to proper match object. 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 - let module = import::import_module(vm, PathBuf::default(), "re").unwrap(); - let match_class = vm.get_attribute(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 { + Ok(PyMatch { start: match_value.start(), end: match_value.end(), - }; - - Ok(PyObject::new(match_value, match_class.clone())) + } + .into_ref(vm) + .into_object()) } /// Compile a regular expression into a Pattern object. /// See also: /// https://docs.python.org/3/library/re.html#re.compile -fn re_compile(vm: &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.get_attribute(module, "Pattern").unwrap(); +fn re_compile(pattern: PyStringRef, vm: &VirtualMachine) -> PyResult> { + let regex = make_regex(vm, &pattern.value)?; - Ok(PyObject::new(regex, pattern_class.clone())) + Ok(regex.into_ref(vm)) } fn pattern_match(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 4a3cc3abc0..cde574fbcd 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -19,6 +19,7 @@ use crate::function::PyFuncArgs; use crate::obj::objbool; use crate::obj::objbuiltinfunc::PyBuiltinFunction; use crate::obj::objcode; +use crate::obj::objcode::PyCodeRef; use crate::obj::objframe; use crate::obj::objfunction::{PyFunction, PyMethod}; use crate::obj::objgenerator; @@ -72,8 +73,8 @@ impl VirtualMachine { } } - pub fn run_code_obj(&self, code: PyObjectRef, scope: Scope) -> PyResult { - let frame = self.ctx.new_frame(code, scope); + pub fn run_code_obj(&self, code: PyCodeRef, scope: Scope) -> PyResult { + let frame = self.ctx.new_frame(code.into_object(), scope); self.run_frame_full(frame) } diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index fd2181182f..2d20bc37a5 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -285,8 +285,7 @@ impl WASMVirtualMachine { ref vm, ref scope, .. }| { source.push('\n'); - let code = - compile::compile(&source, &mode, "".to_string(), vm.ctx.code_type()); + let code = compile::compile(vm, &source, &mode, "".to_string()); 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 { From 00540dec35c130a0aeff0841728cbe26c85bd55b Mon Sep 17 00:00:00 2001 From: ben Date: Sun, 24 Mar 2019 08:12:07 +1300 Subject: [PATCH 08/51] Fix wasm, to reflect that PyObject.typ is now a PyClassRef --- wasm/lib/src/browser_module.rs | 72 ++++++++++++---------------------- wasm/lib/src/convert.rs | 8 ++-- 2 files changed, 31 insertions(+), 49 deletions(-) diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index 710b18e639..a48cf3d342 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -7,12 +7,12 @@ use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use wasm_bindgen_futures::{future_to_promise, JsFuture}; -use rustpython_vm::function::PyFuncArgs; +use rustpython_vm::function::{OptionalArg, PyFuncArgs}; use rustpython_vm::import::import_module; use rustpython_vm::obj::objtype::PyClassRef; use rustpython_vm::obj::{objint, objstr}; use rustpython_vm::pyobject::{ - PyContext, PyObject, PyObjectRef, PyResult, PyValue, TryFromObject, TypeProtocol, + PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol, }; use rustpython_vm::VirtualMachine; @@ -45,8 +45,6 @@ impl FetchResponseFormat { 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)?; - 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)?; @@ -102,7 +100,7 @@ fn browser_fetch(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { }) .and_then(JsFuture::from); - Ok(PyPromise::new_obj(promise_type, future_to_promise(future))) + Ok(PyPromise::from_future(future).into_ref(vm).into_object()) } fn browser_request_animation_frame(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -159,6 +157,8 @@ fn browser_cancel_animation_frame(vm: &VirtualMachine, args: PyFuncArgs) -> PyRe Ok(vm.get_none()) } +pub type PyPromiseRef = PyRef; + #[derive(Debug)] pub struct PyPromise { value: Promise, @@ -171,8 +171,15 @@ impl PyValue for PyPromise { } impl PyPromise { - pub fn new_obj(promise_type: PyClassRef, value: Promise) -> PyObjectRef { - PyObject::new(PyPromise { value }, promise_type.into_object()) + pub fn new(promise: Promise) -> PyPromise { + PyPromise { value: promise } + } + + pub fn from_future(future: F) -> PyPromise + where + F: Future + 'static, + { + PyPromise::new(future_to_promise(future)) } } @@ -193,26 +200,17 @@ pub fn import_promise_type(vm: &VirtualMachine) -> PyResult { } } -fn promise_then(vm: &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(); +fn promise_then( + zelf: PyPromiseRef, + on_fulfill: PyObjectRef, + on_reject: OptionalArg, + vm: &VirtualMachine, +) -> PyPromise { + let on_reject = on_reject.into_option(); let acc_vm = AccessibleVM::from(vm); - let promise = get_promise_value(zelf); - - let ret_future = JsFuture::from(promise).then(move |res| { + let ret_future = JsFuture::from(zelf.value.clone()).then(move |res| { let stored_vm = &acc_vm .upgrade() .expect("that the vm is valid when the promise resolves"); @@ -234,29 +232,13 @@ fn promise_then(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { convert::pyresult_to_jsresult(vm, ret) }); - let ret_promise = future_to_promise(ret_future); - - Ok(PyPromise::new_obj(promise_type, ret_promise)) + PyPromise::from_future(ret_future) } -fn promise_catch(vm: &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(); - +fn promise_catch(zelf: PyPromiseRef, on_reject: PyObjectRef, vm: &VirtualMachine) -> PyPromise { let acc_vm = AccessibleVM::from(vm); - let promise = get_promise_value(zelf); - - let ret_future = JsFuture::from(promise).then(move |res| match res { + let ret_future = JsFuture::from(zelf.value.clone()).then(move |res| match res { Ok(val) => Ok(val), Err(err) => { let stored_vm = acc_vm @@ -269,9 +251,7 @@ fn promise_catch(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { } }); - let ret_promise = future_to_promise(ret_future); - - Ok(PyPromise::new_obj(promise_type, ret_promise)) + PyPromise::from_future(ret_future) } fn browser_alert(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index c7ba1c6161..41f43778fa 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -4,7 +4,7 @@ use wasm_bindgen::{closure::Closure, prelude::*, JsCast}; use rustpython_vm::function::PyFuncArgs; use rustpython_vm::obj::{objbytes, objint, objsequence, objtype}; -use rustpython_vm::pyobject::{DictProtocol, PyObjectRef, PyResult}; +use rustpython_vm::pyobject::{DictProtocol, PyObjectRef, PyResult, PyValue}; use rustpython_vm::VirtualMachine; use crate::browser_module; @@ -159,8 +159,10 @@ 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 - if let Ok(promise_type) = browser_module::import_promise_type(vm) { - return browser_module::PyPromise::new_obj(promise_type, promise.clone()); + if let Ok(_) = browser_module::import_promise_type(vm) { + return browser_module::PyPromise::new(promise.clone()) + .into_ref(vm) + .into_object(); } } if Array::is_array(&js_val) { From faf1925a25f64c4158942ded1e4db5253bc3bd44 Mon Sep 17 00:00:00 2001 From: ben Date: Sun, 24 Mar 2019 09:08:03 +1300 Subject: [PATCH 09/51] Remove usages of PyClassRef::from_pyobj --- vm/src/obj/objslice.rs | 19 ++++++++----------- vm/src/obj/objtype.rs | 4 ++-- vm/src/pyobject.rs | 4 ++-- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/vm/src/obj/objslice.rs b/vm/src/obj/objslice.rs index 9df435290f..d68ae2255f 100644 --- a/vm/src/obj/objslice.rs +++ b/vm/src/obj/objslice.rs @@ -1,9 +1,7 @@ use num_bigint::BigInt; use crate::function::PyFuncArgs; -use crate::pyobject::{ - FromPyObjectRef, PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, -}; +use crate::pyobject::{PyContext, PyObjectRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; use super::objint; @@ -57,14 +55,13 @@ fn slice_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { Ok((cls, Some(start), Some(stop), step)) } }?; - Ok(PyObject::new( - PySlice { - 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()), - }, - PyClassRef::from_pyobj(&cls), - )) + PySlice { + 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()), + } + .into_ref_with_type(vm, cls.clone().downcast().unwrap()) + .map(|x| x.into_object()) } fn get_property_value(vm: &VirtualMachine, value: &Option) -> PyResult { diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 1b53226c9d..b48ba99413 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -241,7 +241,7 @@ pub fn type_new_class( bases.push(vm.ctx.object()); let name = objstr::get_value(name); new( - PyClassRef::from_pyobj(typ), + typ.clone().downcast().unwrap(), &name, bases, objdict::py_dict_to_attributes(dict), @@ -382,7 +382,7 @@ pub fn new( typ, } .into_ref(); - Ok(PyClassRef::from_pyobj(&new_type)) + Ok(new_type.downcast().unwrap()) } #[cfg(test)] diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index deeb4f370c..80eba36da3 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -206,8 +206,8 @@ fn init_type_hierarchy() -> (PyClassRef, PyClassRef) { 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; - let type_type = PyClassRef::from_pyobj(&type_type); - let object_type = PyClassRef::from_pyobj(&object_type); + let type_type: PyClassRef = type_type.downcast().unwrap(); + let object_type: PyClassRef = object_type.downcast().unwrap(); ptr::write(&mut (*object_type_ptr).typ, type_type.clone()); ptr::write(&mut (*type_type_ptr).typ, type_type.clone()); From 017a9ef856fb0f1d9efa4d2ba607078485fddff3 Mon Sep 17 00:00:00 2001 From: jgirardet Date: Sat, 23 Mar 2019 23:04:45 +0100 Subject: [PATCH 10/51] add bytes.__add__ + bytes tests --- tests/snippets/bytes.py | 23 +++++++++++++++++++++++ vm/src/obj/objbytes.rs | 15 ++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 tests/snippets/bytes.py diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py new file mode 100644 index 0000000000..7dae0b5249 --- /dev/null +++ b/tests/snippets/bytes.py @@ -0,0 +1,23 @@ +# comp +a = b"abcd" +b = b"ab" +c = b"abcd" + +assert a > b +assert a >= b +assert b < a +assert b <= a +assert a == c + +# hash not implemented for iterator +# assert hash(iter(a)) == hash(iter(b"abcd")) + +assert repr(a) == "b'abcd'" +assert len(a) == 4 + +assert a + b == b"abcdab" +try: + b"ab" + "ab" + assert false +except TypeError: + assert True diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 8d64cc5076..e7fd126e58 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -67,7 +67,8 @@ pub fn init(context: &PyContext) { "__repr__" => context.new_rustfunc(bytes_repr), "__len__" => context.new_rustfunc(bytes_len), "__iter__" => context.new_rustfunc(bytes_iter), - "__doc__" => context.new_str(bytes_doc.to_string()) + "__doc__" => context.new_str(bytes_doc.to_string()), + "__add__" => context.new_rustfunc(PyBytesRef::add), }); } @@ -201,3 +202,15 @@ fn bytes_iter(obj: PyBytesRef, _vm: &VirtualMachine) -> PyIteratorValue { iterated_obj: obj.into_object(), } } + +impl PyBytesRef { + fn add(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { + if objtype::isinstance(&other, &vm.ctx.bytes_type()) { + let rhs = get_value(&other); + let elements: Vec = self.value.iter().chain(rhs.iter()).cloned().collect(); + Ok(vm.ctx.new_bytes(elements)) + } else { + Err(vm.new_type_error(format!("Cannot add {} and {}", self.as_object(), other))) + } + } +} From a5558e0e32971c8b6c2a5754250e9a81152e31fa Mon Sep 17 00:00:00 2001 From: Joey Date: Sat, 23 Mar 2019 15:05:12 -0700 Subject: [PATCH 11/51] Introduce Either extractor and convert range.__getitem__ --- vm/src/obj/objrange.rs | 115 ++++++++++++++++++----------------------- vm/src/obj/objslice.rs | 4 +- vm/src/pyobject.rs | 44 ++++++++++++++++ 3 files changed, 98 insertions(+), 65 deletions(-) diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index db4ded48dd..8d526227a3 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -7,14 +7,13 @@ use num_traits::{One, Signed, ToPrimitive, Zero}; use crate::function::{OptionalArg, PyFuncArgs}; use crate::pyobject::{ - PyContext, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, + Either2, PyContext, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; use super::objint::{self, PyInt, PyIntRef}; -use super::objslice::PySlice; -use super::objtype; -use super::objtype::PyClassRef; +use super::objslice::PySliceRef; +use super::objtype::{self, PyClassRef}; #[derive(Debug, Clone)] pub struct PyRange { @@ -169,7 +168,7 @@ pub fn init(context: &PyContext) { "__bool__" => context.new_rustfunc(range_bool), "__contains__" => context.new_rustfunc(range_contains), "__doc__" => context.new_str(range_doc.to_string()), - "__getitem__" => context.new_rustfunc(range_getitem), + "__getitem__" => context.new_rustfunc(PyRangeRef::getitem), "__iter__" => context.new_rustfunc(range_iter), "__len__" => context.new_rustfunc(range_len), "__new__" => context.new_rustfunc(range_new), @@ -212,6 +211,53 @@ impl PyRangeRef { } .into_ref_with_type(vm, cls) } + + fn getitem(self, subscript: Either2, vm: &VirtualMachine) -> PyResult { + match subscript { + Either2::A(index) => { + if let Some(value) = self.get(index.value.clone()) { + Ok(PyInt::new(value).into_ref(vm).into_object()) + } else { + Err(vm.new_index_error("range object index out of range".to_string())) + } + } + Either2::B(slice) => { + let new_start = if let Some(int) = slice.start.clone() { + if let Some(i) = self.get(int) { + i + } else { + self.start.clone() + } + } else { + self.start.clone() + }; + + let new_end = if let Some(int) = slice.stop.clone() { + if let Some(i) = self.get(int) { + i + } else { + self.stop.clone() + } + } else { + self.stop.clone() + }; + + let new_step = if let Some(int) = slice.step.clone() { + int * self.step.clone() + } else { + self.step.clone() + }; + + Ok(PyRange { + start: new_start, + stop: new_end, + step: new_step, + } + .into_ref(vm) + .into_object()) + } + } + } } fn range_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -252,65 +298,6 @@ fn range_len(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn range_getitem(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.range_type())), (subscript, None)] - ); - - let range = get_value(zelf); - - if let Some(i) = subscript.payload::() { - 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())) - } - } else 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() - } - } else { - range.start.clone() - }; - - let new_end = if let Some(int) = stop { - if let Some(i) = range.get(int) { - i - } else { - range.stop - } - } else { - range.stop - }; - - let new_step = if let Some(int) = step { - int * range.step - } else { - range.step - }; - - Ok(PyRange { - start: new_start, - stop: new_end, - step: new_step, - } - .into_ref(vm) - .into_object()) - } else { - Err(vm.new_type_error("range indices must be integer or slice".to_string())) - } -} - fn range_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); diff --git a/vm/src/obj/objslice.rs b/vm/src/obj/objslice.rs index 4a7124fdfd..d171b2527b 100644 --- a/vm/src/obj/objslice.rs +++ b/vm/src/obj/objslice.rs @@ -1,7 +1,7 @@ use num_bigint::BigInt; use crate::function::PyFuncArgs; -use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol}; +use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; use super::objint; @@ -21,6 +21,8 @@ impl PyValue for PySlice { } } +pub type PySliceRef = PyRef; + fn slice_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { no_kwargs!(vm, args); let (cls, start, stop, step): ( diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 41a1ca6f34..a90a4ebc08 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1218,6 +1218,50 @@ impl PyObjectPayload for T { } } +pub enum Either2 { + A(A), + B(B), +} + +/// This allows a builtin method to accept arguments that may be one of two +/// types, raising a `TypeError` if it is neither. +/// +/// # Example +/// +/// ``` +/// fn do_something(arg: Either2, vm: &VirtualMachine) { +/// match arg { +/// Either2::A(int)=> { +/// // do something with int +/// } +/// Either2::B(string) => { +/// // do something with string +/// } +/// } +/// } +/// ``` +impl TryFromObject for Either2, PyRef> +where + A: PyValue, + B: PyValue, +{ + fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { + // TODO: downcast could probably be reworked a bit to make these clones not necessary + obj.clone() + .downcast::() + .map(Either2::A) + .or_else(|| obj.clone().downcast::().map(Either2::B)) + .ok_or_else(|| { + vm.new_type_error(format!( + "must be {} or {}, not {}", + A::class(vm), + B::class(vm), + obj.type_pyref() + )) + }) + } +} + #[cfg(test)] mod tests { use super::*; From b6f1ecdb4be116d6242ed05541725adedf9a175c Mon Sep 17 00:00:00 2001 From: Joey Date: Sat, 23 Mar 2019 15:49:31 -0700 Subject: [PATCH 12/51] Fix example --- vm/src/pyobject.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index a90a4ebc08..40a2d56e25 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1229,6 +1229,10 @@ pub enum Either2 { /// # Example /// /// ``` +/// use rustpython_vm::VirtualMachine; +/// use rustpython_vm::obj::{objstr::PyStringRef, objint::PyIntRef}; +/// use rustpython_vm::pyobject::Either2; +/// /// fn do_something(arg: Either2, vm: &VirtualMachine) { /// match arg { /// Either2::A(int)=> { From 3c15d892c5b99f903c5389dc1e5beb6e76b488c3 Mon Sep 17 00:00:00 2001 From: Joey Date: Sat, 23 Mar 2019 15:57:06 -0700 Subject: [PATCH 13/51] Avoid some cloning --- vm/src/pyobject.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index aec210e7a8..befa798926 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -700,16 +700,23 @@ where } impl PyObject { - pub fn downcast(self: Rc) -> Option> { + /// Attempt to downcast this reference to a subclass. + /// + /// If the downcast fails, the original ref is returned in as `Err` so + /// another downcast can be attempted without unnecessary cloning. + /// + /// Note: The returned `Result` is _not_ a `PyResult`, even though the + /// types are compatible. + pub fn downcast(self: Rc) -> Result, PyObjectRef> { if self.payload_is::() { - Some({ + Ok({ PyRef { obj: self, _payload: PhantomData, } }) } else { - None + Err(self) } } } @@ -1228,12 +1235,10 @@ where B: PyValue, { fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { - // TODO: downcast could probably be reworked a bit to make these clones not necessary - obj.clone() - .downcast::() + obj.downcast::() .map(Either2::A) - .or_else(|| obj.clone().downcast::().map(Either2::B)) - .ok_or_else(|| { + .or_else(|obj| obj.clone().downcast::().map(Either2::B)) + .map_err(|obj| { vm.new_type_error(format!( "must be {} or {}, not {}", A::class(vm), From dc681015771d32d3b9b3e08c12d6942f4e5591b8 Mon Sep 17 00:00:00 2001 From: ben Date: Sun, 24 Mar 2019 12:07:29 +1300 Subject: [PATCH 14/51] Refactor type_new_class to use more specific ref types --- vm/src/obj/objtype.rs | 51 +++++++++++++----------------------------- vm/src/stdlib/types.rs | 30 +++++++++++++------------ 2 files changed, 32 insertions(+), 49 deletions(-) diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index b48ba99413..9d3c9fe490 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -4,16 +4,17 @@ use std::fmt; use crate::function::{Args, KwArgs, PyFuncArgs}; use crate::pyobject::{ - IdProtocol, PyAttributes, PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue, - TypeProtocol, + IdProtocol, PyAttributes, PyContext, PyIterable, PyObject, PyObjectRef, PyRef, PyResult, + PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; use super::objdict; use super::objlist::PyList; use super::objproperty::PropertyBuilder; -use super::objstr::{self, PyStringRef}; +use super::objstr::PyStringRef; use super::objtuple::PyTuple; +use crate::obj::objdict::PyDictRef; #[derive(Clone, Debug)] pub struct PyClass { @@ -203,24 +204,10 @@ pub fn get_type_name(typ: &PyObjectRef) -> String { pub fn type_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { debug!("type.__new__ {:?}", args); if args.args.len() == 2 { - arg_check!( - vm, - args, - required = [(_typ, Some(vm.ctx.type_type())), (obj, None)] - ); - Ok(obj.typ()) + Ok(args.args[1].typ()) } else if args.args.len() == 4 { - arg_check!( - vm, - args, - required = [ - (typ, Some(vm.ctx.type_type())), - (name, Some(vm.ctx.str_type())), - (bases, None), - (dict, Some(vm.ctx.dict_type())) - ] - ); - type_new_class(vm, typ, name, bases, dict) + let (typ, name, bases, dict) = args.bind(vm)?; + type_new_class(vm, typ, name, bases, dict).map(|x| x.into_object()) } else { Err(vm.new_type_error(format!(": type_new: {:?}", args))) } @@ -228,25 +215,19 @@ pub fn type_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn type_new_class( vm: &VirtualMachine, - typ: &PyObjectRef, - name: &PyObjectRef, - bases: &PyObjectRef, - dict: &PyObjectRef, -) -> PyResult { - let mut bases: Vec = vm - .extract_elements(bases)? - .iter() - .map(|x| x.clone().downcast().unwrap()) - .collect(); + typ: PyClassRef, + name: PyStringRef, + bases: PyIterable, + dict: PyDictRef, +) -> PyResult { + let mut bases: Vec = bases.iter(vm)?.collect::, _>>()?; bases.push(vm.ctx.object()); - let name = objstr::get_value(name); new( - typ.clone().downcast().unwrap(), - &name, + typ.clone(), + &name.value, bases, - objdict::py_dict_to_attributes(dict), + objdict::py_dict_to_attributes(dict.as_object()), ) - .map(|x| x.into_object()) } pub fn type_call(class: PyClassRef, args: Args, kwargs: KwArgs, vm: &VirtualMachine) -> PyResult { diff --git a/vm/src/stdlib/types.rs b/vm/src/stdlib/types.rs index 09aeb0dacc..8a5ab2939c 100644 --- a/vm/src/stdlib/types.rs +++ b/vm/src/stdlib/types.rs @@ -2,25 +2,27 @@ * Dynamic type creation and names for built in types. */ -use crate::function::PyFuncArgs; +use crate::function::OptionalArg; +use crate::obj::objdict::PyDict; +use crate::obj::objstr::PyStringRef; use crate::obj::objtype; -use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; +use crate::obj::objtype::PyClassRef; +use crate::pyobject::{PyContext, PyIterable, PyObjectRef, PyResult, PyValue, TryFromObject}; use crate::VirtualMachine; -fn types_new_class(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(name, Some(vm.ctx.str_type()))], - optional = [(bases, None), (_kwds, None), (_exec_body, None)] - ); +fn types_new_class( + name: PyStringRef, + bases: OptionalArg>, + vm: &VirtualMachine, +) -> PyResult { + // TODO kwds and exec_body parameter - let bases: PyObjectRef = match bases { - Some(bases) => bases.clone(), - None => vm.ctx.new_tuple(vec![]), + let bases = match bases { + OptionalArg::Present(bases) => bases, + OptionalArg::Missing => PyIterable::try_from_object(vm, vm.ctx.new_tuple(vec![]))?, }; - let dict = vm.ctx.new_dict(); - objtype::type_new_class(vm, &vm.ctx.type_type().into_object(), name, &bases, &dict) + let dict = PyDict::default().into_ref(vm); + objtype::type_new_class(vm, vm.ctx.type_type(), name, bases, dict) } pub fn make_module(ctx: &PyContext) -> PyObjectRef { From 1997fb84108151790504ff76efe46cb7fd1f3502 Mon Sep 17 00:00:00 2001 From: jgirardet Date: Sun, 24 Mar 2019 01:15:36 +0100 Subject: [PATCH 15/51] add bytes.__contains__ + fix err in __add__ --- tests/snippets/bytes.py | 15 +++++++++----- vm/src/obj/objbytes.rs | 46 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py index 7dae0b5249..d26fd26d89 100644 --- a/tests/snippets/bytes.py +++ b/tests/snippets/bytes.py @@ -16,8 +16,13 @@ assert len(a) == 4 assert a + b == b"abcdab" -try: - b"ab" + "ab" - assert false -except TypeError: - assert True + +# contains +assert b"ab" in b"abcd" +assert b"cd" in b"abcd" +assert b"abcd" in b"abcd" +assert b"a" in b"abcd" +assert b"d" in b"abcd" +assert b"dc" not in b"abcd" +assert 97 in b"abcd" +assert 150 not in b"abcd" diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index e7fd126e58..2a4b0114b5 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -69,6 +69,7 @@ pub fn init(context: &PyContext) { "__iter__" => context.new_rustfunc(bytes_iter), "__doc__" => context.new_str(bytes_doc.to_string()), "__add__" => context.new_rustfunc(PyBytesRef::add), + "__contains__" => context.new_rustfunc(PyBytesRef::contains), }); } @@ -203,6 +204,28 @@ fn bytes_iter(obj: PyBytesRef, _vm: &VirtualMachine) -> PyIteratorValue { } } +fn compare_slice(a: &[u8], b: &[u8]) -> bool { + for (i, j) in a.iter().zip(b.iter()) { + if i != j { + return false; + } + } + return true; +} + +fn compare_vec(a: &Vec, b: &Vec) -> bool { + let a_len = a.len(); + let b_len = b.len(); + for (n, i) in a.iter().enumerate() { + if n + b_len <= a_len && *i == b[0] { + if compare_slice(&a[n..n + b_len], b) { + return true; + } + } + } + false +} + impl PyBytesRef { fn add(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.bytes_type()) { @@ -210,7 +233,28 @@ impl PyBytesRef { let elements: Vec = self.value.iter().chain(rhs.iter()).cloned().collect(); Ok(vm.ctx.new_bytes(elements)) } else { - Err(vm.new_type_error(format!("Cannot add {} and {}", self.as_object(), other))) + Err(vm.new_not_implemented_error("".to_string())) + } + } + + fn contains(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { + if objtype::isinstance(&needle, &vm.ctx.bytes_type()) { + if compare_vec(&self.value, &get_value(&needle)) { + return Ok(true); + } else { + return Ok(false); + } + } else if objtype::isinstance(&needle, &vm.ctx.int_type()) { + let c = self + .value + .contains(&objint::get_value(&needle).to_u8().unwrap()); + if c == true { + return Ok(true); + } else { + return Ok(false); + } + } else { + Err(vm.new_type_error(format!("Cannot add {:?} and {:?}", self, needle))) } } } From db8e6486469b5d10e05155af1894db29e71db041 Mon Sep 17 00:00:00 2001 From: ben Date: Sun, 24 Mar 2019 13:41:37 +1300 Subject: [PATCH 16/51] Make PyFunction.code a PyCodeRef, PyGenerator.frame a FrameRef, and other improvements to increase use of specific ref types. --- vm/src/frame.rs | 12 +++--- vm/src/obj/objcode.rs | 83 +++++++++++++------------------------- vm/src/obj/objfunction.rs | 24 ++++++----- vm/src/obj/objgenerator.rs | 72 +++++++++++++-------------------- vm/src/pyobject.rs | 9 ++--- vm/src/stdlib/dis.rs | 19 ++++----- vm/src/stdlib/json.rs | 2 +- vm/src/sysmodule.rs | 32 ++++----------- vm/src/vm.rs | 35 +++++++--------- 9 files changed, 107 insertions(+), 181 deletions(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 79fdfc1f57..f2a54a6325 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -11,7 +11,7 @@ use crate::bytecode; use crate::function::PyFuncArgs; use crate::obj::objbool; use crate::obj::objbuiltinfunc::PyBuiltinFunction; -use crate::obj::objcode; +use crate::obj::objcode::PyCodeRef; use crate::obj::objdict; use crate::obj::objdict::PyDict; use crate::obj::objint::PyInt; @@ -22,7 +22,7 @@ use crate::obj::objstr; use crate::obj::objtype; use crate::obj::objtype::PyClassRef; use crate::pyobject::{ - DictProtocol, IdProtocol, PyContext, PyObjectRef, PyResult, PyValue, TryFromObject, + DictProtocol, IdProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -186,6 +186,8 @@ enum BlockType { }, } +pub type FrameRef = PyRef; + pub struct Frame { pub code: bytecode::CodeObject, // We need 1 stack per frame @@ -211,7 +213,7 @@ pub enum ExecutionResult { pub type FrameResult = Result, PyObjectRef>; impl Frame { - pub fn new(code: PyObjectRef, scope: Scope) -> Frame { + pub fn new(code: PyCodeRef, scope: Scope) -> Frame { //populate the globals and locals //TODO: This is wrong, check https://github.com/nedbat/byterun/blob/31e6c4a8212c35b5157919abff43a7daa0f377c6/byterun/pyvm2.py#L95 /* @@ -224,7 +226,7 @@ impl Frame { // locals.extend(callargs); Frame { - code: objcode::get_value(&code), + code: code.code.clone(), stack: RefCell::new(vec![]), blocks: RefCell::new(vec![]), // save the callargs as locals @@ -562,7 +564,7 @@ impl Frame { } bytecode::Instruction::MakeFunction { flags } => { let _qualified_name = self.pop_value(); - let code_obj = self.pop_value(); + let code_obj = PyCodeRef::try_from_object(vm, self.pop_value())?; let annotations = if flags.contains(bytecode::FunctionOpArg::HAS_ANNOTATIONS) { self.pop_value() diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index 7fe54bf513..fd1511766d 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -13,7 +13,7 @@ use crate::vm::VirtualMachine; pub type PyCodeRef = PyRef; pub struct PyCode { - code: bytecode::CodeObject, + pub code: bytecode::CodeObject, } impl PyCode { @@ -38,30 +38,15 @@ pub fn init(context: &PyContext) { let code_type = context.code_type.as_object(); extend_class!(context, code_type, { "__new__" => context.new_rustfunc(code_new), - "__repr__" => context.new_rustfunc(code_repr) + "__repr__" => context.new_rustfunc(code_repr), + + "co_argcount" => context.new_property(code_co_argcount), + "co_consts" => context.new_property(code_co_consts), + "co_filename" => context.new_property(code_co_filename), + "co_firstlineno" => context.new_property(code_co_firstlineno), + "co_kwonlyargcount" => context.new_property(code_co_kwonlyargcount), + "co_name" => context.new_property(code_co_name), }); - - for (name, f) in &[ - ( - "co_argcount", - code_co_argcount as fn(&VirtualMachine, PyFuncArgs) -> PyResult, - ), - ("co_consts", code_co_consts), - ("co_filename", code_co_filename), - ("co_firstlineno", code_co_firstlineno), - ("co_kwonlyargcount", code_co_kwonlyargcount), - ("co_name", code_co_name), - ] { - context.set_attr(code_type, name, context.new_property(f)) - } -} - -pub fn get_value(obj: &PyObjectRef) -> bytecode::CodeObject { - if let Some(code) = obj.payload::() { - code.code.clone() - } else { - panic!("Inner error getting code {:?}", obj) - } } fn code_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -69,56 +54,42 @@ fn code_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { Err(vm.new_type_error("Cannot directly create code object".to_string())) } -fn code_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(o, Some(vm.ctx.code_type()))]); - - let code = get_value(o); - let repr = format!( +fn code_repr(o: PyCodeRef, _vm: &VirtualMachine) -> String { + let code = &o.code; + format!( "", code.obj_name, o.get_id(), code.source_path, code.first_line_number - ); - Ok(vm.new_str(repr)) -} - -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: &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_argcount(code: PyCodeRef, _vm: &VirtualMachine) -> usize { + code.code.arg_names.len() } -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_filename(code: PyCodeRef, _vm: &VirtualMachine) -> String { + code.code.source_path.clone() } -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_firstlineno(code: PyCodeRef, _vm: &VirtualMachine) -> usize { + code.code.first_line_number } -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_kwonlyargcount(code: PyCodeRef, _vm: &VirtualMachine) -> usize { + code.code.kwonlyarg_names.len() } -fn code_co_consts(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - let code_obj = member_code_obj(vm, args)?; - let consts = code_obj +fn code_co_consts(code: PyCodeRef, vm: &VirtualMachine) -> PyObjectRef { + let consts = code + .code .get_constants() .map(|x| vm.ctx.unwrap_constant(x)) .collect(); - Ok(vm.ctx.new_tuple(consts)) + vm.ctx.new_tuple(consts) } -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)) +fn code_co_name(code: PyCodeRef, _vm: &VirtualMachine) -> String { + code.code.obj_name.clone() } diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index 96fed8eec2..1488ee79c7 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -1,19 +1,22 @@ use crate::frame::Scope; use crate::function::PyFuncArgs; +use crate::obj::objcode::PyCodeRef; use crate::obj::objtype::PyClassRef; -use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyResult, PyValue, TypeProtocol}; +use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; +pub type PyFunctionRef = PyRef; + #[derive(Debug)] pub struct PyFunction { // TODO: these shouldn't be public - pub code: PyObjectRef, + pub code: PyCodeRef, pub scope: Scope, pub defaults: PyObjectRef, } impl PyFunction { - pub fn new(code: PyObjectRef, scope: Scope, defaults: PyObjectRef) -> Self { + pub fn new(code: PyCodeRef, scope: Scope, defaults: PyObjectRef) -> Self { PyFunction { code, scope, @@ -28,6 +31,12 @@ impl PyValue for PyFunction { } } +impl PyFunctionRef { + fn code(self, _vm: &VirtualMachine) -> PyCodeRef { + self.code.clone() + } +} + #[derive(Debug)] pub struct PyMethod { // TODO: these shouldn't be public @@ -51,7 +60,7 @@ pub fn init(context: &PyContext) { let function_type = &context.function_type; extend_class!(context, function_type, { "__get__" => context.new_rustfunc(bind_method), - "__code__" => context.new_property(function_code) + "__code__" => context.new_property(PyFunctionRef::code) }); let builtin_function_or_method_type = &context.builtin_function_or_method_type; @@ -73,10 +82,3 @@ fn bind_method(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bound_method(function.clone(), obj.clone())) } } - -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 b766aafd4d..9718d29a44 100644 --- a/vm/src/obj/objgenerator.rs +++ b/vm/src/obj/objgenerator.rs @@ -2,17 +2,17 @@ * The mythical generator. */ -use crate::frame::{ExecutionResult, Frame}; -use crate::function::PyFuncArgs; +use crate::frame::{ExecutionResult, FrameRef}; use crate::obj::objtype::PyClassRef; -use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol}; +use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; +pub type PyGeneratorRef = PyRef; + #[derive(Debug)] pub struct PyGenerator { - frame: PyObjectRef, + frame: FrameRef, } -type PyGeneratorRef = PyRef; impl PyValue for PyGenerator { fn class(vm: &VirtualMachine) -> PyClassRef { @@ -20,48 +20,23 @@ impl PyValue for PyGenerator { } } -pub fn init(context: &PyContext) { - let generator_type = &context.generator_type; - extend_class!(context, generator_type, { - "__iter__" => context.new_rustfunc(generator_iter), - "__next__" => context.new_rustfunc(generator_next), - "send" => context.new_rustfunc(generator_send) - }); -} - -pub fn new_generator(frame: PyObjectRef, vm: &VirtualMachine) -> PyGeneratorRef { - PyGenerator { frame }.into_ref(vm) -} - -fn generator_iter(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(o, Some(vm.ctx.generator_type()))]); - Ok(o.clone()) -} +impl PyGeneratorRef { + pub fn new(frame: FrameRef, vm: &VirtualMachine) -> PyGeneratorRef { + PyGenerator { frame }.into_ref(vm) + } -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 iter(self, _vm: &VirtualMachine) -> PyGeneratorRef { + self + } -fn generator_send(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(o, Some(vm.ctx.generator_type())), (value, None)] - ); - send(vm, o, value) -} + fn next(self, vm: &VirtualMachine) -> PyResult { + self.send(vm.get_none(), vm) + } -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()); - } else { - panic!("Generator frame isn't a frame."); - } + fn send(self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult { + self.frame.push_value(value.clone()); - match vm.run_frame(frame.clone())? { + match vm.run_frame(self.frame.clone())? { ExecutionResult::Yield(value) => Ok(value), ExecutionResult::Return(_value) => { // Stop iteration! @@ -69,7 +44,14 @@ fn send(vm: &VirtualMachine, gen: &PyObjectRef, value: &PyObjectRef) -> PyResult Err(vm.new_exception(stop_iteration, "End of generator".to_string())) } } - } else { - panic!("Cannot extract frame from non-generator"); } } + +pub fn init(context: &PyContext) { + let generator_type = &context.generator_type; + extend_class!(context, generator_type, { + "__iter__" => context.new_rustfunc(PyGeneratorRef::iter), + "__next__" => context.new_rustfunc(PyGeneratorRef::next), + "send" => context.new_rustfunc(PyGeneratorRef::send) + }); +} diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 80eba36da3..f5b8e06ed6 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -14,7 +14,7 @@ use num_traits::{One, Zero}; use crate::bytecode; use crate::exceptions; -use crate::frame::{Frame, Scope}; +use crate::frame::Scope; use crate::function::{IntoPyNativeFunc, PyFuncArgs}; use crate::obj::objbool; use crate::obj::objbuiltinfunc::PyBuiltinFunction; @@ -22,6 +22,7 @@ use crate::obj::objbytearray; use crate::obj::objbytes; use crate::obj::objclassmethod; use crate::obj::objcode; +use crate::obj::objcode::PyCodeRef; use crate::obj::objcomplex::{self, PyComplex}; use crate::obj::objdict::{self, PyDict}; use crate::obj::objellipsis; @@ -591,10 +592,6 @@ impl PyContext { ) } - pub fn new_frame(&self, code: PyObjectRef, scope: Scope) -> PyObjectRef { - PyObject::new(Frame::new(code, scope), self.frame_type()) - } - pub fn new_property(&self, f: F) -> PyObjectRef where F: IntoPyNativeFunc, @@ -608,7 +605,7 @@ impl PyContext { pub fn new_function( &self, - code_obj: PyObjectRef, + code_obj: PyCodeRef, scope: Scope, defaults: PyObjectRef, ) -> PyObjectRef { diff --git a/vm/src/stdlib/dis.rs b/vm/src/stdlib/dis.rs index 04209d8b3b..222f23383d 100644 --- a/vm/src/stdlib/dis.rs +++ b/vm/src/stdlib/dis.rs @@ -1,23 +1,18 @@ -use crate::function::PyFuncArgs; -use crate::obj::objcode; -use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; +use crate::obj::objcode::PyCodeRef; +use crate::pyobject::{PyContext, PyObjectRef, PyResult, TryFromObject}; use crate::vm::VirtualMachine; -fn dis_dis(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(obj, None)]); - +fn dis_dis(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { // Method or function: if let Ok(co) = vm.get_attribute(obj.clone(), "__code__") { - return dis_disassemble(vm, PyFuncArgs::new(vec![co], vec![])); + return dis_disassemble(co, vm); } - dis_disassemble(vm, PyFuncArgs::new(vec![obj.clone()], vec![])) + dis_disassemble(obj, vm) } -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); +fn dis_disassemble(co: PyObjectRef, vm: &VirtualMachine) -> PyResult { + let code = &PyCodeRef::try_from_object(vm, co)?.code; print!("{}", code); Ok(vm.get_none()) } diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index e1cfbd93b5..2c5c95d34b 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -184,7 +184,7 @@ impl<'de> Visitor<'de> for PyObjectDeserializer<'de> { where E: serde::de::Error, { - Ok(self.vm.ctx.none.clone().into_object()) + Ok(self.vm.get_none()) } } diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index 8a45bc1c7e..a4a37b350c 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -1,10 +1,8 @@ use std::rc::Rc; use std::{env, mem}; -use num_traits::ToPrimitive; - -use crate::function::PyFuncArgs; -use crate::obj::objint; +use crate::frame::FrameRef; +use crate::function::{OptionalArg, PyFuncArgs}; use crate::pyobject::{DictProtocol, PyContext, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; @@ -18,28 +16,12 @@ fn argv(ctx: &PyContext) -> PyObjectRef { ctx.new_list(argv) } -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 { - return Err(vm.new_value_error("call stack is not deep enough".to_string())); - } - return Ok(offset); - } +fn getframe(offset: OptionalArg, vm: &VirtualMachine) -> PyResult { + let offset = offset.into_option().unwrap_or(0); + if offset > vm.frames.borrow().len() - 1 { + return Err(vm.new_value_error("call stack is not deep enough".to_string())); } - Ok(0) -} - -fn getframe(vm: &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.borrow().len() - idx - 1; + let idx = vm.frames.borrow().len() - offset - 1; let frame = &vm.frames.borrow()[idx]; Ok(frame.clone()) } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index cde574fbcd..87d22069c2 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -14,15 +14,13 @@ use std::sync::{Mutex, MutexGuard}; use crate::builtins; use crate::bytecode; -use crate::frame::{ExecutionResult, Frame, Scope}; +use crate::frame::{ExecutionResult, Frame, FrameRef, Scope}; use crate::function::PyFuncArgs; use crate::obj::objbool; use crate::obj::objbuiltinfunc::PyBuiltinFunction; -use crate::obj::objcode; use crate::obj::objcode::PyCodeRef; -use crate::obj::objframe; use crate::obj::objfunction::{PyFunction, PyMethod}; -use crate::obj::objgenerator; +use crate::obj::objgenerator::PyGeneratorRef; use crate::obj::objiter; use crate::obj::objlist::PyList; use crate::obj::objsequence; @@ -31,7 +29,7 @@ use crate::obj::objtuple::PyTuple; use crate::obj::objtype; use crate::obj::objtype::PyClassRef; use crate::pyobject::{ - DictProtocol, IdProtocol, PyContext, PyObjectRef, PyResult, TryFromObject, TryIntoRef, + DictProtocol, IdProtocol, PyContext, PyObjectRef, PyResult, PyValue, TryFromObject, TryIntoRef, TypeProtocol, }; use crate::stdlib; @@ -49,7 +47,7 @@ pub struct VirtualMachine { pub sys_module: PyObjectRef, pub stdlib_inits: RefCell>, pub ctx: PyContext, - pub frames: RefCell>, + pub frames: RefCell>, pub wasm_id: Option, } @@ -74,30 +72,28 @@ impl VirtualMachine { } pub fn run_code_obj(&self, code: PyCodeRef, scope: Scope) -> PyResult { - let frame = self.ctx.new_frame(code.into_object(), scope); + let frame = Frame::new(code, scope).into_ref(self); self.run_frame_full(frame) } - pub fn run_frame_full(&self, frame: PyObjectRef) -> PyResult { + pub fn run_frame_full(&self, frame: FrameRef) -> PyResult { match self.run_frame(frame)? { ExecutionResult::Return(value) => Ok(value), _ => panic!("Got unexpected result from function"), } } - pub fn run_frame(&self, frame: PyObjectRef) -> PyResult { + pub fn run_frame(&self, frame: FrameRef) -> PyResult { self.frames.borrow_mut().push(frame.clone()); - let frame = objframe::get_value(&frame); let result = frame.run(self); self.frames.borrow_mut().pop(); result } - pub fn current_frame(&self) -> Ref { + 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) + &frames[index] }) } @@ -344,21 +340,20 @@ impl VirtualMachine { fn invoke_python_function( &self, - code: &PyObjectRef, + code: &PyCodeRef, scope: &Scope, defaults: &PyObjectRef, args: PyFuncArgs, ) -> PyResult { - let code_object = objcode::get_value(code); let scope = scope.child_scope(&self.ctx); - self.fill_locals_from_args(&code_object, &scope.get_locals(), args, defaults)?; + self.fill_locals_from_args(&code.code, &scope.get_locals(), args, defaults)?; // Construct frame: - let frame = self.ctx.new_frame(code.clone(), scope); + let frame = Frame::new(code.clone(), scope).into_ref(self); // If we have a generator, create a new generator - if code_object.is_generator { - Ok(objgenerator::new_generator(frame, self).into_object()) + if code.code.is_generator { + Ok(PyGeneratorRef::new(frame, self).into_object()) } else { self.run_frame_full(frame) } @@ -379,7 +374,7 @@ impl VirtualMachine { let scope = scope .child_scope_with_locals(cells) .child_scope_with_locals(locals); - let frame = self.ctx.new_frame(code.clone(), scope); + let frame = Frame::new(code.clone(), scope).into_ref(self); return self.run_frame_full(frame); } panic!( From 0f2889a0e5b861c57e97cec3fafc451818e2f1f7 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Sat, 23 Mar 2019 19:46:46 -0500 Subject: [PATCH 17/51] Fix weird rustfmt --- vm/src/obj/objsuper.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index 1920a73fef..17d015a5fe 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -123,10 +123,8 @@ fn super_new( 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 - ))); + return Err(vm + .new_type_error(format!("super argument {} was not supplied", first_arg))); } } } else { From 6eb93a7000a97318fb0c3a380d03fdc3818d304b Mon Sep 17 00:00:00 2001 From: ben Date: Sun, 24 Mar 2019 13:50:50 +1300 Subject: [PATCH 18/51] Move code methods into impl PyCodeRef --- vm/src/obj/objcode.rs | 102 +++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index fd1511766d..a26645b913 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -5,9 +5,8 @@ use std::fmt; use crate::bytecode; -use crate::function::PyFuncArgs; use crate::obj::objtype::PyClassRef; -use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol}; +use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; pub type PyCodeRef = PyRef; @@ -34,62 +33,63 @@ impl PyValue for PyCode { } } -pub fn init(context: &PyContext) { - let code_type = context.code_type.as_object(); - extend_class!(context, code_type, { - "__new__" => context.new_rustfunc(code_new), - "__repr__" => context.new_rustfunc(code_repr), - - "co_argcount" => context.new_property(code_co_argcount), - "co_consts" => context.new_property(code_co_consts), - "co_filename" => context.new_property(code_co_filename), - "co_firstlineno" => context.new_property(code_co_firstlineno), - "co_kwonlyargcount" => context.new_property(code_co_kwonlyargcount), - "co_name" => context.new_property(code_co_name), - }); -} +impl PyCodeRef { + fn new(_cls: PyClassRef, vm: &VirtualMachine) -> PyResult { + Err(vm.new_type_error("Cannot directly create code object".to_string())) + } -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 repr(self, _vm: &VirtualMachine) -> String { + let code = &self.code; + format!( + "", + code.obj_name, + self.get_id(), + code.source_path, + code.first_line_number + ) + } -fn code_repr(o: PyCodeRef, _vm: &VirtualMachine) -> String { - let code = &o.code; - format!( - "", - code.obj_name, - o.get_id(), - code.source_path, - code.first_line_number - ) -} + fn co_argcount(self, _vm: &VirtualMachine) -> usize { + self.code.arg_names.len() + } -fn code_co_argcount(code: PyCodeRef, _vm: &VirtualMachine) -> usize { - code.code.arg_names.len() -} + fn co_filename(self, _vm: &VirtualMachine) -> String { + self.code.source_path.clone() + } -fn code_co_filename(code: PyCodeRef, _vm: &VirtualMachine) -> String { - code.code.source_path.clone() -} + fn co_firstlineno(self, _vm: &VirtualMachine) -> usize { + self.code.first_line_number + } -fn code_co_firstlineno(code: PyCodeRef, _vm: &VirtualMachine) -> usize { - code.code.first_line_number -} + fn co_kwonlyargcount(self, _vm: &VirtualMachine) -> usize { + self.code.kwonlyarg_names.len() + } -fn code_co_kwonlyargcount(code: PyCodeRef, _vm: &VirtualMachine) -> usize { - code.code.kwonlyarg_names.len() -} + fn co_consts(self, vm: &VirtualMachine) -> PyObjectRef { + let consts = self + .code + .get_constants() + .map(|x| vm.ctx.unwrap_constant(x)) + .collect(); + vm.ctx.new_tuple(consts) + } -fn code_co_consts(code: PyCodeRef, vm: &VirtualMachine) -> PyObjectRef { - let consts = code - .code - .get_constants() - .map(|x| vm.ctx.unwrap_constant(x)) - .collect(); - vm.ctx.new_tuple(consts) + fn co_name(self, _vm: &VirtualMachine) -> String { + self.code.obj_name.clone() + } } -fn code_co_name(code: PyCodeRef, _vm: &VirtualMachine) -> String { - code.code.obj_name.clone() +pub fn init(context: &PyContext) { + let code_type = context.code_type.as_object(); + extend_class!(context, code_type, { + "__new__" => context.new_rustfunc(PyCodeRef::new), + "__repr__" => context.new_rustfunc(PyCodeRef::repr), + + "co_argcount" => context.new_property(PyCodeRef::co_argcount), + "co_consts" => context.new_property(PyCodeRef::co_consts), + "co_filename" => context.new_property(PyCodeRef::co_filename), + "co_firstlineno" => context.new_property(PyCodeRef::co_firstlineno), + "co_kwonlyargcount" => context.new_property(PyCodeRef::co_kwonlyargcount), + "co_name" => context.new_property(PyCodeRef::co_name), + }); } From 84d47d21cf74aec990dbb5e4ddc4fa2f449d1298 Mon Sep 17 00:00:00 2001 From: Joey Date: Sat, 23 Mar 2019 17:51:12 -0700 Subject: [PATCH 19/51] Rename to just Either --- vm/src/obj/objrange.rs | 8 ++++---- vm/src/pyobject.rs | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 8d526227a3..468490eeaa 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -7,7 +7,7 @@ use num_traits::{One, Signed, ToPrimitive, Zero}; use crate::function::{OptionalArg, PyFuncArgs}; use crate::pyobject::{ - Either2, PyContext, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, + Either, PyContext, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -212,16 +212,16 @@ impl PyRangeRef { .into_ref_with_type(vm, cls) } - fn getitem(self, subscript: Either2, vm: &VirtualMachine) -> PyResult { + fn getitem(self, subscript: Either, vm: &VirtualMachine) -> PyResult { match subscript { - Either2::A(index) => { + Either::A(index) => { if let Some(value) = self.get(index.value.clone()) { Ok(PyInt::new(value).into_ref(vm).into_object()) } else { Err(vm.new_index_error("range object index out of range".to_string())) } } - Either2::B(slice) => { + Either::B(slice) => { let new_start = if let Some(int) = slice.start.clone() { if let Some(i) = self.get(int) { i diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index befa798926..19a1eee9f7 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1203,7 +1203,7 @@ impl PyObjectPayload for T { } } -pub enum Either2 { +pub enum Either { A(A), B(B), } @@ -1216,28 +1216,28 @@ pub enum Either2 { /// ``` /// use rustpython_vm::VirtualMachine; /// use rustpython_vm::obj::{objstr::PyStringRef, objint::PyIntRef}; -/// use rustpython_vm::pyobject::Either2; +/// use rustpython_vm::pyobject::Either; /// -/// fn do_something(arg: Either2, vm: &VirtualMachine) { +/// fn do_something(arg: Either, vm: &VirtualMachine) { /// match arg { -/// Either2::A(int)=> { +/// Either::A(int)=> { /// // do something with int /// } -/// Either2::B(string) => { +/// Either::B(string) => { /// // do something with string /// } /// } /// } /// ``` -impl TryFromObject for Either2, PyRef> +impl TryFromObject for Either, PyRef> where A: PyValue, B: PyValue, { fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { obj.downcast::() - .map(Either2::A) - .or_else(|obj| obj.clone().downcast::().map(Either2::B)) + .map(Either::A) + .or_else(|obj| obj.clone().downcast::().map(Either::B)) .map_err(|obj| { vm.new_type_error(format!( "must be {} or {}, not {}", From 3f363585e8242387fd1b95d83757b7e187e7c3c0 Mon Sep 17 00:00:00 2001 From: ben Date: Sun, 24 Mar 2019 16:08:17 +1300 Subject: [PATCH 20/51] Panic if value poped of stack isn't a code object. --- vm/src/frame.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index f2a54a6325..2a0951ab39 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -564,7 +564,10 @@ impl Frame { } bytecode::Instruction::MakeFunction { flags } => { let _qualified_name = self.pop_value(); - let code_obj = PyCodeRef::try_from_object(vm, self.pop_value())?; + let code_obj = self + .pop_value() + .downcast() + .expect("Second to top value on the stack must be a code object"); let annotations = if flags.contains(bytecode::FunctionOpArg::HAS_ANNOTATIONS) { self.pop_value() From ec433cdbb91aa9babca4369906237e2db59eb3ff Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Sat, 23 Mar 2019 22:17:21 -0500 Subject: [PATCH 21/51] Fix rustfmt again (?) --- vm/src/obj/objsuper.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index 53e347b9fe..c41cf183e3 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -128,8 +128,9 @@ fn super_new( match vm.get_locals().get_item(first_arg) { Some(obj) => obj.clone(), _ => { - return Err(vm - .new_type_error(format!("super argument {} was not supplied", first_arg))); + return Err( + vm.new_type_error(format!("super arguement {} was not supplied", first_arg)) + ); } } } else { From 12564da369a9b3388b12d01043cda43bb56027c3 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Sat, 23 Mar 2019 22:46:21 -0500 Subject: [PATCH 22/51] Rustfmt? --- vm/src/obj/objsuper.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index c41cf183e3..f04fb66058 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -128,9 +128,8 @@ fn super_new( 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)) - ); + return Err(vm + .new_type_error(format!("super arguement {} was not supplied", first_arg))); } } } else { From 9a6f7aa8a1f58e346e1da4027a608c67d4dfc7ba Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Fri, 22 Mar 2019 19:12:33 +0100 Subject: [PATCH 23/51] Make PyRef::clone not require T implementing Clone It seems to be a weird consequence of using PhantomData, so I just rolled a custom Clone. --- vm/src/obj/objmodule.rs | 2 +- vm/src/obj/objnone.rs | 2 +- vm/src/obj/objobject.rs | 2 +- vm/src/obj/objtuple.rs | 1 - vm/src/obj/objtype.rs | 2 +- 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/vm/src/obj/objmodule.rs b/vm/src/obj/objmodule.rs index 2d1d8921e1..58d8688db2 100644 --- a/vm/src/obj/objmodule.rs +++ b/vm/src/obj/objmodule.rs @@ -3,7 +3,7 @@ use crate::obj::objtype::PyClassRef; use crate::pyobject::{DictProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct PyModule { pub name: String, pub dict: PyObjectRef, diff --git a/vm/src/obj/objnone.rs b/vm/src/obj/objnone.rs index 7b0eefec21..840b00eccc 100644 --- a/vm/src/obj/objnone.rs +++ b/vm/src/obj/objnone.rs @@ -6,7 +6,7 @@ use crate::pyobject::{ }; use crate::vm::VirtualMachine; -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct PyNone; pub type PyNoneRef = PyRef; diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 84ba73fe68..46a90114dc 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -11,7 +11,7 @@ use crate::pyobject::{ }; use crate::vm::VirtualMachine; -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct PyInstance; impl PyValue for PyInstance { diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index e5bf5c1eec..a199e34b18 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -15,7 +15,6 @@ use super::objsequence::{ }; use super::objtype::{self, PyClassRef}; -#[derive(Default)] pub struct PyTuple { // TODO: shouldn't be public // TODO: tuples are immutable, remove this RefCell diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 9d3c9fe490..a8dd3289a6 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -16,7 +16,7 @@ use super::objstr::PyStringRef; use super::objtuple::PyTuple; use crate::obj::objdict::PyDictRef; -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct PyClass { pub name: String, pub mro: Vec, From a895ab35aeadcdc3b6504b531104765221212e18 Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Fri, 22 Mar 2019 19:13:26 +0100 Subject: [PATCH 24/51] Make PyWeak::downgrade take a reference --- vm/src/obj/objweakref.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vm/src/obj/objweakref.rs b/vm/src/obj/objweakref.rs index 3a71e3b630..685e32b704 100644 --- a/vm/src/obj/objweakref.rs +++ b/vm/src/obj/objweakref.rs @@ -11,9 +11,9 @@ pub struct PyWeak { } impl PyWeak { - pub fn downgrade(obj: PyObjectRef) -> PyWeak { + pub fn downgrade(obj: &PyObjectRef) -> PyWeak { PyWeak { - referent: Rc::downgrade(&obj), + referent: Rc::downgrade(obj), } } @@ -33,7 +33,7 @@ pub type PyWeakRef = PyRef; impl PyWeakRef { // TODO callbacks fn create(cls: PyClassRef, referent: PyObjectRef, vm: &VirtualMachine) -> PyResult { - PyWeak::downgrade(referent).into_ref_with_type(vm, cls) + PyWeak::downgrade(&referent).into_ref_with_type(vm, cls) } fn call(self, vm: &VirtualMachine) -> PyObjectRef { From d548f9c476ce4906657da2ce79bf573cae727343 Mon Sep 17 00:00:00 2001 From: jgirardet Date: Sun, 24 Mar 2019 12:02:06 +0100 Subject: [PATCH 25/51] refactor contains --- vm/src/obj/objbytes.rs | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 2a4b0114b5..f3b4b1b754 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -11,6 +11,7 @@ use crate::pyobject::{ use crate::vm::VirtualMachine; use super::objint; +use super::objsequence::get_item; use super::objtype::{self, PyClassRef}; #[derive(Debug)] @@ -204,21 +205,13 @@ fn bytes_iter(obj: PyBytesRef, _vm: &VirtualMachine) -> PyIteratorValue { } } -fn compare_slice(a: &[u8], b: &[u8]) -> bool { - for (i, j) in a.iter().zip(b.iter()) { - if i != j { - return false; - } - } - return true; -} - -fn compare_vec(a: &Vec, b: &Vec) -> bool { +/// return true if b is a subset of a. +fn vec_contains(a: &Vec, b: &Vec) -> bool { let a_len = a.len(); let b_len = b.len(); for (n, i) in a.iter().enumerate() { if n + b_len <= a_len && *i == b[0] { - if compare_slice(&a[n..n + b_len], b) { + if &a[n..n + b_len] == b.as_slice() { return true; } } @@ -239,20 +232,13 @@ impl PyBytesRef { fn contains(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&needle, &vm.ctx.bytes_type()) { - if compare_vec(&self.value, &get_value(&needle)) { - return Ok(true); - } else { - return Ok(false); - } + let result = vec_contains(&self.value, &get_value(&needle)); + Ok(result) } else if objtype::isinstance(&needle, &vm.ctx.int_type()) { - let c = self + let result = self .value .contains(&objint::get_value(&needle).to_u8().unwrap()); - if c == true { - return Ok(true); - } else { - return Ok(false); - } + Ok(result) } else { Err(vm.new_type_error(format!("Cannot add {:?} and {:?}", self, needle))) } From e39df6daefb3ba1dbdb4e3a75770da57c2572d99 Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Fri, 22 Mar 2019 19:18:33 +0100 Subject: [PATCH 26/51] Basic implementation of T.__subclasses__(). Ideally, the weak list should be updated whenever a type is removed, and the list shouldn't be a Vec, but this should be good enough for starters. --- tests/snippets/types_snippet.py | 23 +++++++++++++++++++++++ vm/src/obj/objtype.rs | 26 ++++++++++++++++++++++++-- vm/src/pyobject.rs | 13 +++++++++++-- 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/tests/snippets/types_snippet.py b/tests/snippets/types_snippet.py index ac715751ef..f441552d11 100644 --- a/tests/snippets/types_snippet.py +++ b/tests/snippets/types_snippet.py @@ -26,3 +26,26 @@ assert isinstance(type, type) assert issubclass(type, type) + +class A: pass +class B(A): pass +class C(A): pass +class D(B, C): pass + +assert A.__subclasses__() == [B, C] +assert B.__subclasses__() == [D] +assert C.__subclasses__() == [D] +assert D.__subclasses__() == [] + +del D + +try: # gc sweep is needed here for CPython... + import gc; gc.collect() +except: # ...while RustPython doesn't have `gc` yet. + pass + +assert B.__subclasses__() == [] +assert C.__subclasses__() == [] + +assert type in object.__subclasses__() + diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index a8dd3289a6..89bd8f34a5 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -10,16 +10,18 @@ use crate::pyobject::{ use crate::vm::VirtualMachine; use super::objdict; +use super::objdict::PyDictRef; use super::objlist::PyList; use super::objproperty::PropertyBuilder; use super::objstr::PyStringRef; use super::objtuple::PyTuple; -use crate::obj::objdict::PyDictRef; +use super::objweakref::PyWeak; #[derive(Debug)] pub struct PyClass { pub name: String, pub mro: Vec, + pub subclasses: RefCell>, } impl fmt::Display for PyClass { @@ -146,6 +148,17 @@ impl PyClassRef { Err(vm.new_attribute_error(format!("{} has no attribute '{}'", self, name))) } } + + fn subclasses(self, _vm: &VirtualMachine) -> PyList { + let mut subclasses = self.subclasses.borrow_mut(); + subclasses.retain(|x| x.upgrade().is_some()); + PyList::from( + subclasses + .iter() + .map(|x| x.upgrade().unwrap()) + .collect::>(), + ) + } } /* @@ -168,6 +181,8 @@ pub fn init(ctx: &PyContext) { "__repr__" => ctx.new_rustfunc(PyClassRef::repr), "__prepare__" => ctx.new_rustfunc(PyClassRef::prepare), "__getattribute__" => ctx.new_rustfunc(PyClassRef::getattribute), + "__subclasses__" => ctx.new_rustfunc(PyClassRef::subclasses), + "__getattribute__" => ctx.new_rustfunc(PyClassRef::getattribute), "__instancecheck__" => ctx.new_rustfunc(PyClassRef::instance_check), "__subclasscheck__" => ctx.new_rustfunc(PyClassRef::subclass_check), "__doc__" => ctx.new_str(type_doc.to_string()), @@ -352,17 +367,24 @@ pub fn new( bases: Vec, dict: HashMap, ) -> PyResult { - let mros = bases.into_iter().map(|x| _mro(&x)).collect(); + let mros = bases.iter().map(|x| _mro(&x)).collect(); let mro = linearise_mro(mros).unwrap(); let new_type = PyObject { payload: PyClass { name: String::from(name), mro, + subclasses: RefCell::new(vec![]), }, dict: Some(RefCell::new(dict)), typ, } .into_ref(); + for base in bases { + base.subclasses + .borrow_mut() + .push(PyWeak::downgrade(&new_type)); + } + Ok(new_type.downcast().unwrap()) } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index da16e1ac86..e4488bdb49 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -183,13 +183,14 @@ fn init_type_hierarchy() -> (PyClassRef, PyClassRef) { // 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 (type_type, object_type) = unsafe { let object_type = PyObject { typ: mem::uninitialized(), // ! dict: Some(RefCell::new(PyAttributes::new())), payload: PyClass { name: String::from("object"), mro: vec![], + subclasses: RefCell::new(vec![]), }, } .into_ref(); @@ -200,6 +201,7 @@ fn init_type_hierarchy() -> (PyClassRef, PyClassRef) { payload: PyClass { name: String::from("type"), mro: vec![object_type.clone().downcast().unwrap()], + subclasses: RefCell::new(vec![]), }, } .into_ref(); @@ -214,7 +216,14 @@ fn init_type_hierarchy() -> (PyClassRef, PyClassRef) { ptr::write(&mut (*type_type_ptr).typ, type_type.clone()); (type_type, object_type) - } + }; + + object_type + .subclasses + .borrow_mut() + .push(objweakref::PyWeak::downgrade(&type_type.as_object())); + + (type_type, object_type) } // Basic objects: From d0c4fcb2fa65260c3eb72120cb303c8d1cbf7668 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Sun, 24 Mar 2019 13:14:38 +0100 Subject: [PATCH 27/51] Doing what is needed for all our non-latin friends. --- Cargo.lock | 1 + parser/Cargo.toml | 2 +- parser/src/lexer.rs | 595 +++++++++++++++++++---------------- src/main.rs | 2 +- tests/snippets/unicode_fu.py | 13 + 5 files changed, 333 insertions(+), 280 deletions(-) create mode 100644 tests/snippets/unicode_fu.py diff --git a/Cargo.lock b/Cargo.lock index 818d0af89e..57dadb57b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -760,6 +760,7 @@ dependencies = [ "num-bigint 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)", "regex 0.2.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]] diff --git a/parser/Cargo.toml b/parser/Cargo.toml index e57d421406..7545a27d44 100644 --- a/parser/Cargo.toml +++ b/parser/Cargo.toml @@ -14,4 +14,4 @@ log="0.4.1" regex="0.2.2" num-bigint = "0.2" num-traits = "0.2" - +unicode-xid = "0.1.0" diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index 113f7afb1a..db8fee2c70 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -1,12 +1,15 @@ //! This module takes care of lexing python source text. This means source //! code is translated into separate tokens. +extern crate unicode_xid; + pub use super::token::Tok; use num_bigint::BigInt; use num_traits::Num; use std::cmp::Ordering; use std::collections::HashMap; use std::str::FromStr; +use unicode_xid::UnicodeXID; #[derive(Clone, Copy, PartialEq, Debug)] struct IndentationLevel { @@ -300,7 +303,7 @@ where } } - while self.is_char() { + while self.is_identifier_continuation() { name.push(self.next_char().unwrap()); } let end_pos = self.get_pos(); @@ -540,10 +543,21 @@ where Ok((start_pos, tok, end_pos)) } - fn is_char(&self) -> bool { - match self.chr0 { - Some('a'..='z') | Some('A'..='Z') | Some('_') | Some('0'..='9') => true, - _ => false, + fn is_identifier_start(&self, c: char) -> bool { + match c { + 'a'..='z' | 'A'..='Z' | '_' => true, + c => UnicodeXID::is_xid_start(c), + } + } + + fn is_identifier_continuation(&self) -> bool { + if let Some(c) = self.chr0 { + match c { + 'a'..='z' | 'A'..='Z' | '_' | '0'..='9' => true, + c => UnicodeXID::is_xid_continue(c), + } + } else { + false } } @@ -686,347 +700,372 @@ where } } - match self.chr0 { - Some('0'..='9') => return Some(self.lex_number()), - Some('_') | Some('a'..='z') | Some('A'..='Z') => { + // Check if we have some character: + if let Some(c) = self.chr0 { + // First check identifier: + if self.is_identifier_start(c) { return Some(self.lex_identifier()); - } - Some('#') => { - self.lex_comment(); - continue; - } - Some('"') => { - return Some(self.lex_string(false, false, false, false)); - } - Some('\'') => { - return Some(self.lex_string(false, false, false, false)); - } - 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::EqEqual, tok_end))); + } else { + match c { + '0'..='9' => return Some(self.lex_number()), + '#' => { + self.lex_comment(); + continue; } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Equal, tok_end))); + '"' => { + return Some(self.lex_string(false, false, false, false)); } - } - } - Some('+') => { - let tok_start = self.get_pos(); - self.next_char(); - 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('*') => { - 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::StarEqual, tok_end))); + '\'' => { + return Some(self.lex_string(false, false, false, false)); } - 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::DoubleStarEqual, tok_end))); + return Some(Ok((tok_start, Tok::EqEqual, tok_end))); } _ => { let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::DoubleStar, tok_end))); + return Some(Ok((tok_start, Tok::Equal, tok_end))); } } } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Star, tok_end))); - } - } - } - Some('/') => { - let tok_start = self.get_pos(); - self.next_char(); - match self.chr0 { - Some('=') => { + '+' => { + let tok_start = self.get_pos(); self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::SlashEqual, 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('/') => { + '*' => { + 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::DoubleSlashEqual, tok_end))); + return Some(Ok((tok_start, Tok::StarEqual, tok_end))); + } + Some('*') => { + self.next_char(); + match self.chr0 { + Some('=') => { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok(( + tok_start, + Tok::DoubleStarEqual, + tok_end, + ))); + } + _ => { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::DoubleStar, tok_end))); + } + } } _ => { let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::DoubleSlash, tok_end))); + return Some(Ok((tok_start, Tok::Star, tok_end))); } } } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Slash, tok_end))); + '/' => { + 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::SlashEqual, tok_end))); + } + Some('/') => { + self.next_char(); + match self.chr0 { + Some('=') => { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok(( + tok_start, + Tok::DoubleSlashEqual, + tok_end, + ))); + } + _ => { + let tok_end = self.get_pos(); + return Some(Ok(( + tok_start, + Tok::DoubleSlash, + tok_end, + ))); + } + } + } + _ => { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Slash, tok_end))); + } + } } - } - } - Some('%') => { - let tok_start = self.get_pos(); - self.next_char(); - 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(); - 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(); - 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(); - 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('-') => { - let tok_start = self.get_pos(); - self.next_char(); - match self.chr0 { - Some('=') => { + '%' => { + let tok_start = self.get_pos(); self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::MinusEqual, 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(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Rarrow, 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))); + } } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Minus, tok_end))); + '^' => { + let tok_start = self.get_pos(); + self.next_char(); + 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(); - 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('!') => { - let tok_start = self.get_pos(); - self.next_char(); - match self.chr0 { - Some('=') => { + '&' => { + let tok_start = self.get_pos(); self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::NotEqual, 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))); + } } - _ => panic!("Invalid token '!'"), - } - } - Some('~') => { - return Some(self.eat_single_char(Tok::Tilde)); - } - Some('(') => { - let result = self.eat_single_char(Tok::Lpar); - self.nesting += 1; - return Some(result); - } - Some(')') => { - let result = self.eat_single_char(Tok::Rpar); - if self.nesting == 0 { - return Some(Err(LexicalError::NestingError)); - } - self.nesting -= 1; - return Some(result); - } - Some('[') => { - let result = self.eat_single_char(Tok::Lsqb); - self.nesting += 1; - return Some(result); - } - Some(']') => { - let result = self.eat_single_char(Tok::Rsqb); - if self.nesting == 0 { - return Some(Err(LexicalError::NestingError)); - } - self.nesting -= 1; - return Some(result); - } - Some('{') => { - let result = self.eat_single_char(Tok::Lbrace); - self.nesting += 1; - return Some(result); - } - Some('}') => { - let result = self.eat_single_char(Tok::Rbrace); - if self.nesting == 0 { - return Some(Err(LexicalError::NestingError)); - } - self.nesting -= 1; - return Some(result); - } - Some(':') => { - return Some(self.eat_single_char(Tok::Colon)); - } - Some(';') => { - return Some(self.eat_single_char(Tok::Semi)); - } - Some('<') => { - let tok_start = self.get_pos(); - self.next_char(); - match self.chr0 { - 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::LeftShiftEqual, tok_end))); + return Some(Ok((tok_start, Tok::MinusEqual, tok_end))); + } + Some('>') => { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Rarrow, tok_end))); } _ => { let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::LeftShift, tok_end))); + return Some(Ok((tok_start, Tok::Minus, tok_end))); } } } - Some('=') => { + '@' => { + let tok_start = self.get_pos(); self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::LessEqual, 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))); + } } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Less, tok_end))); + '!' => { + let tok_start = self.get_pos(); + self.next_char(); + if let Some('=') = self.chr0 { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::NotEqual, tok_end))); + } else { + return Some(Err(LexicalError::UnrecognizedToken { tok: '!' })); + } } - } - } - Some('>') => { - let tok_start = self.get_pos(); - self.next_char(); - match self.chr0 { - Some('>') => { + '~' => { + return Some(self.eat_single_char(Tok::Tilde)); + } + '(' => { + let result = self.eat_single_char(Tok::Lpar); + self.nesting += 1; + return Some(result); + } + ')' => { + let result = self.eat_single_char(Tok::Rpar); + if self.nesting == 0 { + return Some(Err(LexicalError::NestingError)); + } + self.nesting -= 1; + return Some(result); + } + '[' => { + let result = self.eat_single_char(Tok::Lsqb); + self.nesting += 1; + return Some(result); + } + ']' => { + let result = self.eat_single_char(Tok::Rsqb); + if self.nesting == 0 { + return Some(Err(LexicalError::NestingError)); + } + self.nesting -= 1; + return Some(result); + } + '{' => { + let result = self.eat_single_char(Tok::Lbrace); + self.nesting += 1; + return Some(result); + } + '}' => { + let result = self.eat_single_char(Tok::Rbrace); + if self.nesting == 0 { + return Some(Err(LexicalError::NestingError)); + } + self.nesting -= 1; + return Some(result); + } + ':' => { + return Some(self.eat_single_char(Tok::Colon)); + } + ';' => { + return Some(self.eat_single_char(Tok::Semi)); + } + '<' => { + let tok_start = self.get_pos(); self.next_char(); match self.chr0 { + Some('<') => { + self.next_char(); + match self.chr0 { + Some('=') => { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok(( + tok_start, + Tok::LeftShiftEqual, + tok_end, + ))); + } + _ => { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::LeftShift, tok_end))); + } + } + } Some('=') => { self.next_char(); let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::RightShiftEqual, tok_end))); + return Some(Ok((tok_start, Tok::LessEqual, tok_end))); } _ => { let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::RightShift, tok_end))); + return Some(Ok((tok_start, Tok::Less, tok_end))); } } } - Some('=') => { + '>' => { + let tok_start = self.get_pos(); + self.next_char(); + match self.chr0 { + Some('>') => { + self.next_char(); + match self.chr0 { + Some('=') => { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok(( + tok_start, + Tok::RightShiftEqual, + tok_end, + ))); + } + _ => { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::RightShift, tok_end))); + } + } + } + Some('=') => { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::GreaterEqual, tok_end))); + } + _ => { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Greater, tok_end))); + } + } + } + ',' => { + let tok_start = self.get_pos(); self.next_char(); let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::GreaterEqual, tok_end))); + return Some(Ok((tok_start, Tok::Comma, tok_end))); } - _ => { + '.' => { + let tok_start = self.get_pos(); + self.next_char(); + 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))); + } + } + '\n' => { + let tok_start = self.get_pos(); + self.next_char(); let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Greater, tok_end))); + self.new_line(); + + // Depending on the nesting level, we emit newline or not: + if self.nesting == 0 { + self.at_begin_of_line = true; + return Some(Ok((tok_start, Tok::Newline, tok_end))); + } else { + continue; + } } + ' ' => { + // Skip whitespaces + self.next_char(); + continue; + } + _ => { + let c = self.next_char(); + return Some(Err(LexicalError::UnrecognizedToken { tok: c.unwrap() })); + } // Ignore all the rest.. } } - Some(',') => { - let tok_start = self.get_pos(); - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Comma, tok_end))); - } - Some('.') => { - let tok_start = self.get_pos(); - self.next_char(); - 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(); - self.next_char(); - let tok_end = self.get_pos(); - self.new_line(); - - // Depending on the nesting level, we emit newline or not: - if self.nesting == 0 { - self.at_begin_of_line = true; - return Some(Ok((tok_start, Tok::Newline, tok_end))); - } else { - continue; - } - } - Some(' ') => { - // Skip whitespaces - self.next_char(); - continue; - } - None => return None, - _ => { - let c = self.next_char(); - return Some(Err(LexicalError::UnrecognizedToken { tok: c.unwrap() })); - } // Ignore all the rest.. + } else { + return None; } } } diff --git a/src/main.rs b/src/main.rs index 70adf10080..750faf98a8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -153,7 +153,7 @@ fn get_prompt(vm: &VirtualMachine, prompt_name: &str) -> String { fn run_shell(vm: &VirtualMachine) -> PyResult { println!( - "Welcome to the magnificent Rust Python {} interpreter", + "Welcome to the magnificent Rust Python {} interpreter \u{1f631} \u{1f596}", crate_version!() ); let vars = vm.ctx.new_scope(); diff --git a/tests/snippets/unicode_fu.py b/tests/snippets/unicode_fu.py new file mode 100644 index 0000000000..96d5bf9770 --- /dev/null +++ b/tests/snippets/unicode_fu.py @@ -0,0 +1,13 @@ + +# Test the unicode support! 👋 + + +áš´=2 + +assert áš´*8 == 16 + +áš´="👋" + +c = áš´*3 + +assert c == '👋👋👋' From c2e1e9b0dc0a4180a1ccb8c8bb5a4c7e5dee27c2 Mon Sep 17 00:00:00 2001 From: Joey Date: Sun, 24 Mar 2019 08:51:26 -0700 Subject: [PATCH 28/51] Convert range to new args style --- vm/src/obj/objint.rs | 7 + vm/src/obj/objrange.rs | 322 ++++++++++++++++------------------------- 2 files changed, 129 insertions(+), 200 deletions(-) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 7c19968da3..345fe90c31 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -1,3 +1,4 @@ +use std::fmt; use std::hash::{Hash, Hasher}; use num_bigint::{BigInt, ToBigInt}; @@ -22,6 +23,12 @@ pub struct PyInt { pub value: BigInt, } +impl fmt::Display for PyInt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + BigInt::fmt(&self.value, f) + } +} + pub type PyIntRef = PyRef; impl PyInt { diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 468490eeaa..9ae2fcd541 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -3,17 +3,15 @@ use std::ops::Mul; use num_bigint::{BigInt, Sign}; use num_integer::Integer; -use num_traits::{One, Signed, ToPrimitive, Zero}; +use num_traits::{One, Signed, Zero}; use crate::function::{OptionalArg, PyFuncArgs}; -use crate::pyobject::{ - Either, PyContext, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, -}; +use crate::pyobject::{Either, PyContext, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; -use super::objint::{self, PyInt, PyIntRef}; +use super::objint::{PyInt, PyIntRef}; use super::objslice::PySliceRef; -use super::objtype::{self, PyClassRef}; +use super::objtype::PyClassRef; #[derive(Debug, Clone)] pub struct PyRange { @@ -31,26 +29,6 @@ impl PyValue for PyRange { } impl PyRange { - #[inline] - pub fn try_len(&self) -> Option { - match self.step.sign() { - Sign::Plus if self.start < self.stop => ((&self.stop - &self.start - 1usize) - / &self.step) - .to_usize() - .map(|sz| sz + 1), - Sign::Minus if self.start > self.stop => ((&self.start - &self.stop - 1usize) - / (-&self.step)) - .to_usize() - .map(|sz| sz + 1), - _ => Some(0), - } - } - - #[inline] - pub fn len(&self) -> usize { - self.try_len().unwrap() - } - #[inline] fn offset(&self, value: &BigInt) -> Option { match self.step.sign() { @@ -60,14 +38,6 @@ impl PyRange { } } - #[inline] - pub fn contains(&self, value: &BigInt) -> bool { - match self.offset(value) { - Some(ref offset) => offset.is_multiple_of(&self.step), - None => false, - } - } - #[inline] pub fn index_of(&self, value: &BigInt) -> Option { match self.offset(value) { @@ -78,15 +48,6 @@ impl PyRange { } } - #[inline] - pub fn count(&self, value: &BigInt) -> usize { - if self.index_of(value).is_some() { - 1 - } else { - 0 - } - } - #[inline] pub fn is_empty(&self) -> bool { (self.start <= self.stop && self.step.is_negative()) @@ -113,40 +74,6 @@ impl PyRange { None } } - - #[inline] - pub fn reversed(&self) -> Self { - // compute the last element that is actually contained within the range - // this is the new start - let remainder = ((&self.stop - &self.start) % &self.step).abs(); - let start = if remainder.is_zero() { - &self.stop - &self.step - } else { - &self.stop - &remainder - }; - - match self.step.sign() { - Sign::Plus => PyRange { - start, - stop: &self.start - 1, - step: -&self.step, - }, - Sign::Minus => PyRange { - start, - stop: &self.start + 1, - step: -&self.step, - }, - Sign::NoSign => unreachable!(), - } - } - - pub fn repr(&self) -> String { - if self.step == BigInt::one() { - format!("range({}, {})", self.start, self.stop) - } else { - format!("range({}, {}, {})", self.start, self.stop, self.step) - } - } } pub fn get_value(obj: &PyObjectRef) -> PyRange { @@ -165,20 +92,20 @@ pub fn init(context: &PyContext) { When step is given, it specifies the increment (or decrement)."; extend_class!(context, range_type, { - "__bool__" => context.new_rustfunc(range_bool), - "__contains__" => context.new_rustfunc(range_contains), + "__bool__" => context.new_rustfunc(PyRangeRef::bool), + "__contains__" => context.new_rustfunc(PyRangeRef::contains), "__doc__" => context.new_str(range_doc.to_string()), "__getitem__" => context.new_rustfunc(PyRangeRef::getitem), - "__iter__" => context.new_rustfunc(range_iter), - "__len__" => context.new_rustfunc(range_len), + "__iter__" => context.new_rustfunc(PyRangeRef::iter), + "__len__" => context.new_rustfunc(PyRangeRef::len), "__new__" => context.new_rustfunc(range_new), - "__repr__" => context.new_rustfunc(range_repr), - "__reversed__" => context.new_rustfunc(range_reversed), - "count" => context.new_rustfunc(range_count), - "index" => context.new_rustfunc(range_index), - "start" => context.new_property(range_start), - "step" => context.new_property(range_step), - "stop" => context.new_property(range_stop) + "__repr__" => context.new_rustfunc(PyRangeRef::repr), + "__reversed__" => context.new_rustfunc(PyRangeRef::reversed), + "count" => context.new_rustfunc(PyRangeRef::count), + "index" => context.new_rustfunc(PyRangeRef::index), + "start" => context.new_property(PyRangeRef::start), + "stop" => context.new_property(PyRangeRef::stop), + "step" => context.new_property(PyRangeRef::step), }); } @@ -212,6 +139,113 @@ impl PyRangeRef { .into_ref_with_type(vm, cls) } + fn start(self, _vm: &VirtualMachine) -> BigInt { + self.start.clone() + } + + fn stop(self, _vm: &VirtualMachine) -> BigInt { + self.stop.clone() + } + + fn step(self, _vm: &VirtualMachine) -> BigInt { + self.step.clone() + } + + fn iter(self: PyRangeRef, _vm: &VirtualMachine) -> PyIteratorValue { + PyIteratorValue { + position: Cell::new(0), + iterated_obj: self.into_object(), + } + } + + fn reversed(self: PyRangeRef, vm: &VirtualMachine) -> PyIteratorValue { + // compute the last element that is actually contained within the range + // this is the new start + let remainder = ((&self.stop - &self.start) % &self.step).abs(); + let start = if remainder.is_zero() { + &self.stop - &self.step + } else { + &self.stop - &remainder + }; + + let reversed = match self.step.sign() { + Sign::Plus => PyRange { + start, + stop: &self.start - 1, + step: -&self.step, + }, + Sign::Minus => PyRange { + start, + stop: &self.start + 1, + step: -&self.step, + }, + Sign::NoSign => unreachable!(), + }; + PyIteratorValue { + position: Cell::new(0), + iterated_obj: reversed.into_ref(vm).into_object(), + } + } + + fn len(self, _vm: &VirtualMachine) -> PyInt { + match self.step.sign() { + Sign::Plus if self.start < self.stop => { + PyInt::new((&self.stop - &self.start - 1usize) / &self.step + 1) + } + Sign::Minus if self.start > self.stop => { + PyInt::new((&self.start - &self.stop - 1usize) / (-&self.step) + 1) + } + Sign::Plus | Sign::Minus => PyInt::new(0), + Sign::NoSign => unreachable!(), + } + } + + fn repr(self, _vm: &VirtualMachine) -> String { + if self.step.is_one() { + format!("range({}, {})", self.start, self.stop) + } else { + format!("range({}, {}, {})", self.start, self.stop, self.step) + } + } + + fn bool(self, _vm: &VirtualMachine) -> bool { + !self.is_empty() + } + + fn contains(self, needle: PyObjectRef, _vm: &VirtualMachine) -> bool { + if let Ok(int) = needle.downcast::() { + match self.offset(&int.value) { + Some(ref offset) => offset.is_multiple_of(&self.step), + None => false, + } + } else { + false + } + } + + fn index(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { + if let Ok(int) = needle.downcast::() { + match self.index_of(&int.value) { + Some(idx) => Ok(PyInt::new(idx)), + None => Err(vm.new_value_error(format!("{} is not in range", int))), + } + } else { + Err(vm.new_value_error("sequence.index(x): x not in sequence".to_string())) + } + } + + fn count(self, item: PyObjectRef, _vm: &VirtualMachine) -> PyInt { + if let Ok(int) = item.downcast::() { + if self.index_of(&int.value).is_some() { + PyInt::new(1) + } else { + PyInt::new(0) + } + } else { + PyInt::new(0) + } + } + fn getitem(self, subscript: Either, vm: &VirtualMachine) -> PyResult { match subscript { Either::A(index) => { @@ -271,115 +305,3 @@ fn range_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(range.into_object()) } - -fn range_iter(range: PyRangeRef, _vm: &VirtualMachine) -> PyIteratorValue { - PyIteratorValue { - position: Cell::new(0), - iterated_obj: range.into_object(), - } -} - -fn range_reversed(zelf: PyRangeRef, vm: &VirtualMachine) -> PyIteratorValue { - let range = zelf.reversed(); - - PyIteratorValue { - position: Cell::new(0), - iterated_obj: range.into_ref(vm).into_object(), - } -} - -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() { - Ok(vm.ctx.new_int(len)) - } else { - Err(vm.new_overflow_error("Python int too large to convert to Rust usize".to_string())) - } -} - -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(); - - Ok(vm.ctx.new_str(repr)) -} - -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(); - - Ok(vm.ctx.new_bool(len > 0)) -} - -fn range_contains(vm: &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: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.range_type())), (needle, None)] - ); - - let range = get_value(zelf); - - if objtype::isinstance(needle, &vm.ctx.int_type()) { - let needle = objint::get_value(needle); - - match range.index_of(&needle) { - Some(idx) => Ok(vm.ctx.new_int(idx)), - None => Err(vm.new_value_error(format!("{} is not in range", needle))), - } - } else { - Err(vm.new_value_error("sequence.index(x): x not in sequence".to_string())) - } -} - -fn range_count(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.range_type())), (item, None)] - ); - - let range = get_value(zelf); - - if objtype::isinstance(item, &vm.ctx.int_type()) { - Ok(vm.ctx.new_int(range.count(&objint::get_value(item)))) - } else { - Ok(vm.ctx.new_int(0)) - } -} - -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: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); - Ok(vm.ctx.new_int(get_value(zelf).stop)) -} - -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)) -} From 9a0113deedde20c7791f6c07936dc3e0c4b21e40 Mon Sep 17 00:00:00 2001 From: Joey Date: Sun, 24 Mar 2019 09:57:26 -0700 Subject: [PATCH 29/51] range: represent w/ int refs --- tests/snippets/builtin_range.py | 4 ++ vm/src/obj/objint.rs | 4 ++ vm/src/obj/objrange.rs | 122 +++++++++++++++++--------------- 3 files changed, 73 insertions(+), 57 deletions(-) diff --git a/tests/snippets/builtin_range.py b/tests/snippets/builtin_range.py index 89014880ce..2ea71f7a7f 100644 --- a/tests/snippets/builtin_range.py +++ b/tests/snippets/builtin_range.py @@ -50,3 +50,7 @@ assert list(reversed(range(5))) == [4, 3, 2, 1, 0] assert list(reversed(range(5, 0, -1))) == [1, 2, 3, 4, 5] assert list(reversed(range(1,10,5))) == [6, 1] + +# range retains the original int refs +i = 2**64 +assert range(i).stop is i diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 345fe90c31..fddc759bc4 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -35,6 +35,10 @@ impl PyInt { pub fn new>(i: T) -> Self { PyInt { value: i.into() } } + + pub fn as_bigint(&self) -> &BigInt { + &self.value + } } impl IntoPyObject for BigInt { diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 9ae2fcd541..380754b5f6 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -15,11 +15,9 @@ use super::objtype::PyClassRef; #[derive(Debug, Clone)] 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, - pub stop: BigInt, - pub step: BigInt, + pub start: PyIntRef, + pub stop: PyIntRef, + pub step: PyIntRef, } impl PyValue for PyRange { @@ -31,32 +29,36 @@ impl PyValue for PyRange { impl PyRange { #[inline] fn offset(&self, value: &BigInt) -> Option { - match self.step.sign() { - Sign::Plus if *value >= self.start && *value < self.stop => Some(value - &self.start), - Sign::Minus if *value <= self.start && *value > self.stop => Some(&self.start - value), + let start = self.start.as_bigint(); + let stop = self.stop.as_bigint(); + let step = self.step.as_bigint(); + match step.sign() { + Sign::Plus if value >= start && value < stop => Some(value - start), + Sign::Minus if value <= self.start.as_bigint() && value > stop => Some(start - value), _ => None, } } #[inline] pub fn index_of(&self, value: &BigInt) -> Option { + let step = self.step.as_bigint(); match self.offset(value) { - Some(ref offset) if offset.is_multiple_of(&self.step) => { - Some((offset / &self.step).abs()) - } + Some(ref offset) if offset.is_multiple_of(step) => Some((offset / step).abs()), Some(_) | None => None, } } #[inline] pub fn is_empty(&self) -> bool { - (self.start <= self.stop && self.step.is_negative()) - || (self.start >= self.stop && self.step.is_positive()) + let start = self.start.as_bigint(); + let stop = self.stop.as_bigint(); + let step = self.step.as_bigint(); + (start <= stop && step.is_negative()) || (start >= stop && step.is_positive()) } #[inline] pub fn forward(&self) -> bool { - self.start < self.stop + self.start.as_bigint() < self.stop.as_bigint() } #[inline] @@ -64,10 +66,14 @@ impl PyRange { where &'a BigInt: Mul, { - let result = &self.start + &self.step * index; + let start = self.start.as_bigint(); + let stop = self.stop.as_bigint(); + let step = self.step.as_bigint(); + + let result = start + step * index; - if (self.forward() && !self.is_empty() && result < self.stop) - || (!self.forward() && !self.is_empty() && result > self.stop) + if (self.forward() && !self.is_empty() && &result < stop) + || (!self.forward() && !self.is_empty() && &result > stop) { Some(result) } else { @@ -114,9 +120,9 @@ type PyRangeRef = PyRef; impl PyRangeRef { fn new(cls: PyClassRef, stop: PyIntRef, vm: &VirtualMachine) -> PyResult { PyRange { - start: Zero::zero(), - stop: stop.value.clone(), - step: One::one(), + start: PyInt::new(BigInt::zero()).into_ref(vm), + stop: stop.clone(), + step: PyInt::new(BigInt::one()).into_ref(vm), } .into_ref_with_type(vm, cls) } @@ -129,25 +135,24 @@ impl PyRangeRef { vm: &VirtualMachine, ) -> PyResult { PyRange { - start: start.value.clone(), - stop: stop.value.clone(), + start: start, + stop: stop, step: step .into_option() - .map(|i| i.value.clone()) - .unwrap_or_else(One::one), + .unwrap_or_else(|| PyInt::new(BigInt::one()).into_ref(vm)), } .into_ref_with_type(vm, cls) } - fn start(self, _vm: &VirtualMachine) -> BigInt { + fn start(self, _vm: &VirtualMachine) -> PyIntRef { self.start.clone() } - fn stop(self, _vm: &VirtualMachine) -> BigInt { + fn stop(self, _vm: &VirtualMachine) -> PyIntRef { self.stop.clone() } - fn step(self, _vm: &VirtualMachine) -> BigInt { + fn step(self, _vm: &VirtualMachine) -> PyIntRef { self.step.clone() } @@ -159,28 +164,31 @@ impl PyRangeRef { } fn reversed(self: PyRangeRef, vm: &VirtualMachine) -> PyIteratorValue { + let start = self.start.as_bigint(); + let stop = self.stop.as_bigint(); + let step = self.step.as_bigint(); + // compute the last element that is actually contained within the range // this is the new start - let remainder = ((&self.stop - &self.start) % &self.step).abs(); - let start = if remainder.is_zero() { - &self.stop - &self.step + let remainder = ((stop - start) % step).abs(); + let new_start = if remainder.is_zero() { + stop - step } else { - &self.stop - &remainder + stop - &remainder }; - let reversed = match self.step.sign() { - Sign::Plus => PyRange { - start, - stop: &self.start - 1, - step: -&self.step, - }, - Sign::Minus => PyRange { - start, - stop: &self.start + 1, - step: -&self.step, - }, + let new_stop: BigInt = match step.sign() { + Sign::Plus => start - 1, + Sign::Minus => start + 1, Sign::NoSign => unreachable!(), }; + + let reversed = PyRange { + start: PyInt::new(new_start).into_ref(vm), + stop: PyInt::new(new_stop).into_ref(vm), + step: PyInt::new(-step).into_ref(vm), + }; + PyIteratorValue { position: Cell::new(0), iterated_obj: reversed.into_ref(vm).into_object(), @@ -188,20 +196,20 @@ impl PyRangeRef { } fn len(self, _vm: &VirtualMachine) -> PyInt { - match self.step.sign() { - Sign::Plus if self.start < self.stop => { - PyInt::new((&self.stop - &self.start - 1usize) / &self.step + 1) - } - Sign::Minus if self.start > self.stop => { - PyInt::new((&self.start - &self.stop - 1usize) / (-&self.step) + 1) - } + let start = self.start.as_bigint(); + let stop = self.stop.as_bigint(); + let step = self.step.as_bigint(); + + match step.sign() { + Sign::Plus if start < stop => PyInt::new((stop - start - 1usize) / step + 1), + Sign::Minus if start > stop => PyInt::new((start - stop - 1usize) / (-step) + 1), Sign::Plus | Sign::Minus => PyInt::new(0), Sign::NoSign => unreachable!(), } } fn repr(self, _vm: &VirtualMachine) -> String { - if self.step.is_one() { + if self.step.as_bigint().is_one() { format!("range({}, {})", self.start, self.stop) } else { format!("range({}, {}, {})", self.start, self.stop, self.step) @@ -215,7 +223,7 @@ impl PyRangeRef { fn contains(self, needle: PyObjectRef, _vm: &VirtualMachine) -> bool { if let Ok(int) = needle.downcast::() { match self.offset(&int.value) { - Some(ref offset) => offset.is_multiple_of(&self.step), + Some(ref offset) => offset.is_multiple_of(self.step.as_bigint()), None => false, } } else { @@ -256,9 +264,9 @@ impl PyRangeRef { } } Either::B(slice) => { - let new_start = if let Some(int) = slice.start.clone() { + let new_start = if let Some(int) = slice.start.as_ref() { if let Some(i) = self.get(int) { - i + PyInt::new(i).into_ref(vm) } else { self.start.clone() } @@ -266,9 +274,9 @@ impl PyRangeRef { self.start.clone() }; - let new_end = if let Some(int) = slice.stop.clone() { + let new_end = if let Some(int) = slice.stop.as_ref() { if let Some(i) = self.get(int) { - i + PyInt::new(i).into_ref(vm) } else { self.stop.clone() } @@ -276,8 +284,8 @@ impl PyRangeRef { self.stop.clone() }; - let new_step = if let Some(int) = slice.step.clone() { - int * self.step.clone() + let new_step = if let Some(int) = slice.step.as_ref() { + PyInt::new(int * self.step.as_bigint()).into_ref(vm) } else { self.step.clone() }; From f2d562a4ccf4b3ae4d6bf6bf3f9e6d157d95ce29 Mon Sep 17 00:00:00 2001 From: Joey Date: Sun, 24 Mar 2019 10:05:25 -0700 Subject: [PATCH 30/51] pyint: use as_bigint() everywhere --- vm/src/frame.rs | 2 +- vm/src/obj/objbool.rs | 4 ++-- vm/src/obj/objenumerate.rs | 2 +- vm/src/obj/objint.rs | 3 +-- vm/src/obj/objrange.rs | 8 ++++---- vm/src/obj/objsequence.rs | 2 +- 6 files changed, 10 insertions(+), 11 deletions(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 2a0951ab39..b9e6cf9653 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -413,7 +413,7 @@ impl Frame { if x.is(&vm.ctx.none()) { None } else if let Some(i) = x.payload::() { - Some(i.value.clone()) + Some(i.as_bigint().clone()) } else { panic!("Expect Int or None as BUILD_SLICE arguments") } diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index e21a3a3296..d4ef4b0fbc 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -25,7 +25,7 @@ pub fn boolval(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { 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(), + Some(i) => !i.as_bigint().is_zero(), None => return Err(vm.new_type_error(String::from("TypeError"))), } } else { @@ -59,7 +59,7 @@ pub fn not(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult { // Retrieve inner int value: pub fn get_value(obj: &PyObjectRef) -> bool { - !obj.payload::().unwrap().value.is_zero() + !obj.payload::().unwrap().as_bigint().is_zero() } fn bool_repr(vm: &VirtualMachine, args: PyFuncArgs) -> Result { diff --git a/vm/src/obj/objenumerate.rs b/vm/src/obj/objenumerate.rs index 0b0e5d4488..68d1e49f6b 100644 --- a/vm/src/obj/objenumerate.rs +++ b/vm/src/obj/objenumerate.rs @@ -32,7 +32,7 @@ fn enumerate_new( vm: &VirtualMachine, ) -> PyResult { let counter = match start { - OptionalArg::Present(start) => start.value.clone(), + OptionalArg::Present(start) => start.as_bigint().clone(), OptionalArg::Missing => BigInt::zero(), }; diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index fddc759bc4..dd40f06a42 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -19,8 +19,7 @@ use crate::obj::objtype::PyClassRef; #[derive(Debug)] pub struct PyInt { - // TODO: shouldn't be public - pub value: BigInt, + value: BigInt, } impl fmt::Display for PyInt { diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 380754b5f6..04c9c273b1 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -222,7 +222,7 @@ impl PyRangeRef { fn contains(self, needle: PyObjectRef, _vm: &VirtualMachine) -> bool { if let Ok(int) = needle.downcast::() { - match self.offset(&int.value) { + match self.offset(int.as_bigint()) { Some(ref offset) => offset.is_multiple_of(self.step.as_bigint()), None => false, } @@ -233,7 +233,7 @@ impl PyRangeRef { fn index(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { if let Ok(int) = needle.downcast::() { - match self.index_of(&int.value) { + match self.index_of(int.as_bigint()) { Some(idx) => Ok(PyInt::new(idx)), None => Err(vm.new_value_error(format!("{} is not in range", int))), } @@ -244,7 +244,7 @@ impl PyRangeRef { fn count(self, item: PyObjectRef, _vm: &VirtualMachine) -> PyInt { if let Ok(int) = item.downcast::() { - if self.index_of(&int.value).is_some() { + if self.index_of(int.as_bigint()).is_some() { PyInt::new(1) } else { PyInt::new(0) @@ -257,7 +257,7 @@ impl PyRangeRef { fn getitem(self, subscript: Either, vm: &VirtualMachine) -> PyResult { match subscript { Either::A(index) => { - if let Some(value) = self.get(index.value.clone()) { + if let Some(value) = self.get(index.as_bigint()) { Ok(PyInt::new(value).into_ref(vm).into_object()) } else { Err(vm.new_index_error("range object index out of range".to_string())) diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index 83d0f9f9f8..b8d91e5909 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -144,7 +144,7 @@ pub fn get_item( subscript: PyObjectRef, ) -> PyResult { if let Some(i) = subscript.payload::() { - return match i.value.to_i32() { + return match i.as_bigint().to_i32() { Some(value) => { if let Some(pos_index) = elements.to_vec().get_pos(value) { let obj = elements[pos_index].clone(); From ca11e7dea79ed413138ee85acdd06b1070801ae6 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Sun, 24 Mar 2019 20:16:38 +0100 Subject: [PATCH 31/51] Add support for emoji as name. --- Cargo.lock | 42 ++++++++++++++++++++++++++++++++++++++++++ parser/Cargo.toml | 1 + parser/src/lexer.rs | 24 +++++++++++++++++------- 3 files changed, 60 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 57dadb57b3..6193772c40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -760,6 +760,7 @@ dependencies = [ "num-bigint 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)", "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "unic-emoji-char 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1044,6 +1045,42 @@ name = "ucd-util" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unic-char-range 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unic-emoji-char" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unic-char-property 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unic-char-range 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unic-ucd-version 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unic-common 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "unicode-normalization" version = "0.1.7" @@ -1364,6 +1401,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" +"checksum unic-char-property 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +"checksum unic-char-range 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" +"checksum unic-common 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" +"checksum unic-emoji-char 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d" +"checksum unic-ucd-version 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" "checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" "checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" diff --git a/parser/Cargo.toml b/parser/Cargo.toml index 7545a27d44..f2f8e5a6b4 100644 --- a/parser/Cargo.toml +++ b/parser/Cargo.toml @@ -15,3 +15,4 @@ regex="0.2.2" num-bigint = "0.2" num-traits = "0.2" unicode-xid = "0.1.0" +unic-emoji-char = "0.9.0" diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index db8fee2c70..defa5e2322 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -1,6 +1,7 @@ //! This module takes care of lexing python source text. This means source //! code is translated into separate tokens. +extern crate unic_emoji_char; extern crate unicode_xid; pub use super::token::Tok; @@ -9,6 +10,7 @@ use num_traits::Num; use std::cmp::Ordering; use std::collections::HashMap; use std::str::FromStr; +use unic_emoji_char::is_emoji_presentation; use unicode_xid::UnicodeXID; #[derive(Clone, Copy, PartialEq, Debug)] @@ -545,7 +547,7 @@ where fn is_identifier_start(&self, c: char) -> bool { match c { - 'a'..='z' | 'A'..='Z' | '_' => true, + '_' => true, c => UnicodeXID::is_xid_start(c), } } @@ -553,7 +555,7 @@ where fn is_identifier_continuation(&self) -> bool { if let Some(c) = self.chr0 { match c { - 'a'..='z' | 'A'..='Z' | '_' | '0'..='9' => true, + '_' | '0'..='9' => true, c => UnicodeXID::is_xid_continue(c), } } else { @@ -705,6 +707,18 @@ where // First check identifier: if self.is_identifier_start(c) { return Some(self.lex_identifier()); + } else if is_emoji_presentation(c) { + let tok_start = self.get_pos(); + self.next_char(); + let tok_end = self.get_pos(); + println!("Emoji: {}", c); + return Some(Ok(( + tok_start, + Tok::Name { + name: c.to_string(), + }, + tok_end, + ))); } else { match c { '0'..='9' => return Some(self.lex_number()), @@ -801,11 +815,7 @@ where } _ => { let tok_end = self.get_pos(); - return Some(Ok(( - tok_start, - Tok::DoubleSlash, - tok_end, - ))); + return Some(Ok((tok_start, Tok::DoubleSlash, tok_end))); } } } From d0fe48d9244ca1a09e2d3e6013e0f9c1d2a25e75 Mon Sep 17 00:00:00 2001 From: jgirardet Date: Mon, 25 Mar 2019 00:08:00 +0100 Subject: [PATCH 32/51] add bytes.__getitem__ --- tests/snippets/bytes.py | 11 +++++++++++ vm/src/obj/objbytes.rs | 32 +++++++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py index d26fd26d89..5bf9251953 100644 --- a/tests/snippets/bytes.py +++ b/tests/snippets/bytes.py @@ -26,3 +26,14 @@ assert b"dc" not in b"abcd" assert 97 in b"abcd" assert 150 not in b"abcd" + +# getitem +d = b"abcdefghij" + +assert d[1] == 98 +assert d[-1] == 106 +assert d[2:6] == b"cdef" +assert d[-6:] == b"efghij" +assert d[1:8:2] == b"bdfh" +# assert d[8:1:-2] == b"igec" +# assert d[-1:-8:-2] == b"jhfd" diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index f3b4b1b754..9c4fcae6eb 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -1,9 +1,8 @@ +use num_traits::ToPrimitive; use std::cell::Cell; use std::hash::{Hash, Hasher}; use std::ops::Deref; -use num_traits::ToPrimitive; - use crate::function::{OptionalArg, PyFuncArgs}; use crate::pyobject::{ PyContext, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, @@ -11,7 +10,7 @@ use crate::pyobject::{ use crate::vm::VirtualMachine; use super::objint; -use super::objsequence::get_item; +use super::objsequence::PySliceableSequence; use super::objtype::{self, PyClassRef}; #[derive(Debug)] @@ -71,6 +70,7 @@ pub fn init(context: &PyContext) { "__doc__" => context.new_str(bytes_doc.to_string()), "__add__" => context.new_rustfunc(PyBytesRef::add), "__contains__" => context.new_rustfunc(PyBytesRef::contains), + "__getitem__" => context.new_rustfunc(PyBytesRef::getitem), }); } @@ -243,4 +243,30 @@ impl PyBytesRef { Err(vm.new_type_error(format!("Cannot add {:?} and {:?}", self, needle))) } } + + fn getitem(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { + if objtype::isinstance(&needle, &vm.ctx.int_type()) { + match objint::get_value(&needle).to_i32() { + Some(pos) => { + if let Some(idx) = self.value.get_pos(pos) { + Ok(vm.ctx.new_int(self.value[idx])) + } else { + Err(vm.new_index_error("index out of range".to_string())) + } + } + None => Err( + vm.new_index_error("cannot fit 'int' into an index-sized integer".to_string()) + ), + } + } else if objtype::isinstance(&needle, &vm.ctx.slice_type()) { + Ok(vm + .ctx + .new_bytes((self.value.get_slice_items(&vm, &needle)).unwrap())) + } else { + Err(vm.new_type_error(format!( + "byte indices must be integers or slices, not {}", + needle.type_pyref() + ))) + } + } } From 3562b8f59c2eccb034ac5c0f3de7638fcd586140 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Mon, 25 Mar 2019 11:21:01 +0000 Subject: [PATCH 33/51] Store class attributes inside PyClass struct. --- vm/src/obj/objtype.rs | 65 ++++++++++++++++++++++--------------------- vm/src/pyobject.rs | 12 ++++++-- 2 files changed, 42 insertions(+), 35 deletions(-) diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 89bd8f34a5..6399d68c9b 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -22,6 +22,7 @@ pub struct PyClass { pub name: String, pub mro: Vec, pub subclasses: RefCell>, + pub attributes: RefCell, } impl fmt::Display for PyClass { @@ -149,6 +150,25 @@ impl PyClassRef { } } + fn set_attr( + self, + attr_name: PyStringRef, + value: PyObjectRef, + vm: &VirtualMachine, + ) -> PyResult<()> { + if let Some(attr) = class_get_attr(&self.type_pyref(), &attr_name.value) { + if let Some(descriptor) = class_get_attr(&attr.type_pyref(), "__set__") { + vm.invoke(descriptor, vec![attr, self.into_object(), value])?; + return Ok(()); + } + } + + self.attributes + .borrow_mut() + .insert(attr_name.to_string(), value); + Ok(()) + } + fn subclasses(self, _vm: &VirtualMachine) -> PyList { let mut subclasses = self.subclasses.borrow_mut(); subclasses.retain(|x| x.upgrade().is_some()); @@ -181,6 +201,7 @@ pub fn init(ctx: &PyContext) { "__repr__" => ctx.new_rustfunc(PyClassRef::repr), "__prepare__" => ctx.new_rustfunc(PyClassRef::prepare), "__getattribute__" => ctx.new_rustfunc(PyClassRef::getattribute), + "__setattr__" => ctx.new_rustfunc(PyClassRef::set_attr), "__subclasses__" => ctx.new_rustfunc(PyClassRef::subclasses), "__getattribute__" => ctx.new_rustfunc(PyClassRef::getattribute), "__instancecheck__" => ctx.new_rustfunc(PyClassRef::instance_check), @@ -260,32 +281,13 @@ pub fn type_call(class: PyClassRef, args: Args, kwargs: KwArgs, vm: &VirtualMach Ok(obj) } -// Very private helper function for class_get_attr -fn class_get_attr_in_dict(class: &PyClassRef, attr_name: &str) -> Option { - if let Some(ref dict) = class.as_object().dict { - dict.borrow().get(attr_name).cloned() - } else { - panic!("Only classes should be in MRO!"); - } -} - -// Very private helper function for class_has_attr -fn class_has_attr_in_dict(class: &PyClassRef, attr_name: &str) -> bool { - if let Some(ref dict) = class.as_object().dict { - dict.borrow().contains_key(attr_name) - } else { - panic!("All classes are expected to have dicts!"); - } -} - // This is the internal get_attr implementation for fast lookup on a class. -pub fn class_get_attr(zelf: &PyClassRef, attr_name: &str) -> Option { - let mro = &zelf.mro; - if let Some(item) = class_get_attr_in_dict(zelf, attr_name) { +pub fn class_get_attr(class: &PyClassRef, attr_name: &str) -> Option { + if let Some(item) = class.attributes.borrow().get(attr_name).cloned() { return Some(item); } - for class in mro { - if let Some(item) = class_get_attr_in_dict(class, attr_name) { + for class in &class.mro { + if let Some(item) = class.attributes.borrow().get(attr_name).cloned() { return Some(item); } } @@ -293,12 +295,12 @@ pub fn class_get_attr(zelf: &PyClassRef, attr_name: &str) -> Option } // This is the internal has_attr implementation for fast lookup on a class. -pub fn class_has_attr(zelf: &PyClassRef, attr_name: &str) -> bool { - class_has_attr_in_dict(zelf, attr_name) - || zelf +pub fn class_has_attr(class: &PyClassRef, attr_name: &str) -> bool { + class.attributes.borrow().contains_key(attr_name) + || class .mro .iter() - .any(|d| class_has_attr_in_dict(d, attr_name)) + .any(|c| c.attributes.borrow().contains_key(attr_name)) } pub fn get_attributes(cls: PyClassRef) -> PyAttributes { @@ -309,10 +311,8 @@ pub fn get_attributes(cls: PyClassRef) -> PyAttributes { base_classes.reverse(); for bc in base_classes { - if let Some(ref dict) = &bc.as_object().dict { - for (name, value) in dict.borrow().iter() { - attributes.insert(name.to_string(), value.clone()); - } + for (name, value) in bc.attributes.borrow().iter() { + attributes.insert(name.to_string(), value.clone()); } } @@ -374,8 +374,9 @@ pub fn new( name: String::from(name), mro, subclasses: RefCell::new(vec![]), + attributes: RefCell::new(dict), }, - dict: Some(RefCell::new(dict)), + dict: None, typ, } .into_ref(); diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index e4488bdb49..81fec3406e 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -186,22 +186,24 @@ fn init_type_hierarchy() -> (PyClassRef, PyClassRef) { let (type_type, object_type) = unsafe { let object_type = PyObject { typ: mem::uninitialized(), // ! - dict: Some(RefCell::new(PyAttributes::new())), + dict: None, payload: PyClass { name: String::from("object"), mro: vec![], subclasses: RefCell::new(vec![]), + attributes: RefCell::new(PyAttributes::new()), }, } .into_ref(); let type_type = PyObject { typ: mem::uninitialized(), // ! - dict: Some(RefCell::new(PyAttributes::new())), + dict: None, payload: PyClass { name: String::from("type"), mro: vec![object_type.clone().downcast().unwrap()], subclasses: RefCell::new(vec![]), + attributes: RefCell::new(PyAttributes::new()), }, } .into_ref(); @@ -655,7 +657,11 @@ impl PyContext { value: V, ) { let obj = obj.into(); - if let Some(PyModule { ref dict, .. }) = obj.payload::() { + if let Some(PyClass { ref attributes, .. }) = obj.payload::() { + attributes + .borrow_mut() + .insert(attr_name.to_string(), value.into()); + } else if let Some(PyModule { ref dict, .. }) = obj.payload::() { dict.set_item(self, attr_name, value.into()) } else if let Some(ref dict) = obj.dict { dict.borrow_mut() From 17c3f9f024f79f972b20f019c4464b9b2a28593b Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Mon, 25 Mar 2019 16:34:07 +0000 Subject: [PATCH 34/51] builtin_locals can just return a reference to the locals dict. --- vm/src/builtins.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 9dfe7afd63..acd0719d80 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -11,7 +11,6 @@ use num_traits::{Signed, ToPrimitive}; use crate::compile; use crate::import::import_module; use crate::obj::objbool; -use crate::obj::objdict; use crate::obj::objint; use crate::obj::objiter; use crate::obj::objstr::{self, PyStringRef}; @@ -29,14 +28,7 @@ use crate::obj::objcode::PyCodeRef; use crate::stdlib::io::io_open; fn get_locals(vm: &VirtualMachine) -> PyObjectRef { - let d = vm.new_dict(); - // TODO: implement dict_iter_items? - let locals = vm.get_locals(); - let key_value_pairs = objdict::get_key_value_pairs(&locals); - for (key, value) in key_value_pairs { - objdict::set_item(&d, vm, &key, &value); - } - d + vm.get_locals() } fn dir_locals(vm: &VirtualMachine) -> PyObjectRef { From 602f2a83da41a4f9d857a8abd55fd581117ab016 Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Mon, 25 Mar 2019 17:35:21 +0100 Subject: [PATCH 35/51] Update grcov --- tests/.travis-runner.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/.travis-runner.sh b/tests/.travis-runner.sh index 8a004d3b19..36b9f43aca 100755 --- a/tests/.travis-runner.sh +++ b/tests/.travis-runner.sh @@ -31,7 +31,7 @@ then zip -0 ccov.zip `find . \( -name "rustpython*.gc*" \) -print` # Install grcov - curl -L https://github.com/mozilla/grcov/releases/download/v0.4.1/grcov-linux-x86_64.tar.bz2 | tar jxf - + curl -L https://github.com/mozilla/grcov/releases/download/v0.4.2/grcov-linux-x86_64.tar.bz2 | tar jxf - ./grcov ccov.zip -s . -t lcov --llvm --branch --ignore-not-existing --ignore-dir "/*" -p "x" > lcov.info From bbb716247266e6c0eca7993e2aacb456c3b79208 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Mon, 25 Mar 2019 16:37:20 +0000 Subject: [PATCH 36/51] Various dictionary changes. * vm.ctx.new_dict returns a PyDictRef * Special case for module goes away. * Instances get a real dictionary. --- vm/src/builtins.rs | 4 +- vm/src/frame.rs | 14 +++-- vm/src/function.rs | 9 +++ vm/src/import.rs | 2 +- vm/src/obj/objdict.rs | 110 +++++++++++++++++++-------------- vm/src/obj/objmodule.rs | 26 ++++---- vm/src/obj/objobject.rs | 38 +++++------- vm/src/obj/objproperty.rs | 4 +- vm/src/obj/objsequence.rs | 2 + vm/src/obj/objtype.rs | 4 +- vm/src/pyobject.rs | 94 +++++++++++++--------------- vm/src/stdlib/json.rs | 2 +- vm/src/vm.rs | 8 +-- wasm/lib/src/browser_module.rs | 1 + wasm/lib/src/convert.rs | 4 +- 15 files changed, 164 insertions(+), 158 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index acd0719d80..4d9fb79896 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -815,9 +815,9 @@ pub fn builtin_build_class_(vm: &VirtualMachine, mut args: PyFuncArgs) -> PyResu let prepare = vm.get_attribute(metaclass.clone().into_object(), "__prepare__")?; let namespace = vm.invoke(prepare, vec![name_arg.clone(), bases.clone()])?; - let cells = vm.new_dict(); + let cells = vm.ctx.new_dict(); - vm.invoke_with_locals(function, cells.clone(), namespace.clone())?; + vm.invoke_with_locals(function, cells.clone().into_object(), namespace.clone())?; let class = vm.call_method( metaclass.as_object(), "__call__", diff --git a/vm/src/frame.rs b/vm/src/frame.rs index b9e6cf9653..3584a57a81 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -119,7 +119,7 @@ impl Scope { } pub fn child_scope(&self, ctx: &PyContext) -> Scope { - self.child_scope_with_locals(ctx.new_dict()) + self.child_scope_with_locals(ctx.new_dict().into_object()) } } @@ -142,7 +142,7 @@ impl NameProtocol for Scope { return Some(value); } - vm.builtins.get_item(name) + vm.get_attribute(vm.builtins.clone(), name).ok() } fn load_cell(&self, _vm: &VirtualMachine, name: &str) -> Option { @@ -386,7 +386,7 @@ impl Frame { Ok(None) } bytecode::Instruction::BuildMap { size, unpack } => { - let map_obj = vm.ctx.new_dict(); + let map_obj = vm.ctx.new_dict().into_object(); for _x in 0..*size { let obj = self.pop_value(); if *unpack { @@ -572,7 +572,7 @@ impl Frame { let annotations = if flags.contains(bytecode::FunctionOpArg::HAS_ANNOTATIONS) { self.pop_value() } else { - vm.new_dict() + vm.ctx.new_dict().into_object() }; let defaults = if flags.contains(bytecode::FunctionOpArg::HAS_DEFAULTS) { @@ -839,8 +839,10 @@ impl Frame { 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() { - self.scope.store_name(&vm, &objstr::get_value(k), v.clone()); + if let Some(dict) = &module.dict { + for (k, v) in dict.get_key_value_pairs().iter() { + self.scope.store_name(&vm, &objstr::get_value(k), v.clone()); + } } Ok(None) } diff --git a/vm/src/function.rs b/vm/src/function.rs index 1e2a38f71e..ed959d85c9 100644 --- a/vm/src/function.rs +++ b/vm/src/function.rs @@ -247,6 +247,15 @@ where } } +impl IntoIterator for KwArgs { + type Item = (String, T); + type IntoIter = std::collections::hash_map::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + /// A list of positional argument values. /// /// A built-in function with a `Args` parameter is analagous to a Python diff --git a/vm/src/import.rs b/vm/src/import.rs index 936b896805..987555915f 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -40,7 +40,7 @@ fn import_uncached_module(vm: &VirtualMachine, current_path: PathBuf, module: &s 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()))?; + vm.run_code_obj(code_obj, Scope::new(None, attrs.clone().into_object()))?; Ok(vm.ctx.new_module(module, attrs)) } diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 0c8d5fe5ec..061c3477e7 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -3,9 +3,9 @@ use std::collections::HashMap; use std::fmt; use std::ops::{Deref, DerefMut}; -use crate::function::{OptionalArg, PyFuncArgs}; +use crate::function::{KwArgs, OptionalArg}; use crate::pyobject::{ - PyAttributes, PyContext, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, + DictProtocol, PyAttributes, PyContext, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue, }; use crate::vm::{ReprGuard, VirtualMachine}; @@ -124,57 +124,48 @@ pub fn py_dict_to_attributes(dict: &PyObjectRef) -> PyAttributes { attrs } -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); - set_item(&dict, vm, &key, &value); - } - Ok(dict) -} - // Python dict methods: -fn dict_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(_ty, Some(vm.ctx.type_type()))], - optional = [(dict_obj, None)] - ); - let dict = vm.ctx.new_dict(); - if let Some(dict_obj) = dict_obj { - if objtype::isinstance(&dict_obj, &vm.ctx.dict_type()) { - for (needle, value) in get_key_value_pairs(&dict_obj) { - set_item(&dict, vm, &needle, &value); - } - } else { - let iter = objiter::get_iter(vm, dict_obj)?; - loop { - fn err(vm: &VirtualMachine) -> PyObjectRef { - vm.new_type_error("Iterator must have exactly two elements".to_string()) +impl PyDictRef { + fn new( + _class: PyClassRef, // TODO Support subclasses of int. + dict_obj: OptionalArg, + kwargs: KwArgs, + vm: &VirtualMachine, + ) -> PyResult { + let dict = vm.ctx.new_dict(); + if let OptionalArg::Present(dict_obj) = dict_obj { + if objtype::isinstance(&dict_obj, &vm.ctx.dict_type()) { + for (needle, value) in get_key_value_pairs(&dict_obj) { + set_item(dict.as_object(), vm, &needle, &value); } - let element = match objiter::get_next_object(vm, &iter)? { - Some(obj) => obj, - None => break, - }; - let elem_iter = objiter::get_iter(vm, &element)?; - let needle = objiter::get_next_object(vm, &elem_iter)?.ok_or_else(|| err(vm))?; - let value = objiter::get_next_object(vm, &elem_iter)?.ok_or_else(|| err(vm))?; - if objiter::get_next_object(vm, &elem_iter)?.is_some() { - return Err(err(vm)); + } else { + let iter = objiter::get_iter(vm, &dict_obj)?; + loop { + 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)? { + Some(obj) => obj, + None => break, + }; + let elem_iter = objiter::get_iter(vm, &element)?; + let needle = + objiter::get_next_object(vm, &elem_iter)?.ok_or_else(|| err(vm))?; + let value = objiter::get_next_object(vm, &elem_iter)?.ok_or_else(|| err(vm))?; + if objiter::get_next_object(vm, &elem_iter)?.is_some() { + return Err(err(vm)); + } + set_item(dict.as_object(), vm, &needle, &value); } - set_item(&dict, vm, &needle, &value); } } + for (needle, value) in kwargs.into_iter() { + let py_needle = vm.new_str(needle); + set_item(&dict.as_object(), vm, &py_needle, &value); + } + Ok(dict) } - for (needle, value) in args.kwargs { - let py_needle = vm.new_str(needle); - set_item(&dict, vm, &py_needle, &value); - } - Ok(dict) -} -impl PyDictRef { fn bool(self, _vm: &VirtualMachine) -> bool { !self.entries.borrow().is_empty() } @@ -302,6 +293,31 @@ impl PyDictRef { } } +impl DictProtocol for PyDictRef { + fn contains_key(&self, k: &str) -> bool { + content_contains_key_str(&self.entries.borrow(), k) + } + + fn get_item(&self, k: &str) -> Option { + content_get_key_str(&self.entries.borrow(), k) + } + + fn get_key_value_pairs(&self) -> Vec<(PyObjectRef, PyObjectRef)> { + get_key_value_pairs(self.as_object()) + } + + // Item set/get: + fn set_item(&self, ctx: &PyContext, key: &str, v: PyObjectRef) { + let key = ctx.new_str(key.to_string()); + set_item_in_content(&mut self.entries.borrow_mut(), &key, &v); + } + + fn del_item(&self, key: &str) { + let mut elements = get_mut_elements(self.as_object()); + elements.remove(key).unwrap(); + } +} + pub fn init(context: &PyContext) { extend_class!(context, &context.dict_type, { "__bool__" => context.new_rustfunc(PyDictRef::bool), @@ -310,7 +326,7 @@ pub fn init(context: &PyContext) { "__delitem__" => context.new_rustfunc(PyDictRef::delitem), "__getitem__" => context.new_rustfunc(PyDictRef::getitem), "__iter__" => context.new_rustfunc(PyDictRef::iter), - "__new__" => context.new_rustfunc(dict_new), + "__new__" => context.new_rustfunc(PyDictRef::new), "__repr__" => context.new_rustfunc(PyDictRef::repr), "__setitem__" => context.new_rustfunc(PyDictRef::setitem), "clear" => context.new_rustfunc(PyDictRef::clear), diff --git a/vm/src/obj/objmodule.rs b/vm/src/obj/objmodule.rs index 58d8688db2..b641f04268 100644 --- a/vm/src/obj/objmodule.rs +++ b/vm/src/obj/objmodule.rs @@ -1,12 +1,10 @@ -use crate::obj::objstr::PyStringRef; use crate::obj::objtype::PyClassRef; -use crate::pyobject::{DictProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue}; +use crate::pyobject::{DictProtocol, PyContext, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; #[derive(Debug)] pub struct PyModule { pub name: String, - pub dict: PyObjectRef, } pub type PyModuleRef = PyRef; @@ -18,23 +16,21 @@ impl PyValue for PyModule { impl PyModuleRef { fn dir(self: PyModuleRef, vm: &VirtualMachine) -> PyResult { - let keys = self - .dict - .get_key_value_pairs() - .iter() - .map(|(k, _v)| k.clone()) - .collect(); - Ok(vm.ctx.new_list(keys)) - } - - fn set_attr(self, attr: PyStringRef, value: PyObjectRef, vm: &VirtualMachine) { - self.dict.set_item(&vm.ctx, &attr.value, value) + if let Some(dict) = &self.into_object().dict { + let keys = dict + .get_key_value_pairs() + .iter() + .map(|(k, _v)| k.clone()) + .collect(); + Ok(vm.ctx.new_list(keys)) + } else { + panic!("Modules should definitely have a dict."); + } } } pub fn init(context: &PyContext) { extend_class!(&context, &context.module_type, { "__dir__" => context.new_rustfunc(PyModuleRef::dir), - "__setattr__" => context.new_rustfunc(PyModuleRef::set_attr) }); } diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 46a90114dc..60473fad03 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -1,5 +1,5 @@ +use super::objdict::{self, PyDictRef}; use super::objlist::PyList; -use super::objmodule::PyModule; use super::objstr::{self, PyStringRef}; use super::objtype; use crate::function::PyFuncArgs; @@ -23,11 +23,12 @@ impl PyValue for PyInstance { pub fn new_instance(vm: &VirtualMachine, mut args: PyFuncArgs) -> PyResult { // more or less __new__ operator let cls = PyClassRef::try_from_object(vm, args.shift())?; - Ok(if cls.is(&vm.ctx.object) { - PyObject::new_without_dict(PyInstance, cls) + let dict = if cls.is(&vm.ctx.object) { + None } else { - PyObject::new(PyInstance, cls) - }) + Some(vm.ctx.new_dict()) + }; + Ok(PyObject::new(PyInstance, cls, dict)) } fn object_eq(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -114,7 +115,7 @@ fn object_setattr( } if let Some(ref dict) = obj.clone().dict { - dict.borrow_mut().insert(attr_name.value.clone(), value); + dict.set_item(&vm.ctx, &attr_name.value, value); Ok(()) } else { let type_name = objtype::get_type_name(obj.type_ref()); @@ -135,7 +136,7 @@ fn object_delattr(obj: PyObjectRef, attr_name: PyStringRef, vm: &VirtualMachine) } if let Some(ref dict) = obj.dict { - dict.borrow_mut().remove(&attr_name.value); + dict.del_item(&attr_name.value); Ok(()) } else { let type_name = objtype::get_type_name(obj.type_ref()); @@ -227,13 +228,9 @@ fn object_class_setter( Err(vm.new_type_error(format!("can't change class of type '{}'", type_repr))) } -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() { - new_dict.set_item(&vm.ctx, &attr, value.clone()); - } - Ok(new_dict) +fn object_dict(object: PyObjectRef, vm: &VirtualMachine) -> PyResult { + if let Some(ref dict) = object.dict { + Ok(dict.clone()) } else { Err(vm.new_type_error("TypeError: no dictionary.".to_string())) } @@ -273,15 +270,8 @@ fn object_getattribute(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { } fn object_getattr(obj: &PyObjectRef, attr_name: &str) -> Option { - // TODO: - // This is an all kinds of wrong work-around for the temporary difference in - // shape between modules and object. It will disappear once that is fixed. - if let Some(PyModule { ref dict, .. }) = obj.payload::() { - return dict.get_item(attr_name); - } - if let Some(ref dict) = obj.dict { - dict.borrow().get(attr_name).cloned() + dict.get_item(attr_name) } else { None } @@ -293,8 +283,8 @@ pub fn get_attributes(obj: &PyObjectRef) -> PyAttributes { // Get instance attributes: if let Some(dict) = &obj.dict { - for (name, value) in dict.borrow().iter() { - attributes.insert(name.to_string(), value.clone()); + for (key, value) in objdict::get_key_value_pairs(dict.as_object()) { + attributes.insert(key.to_string(), value.clone()); } } diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index c5459a9fb2..e6b11904eb 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -200,7 +200,7 @@ impl<'a> PropertyBuilder<'a> { deleter: None, }; - PyObject::new(payload, self.ctx.property_type()) + PyObject::new(payload, self.ctx.property_type(), None) } else { let payload = PyReadOnlyProperty { getter: self.getter.expect( @@ -208,7 +208,7 @@ impl<'a> PropertyBuilder<'a> { ), }; - PyObject::new(payload, self.ctx.readonly_property_type()) + PyObject::new(payload, self.ctx.readonly_property_type(), None) } } } diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index b8d91e5909..80f4bf40f6 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -164,11 +164,13 @@ pub fn get_item( Ok(PyObject::new( PyList::from(elements.to_vec().get_slice_items(vm, &subscript)?), sequence.type_pyref(), + None, )) } else if sequence.payload::().is_some() { Ok(PyObject::new( PyTuple::from(elements.to_vec().get_slice_items(vm, &subscript)?), sequence.type_pyref(), + None, )) } else { panic!("sequence get_item called for non-sequence") diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 6399d68c9b..d87ca3843c 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -110,8 +110,8 @@ impl PyClassRef { format!("", self.name) } - fn prepare(_name: PyStringRef, _bases: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { - vm.new_dict() + fn prepare(_name: PyStringRef, _bases: PyObjectRef, vm: &VirtualMachine) -> PyDictRef { + vm.ctx.new_dict() } fn getattribute(self, name_ref: PyStringRef, vm: &VirtualMachine) -> PyResult { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 81fec3406e..78e82c51bd 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -24,7 +24,7 @@ use crate::obj::objclassmethod; use crate::obj::objcode; use crate::obj::objcode::PyCodeRef; use crate::obj::objcomplex::{self, PyComplex}; -use crate::obj::objdict::{self, PyDict}; +use crate::obj::objdict::{self, PyDict, PyDictRef}; use crate::obj::objellipsis; use crate::obj::objenumerate; use crate::obj::objfilter; @@ -271,7 +271,7 @@ impl PyContext { fn create_object(payload: T, cls: &PyClassRef) -> PyRef { PyRef { - obj: PyObject::new(payload, cls.clone()), + obj: PyObject::new(payload, cls.clone(), None), _payload: PhantomData, } } @@ -521,32 +521,32 @@ impl PyContext { self.object.clone() } - pub fn new_object(&self) -> PyObjectRef { - self.new_instance(self.object.clone(), None) - } - pub fn new_int>(&self, i: T) -> PyObjectRef { - PyObject::new(PyInt::new(i), self.int_type()) + PyObject::new(PyInt::new(i), self.int_type(), None) } pub fn new_float(&self, value: f64) -> PyObjectRef { - PyObject::new(PyFloat::from(value), self.float_type()) + PyObject::new(PyFloat::from(value), self.float_type(), None) } pub fn new_complex(&self, value: Complex64) -> PyObjectRef { - PyObject::new(PyComplex::from(value), self.complex_type()) + PyObject::new(PyComplex::from(value), self.complex_type(), None) } pub fn new_str(&self, s: String) -> PyObjectRef { - PyObject::new(objstr::PyString { value: s }, self.str_type()) + PyObject::new(objstr::PyString { value: s }, self.str_type(), None) } pub fn new_bytes(&self, data: Vec) -> PyObjectRef { - PyObject::new(objbytes::PyBytes::new(data), self.bytes_type()) + PyObject::new(objbytes::PyBytes::new(data), self.bytes_type(), None) } pub fn new_bytearray(&self, data: Vec) -> PyObjectRef { - PyObject::new(objbytearray::PyByteArray::new(data), self.bytearray_type()) + PyObject::new( + objbytearray::PyByteArray::new(data), + self.bytearray_type(), + None, + ) } pub fn new_bool(&self, b: bool) -> PyObjectRef { @@ -558,21 +558,23 @@ impl PyContext { } pub fn new_tuple(&self, elements: Vec) -> PyObjectRef { - PyObject::new(PyTuple::from(elements), self.tuple_type()) + PyObject::new(PyTuple::from(elements), self.tuple_type(), None) } pub fn new_list(&self, elements: Vec) -> PyObjectRef { - PyObject::new(PyList::from(elements), self.list_type()) + PyObject::new(PyList::from(elements), self.list_type(), None) } 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(PySet::default(), self.set_type()) + PyObject::new(PySet::default(), self.set_type(), None) } - pub fn new_dict(&self) -> PyObjectRef { - PyObject::new(PyDict::default(), self.dict_type()) + pub fn new_dict(&self) -> PyDictRef { + PyObject::new(PyDict::default(), self.dict_type(), None) + .downcast() + .unwrap() } pub fn new_class(&self, name: &str, base: PyClassRef) -> PyClassRef { @@ -580,16 +582,16 @@ impl PyContext { } pub fn new_scope(&self) -> Scope { - Scope::new(None, self.new_dict()) + Scope::new(None, self.new_dict().into_object()) } - pub fn new_module(&self, name: &str, dict: PyObjectRef) -> PyObjectRef { + pub fn new_module(&self, name: &str, dict: PyDictRef) -> PyObjectRef { PyObject::new( PyModule { name: name.to_string(), - dict, }, self.module_type.clone(), + Some(dict), ) } @@ -600,6 +602,7 @@ impl PyContext { PyObject::new( PyBuiltinFunction::new(f.into_func()), self.builtin_function_or_method_type(), + None, ) } @@ -611,7 +614,7 @@ impl PyContext { } pub fn new_code_object(&self, code: bytecode::CodeObject) -> PyObjectRef { - PyObject::new(objcode::PyCode::new(code), self.code_type()) + PyObject::new(objcode::PyCode::new(code), self.code_type(), None) } pub fn new_function( @@ -623,18 +626,22 @@ impl PyContext { PyObject::new( PyFunction::new(code_obj, scope, defaults), self.function_type(), + Some(self.new_dict()), ) } pub fn new_bound_method(&self, function: PyObjectRef, object: PyObjectRef) -> PyObjectRef { - PyObject::new(PyMethod::new(object, function), self.bound_method_type()) + PyObject::new( + PyMethod::new(object, function), + self.bound_method_type(), + None, + ) } - pub fn new_instance(&self, class: PyClassRef, dict: Option) -> PyObjectRef { - let dict = dict.unwrap_or_default(); + pub fn new_instance(&self, class: PyClassRef, dict: Option) -> PyObjectRef { PyObject { typ: class, - dict: Some(RefCell::new(dict)), + dict: dict, payload: objobject::PyInstance, } .into_ref() @@ -661,11 +668,8 @@ impl PyContext { attributes .borrow_mut() .insert(attr_name.to_string(), value.into()); - } else if let Some(PyModule { ref dict, .. }) = obj.payload::() { - dict.set_item(self, attr_name, value.into()) } else if let Some(ref dict) = obj.dict { - dict.borrow_mut() - .insert(attr_name.to_string(), value.into()); + dict.set_item(self, attr_name, value.into()); } else { unimplemented!("set_attr unimplemented for: {:?}", obj); }; @@ -707,7 +711,7 @@ where T: ?Sized + PyObjectPayload, { pub typ: PyClassRef, - pub dict: Option>, // __dict__ member + pub dict: Option, // __dict__ member pub payload: T, } @@ -917,8 +921,6 @@ 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 dict, .. }) = self.payload::() { - dict.get_item(k) } else { panic!("TODO {:?}", k) } @@ -927,8 +929,6 @@ impl DictProtocol for PyObjectRef { fn get_key_value_pairs(&self) -> Vec<(PyObjectRef, PyObjectRef)> { if self.payload_is::() { objdict::get_key_value_pairs(self) - } else if let Some(PyModule { ref dict, .. }) = self.payload::() { - dict.get_key_value_pairs() } else { panic!("TODO") } @@ -939,8 +939,6 @@ 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 dict, .. }) = self.payload::() { - dict.set_item(ctx, key, v); } else { panic!("TODO {:?}", self); } @@ -1121,7 +1119,7 @@ where T: PyValue + Sized, { fn into_pyobject(self, vm: &VirtualMachine) -> PyResult { - Ok(PyObject::new(self, T::class(vm))) + Ok(PyObject::new(self, T::class(vm), None)) } } @@ -1143,19 +1141,10 @@ impl PyObject where T: Sized + PyObjectPayload, { - pub fn new(payload: T, typ: PyClassRef) -> PyObjectRef { - PyObject { - typ, - dict: Some(RefCell::new(PyAttributes::new())), - payload, - } - .into_ref() - } - - pub fn new_without_dict(payload: T, typ: PyClassRef) -> PyObjectRef { + pub fn new(payload: T, typ: PyClassRef, dict: Option) -> PyObjectRef { PyObject { typ, - dict: None, + dict: dict, payload, } .into_ref() @@ -1184,7 +1173,7 @@ pub trait PyValue: fmt::Debug + Sized + 'static { fn into_ref(self, vm: &VirtualMachine) -> PyRef { PyRef { - obj: PyObject::new(self, Self::class(vm)), + obj: PyObject::new(self, Self::class(vm), None), _payload: PhantomData, } } @@ -1192,8 +1181,13 @@ pub trait PyValue: fmt::Debug + Sized + 'static { fn into_ref_with_type(self, vm: &VirtualMachine, cls: PyClassRef) -> PyResult> { let class = Self::class(vm); if objtype::issubclass(&cls, &class) { + let dict = if cls.is(&class) { + None + } else { + Some(vm.ctx.new_dict()) + }; Ok(PyRef { - obj: PyObject::new(self, cls), + obj: PyObject::new(self, cls, dict), _payload: PhantomData, }) } else { diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index 2c5c95d34b..eac3619c26 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -177,7 +177,7 @@ impl<'de> Visitor<'de> for PyObjectDeserializer<'de> { }; dict.set_item(&self.vm.ctx, &key, value); } - Ok(dict) + Ok(dict.into_object()) } fn visit_unit(self) -> Result diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 7713c561da..0ee9798ab0 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -141,10 +141,6 @@ impl VirtualMachine { self.ctx.new_bool(b) } - pub fn new_dict(&self) -> PyObjectRef { - self.ctx.new_dict() - } - pub fn new_empty_exception(&self, exc_type: PyClassRef) -> PyResult { info!("New exception created: no msg"); let args = PyFuncArgs::default(); @@ -454,11 +450,11 @@ impl VirtualMachine { // Do we support `**kwargs` ? let kwargs = match code_object.varkeywords { bytecode::Varargs::Named(ref kwargs_name) => { - let d = self.new_dict(); + let d = self.ctx.new_dict().into_object(); locals.set_item(&self.ctx, kwargs_name, d.clone()); Some(d) } - bytecode::Varargs::Unnamed => Some(self.new_dict()), + bytecode::Varargs::Unnamed => Some(self.ctx.new_dict().into_object()), bytecode::Varargs::None => None, }; diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index a309284cf3..0e13aeb3ab 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -363,6 +363,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { doc: window().document().expect("Document missing from window"), }, document_class.clone(), + None, ); let element = py_class!(ctx, "Element", ctx.object(), { diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index ecdb4b1102..588c7571cd 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -187,13 +187,13 @@ pub fn js_to_py(vm: &VirtualMachine, js_val: JsValue) -> PyObjectRef { u8_array.for_each(&mut |byte, _, _| vec.push(byte)); vm.ctx.new_bytes(vec) } else { - let dict = vm.new_dict(); + let dict = vm.ctx.new_dict(); 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); dict.set_item(&vm.ctx, &String::from(js_sys::JsString::from(key)), py_val); } - dict + dict.into_object() } } else if js_val.is_function() { let func = js_sys::Function::from(js_val); From 55e0fb1f19f84d377f9aa5368d606e40f2a49371 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Mon, 25 Mar 2019 16:39:25 +0000 Subject: [PATCH 37/51] AST class for nodes instead of just using object everywhere. --- vm/src/stdlib/ast.rs | 459 ++++++++++++++++++++++--------------------- 1 file changed, 238 insertions(+), 221 deletions(-) diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index a3010a5199..0011407aba 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -11,9 +11,20 @@ use rustpython_parser::{ast, parser}; use crate::function::PyFuncArgs; use crate::obj::objstr; -use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; +use crate::obj::objtype::PyClassRef; +use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; +#[derive(Debug)] +struct AstNode; +// type AstNodeRef = PyRef; + +impl PyValue for AstNode { + fn class(vm: &VirtualMachine) -> PyClassRef { + vm.class("ast", "AST") + } +} + /* * Idea: maybe we can create a sort of struct with some helper functions? struct AstToPyAst { @@ -30,35 +41,35 @@ impl AstToPyAst { } */ -fn program_to_ast(ctx: &PyContext, program: &ast::Program) -> PyObjectRef { +fn program_to_ast(vm: &VirtualMachine, program: &ast::Program) -> PyObjectRef { let mut body = vec![]; for statement in &program.statements { - body.push(statement_to_ast(ctx, statement)); + body.push(statement_to_ast(&vm, statement)); } // TODO: create Module node: // let ast_node = ctx.new_instance(this.Module); - let ast_node = ctx.new_object(); - let py_body = ctx.new_list(body); - ctx.set_attr(&ast_node, "body", py_body); + let ast_node = create_node(vm, "program"); + let py_body = vm.ctx.new_list(body); + vm.ctx.set_attr(&ast_node, "body", py_body); ast_node } // Create a node class instance -fn create_node(ctx: &PyContext, _name: &str) -> PyObjectRef { +fn create_node(vm: &VirtualMachine, _name: &str) -> PyObjectRef { // TODO: instantiate a class of type given by name // TODO: lookup in the current module? - ctx.new_object() + PyObject::new(AstNode, AstNode::class(vm), Some(vm.ctx.new_dict())) } -fn statements_to_ast(ctx: &PyContext, statements: &[ast::LocatedStatement]) -> PyObjectRef { +fn statements_to_ast(vm: &VirtualMachine, statements: &[ast::LocatedStatement]) -> PyObjectRef { let mut py_statements = vec![]; for statement in statements { - py_statements.push(statement_to_ast(ctx, statement)); + py_statements.push(statement_to_ast(vm, statement)); } - ctx.new_list(py_statements) + vm.ctx.new_list(py_statements) } -fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObjectRef { +fn statement_to_ast(vm: &VirtualMachine, statement: &ast::LocatedStatement) -> PyObjectRef { let node = match &statement.node { ast::Statement::ClassDef { name, @@ -66,17 +77,18 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj decorator_list, .. } => { - let node = create_node(ctx, "ClassDef"); + let node = create_node(vm, "ClassDef"); // Set name: - ctx.set_attr(&node, "name", ctx.new_str(name.to_string())); + vm.ctx + .set_attr(&node, "name", vm.ctx.new_str(name.to_string())); // Set body: - let py_body = statements_to_ast(ctx, body); - ctx.set_attr(&node, "body", py_body); + let py_body = statements_to_ast(vm, body); + vm.ctx.set_attr(&node, "body", py_body); - let py_decorator_list = expressions_to_ast(ctx, decorator_list); - ctx.set_attr(&node, "decorator_list", py_decorator_list); + let py_decorator_list = expressions_to_ast(vm, decorator_list); + vm.ctx.set_attr(&node, "decorator_list", py_decorator_list); node } ast::Statement::FunctionDef { @@ -86,80 +98,83 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj decorator_list, returns, } => { - let node = create_node(ctx, "FunctionDef"); + let node = create_node(vm, "FunctionDef"); // Set name: - ctx.set_attr(&node, "name", ctx.new_str(name.to_string())); + vm.ctx + .set_attr(&node, "name", vm.ctx.new_str(name.to_string())); - ctx.set_attr(&node, "args", parameters_to_ast(ctx, args)); + vm.ctx.set_attr(&node, "args", parameters_to_ast(vm, args)); // Set body: - let py_body = statements_to_ast(ctx, body); - ctx.set_attr(&node, "body", py_body); + let py_body = statements_to_ast(vm, body); + vm.ctx.set_attr(&node, "body", py_body); - let py_decorator_list = expressions_to_ast(ctx, decorator_list); - ctx.set_attr(&node, "decorator_list", py_decorator_list); + let py_decorator_list = expressions_to_ast(vm, decorator_list); + vm.ctx.set_attr(&node, "decorator_list", py_decorator_list); let py_returns = if let Some(hint) = returns { - expression_to_ast(ctx, hint) + expression_to_ast(vm, hint) } else { - ctx.none() + vm.ctx.none() }; - ctx.set_attr(&node, "returns", py_returns); + vm.ctx.set_attr(&node, "returns", py_returns); node } - ast::Statement::Continue => create_node(ctx, "Continue"), - ast::Statement::Break => create_node(ctx, "Break"), - ast::Statement::Pass => create_node(ctx, "Pass"), + ast::Statement::Continue => create_node(vm, "Continue"), + ast::Statement::Break => create_node(vm, "Break"), + ast::Statement::Pass => create_node(vm, "Pass"), ast::Statement::Assert { test, msg } => { - let node = create_node(ctx, "Pass"); + let node = create_node(vm, "Pass"); - ctx.set_attr(&node, "test", expression_to_ast(ctx, test)); + vm.ctx.set_attr(&node, "test", expression_to_ast(vm, test)); let py_msg = match msg { - Some(msg) => expression_to_ast(ctx, msg), - None => ctx.none(), + Some(msg) => expression_to_ast(vm, msg), + None => vm.ctx.none(), }; - ctx.set_attr(&node, "msg", py_msg); + vm.ctx.set_attr(&node, "msg", py_msg); node } ast::Statement::Delete { targets } => { - let node = create_node(ctx, "Delete"); + let node = create_node(vm, "Delete"); - let py_targets = - ctx.new_tuple(targets.iter().map(|v| expression_to_ast(ctx, v)).collect()); - ctx.set_attr(&node, "targets", py_targets); + let py_targets = vm + .ctx + .new_tuple(targets.iter().map(|v| expression_to_ast(vm, v)).collect()); + vm.ctx.set_attr(&node, "targets", py_targets); node } ast::Statement::Return { value } => { - let node = create_node(ctx, "Return"); + let node = create_node(vm, "Return"); let py_value = if let Some(value) = value { - ctx.new_tuple(value.iter().map(|v| expression_to_ast(ctx, v)).collect()) + vm.ctx + .new_tuple(value.iter().map(|v| expression_to_ast(vm, v)).collect()) } else { - ctx.none() + vm.ctx.none() }; - ctx.set_attr(&node, "value", py_value); + vm.ctx.set_attr(&node, "value", py_value); node } ast::Statement::If { test, body, orelse } => { - let node = create_node(ctx, "If"); + let node = create_node(vm, "If"); - let py_test = expression_to_ast(ctx, test); - ctx.set_attr(&node, "test", py_test); + let py_test = expression_to_ast(vm, test); + vm.ctx.set_attr(&node, "test", py_test); - let py_body = statements_to_ast(ctx, body); - ctx.set_attr(&node, "body", py_body); + let py_body = statements_to_ast(vm, body); + vm.ctx.set_attr(&node, "body", py_body); let py_orelse = if let Some(orelse) = orelse { - statements_to_ast(ctx, orelse) + statements_to_ast(vm, orelse) } else { - ctx.none() + vm.ctx.none() }; - ctx.set_attr(&node, "orelse", py_orelse); + vm.ctx.set_attr(&node, "orelse", py_orelse); node } @@ -169,49 +184,49 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj body, orelse, } => { - let node = create_node(ctx, "For"); + let node = create_node(vm, "For"); - let py_target = expression_to_ast(ctx, target); - ctx.set_attr(&node, "target", py_target); + let py_target = expression_to_ast(vm, target); + vm.ctx.set_attr(&node, "target", py_target); - let py_iter = expressions_to_ast(ctx, iter); - ctx.set_attr(&node, "iter", py_iter); + let py_iter = expressions_to_ast(vm, iter); + vm.ctx.set_attr(&node, "iter", py_iter); - let py_body = statements_to_ast(ctx, body); - ctx.set_attr(&node, "body", py_body); + let py_body = statements_to_ast(vm, body); + vm.ctx.set_attr(&node, "body", py_body); let py_orelse = if let Some(orelse) = orelse { - statements_to_ast(ctx, orelse) + statements_to_ast(vm, orelse) } else { - ctx.none() + vm.ctx.none() }; - ctx.set_attr(&node, "orelse", py_orelse); + vm.ctx.set_attr(&node, "orelse", py_orelse); node } ast::Statement::While { test, body, orelse } => { - let node = create_node(ctx, "While"); + let node = create_node(vm, "While"); - let py_test = expression_to_ast(ctx, test); - ctx.set_attr(&node, "test", py_test); + let py_test = expression_to_ast(vm, test); + vm.ctx.set_attr(&node, "test", py_test); - let py_body = statements_to_ast(ctx, body); - ctx.set_attr(&node, "body", py_body); + let py_body = statements_to_ast(vm, body); + vm.ctx.set_attr(&node, "body", py_body); let py_orelse = if let Some(orelse) = orelse { - statements_to_ast(ctx, orelse) + statements_to_ast(vm, orelse) } else { - ctx.none() + vm.ctx.none() }; - ctx.set_attr(&node, "orelse", py_orelse); + vm.ctx.set_attr(&node, "orelse", py_orelse); node } ast::Statement::Expression { expression } => { - let node = create_node(ctx, "Expr"); + let node = create_node(vm, "Expr"); - let value = expression_to_ast(ctx, expression); - ctx.set_attr(&node, "value", value); + let value = expression_to_ast(vm, expression); + vm.ctx.set_attr(&node, "value", value); node } @@ -221,38 +236,38 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj }; // set lineno on node: - let lineno = ctx.new_int(statement.location.get_row()); - ctx.set_attr(&node, "lineno", lineno); + let lineno = vm.ctx.new_int(statement.location.get_row()); + vm.ctx.set_attr(&node, "lineno", lineno); node } -fn expressions_to_ast(ctx: &PyContext, expressions: &[ast::Expression]) -> PyObjectRef { +fn expressions_to_ast(vm: &VirtualMachine, expressions: &[ast::Expression]) -> PyObjectRef { let mut py_expression_nodes = vec![]; for expression in expressions { - py_expression_nodes.push(expression_to_ast(ctx, expression)); + py_expression_nodes.push(expression_to_ast(vm, expression)); } - ctx.new_list(py_expression_nodes) + vm.ctx.new_list(py_expression_nodes) } -fn expression_to_ast(ctx: &PyContext, expression: &ast::Expression) -> PyObjectRef { +fn expression_to_ast(vm: &VirtualMachine, expression: &ast::Expression) -> PyObjectRef { let node = match &expression { ast::Expression::Call { function, args, .. } => { - let node = create_node(ctx, "Call"); + let node = create_node(vm, "Call"); - let py_func_ast = expression_to_ast(ctx, function); - ctx.set_attr(&node, "func", py_func_ast); + let py_func_ast = expression_to_ast(vm, function); + vm.ctx.set_attr(&node, "func", py_func_ast); - let py_args = expressions_to_ast(ctx, args); - ctx.set_attr(&node, "args", py_args); + let py_args = expressions_to_ast(vm, args); + vm.ctx.set_attr(&node, "args", py_args); node } ast::Expression::Binop { a, op, b } => { - let node = create_node(ctx, "BinOp"); + let node = create_node(vm, "BinOp"); - let py_a = expression_to_ast(ctx, a); - ctx.set_attr(&node, "left", py_a); + let py_a = expression_to_ast(vm, a); + vm.ctx.set_attr(&node, "left", py_a); // Operator: let str_op = match op { @@ -270,15 +285,15 @@ fn expression_to_ast(ctx: &PyContext, expression: &ast::Expression) -> PyObjectR ast::Operator::BitAnd => "BitAnd", ast::Operator::FloorDiv => "FloorDiv", }; - let py_op = ctx.new_str(str_op.to_string()); - ctx.set_attr(&node, "op", py_op); + let py_op = vm.ctx.new_str(str_op.to_string()); + vm.ctx.set_attr(&node, "op", py_op); - let py_b = expression_to_ast(ctx, b); - ctx.set_attr(&node, "right", py_b); + let py_b = expression_to_ast(vm, b); + vm.ctx.set_attr(&node, "right", py_b); node } ast::Expression::Unop { op, a } => { - let node = create_node(ctx, "UnaryOp"); + let node = create_node(vm, "UnaryOp"); let str_op = match op { ast::UnaryOperator::Not => "Not", @@ -286,37 +301,37 @@ fn expression_to_ast(ctx: &PyContext, expression: &ast::Expression) -> PyObjectR ast::UnaryOperator::Neg => "USub", ast::UnaryOperator::Pos => "UAdd", }; - let py_op = ctx.new_str(str_op.to_string()); - ctx.set_attr(&node, "op", py_op); + let py_op = vm.ctx.new_str(str_op.to_string()); + vm.ctx.set_attr(&node, "op", py_op); - let py_a = expression_to_ast(ctx, a); - ctx.set_attr(&node, "operand", py_a); + let py_a = expression_to_ast(vm, a); + vm.ctx.set_attr(&node, "operand", py_a); node } ast::Expression::BoolOp { a, op, b } => { - let node = create_node(ctx, "BoolOp"); + let node = create_node(vm, "BoolOp"); // Attach values: - let py_a = expression_to_ast(ctx, a); - let py_b = expression_to_ast(ctx, b); - let py_values = ctx.new_tuple(vec![py_a, py_b]); - ctx.set_attr(&node, "values", py_values); + let py_a = expression_to_ast(vm, a); + let py_b = expression_to_ast(vm, b); + let py_values = vm.ctx.new_tuple(vec![py_a, py_b]); + vm.ctx.set_attr(&node, "values", py_values); let str_op = match op { ast::BooleanOperator::And => "And", ast::BooleanOperator::Or => "Or", }; - let py_op = ctx.new_str(str_op.to_string()); - ctx.set_attr(&node, "op", py_op); + let py_op = vm.ctx.new_str(str_op.to_string()); + vm.ctx.set_attr(&node, "op", py_op); node } ast::Expression::Compare { a, op, b } => { - let node = create_node(ctx, "Compare"); + let node = create_node(vm, "Compare"); - let py_a = expression_to_ast(ctx, a); - ctx.set_attr(&node, "left", py_a); + let py_a = expression_to_ast(vm, a); + vm.ctx.set_attr(&node, "left", py_a); // Operator: let str_op = match op { @@ -331,282 +346,283 @@ fn expression_to_ast(ctx: &PyContext, expression: &ast::Expression) -> PyObjectR ast::Comparison::Is => "Is", ast::Comparison::IsNot => "IsNot", }; - let py_ops = ctx.new_list(vec![ctx.new_str(str_op.to_string())]); - ctx.set_attr(&node, "ops", py_ops); + let py_ops = vm.ctx.new_list(vec![vm.ctx.new_str(str_op.to_string())]); + vm.ctx.set_attr(&node, "ops", py_ops); - let py_b = ctx.new_list(vec![expression_to_ast(ctx, b)]); - ctx.set_attr(&node, "comparators", py_b); + let py_b = vm.ctx.new_list(vec![expression_to_ast(vm, b)]); + vm.ctx.set_attr(&node, "comparators", py_b); node } ast::Expression::Identifier { name } => { - let node = create_node(ctx, "Identifier"); + let node = create_node(vm, "Identifier"); // Id: - let py_name = ctx.new_str(name.clone()); - ctx.set_attr(&node, "id", py_name); + let py_name = vm.ctx.new_str(name.clone()); + vm.ctx.set_attr(&node, "id", py_name); node } ast::Expression::Lambda { args, body } => { - let node = create_node(ctx, "Lambda"); + let node = create_node(vm, "Lambda"); - ctx.set_attr(&node, "args", parameters_to_ast(ctx, args)); + vm.ctx.set_attr(&node, "args", parameters_to_ast(vm, args)); - let py_body = expression_to_ast(ctx, body); - ctx.set_attr(&node, "body", py_body); + let py_body = expression_to_ast(vm, body); + vm.ctx.set_attr(&node, "body", py_body); node } ast::Expression::IfExpression { test, body, orelse } => { - let node = create_node(ctx, "IfExp"); + let node = create_node(vm, "IfExp"); - let py_test = expression_to_ast(ctx, test); - ctx.set_attr(&node, "test", py_test); + let py_test = expression_to_ast(vm, test); + vm.ctx.set_attr(&node, "test", py_test); - let py_body = expression_to_ast(ctx, body); - ctx.set_attr(&node, "body", py_body); + let py_body = expression_to_ast(vm, body); + vm.ctx.set_attr(&node, "body", py_body); - let py_orelse = expression_to_ast(ctx, orelse); - ctx.set_attr(&node, "orelse", py_orelse); + let py_orelse = expression_to_ast(vm, orelse); + vm.ctx.set_attr(&node, "orelse", py_orelse); node } ast::Expression::Number { value } => { - let node = create_node(ctx, "Num"); + let node = create_node(vm, "Num"); let py_n = match value { - ast::Number::Integer { value } => ctx.new_int(value.clone()), - ast::Number::Float { value } => ctx.new_float(*value), + ast::Number::Integer { value } => vm.ctx.new_int(value.clone()), + ast::Number::Float { value } => vm.ctx.new_float(*value), ast::Number::Complex { real, imag } => { - ctx.new_complex(Complex64::new(*real, *imag)) + vm.ctx.new_complex(Complex64::new(*real, *imag)) } }; - ctx.set_attr(&node, "n", py_n); + vm.ctx.set_attr(&node, "n", py_n); node } ast::Expression::True => { - let node = create_node(ctx, "NameConstant"); + let node = create_node(vm, "NameConstant"); - ctx.set_attr(&node, "value", ctx.new_bool(true)); + vm.ctx.set_attr(&node, "value", vm.ctx.new_bool(true)); node } ast::Expression::False => { - let node = create_node(ctx, "NameConstant"); + let node = create_node(vm, "NameConstant"); - ctx.set_attr(&node, "value", ctx.new_bool(false)); + vm.ctx.set_attr(&node, "value", vm.ctx.new_bool(false)); node } ast::Expression::None => { - let node = create_node(ctx, "NameConstant"); + let node = create_node(vm, "NameConstant"); - ctx.set_attr(&node, "value", ctx.none()); + vm.ctx.set_attr(&node, "value", vm.ctx.none()); node } - ast::Expression::Ellipsis => create_node(ctx, "Ellipsis"), + ast::Expression::Ellipsis => create_node(vm, "Ellipsis"), ast::Expression::List { elements } => { - let node = create_node(ctx, "List"); + let node = create_node(vm, "List"); - let elts = elements.iter().map(|e| expression_to_ast(ctx, e)).collect(); - let py_elts = ctx.new_list(elts); - ctx.set_attr(&node, "elts", py_elts); + let elts = elements.iter().map(|e| expression_to_ast(vm, e)).collect(); + let py_elts = vm.ctx.new_list(elts); + vm.ctx.set_attr(&node, "elts", py_elts); node } ast::Expression::Tuple { elements } => { - let node = create_node(ctx, "Tuple"); + let node = create_node(vm, "Tuple"); - let elts = elements.iter().map(|e| expression_to_ast(ctx, e)).collect(); - let py_elts = ctx.new_list(elts); - ctx.set_attr(&node, "elts", py_elts); + let elts = elements.iter().map(|e| expression_to_ast(vm, e)).collect(); + let py_elts = vm.ctx.new_list(elts); + vm.ctx.set_attr(&node, "elts", py_elts); node } ast::Expression::Set { elements } => { - let node = create_node(ctx, "Set"); + let node = create_node(vm, "Set"); - let elts = elements.iter().map(|e| expression_to_ast(ctx, e)).collect(); - let py_elts = ctx.new_list(elts); - ctx.set_attr(&node, "elts", py_elts); + let elts = elements.iter().map(|e| expression_to_ast(vm, e)).collect(); + let py_elts = vm.ctx.new_list(elts); + vm.ctx.set_attr(&node, "elts", py_elts); node } ast::Expression::Dict { elements } => { - let node = create_node(ctx, "Dict"); + let node = create_node(vm, "Dict"); let mut keys = Vec::new(); let mut values = Vec::new(); for (k, v) in elements { - keys.push(expression_to_ast(ctx, k)); - values.push(expression_to_ast(ctx, v)); + keys.push(expression_to_ast(vm, k)); + values.push(expression_to_ast(vm, v)); } - let py_keys = ctx.new_list(keys); - ctx.set_attr(&node, "keys", py_keys); + let py_keys = vm.ctx.new_list(keys); + vm.ctx.set_attr(&node, "keys", py_keys); - let py_values = ctx.new_list(values); - ctx.set_attr(&node, "values", py_values); + let py_values = vm.ctx.new_list(values); + vm.ctx.set_attr(&node, "values", py_values); node } ast::Expression::Comprehension { kind, generators } => { let node = match kind.deref() { ast::ComprehensionKind::GeneratorExpression { .. } => { - create_node(ctx, "GeneratorExp") + create_node(vm, "GeneratorExp") } - ast::ComprehensionKind::List { .. } => create_node(ctx, "ListComp"), - ast::ComprehensionKind::Set { .. } => create_node(ctx, "SetComp"), - ast::ComprehensionKind::Dict { .. } => create_node(ctx, "DictComp"), + ast::ComprehensionKind::List { .. } => create_node(vm, "ListComp"), + ast::ComprehensionKind::Set { .. } => create_node(vm, "SetComp"), + ast::ComprehensionKind::Dict { .. } => create_node(vm, "DictComp"), }; let g = generators .iter() - .map(|g| comprehension_to_ast(ctx, g)) + .map(|g| comprehension_to_ast(vm, g)) .collect(); - let py_generators = ctx.new_list(g); - ctx.set_attr(&node, "generators", py_generators); + let py_generators = vm.ctx.new_list(g); + vm.ctx.set_attr(&node, "generators", py_generators); node } ast::Expression::Yield { value } => { - let node = create_node(ctx, "Yield"); + let node = create_node(vm, "Yield"); let py_value = match value { - Some(value) => expression_to_ast(ctx, value), - None => ctx.none(), + Some(value) => expression_to_ast(vm, value), + None => vm.ctx.none(), }; - ctx.set_attr(&node, "value", py_value); + vm.ctx.set_attr(&node, "value", py_value); node } ast::Expression::YieldFrom { value } => { - let node = create_node(ctx, "YieldFrom"); + let node = create_node(vm, "YieldFrom"); - let py_value = expression_to_ast(ctx, value); - ctx.set_attr(&node, "value", py_value); + let py_value = expression_to_ast(vm, value); + vm.ctx.set_attr(&node, "value", py_value); node } ast::Expression::Subscript { a, b } => { - let node = create_node(ctx, "Subscript"); + let node = create_node(vm, "Subscript"); - let py_value = expression_to_ast(ctx, a); - ctx.set_attr(&node, "value", py_value); + let py_value = expression_to_ast(vm, a); + vm.ctx.set_attr(&node, "value", py_value); - let py_slice = expression_to_ast(ctx, b); - ctx.set_attr(&node, "slice", py_slice); + let py_slice = expression_to_ast(vm, b); + vm.ctx.set_attr(&node, "slice", py_slice); node } ast::Expression::Attribute { value, name } => { - let node = create_node(ctx, "Attribute"); + let node = create_node(vm, "Attribute"); - let py_value = expression_to_ast(ctx, value); - ctx.set_attr(&node, "value", py_value); + let py_value = expression_to_ast(vm, value); + vm.ctx.set_attr(&node, "value", py_value); - let py_attr = ctx.new_str(name.to_string()); - ctx.set_attr(&node, "attr", py_attr); + let py_attr = vm.ctx.new_str(name.to_string()); + vm.ctx.set_attr(&node, "attr", py_attr); node } ast::Expression::Starred { value } => { - let node = create_node(ctx, "Starred"); + let node = create_node(vm, "Starred"); - let py_value = expression_to_ast(ctx, value); - ctx.set_attr(&node, "value", py_value); + let py_value = expression_to_ast(vm, value); + vm.ctx.set_attr(&node, "value", py_value); node } ast::Expression::Slice { elements } => { - let node = create_node(ctx, "Slice"); + let node = create_node(vm, "Slice"); - let py_value = expressions_to_ast(ctx, elements); - ctx.set_attr(&node, "bounds", py_value); + let py_value = expressions_to_ast(vm, elements); + vm.ctx.set_attr(&node, "bounds", py_value); node } - ast::Expression::String { value } => string_to_ast(ctx, value), + ast::Expression::String { value } => string_to_ast(vm, value), ast::Expression::Bytes { value } => { - let node = create_node(ctx, "Bytes"); - ctx.set_attr(&node, "s", ctx.new_bytes(value.clone())); + let node = create_node(vm, "Bytes"); + vm.ctx.set_attr(&node, "s", vm.ctx.new_bytes(value.clone())); node } }; // TODO: retrieve correct lineno: - let lineno = ctx.new_int(1); - ctx.set_attr(&node, "lineno", lineno); + let lineno = vm.ctx.new_int(1); + vm.ctx.set_attr(&node, "lineno", lineno); node } -fn parameters_to_ast(ctx: &PyContext, args: &ast::Parameters) -> PyObjectRef { - let node = create_node(ctx, "arguments"); +fn parameters_to_ast(vm: &VirtualMachine, args: &ast::Parameters) -> PyObjectRef { + let node = create_node(vm, "arguments"); - ctx.set_attr( + vm.ctx.set_attr( &node, "args", - ctx.new_list(args.args.iter().map(|a| parameter_to_ast(ctx, a)).collect()), + vm.ctx + .new_list(args.args.iter().map(|a| parameter_to_ast(vm, a)).collect()), ); node } -fn parameter_to_ast(ctx: &PyContext, parameter: &ast::Parameter) -> PyObjectRef { - let node = create_node(ctx, "arg"); +fn parameter_to_ast(vm: &VirtualMachine, parameter: &ast::Parameter) -> PyObjectRef { + let node = create_node(vm, "arg"); - let py_arg = ctx.new_str(parameter.arg.to_string()); - ctx.set_attr(&node, "arg", py_arg); + let py_arg = vm.ctx.new_str(parameter.arg.to_string()); + vm.ctx.set_attr(&node, "arg", py_arg); let py_annotation = if let Some(annotation) = ¶meter.annotation { - expression_to_ast(ctx, annotation) + expression_to_ast(vm, annotation) } else { - ctx.none() + vm.ctx.none() }; - ctx.set_attr(&node, "annotation", py_annotation); + vm.ctx.set_attr(&node, "annotation", py_annotation); node } -fn comprehension_to_ast(ctx: &PyContext, comprehension: &ast::Comprehension) -> PyObjectRef { - let node = create_node(ctx, "comprehension"); +fn comprehension_to_ast(vm: &VirtualMachine, comprehension: &ast::Comprehension) -> PyObjectRef { + let node = create_node(vm, "comprehension"); - let py_target = expression_to_ast(ctx, &comprehension.target); - ctx.set_attr(&node, "target", py_target); + let py_target = expression_to_ast(vm, &comprehension.target); + vm.ctx.set_attr(&node, "target", py_target); - let py_iter = expression_to_ast(ctx, &comprehension.iter); - ctx.set_attr(&node, "iter", py_iter); + let py_iter = expression_to_ast(vm, &comprehension.iter); + vm.ctx.set_attr(&node, "iter", py_iter); - let py_ifs = expressions_to_ast(ctx, &comprehension.ifs); - ctx.set_attr(&node, "ifs", py_ifs); + let py_ifs = expressions_to_ast(vm, &comprehension.ifs); + vm.ctx.set_attr(&node, "ifs", py_ifs); node } -fn string_to_ast(ctx: &PyContext, string: &ast::StringGroup) -> PyObjectRef { +fn string_to_ast(vm: &VirtualMachine, string: &ast::StringGroup) -> PyObjectRef { match string { ast::StringGroup::Constant { value } => { - let node = create_node(ctx, "Str"); - ctx.set_attr(&node, "s", ctx.new_str(value.clone())); + let node = create_node(vm, "Str"); + vm.ctx.set_attr(&node, "s", vm.ctx.new_str(value.clone())); node } ast::StringGroup::FormattedValue { value, .. } => { - let node = create_node(ctx, "FormattedValue"); - let py_value = expression_to_ast(ctx, value); - ctx.set_attr(&node, "value", py_value); + let node = create_node(vm, "FormattedValue"); + let py_value = expression_to_ast(vm, value); + vm.ctx.set_attr(&node, "value", py_value); node } ast::StringGroup::Joined { values } => { - let node = create_node(ctx, "JoinedStr"); - let py_values = ctx.new_list( + let node = create_node(vm, "JoinedStr"); + let py_values = vm.ctx.new_list( values .iter() - .map(|value| string_to_ast(ctx, value)) + .map(|value| string_to_ast(vm, value)) .collect(), ); - ctx.set_attr(&node, "values", py_values); + vm.ctx.set_attr(&node, "values", py_values); node } } @@ -619,7 +635,7 @@ fn ast_parse(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { let internal_ast = parser::parse_program(&source_string) .map_err(|err| vm.new_value_error(format!("{}", err)))?; // source.clone(); - let ast_node = program_to_ast(&vm.ctx, &internal_ast); + let ast_node = program_to_ast(&vm, &internal_ast); Ok(ast_node) } @@ -628,6 +644,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { "parse" => ctx.new_rustfunc(ast_parse), "Module" => py_class!(ctx, "_ast.Module", ctx.object(), {}), "FunctionDef" => py_class!(ctx, "_ast.FunctionDef", ctx.object(), {}), - "Call" => py_class!(ctx, "_ast.Call", ctx.object(), {}) + "Call" => py_class!(ctx, "_ast.Call", ctx.object(), {}), + "AST" => py_class!(ctx, "_ast.AST", ctx.object(), {}), }) } From e44b02b88f726408478377fc62f094661d59c8ce Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Mon, 25 Mar 2019 17:52:37 +0100 Subject: [PATCH 38/51] Avoid trace log in tests --- tests/test_snippets.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/test_snippets.py b/tests/test_snippets.py index 4be73b7bde..bc4c43a775 100644 --- a/tests/test_snippets.py +++ b/tests/test_snippets.py @@ -65,8 +65,7 @@ def run_via_cpython_bytecode(filename, test_type): # Step2: run cpython bytecode: env = os.environ.copy() - log_level = 'info' if test_type == _TestType.benchmark else 'debug' - env['RUST_LOG'] = '{},cargo=error,jobserver=error'.format(log_level) + env['RUST_LOG'] = 'info,cargo=error,jobserver=error' env['RUST_BACKTRACE'] = '1' with pushd(CPYTHON_RUNNER_DIR): subprocess.check_call(['cargo', 'run', bytecode_filename], env=env) @@ -74,8 +73,7 @@ def run_via_cpython_bytecode(filename, test_type): def run_via_rustpython(filename, test_type): env = os.environ.copy() - log_level = 'info' if test_type == _TestType.benchmark else 'trace' - env['RUST_LOG'] = '{},cargo=error,jobserver=error'.format(log_level) + env['RUST_LOG'] = 'info,cargo=error,jobserver=error' env['RUST_BACKTRACE'] = '1' if env.get('CODE_COVERAGE', 'false') == 'true': subprocess.check_call( From 4ab59a8e4c369104ab8518f651038877ada301f1 Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Mon, 25 Mar 2019 17:53:13 +0100 Subject: [PATCH 39/51] Do not run benchmarks in the main test suite --- tests/test_snippets.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_snippets.py b/tests/test_snippets.py index bc4c43a775..7aa9c2456b 100644 --- a/tests/test_snippets.py +++ b/tests/test_snippets.py @@ -17,7 +17,6 @@ class _TestType(enum.Enum): functional = 1 - benchmark = 2 logger = logging.getLogger('tests') @@ -25,7 +24,6 @@ class _TestType(enum.Enum): TEST_ROOT = os.path.abspath(os.path.join(ROOT_DIR, 'tests')) TEST_DIRS = { _TestType.functional: os.path.join(TEST_ROOT, 'snippets'), - _TestType.benchmark: os.path.join(TEST_ROOT, 'benchmarks'), } CPYTHON_RUNNER_DIR = os.path.abspath(os.path.join(ROOT_DIR, 'py_code_object')) RUSTPYTHON_RUNNER_DIR = os.path.abspath(os.path.join(ROOT_DIR)) From 16e04cf298a67f1c2686fe846177789425a26df4 Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Mon, 25 Mar 2019 17:54:04 +0100 Subject: [PATCH 40/51] Run rustpython tests without cargo middleman --- tests/test_snippets.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/test_snippets.py b/tests/test_snippets.py index 7aa9c2456b..7edbd7b7e5 100644 --- a/tests/test_snippets.py +++ b/tests/test_snippets.py @@ -73,12 +73,13 @@ def run_via_rustpython(filename, test_type): env = os.environ.copy() env['RUST_LOG'] = 'info,cargo=error,jobserver=error' env['RUST_BACKTRACE'] = '1' + + target = 'release' if env.get('CODE_COVERAGE', 'false') == 'true': - subprocess.check_call( - ['cargo', 'run', filename], env=env) - else: - subprocess.check_call( - ['cargo', 'run', '--release', filename], env=env) + target = 'debug' + binary = os.path.abspath(os.path.join(ROOT_DIR, 'target', target, 'rustpython')) + + subprocess.check_call([binary, filename], env=env) def create_test_function(cls, filename, method, test_type): @@ -120,4 +121,7 @@ def get_test_files(): # @populate('cpython_bytecode') @populate('rustpython') class SampleTestCase(unittest.TestCase): - pass + @classmethod + def setUpClass(cls): + subprocess.check_call(['cargo', 'build']) + subprocess.check_call(['cargo', 'build', '--release']) From 2131a8130cfcf344a81437d7c7413211976dc25e Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Mon, 25 Mar 2019 17:55:17 +0100 Subject: [PATCH 41/51] Clean up some tests --- tests/snippets/append.py | 2 -- tests/snippets/strings.py | 8 ++++++++ tests/snippets/xfail_3.1.2.17.py | 7 ------- 3 files changed, 8 insertions(+), 9 deletions(-) delete mode 100644 tests/snippets/append.py delete mode 100644 tests/snippets/xfail_3.1.2.17.py diff --git a/tests/snippets/append.py b/tests/snippets/append.py deleted file mode 100644 index a0490cb6d3..0000000000 --- a/tests/snippets/append.py +++ /dev/null @@ -1,2 +0,0 @@ -x = [] -x.append(1) diff --git a/tests/snippets/strings.py b/tests/snippets/strings.py index 4483b37f62..31511db11e 100644 --- a/tests/snippets/strings.py +++ b/tests/snippets/strings.py @@ -1,3 +1,5 @@ +from testutils import assert_raises + assert "a" == 'a' assert """a""" == "a" assert len(""" " "" " "" """) == 11 @@ -124,3 +126,9 @@ assert 'z' > 'b' assert 'z' >= 'b' assert 'a' >= 'a' + +def try_mutate_str(): + word = "word" + word[0] = 'x' + +assert_raises(TypeError, try_mutate_str) diff --git a/tests/snippets/xfail_3.1.2.17.py b/tests/snippets/xfail_3.1.2.17.py deleted file mode 100644 index fc956bef43..0000000000 --- a/tests/snippets/xfail_3.1.2.17.py +++ /dev/null @@ -1,7 +0,0 @@ -word = "Python" - -word[0] = "J" # Should raise a error, immutable -word[2:] = "Jy" # Should raise a error, immutable - - - From 6ae10ed938a12dfdcede666a147fb4bba99666b7 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Mon, 25 Mar 2019 20:23:13 +0100 Subject: [PATCH 42/51] Fix build and add extend_class macro usage for set and frozenset --- vm/src/obj/objset.rs | 180 +++++++++++++++---------------------------- 1 file changed, 62 insertions(+), 118 deletions(-) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 300dc9bca9..72ee8c8970 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -85,12 +85,12 @@ fn create_set( PySet { elements: RefCell::new(elements), }, - PySet::class(vm).into_object(), + PySet::class(vm), )) } else if objtype::issubclass(&cls, &vm.ctx.frozenset_type()) { Ok(PyObject::new( PyFrozenSet { elements: elements }, - PyFrozenSet::class(vm).into_object(), + PyFrozenSet::class(vm), )) } else { Err(vm.new_type_error(format!("{} is not a subtype of set or frozenset", cls))) @@ -593,65 +593,43 @@ pub fn init(context: &PyContext) { set(iterable) -> new set object\n\n\ Build an unordered collection of unique elements."; - context.set_attr(set_type, "__contains__", context.new_rustfunc(set_contains)); - context.set_attr(set_type, "__len__", context.new_rustfunc(set_len)); - context.set_attr(set_type, "__new__", context.new_rustfunc(set_new)); - context.set_attr(set_type, "__repr__", context.new_rustfunc(set_repr)); - context.set_attr(set_type, "__eq__", context.new_rustfunc(set_eq)); - context.set_attr(set_type, "__ge__", context.new_rustfunc(set_ge)); - context.set_attr(set_type, "__gt__", context.new_rustfunc(set_gt)); - context.set_attr(set_type, "__le__", context.new_rustfunc(set_le)); - context.set_attr(set_type, "__lt__", context.new_rustfunc(set_lt)); - context.set_attr(set_type, "issubset", context.new_rustfunc(set_le)); - context.set_attr(set_type, "issuperset", context.new_rustfunc(set_ge)); - context.set_attr(set_type, "union", context.new_rustfunc(set_union)); - context.set_attr(set_type, "__or__", context.new_rustfunc(set_union)); - context.set_attr( - set_type, - "intersection", - context.new_rustfunc(set_intersection), - ); - context.set_attr(set_type, "__and__", context.new_rustfunc(set_intersection)); - context.set_attr(set_type, "difference", context.new_rustfunc(set_difference)); - context.set_attr(set_type, "__sub__", context.new_rustfunc(set_difference)); - context.set_attr( - set_type, - "symmetric_difference", - context.new_rustfunc(set_symmetric_difference), - ); - context.set_attr( - set_type, - "__xor__", - context.new_rustfunc(set_symmetric_difference), - ); - context.set_attr(set_type, "__doc__", context.new_str(set_doc.to_string())); - context.set_attr(set_type, "add", context.new_rustfunc(set_add)); - context.set_attr(set_type, "remove", context.new_rustfunc(set_remove)); - context.set_attr(set_type, "discard", context.new_rustfunc(set_discard)); - context.set_attr(set_type, "clear", context.new_rustfunc(set_clear)); - context.set_attr(set_type, "copy", context.new_rustfunc(set_copy)); - context.set_attr(set_type, "pop", context.new_rustfunc(set_pop)); - context.set_attr(set_type, "update", context.new_rustfunc(set_update)); - context.set_attr(set_type, "__ior__", context.new_rustfunc(set_ior)); - context.set_attr( - set_type, - "intersection_update", - context.new_rustfunc(set_intersection_update), - ); - context.set_attr(set_type, "__iand__", context.new_rustfunc(set_iand)); - context.set_attr( - set_type, - "difference_update", - context.new_rustfunc(set_difference_update), - ); - context.set_attr(set_type, "__isub__", context.new_rustfunc(set_isub)); - context.set_attr( - set_type, - "symmetric_difference_update", - context.new_rustfunc(set_symmetric_difference_update), - ); - context.set_attr(set_type, "__ixor__", context.new_rustfunc(set_ixor)); - context.set_attr(set_type, "__iter__", context.new_rustfunc(set_iter)); + extend_class!(context, set_type, { + "__contains__" => context.new_rustfunc(set_contains), + "__len__" => context.new_rustfunc(set_len), + "__new__" => context.new_rustfunc(set_new), + "__repr__" => context.new_rustfunc(set_repr), + "__eq__" => context.new_rustfunc(set_eq), + "__ge__" => context.new_rustfunc(set_ge), + "__gt__" => context.new_rustfunc(set_gt), + "__le__" => context.new_rustfunc(set_le), + "__lt__" => context.new_rustfunc(set_lt), + "issubset" => context.new_rustfunc(set_le), + "issuperset" => context.new_rustfunc(set_ge), + "union" => context.new_rustfunc(set_union), + "__or__" => context.new_rustfunc(set_union), + "intersection" => context.new_rustfunc(set_intersection), + "__and__" => context.new_rustfunc(set_intersection), + "difference" => context.new_rustfunc(set_difference), + "__sub__" => context.new_rustfunc(set_difference), + "symmetric_difference" => context.new_rustfunc(set_symmetric_difference), + "__xor__" => context.new_rustfunc(set_symmetric_difference), + "__doc__" => context.new_str(set_doc.to_string()), + "add" => context.new_rustfunc(set_add), + "remove" => context.new_rustfunc(set_remove), + "discard" => context.new_rustfunc(set_discard), + "clear" => context.new_rustfunc(set_clear), + "copy" => context.new_rustfunc(set_copy), + "pop" => context.new_rustfunc(set_pop), + "update" => context.new_rustfunc(set_update), + "__ior__" => context.new_rustfunc(set_ior), + "intersection_update" => context.new_rustfunc(set_intersection_update), + "__iand__" => context.new_rustfunc(set_iand), + "difference_update" => context.new_rustfunc(set_difference_update), + "__isub__" => context.new_rustfunc(set_isub), + "symmetric_difference_update" => context.new_rustfunc(set_symmetric_difference_update), + "__ixor__" => context.new_rustfunc(set_ixor), + "__iter__" => context.new_rustfunc(set_iter) + }); let frozenset_type = &context.frozenset_type; @@ -659,61 +637,27 @@ pub fn init(context: &PyContext) { frozenset(iterable) -> frozenset object\n\n\ Build an immutable unordered collection of unique elements."; - context.set_attr(frozenset_type, "__new__", context.new_rustfunc(set_new)); - context.set_attr(frozenset_type, "__eq__", context.new_rustfunc(set_eq)); - context.set_attr(frozenset_type, "__ge__", context.new_rustfunc(set_ge)); - context.set_attr(frozenset_type, "__gt__", context.new_rustfunc(set_gt)); - context.set_attr(frozenset_type, "__le__", context.new_rustfunc(set_le)); - context.set_attr(frozenset_type, "__lt__", context.new_rustfunc(set_lt)); - context.set_attr(frozenset_type, "issubset", context.new_rustfunc(set_le)); - context.set_attr(frozenset_type, "issuperset", context.new_rustfunc(set_ge)); - context.set_attr(frozenset_type, "union", context.new_rustfunc(set_union)); - context.set_attr(frozenset_type, "__or__", context.new_rustfunc(set_union)); - context.set_attr( - frozenset_type, - "intersection", - context.new_rustfunc(set_intersection), - ); - context.set_attr( - frozenset_type, - "__and__", - context.new_rustfunc(set_intersection), - ); - context.set_attr( - frozenset_type, - "difference", - context.new_rustfunc(set_difference), - ); - context.set_attr( - frozenset_type, - "__sub__", - context.new_rustfunc(set_difference), - ); - context.set_attr( - frozenset_type, - "symmetric_difference", - context.new_rustfunc(set_symmetric_difference), - ); - context.set_attr( - frozenset_type, - "__xor__", - context.new_rustfunc(set_symmetric_difference), - ); - context.set_attr( - frozenset_type, - "__contains__", - context.new_rustfunc(set_contains), - ); - context.set_attr(frozenset_type, "__len__", context.new_rustfunc(set_len)); - context.set_attr( - frozenset_type, - "__doc__", - context.new_str(frozenset_doc.to_string()), - ); - context.set_attr( - frozenset_type, - "__repr__", - context.new_rustfunc(frozenset_repr), - ); - context.set_attr(frozenset_type, "copy", context.new_rustfunc(set_copy)); + extend_class!(context, frozenset_type, { + "__new__" => context.new_rustfunc(set_new), + "__eq__" => context.new_rustfunc(set_eq), + "__ge__" => context.new_rustfunc(set_ge), + "__gt__" => context.new_rustfunc(set_gt), + "__le__" => context.new_rustfunc(set_le), + "__lt__" => context.new_rustfunc(set_lt), + "issubset" => context.new_rustfunc(set_le), + "issuperset" => context.new_rustfunc(set_ge), + "union" => context.new_rustfunc(set_union), + "__or__" => context.new_rustfunc(set_union), + "intersection" => context.new_rustfunc(set_intersection), + "__and__" => context.new_rustfunc(set_intersection), + "difference" => context.new_rustfunc(set_difference), + "__sub__" => context.new_rustfunc(set_difference), + "symmetric_difference" => context.new_rustfunc(set_symmetric_difference), + "__xor__" => context.new_rustfunc(set_symmetric_difference), + "__contains__" => context.new_rustfunc(set_contains), + "__len__" => context.new_rustfunc(set_len), + "__doc__" => context.new_str(frozenset_doc.to_string()), + "__repr__" => context.new_rustfunc(frozenset_repr), + "copy" => context.new_rustfunc(set_copy) + }); } From 17b816fbf83069332441c98daee231a9772cf809 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Mon, 25 Mar 2019 20:37:47 +0000 Subject: [PATCH 43/51] Fix objset compilation error caused by merge race. --- vm/src/obj/objset.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 72ee8c8970..7071cc6e4c 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -86,11 +86,13 @@ fn create_set( elements: RefCell::new(elements), }, PySet::class(vm), + None, )) } else if objtype::issubclass(&cls, &vm.ctx.frozenset_type()) { Ok(PyObject::new( PyFrozenSet { elements: elements }, PyFrozenSet::class(vm), + None, )) } else { Err(vm.new_type_error(format!("{} is not a subtype of set or frozenset", cls))) From cc4f3fdb4097a6701158da7c30a8bf1f70e2f954 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Mon, 25 Mar 2019 19:18:01 -0700 Subject: [PATCH 44/51] Clean up TypeProtocol --- vm/src/builtins.rs | 14 +++++++------- vm/src/exceptions.rs | 2 +- vm/src/frame.rs | 6 +++--- vm/src/function.rs | 2 +- vm/src/macros.rs | 2 +- vm/src/obj/objfloat.rs | 2 +- vm/src/obj/objfunction.rs | 2 +- vm/src/obj/objint.rs | 10 +++++----- vm/src/obj/objiter.rs | 2 +- vm/src/obj/objnone.rs | 10 +++++----- vm/src/obj/objobject.rs | 24 ++++++++++++------------ vm/src/obj/objproperty.rs | 10 ++++++---- vm/src/obj/objsequence.rs | 4 ++-- vm/src/obj/objset.rs | 30 +++++++++++++++--------------- vm/src/obj/objstr.rs | 6 +++--- vm/src/obj/objsuper.rs | 2 +- vm/src/obj/objtype.rs | 28 +++++++++------------------- vm/src/pyobject.rs | 36 ++++++++++++++++++------------------ vm/src/stdlib/json.rs | 2 +- vm/src/vm.rs | 16 ++++++++-------- 20 files changed, 101 insertions(+), 109 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 4d9fb79896..fbd2d53568 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -88,7 +88,7 @@ fn builtin_bin(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { fn builtin_callable(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, None)]); - let is_callable = objtype::class_has_attr(&obj.type_pyref(), "__call__"); + let is_callable = objtype::class_has_attr(&obj.class(), "__call__"); Ok(vm.new_bool(is_callable)) } @@ -240,7 +240,7 @@ fn make_scope( } else if vm.isinstance(arg, &dict_type)? { Some(arg) } else { - let arg_typ = arg.typ(); + let arg_typ = arg.class(); let actual_type = vm.to_pystr(&arg_typ)?; let expected_type_name = vm.to_pystr(&dict_type)?; return Err(vm.new_type_error(format!( @@ -368,7 +368,7 @@ fn builtin_len(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(value) => vm.invoke(value, PyFuncArgs::default()), Err(..) => Err(vm.new_type_error(format!( "object of type '{}' has no method {:?}", - objtype::get_type_name(&obj.typ()), + objtype::get_type_name(&obj.class()), len_method_name ))), } @@ -607,7 +607,7 @@ fn builtin_reversed(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { // TODO: fallback to using __len__ and __getitem__, if object supports sequence protocol Err(..) => Err(vm.new_type_error(format!( "'{}' object is not reversible", - objtype::get_type_name(&obj.typ()), + objtype::get_type_name(&obj.class()), ))), } } @@ -802,9 +802,9 @@ pub fn builtin_build_class_(vm: &VirtualMachine, mut args: PyFuncArgs) -> PyResu }; for base in bases.clone() { - if objtype::issubclass(&base.type_pyref(), &metaclass) { - metaclass = base.type_pyref(); - } else if !objtype::issubclass(&metaclass, &base.type_pyref()) { + if objtype::issubclass(&base.class(), &metaclass) { + metaclass = base.class(); + } else if !objtype::issubclass(&metaclass, &base.class()) { 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 3eb962edfe..7177527da4 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -68,7 +68,7 @@ fn exception_str(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(exc, Some(vm.ctx.exceptions.exception_type.clone()))] ); - let type_name = objtype::get_type_name(&exc.typ()); + let type_name = objtype::get_type_name(&exc.class()); let msg = if let Ok(m) = vm.get_attribute(exc.clone(), "msg") { match vm.to_pystr(&m) { Ok(msg) => msg, diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 3584a57a81..53479f8fc8 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -949,7 +949,7 @@ impl Frame { // let args = PyFuncArgs::default(); // TODO: what happens when we got an error during handling exception? let args = if let Some(exc) = exc { - let exc_type = exc.typ(); + let exc_type = exc.class().into_object(); let exc_val = exc.clone(); let exc_tb = vm.ctx.none(); // TODO: retrieve traceback? vec![exc_type, exc_val, exc_tb] @@ -1093,7 +1093,7 @@ impl Frame { Ok(found) => Ok(found), Err(_) => Err(vm.new_type_error(format!( "{} has no __contains__ method", - objtype::get_type_name(&haystack.typ()) + objtype::get_type_name(&haystack.class()) ))), } } @@ -1103,7 +1103,7 @@ impl Frame { Ok(found) => Ok(vm.ctx.new_bool(!objbool::get_value(&found))), Err(_) => Err(vm.new_type_error(format!( "{} has no __contains__ method", - objtype::get_type_name(&haystack.typ()) + objtype::get_type_name(&haystack.class()) ))), } } diff --git a/vm/src/function.rs b/vm/src/function.rs index ed959d85c9..1c61ffc87c 100644 --- a/vm/src/function.rs +++ b/vm/src/function.rs @@ -99,7 +99,7 @@ impl PyFuncArgs { Ok(Some(kwarg)) } else { let expected_ty_name = vm.to_pystr(&ty)?; - let actual_ty_name = vm.to_pystr(&kwarg.typ())?; + let actual_ty_name = vm.to_pystr(&kwarg.class())?; Err(vm.new_type_error(format!( "argument of type {} is required for named parameter `{}` (got: {})", expected_ty_name, key, actual_ty_name diff --git a/vm/src/macros.rs b/vm/src/macros.rs index 0c205ffc80..a631867976 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -20,7 +20,7 @@ macro_rules! type_check { let arg = &$args.args[$arg_count]; if !$crate::obj::objtype::isinstance(arg, &expected_type) { - let arg_typ = arg.typ(); + let arg_typ = arg.class(); let expected_type_name = $vm.to_pystr(&expected_type)?; let actual_type = $vm.to_pystr(&arg_typ)?; return Err($vm.new_type_error(format!( diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 958d368a89..5e68f59603 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -190,7 +190,7 @@ impl PyFloatRef { } } } else { - let type_name = objtype::get_type_name(&arg.typ()); + let type_name = objtype::get_type_name(&arg.class()); return Err(vm.new_type_error(format!("can't convert {} to float", type_name))); }; PyFloat { value }.into_ref_with_type(vm, cls) diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index 1488ee79c7..507d7697a9 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -76,7 +76,7 @@ fn bind_method(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(function, None), (obj, None), (cls, None)] ); - if obj.is(&vm.get_none()) && !cls.is(&obj.typ()) { + if obj.is(&vm.get_none()) && !cls.is(&obj.class()) { Ok(function.clone()) } else { Ok(vm.ctx.new_bound_method(function.clone(), obj.clone())) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index dd40f06a42..f31997eadb 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -213,8 +213,8 @@ impl PyIntRef { 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()) + objtype::get_type_name(&self.as_object().class()), + objtype::get_type_name(&other.class()) ))); } @@ -236,8 +236,8 @@ impl PyIntRef { 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()) + objtype::get_type_name(&self.as_object().class()), + objtype::get_type_name(&other.class()) ))); } @@ -420,7 +420,7 @@ pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult PyResult { vm.call_method(iter_target, "__iter__", vec![]) - // let type_str = objstr::get_value(&vm.to_str(iter_target.typ()).unwrap()); + // let type_str = objstr::get_value(&vm.to_str(iter_target.class()).unwrap()); // let type_error = vm.new_type_error(format!("Cannot iterate over {}", type_str)); // return Err(type_error); } diff --git a/vm/src/obj/objnone.rs b/vm/src/obj/objnone.rs index 840b00eccc..aff20f465f 100644 --- a/vm/src/obj/objnone.rs +++ b/vm/src/obj/objnone.rs @@ -12,7 +12,7 @@ pub type PyNoneRef = PyRef; impl PyValue for PyNone { fn class(vm: &VirtualMachine) -> PyClassRef { - vm.ctx.none().type_pyref() + vm.ctx.none().class() } } @@ -44,7 +44,7 @@ impl PyNoneRef { fn get_attribute(self, name: PyStringRef, vm: &VirtualMachine) -> PyResult { trace!("None.__getattribute__({:?}, {:?})", self, name); - let cls = self.typ(); + let cls = self.class(); // 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 @@ -69,7 +69,7 @@ impl PyNoneRef { } if let Some(attr) = class_get_attr(&cls, &name.value) { - let attr_class = attr.type_pyref(); + let attr_class = attr.class(); if class_has_attr(&attr_class, "__set__") { if let Some(get_func) = class_get_attr(&attr_class, "__get__") { return call_descriptor( @@ -88,7 +88,7 @@ impl PyNoneRef { // Ok(obj_attr) // } else if let Some(attr) = class_get_attr(&cls, &name.value) { - let attr_class = attr.type_pyref(); + let attr_class = attr.class(); if let Some(get_func) = class_get_attr(&attr_class, "__get__") { call_descriptor(attr, get_func, self.into_object(), cls.into_object(), vm) } else { @@ -107,7 +107,7 @@ fn none_new(_: PyClassRef, vm: &VirtualMachine) -> PyNoneRef { } pub fn init(context: &PyContext) { - extend_class!(context, &context.none.typ(), { + extend_class!(context, &context.none.class(), { "__new__" => context.new_rustfunc(none_new), "__repr__" => context.new_rustfunc(PyNoneRef::repr), "__bool__" => context.new_rustfunc(PyNoneRef::bool), diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 60473fad03..14dbb2e26f 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -104,10 +104,10 @@ fn object_setattr( vm: &VirtualMachine, ) -> PyResult<()> { trace!("object.__setattr__({:?}, {}, {:?})", obj, attr_name, value); - let cls = obj.type_pyref(); + let cls = obj.class(); if let Some(attr) = objtype::class_get_attr(&cls, &attr_name.value) { - if let Some(descriptor) = objtype::class_get_attr(&attr.type_pyref(), "__set__") { + if let Some(descriptor) = objtype::class_get_attr(&attr.class(), "__set__") { return vm .invoke(descriptor, vec![attr, obj.clone(), value]) .map(|_| ()); @@ -118,7 +118,7 @@ fn object_setattr( dict.set_item(&vm.ctx, &attr_name.value, value); Ok(()) } else { - let type_name = objtype::get_type_name(obj.type_ref()); + let type_name = objtype::get_type_name(&obj.class()); Err(vm.new_attribute_error(format!( "'{}' object has no attribute '{}'", type_name, &attr_name.value @@ -127,10 +127,10 @@ fn object_setattr( } fn object_delattr(obj: PyObjectRef, attr_name: PyStringRef, vm: &VirtualMachine) -> PyResult<()> { - let cls = obj.type_pyref(); + let cls = obj.class(); if let Some(attr) = objtype::class_get_attr(&cls, &attr_name.value) { - if let Some(descriptor) = objtype::class_get_attr(&attr.type_pyref(), "__delete__") { + if let Some(descriptor) = objtype::class_get_attr(&attr.class(), "__delete__") { return vm.invoke(descriptor, vec![attr, obj.clone()]).map(|_| ()); } } @@ -139,7 +139,7 @@ fn object_delattr(obj: PyObjectRef, attr_name: PyStringRef, vm: &VirtualMachine) dict.del_item(&attr_name.value); Ok(()) } else { - let type_name = objtype::get_type_name(obj.type_ref()); + let type_name = objtype::get_type_name(&obj.class()); Err(vm.new_attribute_error(format!( "'{}' object has no attribute '{}'", type_name, &attr_name.value @@ -154,7 +154,7 @@ fn object_str(vm: &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 type_name = objtype::get_type_name(&obj.class()); let address = obj.get_id(); Ok(vm.new_str(format!("<{} object at 0x{:x}>", type_name, address))) } @@ -216,7 +216,7 @@ fn object_init(vm: &VirtualMachine, _args: PyFuncArgs) -> PyResult { } fn object_class(obj: PyObjectRef, _vm: &VirtualMachine) -> PyObjectRef { - obj.typ() + obj.class().into_object() } fn object_class_setter( @@ -224,7 +224,7 @@ fn object_class_setter( _value: PyObjectRef, vm: &VirtualMachine, ) -> PyResult { - let type_repr = vm.to_pystr(&instance.typ())?; + let type_repr = vm.to_pystr(&instance.class())?; Err(vm.new_type_error(format!("can't change class of type '{}'", type_repr))) } @@ -247,10 +247,10 @@ fn object_getattribute(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { ); let name = objstr::get_value(&name_str); trace!("object.__getattribute__({:?}, {:?})", obj, name); - let cls = obj.type_pyref(); + let cls = obj.class(); if let Some(attr) = objtype::class_get_attr(&cls, &name) { - let attr_class = attr.type_pyref(); + let attr_class = attr.class(); if objtype::class_has_attr(&attr_class, "__set__") { if let Some(descriptor) = objtype::class_get_attr(&attr_class, "__get__") { return vm.invoke(descriptor, vec![attr, obj.clone(), cls.into_object()]); @@ -279,7 +279,7 @@ fn object_getattr(obj: &PyObjectRef, attr_name: &str) -> Option { pub fn get_attributes(obj: &PyObjectRef) -> PyAttributes { // Get class attributes: - let mut attributes = objtype::get_attributes(obj.type_pyref()); + let mut attributes = objtype::get_attributes(obj.class()); // Get instance attributes: if let Some(dict) = &obj.dict { diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index e6b11904eb..9d72171106 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -6,7 +6,9 @@ use crate::function::IntoPyNativeFunc; use crate::function::OptionalArg; use crate::obj::objstr::PyStringRef; use crate::obj::objtype::PyClassRef; -use crate::pyobject::{IdProtocol, PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue}; +use crate::pyobject::{ + IdProtocol, PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, +}; use crate::vm::VirtualMachine; /// Read-only property, doesn't have __set__ or __delete__ @@ -137,7 +139,7 @@ impl PyPropertyRef { setter: self.setter.clone(), deleter: self.deleter.clone(), } - .into_ref_with_type(vm, self.typ()) + .into_ref_with_type(vm, TypeProtocol::class(&self)) } fn setter(self, setter: Option, vm: &VirtualMachine) -> PyResult { @@ -146,7 +148,7 @@ impl PyPropertyRef { setter: setter.or_else(|| self.setter.clone()), deleter: self.deleter.clone(), } - .into_ref_with_type(vm, self.typ()) + .into_ref_with_type(vm, TypeProtocol::class(&self)) } fn deleter(self, deleter: Option, vm: &VirtualMachine) -> PyResult { @@ -155,7 +157,7 @@ impl PyPropertyRef { setter: self.setter.clone(), deleter: deleter.or_else(|| self.deleter.clone()), } - .into_ref_with_type(vm, self.typ()) + .into_ref_with_type(vm, TypeProtocol::class(&self)) } } diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index 80f4bf40f6..a07b2454de 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -163,13 +163,13 @@ pub fn get_item( if sequence.payload::().is_some() { Ok(PyObject::new( PyList::from(elements.to_vec().get_slice_items(vm, &subscript)?), - sequence.type_pyref(), + sequence.class(), None, )) } else if sequence.payload::().is_some() { Ok(PyObject::new( PyTuple::from(elements.to_vec().get_slice_items(vm, &subscript)?), - sequence.type_pyref(), + sequence.class(), None, )) } else { diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 7071cc6e4c..ad82c34de0 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -231,16 +231,16 @@ fn set_new(cls: PyClassRef, iterable: OptionalArg, vm: &VirtualMach fn set_len(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("set.len called with: {:?}", args); arg_check!(vm, args, required = [(s, None)]); - validate_set_or_frozenset(vm, s.type_pyref())?; + validate_set_or_frozenset(vm, s.class())?; let elements = get_elements(s); Ok(vm.context().new_int(elements.len())) } fn set_copy(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { trace!("set.copy called with: {:?}", obj); - validate_set_or_frozenset(vm, obj.type_pyref())?; + validate_set_or_frozenset(vm, obj.class())?; let elements = get_elements(&obj).clone(); - create_set(vm, elements, obj.type_pyref()) + create_set(vm, elements, obj.class()) } fn set_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -265,7 +265,7 @@ fn set_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn set_contains(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(set, None), (needle, None)]); - validate_set_or_frozenset(vm, set.type_pyref())?; + validate_set_or_frozenset(vm, set.class())?; for element in get_elements(set).iter() { match vm._eq(needle.clone(), element.1.clone()) { Ok(value) => { @@ -333,8 +333,8 @@ fn set_compare_inner( ) -> PyResult { arg_check!(vm, args, required = [(zelf, None), (other, None)]); - validate_set_or_frozenset(vm, zelf.type_pyref())?; - validate_set_or_frozenset(vm, other.type_pyref())?; + validate_set_or_frozenset(vm, zelf.class())?; + validate_set_or_frozenset(vm, other.class())?; let get_zelf = |swap: bool| -> &PyObjectRef { if swap { @@ -370,13 +370,13 @@ fn set_compare_inner( } fn set_union(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - validate_set_or_frozenset(vm, zelf.type_pyref())?; - validate_set_or_frozenset(vm, other.type_pyref())?; + validate_set_or_frozenset(vm, zelf.class())?; + validate_set_or_frozenset(vm, other.class())?; let mut elements = get_elements(&zelf).clone(); elements.extend(get_elements(&other).clone()); - create_set(vm, elements, zelf.type_pyref()) + create_set(vm, elements, zelf.class()) } fn set_intersection(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { @@ -392,8 +392,8 @@ fn set_symmetric_difference( other: PyObjectRef, vm: &VirtualMachine, ) -> PyResult { - validate_set_or_frozenset(vm, zelf.type_pyref())?; - validate_set_or_frozenset(vm, other.type_pyref())?; + validate_set_or_frozenset(vm, zelf.class())?; + validate_set_or_frozenset(vm, other.class())?; let mut elements = HashMap::new(); for element in get_elements(&zelf).iter() { @@ -410,7 +410,7 @@ fn set_symmetric_difference( } } - create_set(vm, elements, zelf.type_pyref()) + create_set(vm, elements, zelf.class()) } enum SetCombineOperation { @@ -424,8 +424,8 @@ fn set_combine_inner( vm: &VirtualMachine, op: SetCombineOperation, ) -> PyResult { - validate_set_or_frozenset(vm, zelf.type_pyref())?; - validate_set_or_frozenset(vm, other.type_pyref())?; + validate_set_or_frozenset(vm, zelf.class())?; + validate_set_or_frozenset(vm, other.class())?; let mut elements = HashMap::new(); for element in get_elements(&zelf).iter() { @@ -439,7 +439,7 @@ fn set_combine_inner( } } - create_set(vm, elements, zelf.type_pyref()) + create_set(vm, elements, zelf.class()) } fn set_pop(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 67ee8e5e7e..aa62812a24 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -715,7 +715,7 @@ fn str_format(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { let zelf = &args.args[0]; if !objtype::isinstance(&zelf, &vm.ctx.str_type()) { - let zelf_typ = zelf.typ(); + let zelf_typ = zelf.class(); let actual_type = vm.to_pystr(&zelf_typ)?; return Err(vm.new_type_error(format!( "descriptor 'format' requires a 'str' object but received a '{}'", @@ -738,7 +738,7 @@ fn call_object_format(vm: &VirtualMachine, argument: PyObjectRef, format_spec: & let returned_type = vm.ctx.new_str(format_spec.to_string()); let result = vm.call_method(&argument, "__format__", vec![returned_type])?; if !objtype::isinstance(&result, &vm.ctx.str_type()) { - let result_type = result.typ(); + let result_type = result.class(); let actual_type = vm.to_pystr(&result_type)?; return Err(vm.new_type_error(format!("__format__ must return a str, not {}", actual_type))); } @@ -809,7 +809,7 @@ fn str_new( OptionalArg::Present(ref input) => vm.to_str(input)?.into_object(), OptionalArg::Missing => vm.new_str("".to_string()), }; - if string.typ().is(&cls) { + if string.class().is(&cls) { TryFromObject::try_from_object(vm, string) } else { let payload = string.payload::().unwrap(); diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index f04fb66058..fad365e249 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -112,7 +112,7 @@ fn super_new( // Check type argument: if !objtype::isinstance(py_type.as_object(), &vm.get_type()) { - let type_name = objtype::get_type_name(py_type.as_object().type_ref()); + let type_name = objtype::get_type_name(&py_type.as_object().class()); return Err(vm.new_type_error(format!( "super() argument 1 must be type, not {}", type_name diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index d87ca3843c..857ec402fb 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -39,12 +39,6 @@ impl PyValue for PyClass { } } -impl TypeProtocol for PyClassRef { - fn type_ref(&self) -> &PyObjectRef { - &self.as_object().type_ref() - } -} - struct IterMro<'a> { cls: &'a PyClassRef, offset: Option, @@ -117,10 +111,10 @@ impl PyClassRef { fn getattribute(self, name_ref: PyStringRef, vm: &VirtualMachine) -> PyResult { let name = &name_ref.value; trace!("type.__getattribute__({:?}, {:?})", self, name); - let mcl = self.type_pyref(); + let mcl = self.class(); if let Some(attr) = class_get_attr(&mcl, &name) { - let attr_class = attr.type_pyref(); + let attr_class = attr.class(); if class_has_attr(&attr_class, "__set__") { if let Some(descriptor) = class_get_attr(&attr_class, "__get__") { return vm.invoke( @@ -132,7 +126,7 @@ impl PyClassRef { } if let Some(attr) = class_get_attr(&self, &name) { - let attr_class = attr.type_pyref(); + let attr_class = attr.class(); if let Some(descriptor) = class_get_attr(&attr_class, "__get__") { let none = vm.get_none(); return vm.invoke(descriptor, vec![attr, none, self.into_object()]); @@ -156,8 +150,8 @@ impl PyClassRef { value: PyObjectRef, vm: &VirtualMachine, ) -> PyResult<()> { - if let Some(attr) = class_get_attr(&self.type_pyref(), &attr_name.value) { - if let Some(descriptor) = class_get_attr(&attr.type_pyref(), "__set__") { + if let Some(attr) = class_get_attr(&self.class(), &attr_name.value) { + if let Some(descriptor) = class_get_attr(&attr.class(), "__set__") { vm.invoke(descriptor, vec![attr, self.into_object(), value])?; return Ok(()); } @@ -218,7 +212,7 @@ fn _mro(cls: &PyClassRef) -> Vec { /// 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: &PyClassRef) -> bool { - issubclass(&obj.type_pyref(), &cls) + issubclass(&obj.class(), &cls) } /// Determines if `subclass` is actually a subclass of `cls`, this doesn't call __subclasscheck__, @@ -229,18 +223,14 @@ pub fn issubclass(subclass: &PyClassRef, cls: &PyClassRef) -> bool { subclass.is(cls) || mro.iter().any(|c| c.is(cls.as_object())) } -pub fn get_type_name(typ: &PyObjectRef) -> String { - if let Some(PyClass { name, .. }) = &typ.payload::() { - name.clone() - } else { - panic!("Cannot get type_name of non-type type {:?}", typ); - } +pub fn get_type_name(typ: &PyClassRef) -> String { + typ.name.clone() } pub fn type_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { debug!("type.__new__ {:?}", args); if args.args.len() == 2 { - Ok(args.args[1].typ()) + Ok(args.args[1].class().into_object()) } else if args.args.len() == 4 { let (typ, name, bases, dict) = args.bind(vm)?; type_new_class(vm, typ, name, bases, dict).map(|x| x.into_object()) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 78e82c51bd..739012c5ec 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -88,7 +88,7 @@ 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()); + let type_name = objtype::get_type_name(&self.class()); // 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" { @@ -101,7 +101,7 @@ impl fmt::Display for PyObject { if let Some(PyModule { ref name, .. }) = self.payload::() { return write!(f, "module '{}'", name); } - write!(f, "'{}' object", objtype::get_type_name(&self.typ())) + write!(f, "'{}' object", objtype::get_type_name(&self.class())) } } @@ -163,7 +163,7 @@ pub struct PyNotImplemented; impl PyValue for PyNotImplemented { fn class(vm: &VirtualMachine) -> PyClassRef { - vm.ctx.not_implemented().type_pyref() + vm.ctx.not_implemented().class() } } @@ -772,7 +772,7 @@ impl PyRef { pub fn typ(&self) -> PyClassRef { PyRef { - obj: self.obj.typ(), + obj: self.obj.class().into_object(), _payload: PhantomData, } } @@ -802,7 +802,7 @@ where } else { let class = T::class(vm); let expected_type = vm.to_pystr(&class)?; - let actual_type = vm.to_pystr(&obj.typ())?; + let actual_type = vm.to_pystr(&obj.class())?; Err(vm.new_type_error(format!( "Expected type {}, not {}", expected_type, actual_type, @@ -877,18 +877,12 @@ impl IdProtocol for PyRef { } pub trait TypeProtocol { - fn typ(&self) -> PyObjectRef { - self.type_ref().clone() - } - fn type_pyref(&self) -> PyClassRef { - self.typ().downcast().unwrap() - } - fn type_ref(&self) -> &PyObjectRef; + fn class(&self) -> PyClassRef; } impl TypeProtocol for PyObjectRef { - fn type_ref(&self) -> &PyObjectRef { - (**self).type_ref() + fn class(&self) -> PyClassRef { + (**self).class() } } @@ -896,8 +890,14 @@ impl TypeProtocol for PyObject where T: ?Sized + PyObjectPayload, { - fn type_ref(&self) -> &PyObjectRef { - self.typ.as_object() + fn class(&self) -> PyClassRef { + self.typ.clone() + } +} + +impl TypeProtocol for PyRef { + fn class(&self) -> PyClassRef { + self.obj.typ.clone() } } @@ -956,7 +956,7 @@ pub trait BufferProtocol { impl BufferProtocol for PyObjectRef { fn readonly(&self) -> bool { - match objtype::get_type_name(&self.typ()).as_ref() { + match objtype::get_type_name(&self.class()).as_ref() { "bytes" => false, "bytearray" | "memoryview" => true, _ => panic!("Bytes-Like type expected not {:?}", self), @@ -1249,7 +1249,7 @@ where "must be {} or {}, not {}", A::class(vm), B::class(vm), - obj.type_pyref() + obj.class() )) }) } diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index eac3619c26..b881b7a724 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -74,7 +74,7 @@ impl<'s> serde::Serialize for PyObjectSerializer<'s> { } else { Err(serde::ser::Error::custom(format!( "Object of type '{:?}' is not serializable", - self.pyobject.typ() + self.pyobject.class() ))) } } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 0ee9798ab0..92c264bcd9 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -174,8 +174,8 @@ impl VirtualMachine { b: PyObjectRef, op: &str, ) -> PyObjectRef { - let a_type_name = objtype::get_type_name(&a.typ()); - let b_type_name = objtype::get_type_name(&b.typ()); + let a_type_name = objtype::get_type_name(&a.class()); + let b_type_name = objtype::get_type_name(&b.class()); self.new_type_error(format!( "Unsupported operand types for '{}': '{}' and '{}'", op, a_type_name, b_type_name @@ -270,7 +270,7 @@ impl VirtualMachine { pub fn isinstance(&self, obj: &PyObjectRef, cls: &PyClassRef) -> 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.as_object()) { + if Rc::ptr_eq(&obj.class().into_object(), cls.as_object()) { Ok(true) } else { let ret = self.call_method(cls.as_object(), "__instancecheck__", vec![obj.clone()])?; @@ -286,10 +286,10 @@ impl VirtualMachine { } pub fn call_get_descriptor(&self, attr: PyObjectRef, obj: PyObjectRef) -> PyResult { - let attr_class = attr.type_pyref(); + let attr_class = attr.class(); if let Some(descriptor) = objtype::class_get_attr(&attr_class, "__get__") { - let cls = obj.typ(); - self.invoke(descriptor, vec![attr, obj.clone(), cls]) + let cls = obj.class(); + self.invoke(descriptor, vec![attr, obj.clone(), cls.into_object()]) } else { Ok(attr) } @@ -300,7 +300,7 @@ impl VirtualMachine { T: Into, { // This is only used in the vm for magic methods, which use a greatly simplified attribute lookup. - let cls = obj.type_pyref(); + let cls = obj.class(); match objtype::class_get_attr(&cls, method_name) { Some(func) => { trace!( @@ -583,7 +583,7 @@ impl VirtualMachine { // get_method should be used for internal access to magic methods (by-passing // the full getattribute look-up. pub fn get_method(&self, obj: PyObjectRef, method_name: &str) -> PyResult { - let cls = obj.type_pyref(); + let cls = obj.class(); match objtype::class_get_attr(&cls, method_name) { Some(method) => self.call_get_descriptor(method, obj.clone()), None => Err(self.new_type_error(format!("{} has no method {:?}", obj, method_name))), From 6474a4a6ef895c8b57e4482ec6d26b0474ffbd2c Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Mon, 25 Mar 2019 19:18:07 -0700 Subject: [PATCH 45/51] Remove objtype::get_type_name() --- vm/src/builtins.rs | 7 ++----- vm/src/exceptions.rs | 3 +-- vm/src/frame.rs | 14 ++++++-------- vm/src/obj/objfloat.rs | 3 +-- vm/src/obj/objint.rs | 15 +++------------ vm/src/obj/objobject.rs | 11 +++++------ vm/src/obj/objsuper.rs | 3 +-- vm/src/obj/objtype.rs | 4 ---- vm/src/pyobject.rs | 6 +++--- vm/src/vm.rs | 6 +++--- 10 files changed, 25 insertions(+), 47 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index fbd2d53568..27ce8aadda 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -368,7 +368,7 @@ fn builtin_len(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(value) => vm.invoke(value, PyFuncArgs::default()), Err(..) => Err(vm.new_type_error(format!( "object of type '{}' has no method {:?}", - objtype::get_type_name(&obj.class()), + obj.class(), len_method_name ))), } @@ -605,10 +605,7 @@ fn builtin_reversed(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { match vm.get_method(obj.clone(), "__reversed__") { Ok(value) => vm.invoke(value, PyFuncArgs::default()), // TODO: fallback to using __len__ and __getitem__, if object supports sequence protocol - Err(..) => Err(vm.new_type_error(format!( - "'{}' object is not reversible", - objtype::get_type_name(&obj.class()), - ))), + Err(..) => Err(vm.new_type_error(format!("'{}' object is not reversible", obj.class()))), } } // builtin_reversed diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 7177527da4..f850d76974 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -68,7 +68,6 @@ fn exception_str(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(exc, Some(vm.ctx.exceptions.exception_type.clone()))] ); - let type_name = objtype::get_type_name(&exc.class()); let msg = if let Ok(m) = vm.get_attribute(exc.clone(), "msg") { match vm.to_pystr(&m) { Ok(msg) => msg, @@ -77,7 +76,7 @@ fn exception_str(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { } else { panic!("Error message must be set"); }; - let s = format!("{}: {}", type_name, msg); + let s = format!("{}: {}", exc.class(), msg); Ok(vm.new_str(s)) } diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 53479f8fc8..6f8815cbec 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -1091,20 +1091,18 @@ impl Frame { 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!( - "{} has no __contains__ method", - objtype::get_type_name(&haystack.class()) - ))), + Err(_) => { + Err(vm.new_type_error(format!("{} has no __contains__ method", haystack.class()))) + } } } 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!( - "{} has no __contains__ method", - objtype::get_type_name(&haystack.class()) - ))), + Err(_) => { + Err(vm.new_type_error(format!("{} has no __contains__ method", haystack.class()))) + } } } diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 5e68f59603..e261a689eb 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -190,8 +190,7 @@ impl PyFloatRef { } } } else { - let type_name = objtype::get_type_name(&arg.class()); - return Err(vm.new_type_error(format!("can't convert {} to float", type_name))); + return Err(vm.new_type_error(format!("can't convert {} to float", arg.class()))); }; PyFloat { value }.into_ref_with_type(vm, cls) } diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index f31997eadb..e2203469ae 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -211,11 +211,7 @@ impl PyIntRef { 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 '{}'", - objtype::get_type_name(&self.as_object().class()), - objtype::get_type_name(&other.class()) - ))); + return Ok(vm.ctx.not_implemented()); } if let Some(n_bits) = get_value(&other).to_usize() { @@ -234,11 +230,7 @@ impl PyIntRef { 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 '{}'", - objtype::get_type_name(&self.as_object().class()), - objtype::get_type_name(&other.class()) - ))); + return Ok(vm.ctx.not_implemented()); } if let Some(n_bits) = get_value(&other).to_usize() { @@ -420,10 +412,9 @@ pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult 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.class()); let address = obj.get_id(); - Ok(vm.new_str(format!("<{} object at 0x{:x}>", type_name, address))) + Ok(vm.new_str(format!("<{} object at 0x{:x}>", obj.class(), address))) } pub fn object_dir(obj: PyObjectRef, vm: &VirtualMachine) -> PyList { diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index fad365e249..0061b41e9d 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -112,10 +112,9 @@ fn super_new( // Check type argument: if !objtype::isinstance(py_type.as_object(), &vm.get_type()) { - let type_name = objtype::get_type_name(&py_type.as_object().class()); return Err(vm.new_type_error(format!( "super() argument 1 must be type, not {}", - type_name + py_type.class() ))); } diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 857ec402fb..14958300bf 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -223,10 +223,6 @@ pub fn issubclass(subclass: &PyClassRef, cls: &PyClassRef) -> bool { subclass.is(cls) || mro.iter().any(|c| c.is(cls.as_object())) } -pub fn get_type_name(typ: &PyClassRef) -> String { - typ.name.clone() -} - pub fn type_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { debug!("type.__new__ {:?}", args); if args.args.len() == 2 { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 739012c5ec..f15c5e09b2 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -88,7 +88,7 @@ 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.class()); + let type_name = self.class().name.clone(); // 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" { @@ -101,7 +101,7 @@ impl fmt::Display for PyObject { if let Some(PyModule { ref name, .. }) = self.payload::() { return write!(f, "module '{}'", name); } - write!(f, "'{}' object", objtype::get_type_name(&self.class())) + write!(f, "'{}' object", self.class()) } } @@ -956,7 +956,7 @@ pub trait BufferProtocol { impl BufferProtocol for PyObjectRef { fn readonly(&self) -> bool { - match objtype::get_type_name(&self.class()).as_ref() { + match self.class().name.as_str() { "bytes" => false, "bytearray" | "memoryview" => true, _ => panic!("Bytes-Like type expected not {:?}", self), diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 92c264bcd9..df092bdc8b 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -174,11 +174,11 @@ impl VirtualMachine { b: PyObjectRef, op: &str, ) -> PyObjectRef { - let a_type_name = objtype::get_type_name(&a.class()); - let b_type_name = objtype::get_type_name(&b.class()); self.new_type_error(format!( "Unsupported operand types for '{}': '{}' and '{}'", - op, a_type_name, b_type_name + op, + a.class(), + b.class() )) } From 8bdc766bedf04063a5a0027f73db6d469202570a Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Mon, 25 Mar 2019 19:32:58 -0700 Subject: [PATCH 46/51] Use name field directly --- vm/src/builtins.rs | 6 ++++-- vm/src/exceptions.rs | 2 +- vm/src/frame.rs | 14 ++++++++------ vm/src/obj/objfloat.rs | 2 +- vm/src/obj/objint.rs | 2 +- vm/src/obj/objobject.rs | 6 +++--- vm/src/obj/objsuper.rs | 2 +- vm/src/pyobject.rs | 2 +- vm/src/vm.rs | 4 ++-- 9 files changed, 22 insertions(+), 18 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 27ce8aadda..8a4a6adc30 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -368,7 +368,7 @@ fn builtin_len(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(value) => vm.invoke(value, PyFuncArgs::default()), Err(..) => Err(vm.new_type_error(format!( "object of type '{}' has no method {:?}", - obj.class(), + obj.class().name, len_method_name ))), } @@ -605,7 +605,9 @@ fn builtin_reversed(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { match vm.get_method(obj.clone(), "__reversed__") { Ok(value) => vm.invoke(value, PyFuncArgs::default()), // TODO: fallback to using __len__ and __getitem__, if object supports sequence protocol - Err(..) => Err(vm.new_type_error(format!("'{}' object is not reversible", obj.class()))), + Err(..) => { + Err(vm.new_type_error(format!("'{}' object is not reversible", obj.class().name))) + } } } // builtin_reversed diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index f850d76974..fb757cd0f3 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -76,7 +76,7 @@ fn exception_str(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { } else { panic!("Error message must be set"); }; - let s = format!("{}: {}", exc.class(), msg); + let s = format!("{}: {}", exc.class().name, msg); Ok(vm.new_str(s)) } diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 6f8815cbec..6fd5cfea80 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -1091,18 +1091,20 @@ impl Frame { 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!("{} has no __contains__ method", haystack.class()))) - } + Err(_) => Err(vm.new_type_error(format!( + "{} has no __contains__ method", + haystack.class().name + ))), } } 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!("{} has no __contains__ method", haystack.class()))) - } + Err(_) => Err(vm.new_type_error(format!( + "{} has no __contains__ method", + haystack.class().name + ))), } } diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index e261a689eb..ca6bad9d22 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -190,7 +190,7 @@ impl PyFloatRef { } } } else { - return Err(vm.new_type_error(format!("can't convert {} to float", arg.class()))); + return Err(vm.new_type_error(format!("can't convert {} to float", arg.class().name))); }; PyFloat { value }.into_ref_with_type(vm, cls) } diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index e2203469ae..c7f0ff5af4 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -414,7 +414,7 @@ pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult PyResult { fn object_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, Some(vm.ctx.object()))]); let address = obj.get_id(); - Ok(vm.new_str(format!("<{} object at 0x{:x}>", obj.class(), address))) + Ok(vm.new_str(format!("<{} object at 0x{:x}>", obj.class().name, address))) } pub fn object_dir(obj: PyObjectRef, vm: &VirtualMachine) -> PyList { diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index 0061b41e9d..24424d265f 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -114,7 +114,7 @@ fn super_new( if !objtype::isinstance(py_type.as_object(), &vm.get_type()) { return Err(vm.new_type_error(format!( "super() argument 1 must be type, not {}", - py_type.class() + py_type.class().name ))); } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index f15c5e09b2..afee7060e2 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -101,7 +101,7 @@ impl fmt::Display for PyObject { if let Some(PyModule { ref name, .. }) = self.payload::() { return write!(f, "module '{}'", name); } - write!(f, "'{}' object", self.class()) + write!(f, "'{}' object", self.class().name) } } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index df092bdc8b..41321c2667 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -177,8 +177,8 @@ impl VirtualMachine { self.new_type_error(format!( "Unsupported operand types for '{}': '{}' and '{}'", op, - a.class(), - b.class() + a.class().name, + b.class().name )) } From 625d2357bd493175a7379eaf876fc33711fdb1c1 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Mon, 25 Mar 2019 19:34:24 -0700 Subject: [PATCH 47/51] Fix wasm --- wasm/lib/src/wasm_builtins.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wasm/lib/src/wasm_builtins.rs b/wasm/lib/src/wasm_builtins.rs index d5bc830dfe..ada0c83f53 100644 --- a/wasm/lib/src/wasm_builtins.rs +++ b/wasm/lib/src/wasm_builtins.rs @@ -25,7 +25,7 @@ pub fn format_print_args(vm: &VirtualMachine, args: PyFuncArgs) -> Result Result Date: Tue, 26 Mar 2019 15:16:30 +0000 Subject: [PATCH 48/51] Change types inside Scope to PyDictRef. --- vm/src/builtins.rs | 28 +++++++++-------------- vm/src/frame.rs | 29 +++++++++++------------ vm/src/import.rs | 2 +- vm/src/obj/objframe.rs | 52 ++++++++++++++++++------------------------ vm/src/pyobject.rs | 10 +++++--- vm/src/vm.rs | 9 ++++---- 6 files changed, 59 insertions(+), 71 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 8a4a6adc30..1de24b86ab 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -11,6 +11,7 @@ use num_traits::{Signed, ToPrimitive}; use crate::compile; use crate::import::import_module; use crate::obj::objbool; +use crate::obj::objdict::PyDictRef; use crate::obj::objint; use crate::obj::objiter; use crate::obj::objstr::{self, PyStringRef}; @@ -27,14 +28,6 @@ use crate::obj::objcode::PyCodeRef; #[cfg(not(target_arch = "wasm32"))] use crate::stdlib::io::io_open; -fn get_locals(vm: &VirtualMachine) -> PyObjectRef { - vm.get_locals() -} - -fn dir_locals(vm: &VirtualMachine) -> PyObjectRef { - get_locals(vm) -} - fn builtin_abs(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(x, None)]); match vm.get_method(x.clone(), "__abs__") { @@ -146,7 +139,7 @@ fn builtin_delattr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { fn builtin_dir(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { if args.args.is_empty() { - Ok(dir_locals(vm)) + Ok(vm.get_locals().into_object()) } else { let obj = args.args.into_iter().next().unwrap(); let seq = vm.call_method(&obj, "__dir__", vec![])?; @@ -254,11 +247,11 @@ fn make_scope( let current_scope = vm.current_scope(); let globals = match globals { - Some(dict) => dict.clone(), + Some(dict) => dict.clone().downcast().unwrap(), None => current_scope.globals.clone(), }; let locals = match locals { - Some(dict) => Some(dict.clone()), + Some(dict) => dict.clone().downcast().ok(), None => current_scope.get_only_locals(), }; @@ -300,7 +293,7 @@ fn builtin_getattr( } } -fn builtin_globals(vm: &VirtualMachine, _args: PyFuncArgs) -> PyResult { +fn builtin_globals(vm: &VirtualMachine) -> PyResult { Ok(vm.current_scope().globals.clone()) } @@ -374,9 +367,8 @@ fn builtin_len(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn builtin_locals(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args); - Ok(vm.get_locals()) +fn builtin_locals(vm: &VirtualMachine) -> PyDictRef { + vm.get_locals() } fn builtin_max(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -814,13 +806,15 @@ pub fn builtin_build_class_(vm: &VirtualMachine, mut args: PyFuncArgs) -> PyResu let prepare = vm.get_attribute(metaclass.clone().into_object(), "__prepare__")?; let namespace = vm.invoke(prepare, vec![name_arg.clone(), bases.clone()])?; + let namespace: PyDictRef = TryFromObject::try_from_object(vm, namespace)?; + let cells = vm.ctx.new_dict(); - vm.invoke_with_locals(function, cells.clone().into_object(), namespace.clone())?; + vm.invoke_with_locals(function, cells.clone(), namespace.clone())?; let class = vm.call_method( metaclass.as_object(), "__call__", - vec![name_arg, bases, namespace], + vec![name_arg, bases, namespace.into_object()], )?; cells.set_item(&vm.ctx, "__class__", class.clone()); Ok(class) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 6fd5cfea80..88a619eb36 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -12,8 +12,7 @@ use crate::function::PyFuncArgs; use crate::obj::objbool; use crate::obj::objbuiltinfunc::PyBuiltinFunction; use crate::obj::objcode::PyCodeRef; -use crate::obj::objdict; -use crate::obj::objdict::PyDict; +use crate::obj::objdict::{self, PyDictRef}; use crate::obj::objint::PyInt; use crate::obj::objiter; use crate::obj::objlist; @@ -80,8 +79,8 @@ impl<'a, T> Iterator for Iter<'a, T> { #[derive(Clone)] pub struct Scope { - locals: RcList, - pub globals: PyObjectRef, + locals: RcList, + pub globals: PyDictRef, } impl fmt::Debug for Scope { @@ -92,7 +91,7 @@ impl fmt::Debug for Scope { } impl Scope { - pub fn new(locals: Option, globals: PyObjectRef) -> Scope { + pub fn new(locals: Option, globals: PyDictRef) -> Scope { let locals = match locals { Some(dict) => RcList::new().insert(dict), None => RcList::new(), @@ -100,18 +99,18 @@ impl Scope { Scope { locals, globals } } - pub fn get_locals(&self) -> PyObjectRef { + pub fn get_locals(&self) -> PyDictRef { match self.locals.iter().next() { Some(dict) => dict.clone(), None => self.globals.clone(), } } - pub fn get_only_locals(&self) -> Option { + pub fn get_only_locals(&self) -> Option { self.locals.iter().next().cloned() } - pub fn child_scope_with_locals(&self, locals: PyObjectRef) -> Scope { + pub fn child_scope_with_locals(&self, locals: PyDictRef) -> Scope { Scope { locals: self.locals.clone().insert(locals), globals: self.globals.clone(), @@ -119,7 +118,7 @@ impl Scope { } pub fn child_scope(&self, ctx: &PyContext) -> Scope { - self.child_scope_with_locals(ctx.new_dict().into_object()) + self.child_scope_with_locals(ctx.new_dict()) } } @@ -1232,13 +1231,11 @@ impl fmt::Debug for Frame { .iter() .map(|elem| format!("\n > {:?}", elem)) .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::(), - None => panic!("locals unexpectedly not wrapping a dict!",), - }; + let dict = self.scope.get_locals(); + let local_str = objdict::get_key_value_pairs_from_content(&dict.entries.borrow()) + .iter() + .map(|elem| format!("\n {:?} = {:?}", elem.0, elem.1)) + .collect::(); write!( f, "Frame Object {{ \n Stack:{}\n Blocks:{}\n Locals:{}\n}}", diff --git a/vm/src/import.rs b/vm/src/import.rs index 987555915f..936b896805 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -40,7 +40,7 @@ fn import_uncached_module(vm: &VirtualMachine, current_path: PathBuf, module: &s 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().into_object()))?; + vm.run_code_obj(code_obj, Scope::new(None, attrs.clone()))?; Ok(vm.ctx.new_module(module, attrs)) } diff --git a/vm/src/obj/objframe.rs b/vm/src/obj/objframe.rs index 8657dc2800..b4ed15f74a 100644 --- a/vm/src/obj/objframe.rs +++ b/vm/src/obj/objframe.rs @@ -2,43 +2,35 @@ */ -use crate::frame::Frame; -use crate::function::PyFuncArgs; -use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; +use super::objcode::PyCodeRef; +use super::objdict::PyDictRef; +use crate::frame::FrameRef; +use crate::pyobject::{PyContext, PyResult}; use crate::vm::VirtualMachine; pub fn init(context: &PyContext) { - let frame_type = &context.frame_type; - extend_class!(context, frame_type, { - "__new__" => context.new_rustfunc(frame_new), - "__repr__" => context.new_rustfunc(frame_repr), - "f_locals" => context.new_property(frame_flocals), - "f_code" => context.new_property(frame_fcode) + extend_class!(context, &context.frame_type, { + "__new__" => context.new_rustfunc(FrameRef::new), + "__repr__" => context.new_rustfunc(FrameRef::repr), + "f_locals" => context.new_property(FrameRef::flocals), + "f_code" => context.new_property(FrameRef::fcode), }); } -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())) -} +impl FrameRef { + fn new(_class: FrameRef, vm: &VirtualMachine) -> PyResult { + Err(vm.new_type_error("Cannot directly create frame object".to_string())) + } -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 repr(self, _vm: &VirtualMachine) -> String { + "".to_string() + } -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: &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())) -} + fn flocals(self, _vm: &VirtualMachine) -> PyDictRef { + self.scope.get_locals() + } -pub fn get_value(obj: &PyObjectRef) -> &Frame { - &obj.payload::().unwrap() + fn fcode(self, vm: &VirtualMachine) -> PyCodeRef { + vm.ctx.new_code_object(self.code.clone()) + } } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index afee7060e2..aa3348be59 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -582,7 +582,7 @@ impl PyContext { } pub fn new_scope(&self) -> Scope { - Scope::new(None, self.new_dict().into_object()) + Scope::new(None, self.new_dict()) } pub fn new_module(&self, name: &str, dict: PyDictRef) -> PyObjectRef { @@ -613,8 +613,10 @@ impl PyContext { PropertyBuilder::new(self).add_getter(f).create() } - pub fn new_code_object(&self, code: bytecode::CodeObject) -> PyObjectRef { + pub fn new_code_object(&self, code: bytecode::CodeObject) -> PyCodeRef { PyObject::new(objcode::PyCode::new(code), self.code_type(), None) + .downcast() + .unwrap() } pub fn new_function( @@ -683,7 +685,9 @@ 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()).into_object() + } bytecode::Constant::Tuple { ref elements } => { let elements = elements .iter() diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 41321c2667..321d659b8f 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -19,6 +19,7 @@ use crate::function::PyFuncArgs; use crate::obj::objbool; use crate::obj::objbuiltinfunc::PyBuiltinFunction; use crate::obj::objcode::PyCodeRef; +use crate::obj::objdict::PyDictRef; use crate::obj::objfunction::{PyFunction, PyMethod}; use crate::obj::objgenerator::PyGeneratorRef; use crate::obj::objiter; @@ -231,7 +232,7 @@ impl VirtualMachine { self.ctx.object() } - pub fn get_locals(&self) -> PyObjectRef { + pub fn get_locals(&self) -> PyDictRef { self.current_scope().get_locals().clone() } @@ -371,8 +372,8 @@ impl VirtualMachine { pub fn invoke_with_locals( &self, function: PyObjectRef, - cells: PyObjectRef, - locals: PyObjectRef, + cells: PyDictRef, + locals: PyDictRef, ) -> PyResult { if let Some(PyFunction { code, @@ -395,7 +396,7 @@ impl VirtualMachine { fn fill_locals_from_args( &self, code_object: &bytecode::CodeObject, - locals: &PyObjectRef, + locals: &PyDictRef, args: PyFuncArgs, defaults: &PyObjectRef, ) -> PyResult<()> { From 4bee9d9cf260fd71cd7c61c5775c7bae7bf18a13 Mon Sep 17 00:00:00 2001 From: jgirardet Date: Tue, 26 Mar 2019 18:46:10 +0100 Subject: [PATCH 49/51] fix bytes.__repr__ according to pyton formating --- tests/snippets/bytes.py | 7 +++++++ vm/src/obj/objbytes.rs | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py index 5bf9251953..2473f44fb3 100644 --- a/tests/snippets/bytes.py +++ b/tests/snippets/bytes.py @@ -1,3 +1,10 @@ +# repr +assert repr(bytes([0, 1, 2])) == repr(b'\x00\x01\x02') +assert ( +repr(bytes([0, 9, 10, 11, 13, 31, 32, 33, 89, 120, 255]) +== "b'\\x00\\t\\n\\x0b\\r\\x1f !Yx\\xff'") +) + # comp a = b"abcd" b = b"ab" diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 9c4fcae6eb..8b910cf569 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -194,7 +194,7 @@ pub fn get_value<'a>(obj: &'a PyObjectRef) -> impl Deref> + 'a 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(); + let data = format_bytes(&value); Ok(vm.new_str(format!("b'{}'", data))) } @@ -219,6 +219,38 @@ fn vec_contains(a: &Vec, b: &Vec) -> bool { false } +fn format_bytes(chars: &Vec) -> String { + let mut res = String::new(); + for i in chars { + res.push_str(BYTES_REPR[*i as usize]) + } + res +} + +const BYTES_REPR: &'static [&'static str] = &[ + "\\x00", "\\x01", "\\x02", "\\x03", "\\x04", "\\x05", "\\x06", "\\x07", "\\x08", "\\t", "\\n", + "\\x0b", "\\x0c", "\\r", "\\x0e", "\\x0f", "\\x10", "\\x11", "\\x12", "\\x13", "\\x14", + "\\x15", "\\x16", "\\x17", "\\x18", "\\x19", "\\x1a", "\\x1b", "\\x1c", "\\x1d", "\\x1e", + "\\x1f", " ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/", "0", + "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?", "@", "A", "B", "C", + "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", + "W", "X", "Y", "Z", "[", "\\", "]", "^", "_", "`", "a", "b", "c", "d", "e", "f", "g", "h", "i", + "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", + "}", "~", "\\x7f", "\\x80", "\\x81", "\\x82", "\\x83", "\\x84", "\\x85", "\\x86", "\\x87", + "\\x88", "\\x89", "\\x8a", "\\x8b", "\\x8c", "\\x8d", "\\x8e", "\\x8f", "\\x90", "\\x91", + "\\x92", "\\x93", "\\x94", "\\x95", "\\x96", "\\x97", "\\x98", "\\x99", "\\x9a", "\\x9b", + "\\x9c", "\\x9d", "\\x9e", "\\x9f", "\\xa0", "\\xa1", "\\xa2", "\\xa3", "\\xa4", "\\xa5", + "\\xa6", "\\xa7", "\\xa8", "\\xa9", "\\xaa", "\\xab", "\\xac", "\\xad", "\\xae", "\\xaf", + "\\xb0", "\\xb1", "\\xb2", "\\xb3", "\\xb4", "\\xb5", "\\xb6", "\\xb7", "\\xb8", "\\xb9", + "\\xba", "\\xbb", "\\xbc", "\\xbd", "\\xbe", "\\xbf", "\\xc0", "\\xc1", "\\xc2", "\\xc3", + "\\xc4", "\\xc5", "\\xc6", "\\xc7", "\\xc8", "\\xc9", "\\xca", "\\xcb", "\\xcc", "\\xcd", + "\\xce", "\\xcf", "\\xd0", "\\xd1", "\\xd2", "\\xd3", "\\xd4", "\\xd5", "\\xd6", "\\xd7", + "\\xd8", "\\xd9", "\\xda", "\\xdb", "\\xdc", "\\xdd", "\\xde", "\\xdf", "\\xe0", "\\xe1", + "\\xe2", "\\xe3", "\\xe4", "\\xe5", "\\xe6", "\\xe7", "\\xe8", "\\xe9", "\\xea", "\\xeb", + "\\xec", "\\xed", "\\xee", "\\xef", "\\xf0", "\\xf1", "\\xf2", "\\xf3", "\\xf4", "\\xf5", + "\\xf6", "\\xf7", "\\xf8", "\\xf9", "\\xfa", "\\xfb", "\\xfc", "\\xfd", "\\xfe", "\\xff", +]; + impl PyBytesRef { fn add(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if objtype::isinstance(&other, &vm.ctx.bytes_type()) { From c5203a4934621292fb87d7e23d91b4068fd9ae51 Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Tue, 26 Mar 2019 19:00:06 +0100 Subject: [PATCH 50/51] Remove .gcno files before a coverage run --- tests/.travis-runner.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/.travis-runner.sh b/tests/.travis-runner.sh index 36b9f43aca..f555265898 100755 --- a/tests/.travis-runner.sh +++ b/tests/.travis-runner.sh @@ -13,6 +13,7 @@ pip install pipenv if [ $CODE_COVERAGE = "true" ] then find . -name '*.gcda' -delete + find . -name '*.gcno' -delete export CARGO_INCREMENTAL=0 export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Zno-landing-pads" From 7044df82f14499c574e906060669b135bff6db02 Mon Sep 17 00:00:00 2001 From: jgirardet Date: Tue, 26 Mar 2019 21:36:48 +0100 Subject: [PATCH 51/51] fix class() --- tests/snippets/bytes.py | 46 +++++++++++++++++ vm/src/obj/objbytes.rs | 106 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 tests/snippets/bytes.py diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py new file mode 100644 index 0000000000..2473f44fb3 --- /dev/null +++ b/tests/snippets/bytes.py @@ -0,0 +1,46 @@ +# repr +assert repr(bytes([0, 1, 2])) == repr(b'\x00\x01\x02') +assert ( +repr(bytes([0, 9, 10, 11, 13, 31, 32, 33, 89, 120, 255]) +== "b'\\x00\\t\\n\\x0b\\r\\x1f !Yx\\xff'") +) + +# comp +a = b"abcd" +b = b"ab" +c = b"abcd" + +assert a > b +assert a >= b +assert b < a +assert b <= a +assert a == c + +# hash not implemented for iterator +# assert hash(iter(a)) == hash(iter(b"abcd")) + +assert repr(a) == "b'abcd'" +assert len(a) == 4 + +assert a + b == b"abcdab" + +# contains +assert b"ab" in b"abcd" +assert b"cd" in b"abcd" +assert b"abcd" in b"abcd" +assert b"a" in b"abcd" +assert b"d" in b"abcd" +assert b"dc" not in b"abcd" +assert 97 in b"abcd" +assert 150 not in b"abcd" + +# getitem +d = b"abcdefghij" + +assert d[1] == 98 +assert d[-1] == 106 +assert d[2:6] == b"cdef" +assert d[-6:] == b"efghij" +assert d[1:8:2] == b"bdfh" +# assert d[8:1:-2] == b"igec" +# assert d[-1:-8:-2] == b"jhfd" diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 8d64cc5076..70378cc470 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -11,6 +11,7 @@ use crate::pyobject::{ use crate::vm::VirtualMachine; use super::objint; +use super::objsequence::PySliceableSequence; use super::objtype::{self, PyClassRef}; #[derive(Debug)] @@ -67,7 +68,10 @@ pub fn init(context: &PyContext) { "__repr__" => context.new_rustfunc(bytes_repr), "__len__" => context.new_rustfunc(bytes_len), "__iter__" => context.new_rustfunc(bytes_iter), - "__doc__" => context.new_str(bytes_doc.to_string()) + "__doc__" => context.new_str(bytes_doc.to_string()), + "__add__" => context.new_rustfunc(PyBytesRef::add), + "__contains__" => context.new_rustfunc(PyBytesRef::contains), + "__getitem__" => context.new_rustfunc(PyBytesRef::getitem), }); } @@ -191,7 +195,7 @@ pub fn get_value<'a>(obj: &'a PyObjectRef) -> impl Deref> + 'a 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(); + let data = format_bytes(&value); Ok(vm.new_str(format!("b'{}'", data))) } @@ -201,3 +205,101 @@ fn bytes_iter(obj: PyBytesRef, _vm: &VirtualMachine) -> PyIteratorValue { iterated_obj: obj.into_object(), } } + +/// return true if b is a subset of a. +fn vec_contains(a: &Vec, b: &Vec) -> bool { + let a_len = a.len(); + let b_len = b.len(); + for (n, i) in a.iter().enumerate() { + if n + b_len <= a_len && *i == b[0] { + if &a[n..n + b_len] == b.as_slice() { + return true; + } + } + } + false +} + +fn format_bytes(chars: &Vec) -> String { + let mut res = String::new(); + for i in chars { + res.push_str(BYTES_REPR[*i as usize]) + } + res +} + +const BYTES_REPR: &'static [&'static str] = &[ + "\\x00", "\\x01", "\\x02", "\\x03", "\\x04", "\\x05", "\\x06", "\\x07", "\\x08", "\\t", "\\n", + "\\x0b", "\\x0c", "\\r", "\\x0e", "\\x0f", "\\x10", "\\x11", "\\x12", "\\x13", "\\x14", + "\\x15", "\\x16", "\\x17", "\\x18", "\\x19", "\\x1a", "\\x1b", "\\x1c", "\\x1d", "\\x1e", + "\\x1f", " ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/", "0", + "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?", "@", "A", "B", "C", + "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", + "W", "X", "Y", "Z", "[", "\\", "]", "^", "_", "`", "a", "b", "c", "d", "e", "f", "g", "h", "i", + "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", + "}", "~", "\\x7f", "\\x80", "\\x81", "\\x82", "\\x83", "\\x84", "\\x85", "\\x86", "\\x87", + "\\x88", "\\x89", "\\x8a", "\\x8b", "\\x8c", "\\x8d", "\\x8e", "\\x8f", "\\x90", "\\x91", + "\\x92", "\\x93", "\\x94", "\\x95", "\\x96", "\\x97", "\\x98", "\\x99", "\\x9a", "\\x9b", + "\\x9c", "\\x9d", "\\x9e", "\\x9f", "\\xa0", "\\xa1", "\\xa2", "\\xa3", "\\xa4", "\\xa5", + "\\xa6", "\\xa7", "\\xa8", "\\xa9", "\\xaa", "\\xab", "\\xac", "\\xad", "\\xae", "\\xaf", + "\\xb0", "\\xb1", "\\xb2", "\\xb3", "\\xb4", "\\xb5", "\\xb6", "\\xb7", "\\xb8", "\\xb9", + "\\xba", "\\xbb", "\\xbc", "\\xbd", "\\xbe", "\\xbf", "\\xc0", "\\xc1", "\\xc2", "\\xc3", + "\\xc4", "\\xc5", "\\xc6", "\\xc7", "\\xc8", "\\xc9", "\\xca", "\\xcb", "\\xcc", "\\xcd", + "\\xce", "\\xcf", "\\xd0", "\\xd1", "\\xd2", "\\xd3", "\\xd4", "\\xd5", "\\xd6", "\\xd7", + "\\xd8", "\\xd9", "\\xda", "\\xdb", "\\xdc", "\\xdd", "\\xde", "\\xdf", "\\xe0", "\\xe1", + "\\xe2", "\\xe3", "\\xe4", "\\xe5", "\\xe6", "\\xe7", "\\xe8", "\\xe9", "\\xea", "\\xeb", + "\\xec", "\\xed", "\\xee", "\\xef", "\\xf0", "\\xf1", "\\xf2", "\\xf3", "\\xf4", "\\xf5", + "\\xf6", "\\xf7", "\\xf8", "\\xf9", "\\xfa", "\\xfb", "\\xfc", "\\xfd", "\\xfe", "\\xff", +]; + +impl PyBytesRef { + fn add(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { + if objtype::isinstance(&other, &vm.ctx.bytes_type()) { + let rhs = get_value(&other); + let elements: Vec = self.value.iter().chain(rhs.iter()).cloned().collect(); + Ok(vm.ctx.new_bytes(elements)) + } else { + Err(vm.new_not_implemented_error("".to_string())) + } + } + + fn contains(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { + if objtype::isinstance(&needle, &vm.ctx.bytes_type()) { + let result = vec_contains(&self.value, &get_value(&needle)); + Ok(result) + } else if objtype::isinstance(&needle, &vm.ctx.int_type()) { + let result = self + .value + .contains(&objint::get_value(&needle).to_u8().unwrap()); + Ok(result) + } else { + Err(vm.new_type_error(format!("Cannot add {:?} and {:?}", self, needle))) + } + } + + fn getitem(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { + if objtype::isinstance(&needle, &vm.ctx.int_type()) { + match objint::get_value(&needle).to_i32() { + Some(pos) => { + if let Some(idx) = self.value.get_pos(pos) { + Ok(vm.ctx.new_int(self.value[idx])) + } else { + Err(vm.new_index_error("index out of range".to_string())) + } + } + None => Err( + vm.new_index_error("cannot fit 'int' into an index-sized integer".to_string()) + ), + } + } else if objtype::isinstance(&needle, &vm.ctx.slice_type()) { + Ok(vm + .ctx + .new_bytes((self.value.get_slice_items(&vm, &needle)).unwrap())) + } else { + Err(vm.new_type_error(format!( + "byte indices must be integers or slices, not {}", + needle.class() + ))) + } + } +}