diff --git a/Cargo.lock b/Cargo.lock index 25a156c8c..aa31678ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,3 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. [[package]] name = "aho-corasick" version = "0.6.4" diff --git a/src/main.rs b/src/main.rs index 52be6656e..e6d5e5684 100644 --- a/src/main.rs +++ b/src/main.rs @@ -68,7 +68,7 @@ fn main() { handle_exception(&mut vm, result); } -fn _run_string(vm: &mut VirtualMachine, source: &str, source_path: Option) -> PyResult { +fn _run_string(vm: &mut VirtualMachine, source: &str, source_path: String) -> PyResult { let code_obj = compile::compile(vm, source, &compile::Mode::Exec, source_path)?; // trace!("Code object: {:?}", code_obj.borrow()); let builtins = vm.get_builtin_scope(); @@ -91,7 +91,7 @@ fn run_command(vm: &mut VirtualMachine, mut source: String) -> PyResult { // This works around https://github.com/RustPython/RustPython/issues/17 source.push_str("\n"); - _run_string(vm, &source, None) + _run_string(vm, &source, "".to_string()) } fn run_module(vm: &mut VirtualMachine, module: &str) -> PyResult { @@ -105,7 +105,7 @@ fn run_script(vm: &mut VirtualMachine, script_file: &str) -> PyResult { // Parse an ast from it: let filepath = Path::new(script_file); match parser::read_file(filepath) { - Ok(source) => _run_string(vm, &source, Some(filepath.to_str().unwrap().to_string())), + Ok(source) => _run_string(vm, &source, filepath.to_str().unwrap().to_string()), Err(msg) => { error!("Parsing went horribly wrong: {}", msg); std::process::exit(1); @@ -114,7 +114,7 @@ fn run_script(vm: &mut VirtualMachine, script_file: &str) -> PyResult { } fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> bool { - match compile::compile(vm, source, &compile::Mode::Single, None) { + match compile::compile(vm, source, &compile::Mode::Single, "".to_string()) { Ok(code) => { match vm.run_code_obj(code, scope) { Ok(_value) => { diff --git a/tests/snippets/code.py b/tests/snippets/code.py new file mode 100644 index 000000000..ab3353e3b --- /dev/null +++ b/tests/snippets/code.py @@ -0,0 +1,28 @@ +c1 = compile("1 + 1", "", 'eval') + +code_class = type(c1) + +def f(x, y, *args, power=1, **kwargs): + assert code_class == type(c1) + z = x * y + return z ** power + +c2 = f.__code__ +# print(c2) +assert type(c2) == code_class +print(dir(c2)) +assert c2.co_argcount == 2 +# assert c2.co_cellvars == () +# assert isinstance(c2.co_code, bytes) +# assert c2.co_consts == (None,) +assert "code.py" in c2.co_filename +assert c2.co_firstlineno == 5, str(c2.co_firstlineno) +# assert isinstance(c2.co_flags, int) # 'OPTIMIZED, NEWLOCALS, NOFREE' +# assert c2.co_freevars == (), str(c2.co_freevars) +assert c2.co_kwonlyargcount == 1, (c2.co_kwonlyargcount) +# assert c2.co_lnotab == 0, c2.co_lnotab # b'\x00\x01' # Line number table +assert c2.co_name == 'f', c2.co_name +# assert c2.co_names == ('code_class', 'type', 'c1', 'AssertionError'), c2.co_names # , c2.co_names +# assert c2.co_nlocals == 4, c2.co_nlocals # +# assert c2.co_stacksize == 2, 'co_stacksize', +# assert c2.co_varnames == ('x', 'y', 'power', 'z'), c2.co_varnames diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 355ae3a4b..75cd7afaf 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -150,7 +150,7 @@ fn builtin_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let filename = objstr::get_value(filename); - compile::compile(vm, &source, &mode, Some(filename)) + compile::compile(vm, &source, &mode, filename) } fn builtin_delattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -200,7 +200,7 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let source = objstr::get_value(source); // TODO: fix this newline bug: let source = format!("{}\n", source); - compile::compile(vm, &source, &mode, None)? + compile::compile(vm, &source, &mode, "".to_string())? } else { return Err(vm.new_type_error("code argument must be str or code object".to_string())); }; @@ -246,7 +246,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let source = objstr::get_value(source); // TODO: fix this newline bug: let source = format!("{}\n", source); - compile::compile(vm, &source, &mode, None)? + compile::compile(vm, &source, &mode, "".to_string())? } else if objtype::isinstance(source, &vm.ctx.code_type()) { source.clone() } else { diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index 77b12917a..c0309f841 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -22,7 +22,8 @@ pub struct CodeObject { pub varargs: Option>, // *args or * pub kwonlyarg_names: Vec, pub varkeywords: Option>, // **kwargs or ** - pub source_path: Option, + pub source_path: String, + pub first_line_number: usize, pub obj_name: String, // Name of the object that created this code object pub is_generator: bool, } @@ -33,7 +34,8 @@ impl CodeObject { varargs: Option>, kwonlyarg_names: Vec, varkeywords: Option>, - source_path: Option, + source_path: String, + first_line_number: usize, obj_name: String, ) -> CodeObject { CodeObject { @@ -45,6 +47,7 @@ impl CodeObject { kwonlyarg_names, varkeywords, source_path, + first_line_number, obj_name, is_generator: false, } diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 9050e59ce..1aa8cce27 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -24,10 +24,10 @@ pub fn compile( vm: &mut VirtualMachine, source: &str, mode: &Mode, - source_path: Option, + source_path: String, ) -> PyResult { let mut compiler = Compiler::new(); - compiler.source_path = source_path.clone(); + compiler.source_path = Some(source_path.clone()); compiler.push_new_code_object(source_path, "".to_string()); let syntax_error = vm.context().exceptions.syntax_error.clone(); let result = match mode { @@ -78,13 +78,15 @@ impl Compiler { } } - fn push_new_code_object(&mut self, source_path: Option, obj_name: String) { + fn push_new_code_object(&mut self, source_path: String, obj_name: String) { + let line_number = self.get_source_line_number(); self.code_object_stack.push(CodeObject::new( Vec::new(), None, Vec::new(), None, source_path.clone(), + line_number, obj_name, )); } @@ -453,12 +455,14 @@ impl Compiler { } => { self.prepare_decorators(decorator_list)?; self.emit(Instruction::LoadBuildClass); + let line_number = self.get_source_line_number(); self.code_object_stack.push(CodeObject::new( vec![String::from("__locals__")], None, vec![], None, - self.source_path.clone(), + self.source_path.clone().unwrap(), + line_number, name.clone(), )); self.emit(Instruction::LoadName { @@ -653,12 +657,14 @@ impl Compiler { }); } + let line_number = self.get_source_line_number(); self.code_object_stack.push(CodeObject::new( args.args.clone(), args.vararg.clone(), args.kwonlyargs.clone(), args.kwarg.clone(), - self.source_path.clone(), + self.source_path.clone().unwrap(), + line_number, name.to_string(), )); @@ -1162,13 +1168,15 @@ impl Compiler { } .to_string(); + let line_number = self.get_source_line_number(); // Create magnificent function : self.code_object_stack.push(CodeObject::new( vec![".0".to_string()], None, vec![], None, - self.source_path.clone(), + self.source_path.clone().unwrap(), + line_number, name.clone(), )); @@ -1338,6 +1346,10 @@ impl Compiler { self.current_source_location = location.clone(); } + fn get_source_line_number(&mut self) -> usize { + self.current_source_location.get_row() + } + fn mark_generator(&mut self) { self.current_code_object().is_generator = true; } @@ -1352,7 +1364,7 @@ mod tests { use rustpython_parser::parser; fn compile_exec(source: &str) -> CodeObject { let mut compiler = Compiler::new(); - compiler.push_new_code_object(Option::None, "".to_string()); + compiler.push_new_code_object("source_path".to_string(), "".to_string()); let ast = parser::parse_program(&source.to_string()).unwrap(); compiler.compile_program(&ast).unwrap(); compiler.pop_code_object() diff --git a/vm/src/eval.rs b/vm/src/eval.rs index f5f690e95..ef27e63af 100644 --- a/vm/src/eval.rs +++ b/vm/src/eval.rs @@ -4,8 +4,13 @@ use super::compile; use super::pyobject::{PyObjectRef, PyResult}; use super::vm::VirtualMachine; -pub fn eval(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> PyResult { - match compile::compile(vm, source, &compile::Mode::Eval, None) { +pub fn eval( + vm: &mut VirtualMachine, + source: &str, + scope: PyObjectRef, + source_path: &str, +) -> PyResult { + match compile::compile(vm, source, &compile::Mode::Eval, source_path.to_string()) { Ok(bytecode) => { debug!("Code object: {:?}", bytecode); vm.run_code_obj(bytecode, scope) @@ -24,7 +29,7 @@ mod tests { let source = String::from("print('Hello world')\n"); let mut vm = VirtualMachine::new(); let vars = vm.context().new_scope(None); - let _result = eval(&mut vm, &source, vars); + let _result = eval(&mut vm, &source, vars, ""); // TODO: check result? //assert_eq!( diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 86c0bc29d..474b6d890 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -70,7 +70,7 @@ impl Frame { // locals.extend(callargs); Frame { - code: objcode::copy_code(&code), + code: objcode::get_value(&code), stack: vec![], blocks: vec![], // save the callargs as locals @@ -88,11 +88,7 @@ impl Frame { } pub fn run_frame(&mut self, vm: &mut VirtualMachine) -> Result { - let filename = if let Some(source_path) = &self.code.source_path { - source_path.to_string() - } else { - "".to_string() - }; + let filename = &self.code.source_path.to_string(); let prev_frame = mem::replace(&mut vm.current_frame, Some(vm.ctx.new_frame(self.clone()))); @@ -661,13 +657,10 @@ impl Frame { module: &str, symbol: &Option, ) -> FrameResult { - let current_path = match &self.code.source_path { - Some(source_path) => { - let mut source_pathbuf = PathBuf::from(source_path); - source_pathbuf.pop(); - source_pathbuf - } - None => PathBuf::from("."), + let current_path = { + let mut source_pathbuf = PathBuf::from(&self.code.source_path); + source_pathbuf.pop(); + source_pathbuf }; let obj = import(vm, current_path, module, symbol)?; @@ -678,13 +671,10 @@ impl Frame { } fn import_star(&mut self, vm: &mut VirtualMachine, module: &str) -> FrameResult { - let current_path = match &self.code.source_path { - Some(source_path) => { - let mut source_pathbuf = PathBuf::from(source_path); - source_pathbuf.pop(); - source_pathbuf - } - None => PathBuf::from("."), + let current_path = { + let mut source_pathbuf = PathBuf::from(&self.code.source_path); + source_pathbuf.pop(); + source_pathbuf }; // Grab all the names from the module and put them in the context diff --git a/vm/src/import.rs b/vm/src/import.rs index be8807881..d7393bd67 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -35,7 +35,7 @@ fn import_uncached_module( vm, &source, &compile::Mode::Exec, - Some(filepath.to_str().unwrap().to_string()), + filepath.to_str().unwrap().to_string(), )?; // trace!("Code object: {:?}", code_obj); diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index 70d9f2d54..2649c1190 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -4,7 +4,7 @@ use super::super::bytecode; use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objtype; @@ -13,15 +13,26 @@ pub fn init(context: &PyContext) { let code_type = &context.code_type; context.set_attr(code_type, "__new__", context.new_rustfunc(code_new)); context.set_attr(code_type, "__repr__", context.new_rustfunc(code_repr)); + + for (name, f) in vec![ + ( + "co_argcount", + code_co_argcount as fn(&mut VirtualMachine, PyFuncArgs) -> PyResult, + ), + ("co_filename", code_co_filename), + ("co_firstlineno", code_co_firstlineno), + ("co_kwonlyargcount", code_co_kwonlyargcount), + ("co_name", code_co_name), + ] { + context.set_attr(code_type, name, context.new_member_descriptor(f)) + } } -/// Extract rust bytecode object from a python code object. -pub fn copy_code(code_obj: &PyObjectRef) -> bytecode::CodeObject { - let code_obj = code_obj.borrow(); - if let PyObjectPayload::Code { ref code } = code_obj.payload { +pub fn get_value(obj: &PyObjectRef) -> bytecode::CodeObject { + if let PyObjectPayload::Code { code } = &obj.borrow().payload { code.clone() } else { - panic!("Must be code obj"); + panic!("Inner error getting code {:?}", obj) } } @@ -33,18 +44,54 @@ fn code_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn code_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(o, Some(vm.ctx.code_type()))]); - // Fetch actual code: - let code = copy_code(o); - - let file = if let Some(source_path) = code.source_path { - format!(", file {}", source_path) - } else { - String::new() - }; - - // TODO: fetch proper line info from code object - let line = ", line 1".to_string(); - - let repr = format!("", file, line); + let code = get_value(o); + let repr = format!( + "", + code.obj_name, + o.get_id(), + code.source_path, + code.first_line_number + ); Ok(vm.new_str(repr)) } + +fn member_code_obj( + vm: &mut VirtualMachine, + args: PyFuncArgs, +) -> Result { + arg_check!( + vm, + args, + required = [ + (zelf, Some(vm.ctx.code_type())), + (_cls, Some(vm.ctx.type_type())) + ] + ); + Ok(get_value(zelf)) +} + +fn code_co_argcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + let code_obj = member_code_obj(vm, args)?; + Ok(vm.ctx.new_int(code_obj.arg_names.len())) +} + +fn code_co_filename(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + let code_obj = member_code_obj(vm, args)?; + let source_path = code_obj.source_path; + Ok(vm.new_str(source_path)) +} + +fn code_co_firstlineno(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + let code_obj = member_code_obj(vm, args)?; + Ok(vm.ctx.new_int(code_obj.first_line_number)) +} + +fn code_co_kwonlyargcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + let code_obj = member_code_obj(vm, args)?; + Ok(vm.ctx.new_int(code_obj.kwonlyarg_names.len())) +} + +fn code_co_name(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + let code_obj = member_code_obj(vm, args)?; + Ok(vm.new_str(code_obj.obj_name)) +} diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 435e7bcbc..cd7567c8e 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -11,7 +11,7 @@ use std::collections::hash_map::HashMap; use super::builtins; use super::bytecode; use super::frame::Frame; -use super::obj::objcode::copy_code; +use super::obj::objcode; use super::obj::objgenerator; use super::obj::objiter; use super::obj::objsequence; @@ -282,7 +282,7 @@ impl VirtualMachine { defaults: &PyObjectRef, args: PyFuncArgs, ) -> PyResult { - let code_object = copy_code(code); + let code_object = objcode::get_value(code); let scope = self.ctx.new_scope(Some(scope.clone())); self.fill_scope_from_args(&code_object, &scope, args, defaults)?; diff --git a/wasm/lib/src/lib.rs b/wasm/lib/src/lib.rs index f7da7edd1..7ad3f1956 100644 --- a/wasm/lib/src/lib.rs +++ b/wasm/lib/src/lib.rs @@ -123,7 +123,7 @@ fn eval(vm: &mut VirtualMachine, source: &str, vars: PyObjectRef) -> PyResult { source.push('\n'); } - let code_obj = compile::compile(vm, &source, &compile::Mode::Exec, None)?; + let code_obj = compile::compile(vm, &source, &compile::Mode::Exec, "".to_string())?; vm.run_code_obj(code_obj, vars) }