diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 72ab44d4c..c88ca6769 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -1,3 +1,5 @@ +#![recursion_limit = "128"] + extern crate proc_macro; #[macro_use] @@ -38,3 +40,10 @@ pub fn pyimpl(attr: TokenStream, item: TokenStream) -> TokenStream { let item = parse_macro_input!(item as Item); result_to_tokens(pyclass::impl_pyimpl(attr, item)) } + +#[proc_macro_attribute] +pub fn pystruct_sequence(attr: TokenStream, item: TokenStream) -> TokenStream { + let attr = parse_macro_input!(attr as AttributeArgs); + let item = parse_macro_input!(item as Item); + result_to_tokens(pyclass::impl_pystruct_sequence(attr, item)) +} diff --git a/derive/src/pyclass.rs b/derive/src/pyclass.rs index a2cf57b4a..021ee9292 100644 --- a/derive/src/pyclass.rs +++ b/derive/src/pyclass.rs @@ -2,7 +2,9 @@ use super::Diagnostic; use proc_macro2::TokenStream as TokenStream2; use quote::quote; use std::collections::HashMap; -use syn::{Attribute, AttributeArgs, Ident, ImplItem, Item, Lit, Meta, MethodSig, NestedMeta}; +use syn::{ + Attribute, AttributeArgs, Ident, ImplItem, Index, Item, Lit, Meta, MethodSig, NestedMeta, +}; enum ClassItem { Method { @@ -266,16 +268,12 @@ pub fn impl_pyimpl(_attr: AttributeArgs, item: Item) -> Result Result { - let (item, ident, attrs) = match item { - Item::Struct(struc) => (quote!(#struc), struc.ident, struc.attrs), - Item::Enum(enu) => (quote!(#enu), enu.ident, enu.attrs), - other => bail_span!( - other, - "#[pyclass] can only be on a struct or enum declaration" - ), - }; - +fn generate_class_def( + ident: &Ident, + attr_name: &'static str, + attr: AttributeArgs, + attrs: &Vec, +) -> Result { let mut class_name = None; for attr in attr { if let NestedMeta::Meta(meta) = attr { @@ -284,7 +282,11 @@ pub fn impl_pyclass(attr: AttributeArgs, item: Item) -> Result Result = #doc; @@ -324,3 +325,103 @@ pub fn impl_pyclass(attr: AttributeArgs, item: Item) -> Result Result { + let (item, ident, attrs) = match item { + Item::Struct(struc) => (quote!(#struc), struc.ident, struc.attrs), + Item::Enum(enu) => (quote!(#enu), enu.ident, enu.attrs), + other => bail_span!( + other, + "#[pyclass] can only be on a struct or enum declaration" + ), + }; + + let class_def = generate_class_def(&ident, "pyclass", attr, &attrs)?; + + let ret = quote! { + #item + #class_def + }; + Ok(ret) +} + +pub fn impl_pystruct_sequence(attr: AttributeArgs, item: Item) -> Result { + if let Item::Struct(struc) = item { + let class_def = generate_class_def(&struc.ident, "pystruct_sequence", attr, &struc.attrs)?; + let mut methods = Vec::new(); + let mut method_references = Vec::new(); + let mut field_names = Vec::new(); + for (i, field) in struc.fields.iter().enumerate() { + let idx = Index::from(i); + if let Some(ref field_name) = field.ident { + let method_name = format!("get_{}", field_name); + let method_ident = Ident::new(&method_name, field_name.span()); + + let method = quote! { + fn #method_ident( + zelf: &::rustpython_vm::obj::objtuple::PyTuple, + _vm: &::rustpython_vm::vm::VirtualMachine) + -> ::rustpython_vm::pyobject::PyObjectRef { + zelf.fast_getitem(#idx) + } + }; + methods.push(method); + let field_name_str = field_name.to_string(); + let method_reference = quote! { + class.set_str_attr( + #field_name_str, + ::rustpython_vm::obj::objproperty::PropertyBuilder::new(ctx) + .add_getter(Self::#method_ident) + .create(), + ); + }; + method_references.push(method_reference); + field_names.push(quote!(#field_name)); + } else { + field_names.push(quote!(#idx)); + } + } + + let ty = &struc.ident; + let ret = quote! { + #struc + #class_def + impl #ty { + fn into_struct_sequence(&self, + vm: &::rustpython_vm::vm::VirtualMachine, + cls: ::rustpython_vm::obj::objtype::PyClassRef, + ) -> ::rustpython_vm::pyobject::PyResult<::rustpython_vm::obj::objtuple::PyTupleRef> { + let tuple: ::rustpython_vm::obj::objtuple::PyTuple = + vec![#(::rustpython_vm::pyobject::IntoPyObject + ::into_pyobject(self.#field_names, vm)? + ),*].into(); + ::rustpython_vm::pyobject::PyValue::into_ref_with_type(tuple, vm, cls) + } + + #(#methods)* + } + impl ::rustpython_vm::pyobject::PyClassImpl for #ty { + fn impl_extend_class( + ctx: &::rustpython_vm::pyobject::PyContext, + class: &::rustpython_vm::obj::objtype::PyClassRef, + ) { + #(#method_references)* + } + + fn make_class( + ctx: &::rustpython_vm::pyobject::PyContext + ) -> ::rustpython_vm::obj::objtype::PyClassRef { + let py_class = ctx.new_class(::NAME, ctx.tuple_type()); + Self::extend_class(ctx, &py_class); + py_class + } + } + }; + Ok(ret) + } else { + bail_span!( + item, + "#[pystruct_sequence] can only be on a struct declaration" + ) + } +} diff --git a/tests/snippets/sysmod.py b/tests/snippets/sysmod.py index 423ecf08d..7de155e17 100644 --- a/tests/snippets/sysmod.py +++ b/tests/snippets/sysmod.py @@ -7,3 +7,8 @@ assert sys.platform == "linux" or sys.platform == "darwin" or sys.platform == "w assert isinstance(sys.builtin_module_names, tuple) assert 'sys' in sys.builtin_module_names + +assert isinstance(sys.flags, tuple) +assert type(sys.flags).__name__ == "flags" +assert type(sys.flags.optimize) is int +assert sys.flags[3] == sys.flags.optimize diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 795e17d62..e4b828f3c 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -40,6 +40,12 @@ impl PyValue for PyTuple { } } +impl PyTuple { + pub fn fast_getitem(&self, idx: usize) -> PyObjectRef { + self.elements.borrow()[idx].clone() + } +} + pub type PyTupleRef = PyRef; impl PyTupleRef { diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index 8c6bafe74..6400c77f3 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -4,7 +4,7 @@ use std::{env, mem}; use crate::frame::FrameRef; use crate::function::{OptionalArg, PyFuncArgs}; use crate::obj::objstr::PyStringRef; -use crate::pyobject::{IntoPyObject, ItemProtocol, PyContext, PyObjectRef, PyResult}; +use crate::pyobject::{IntoPyObject, ItemProtocol, PyClassImpl, PyContext, PyObjectRef, PyResult}; use crate::vm::VirtualMachine; /* @@ -27,6 +27,26 @@ fn getframe(offset: OptionalArg, vm: &VirtualMachine) -> PyResult PyResult { arg_check!(vm, args, required = [(object, None)]); let size = Rc::strong_count(&object); @@ -48,6 +68,12 @@ fn sys_intern(value: PyStringRef, _vm: &VirtualMachine) -> PyStringRef { pub fn make_module(vm: &VirtualMachine, module: PyObjectRef, builtins: PyObjectRef) { let ctx = &vm.ctx; + let flags_type = SysFlags::make_class(ctx); + // TODO parse command line arguments and environment variables to populate SysFlags + let flags = SysFlags::default() + .into_struct_sequence(vm, flags_type) + .unwrap(); + let path_list = match env::var_os("PYTHONPATH") { Some(paths) => env::split_paths(&paths) .map(|path| { @@ -152,6 +178,7 @@ settrace() -- set the global debug tracing function extend_module!(vm, module, { "argv" => argv(ctx), "builtin_module_names" => ctx.new_tuple(module_names.iter().map(|v| v.into_pyobject(vm).unwrap()).collect()), + "flags" => flags, "getrefcount" => ctx.new_rustfunc(sys_getrefcount), "getsizeof" => ctx.new_rustfunc(sys_getsizeof), "intern" => ctx.new_rustfunc(sys_intern),