From e89d00f167187f5514a75910285f6901e78beadf Mon Sep 17 00:00:00 2001 From: Noah <33094578+coolreader18@users.noreply.github.com> Date: Thu, 3 Oct 2019 19:16:00 +0000 Subject: [PATCH 1/2] Correctly handle a SystemExit unwound to the top level of execution --- Cargo.lock | 1 + Cargo.toml | 1 + src/main.rs | 51 +++++++++++++++++++++++++++++++++++---------- vm/src/sysmodule.rs | 7 +++---- vm/src/vm.rs | 2 +- 5 files changed, 46 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 228def36d1..5e845c4086 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1092,6 +1092,7 @@ dependencies = [ "flame 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "flamescope 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "rustpython-compiler 0.1.1", "rustpython-parser 0.1.1", "rustpython-vm 0.1.1", diff --git a/Cargo.toml b/Cargo.toml index 2a9d826ab9..8192b42a2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ rustpython-compiler = {path = "compiler", version = "0.1.1"} rustpython-parser = {path = "parser", version = "0.1.1"} rustpython-vm = {path = "vm", version = "0.1.1"} dirs = "2.0" +num-traits = "0.2.8" flame = { version = "0.2", optional = true } flamescope = { version = "0.1", optional = true } diff --git a/src/main.rs b/src/main.rs index 104639b3ab..65adc69e43 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,9 @@ use clap::{App, AppSettings, Arg, ArgMatches}; use rustpython_compiler::{compile, error::CompileError, error::CompileErrorType}; use rustpython_parser::error::ParseErrorType; use rustpython_vm::{ - import, print_exception, + import, match_class, + obj::{objint::PyInt, objtuple::PyTuple, objtype}, + print_exception, pyobject::{ItemProtocol, PyObjectRef, PyResult}, scope::Scope, util, PySettings, VirtualMachine, @@ -30,17 +32,47 @@ fn main() { let vm = VirtualMachine::new(settings); let res = run_rustpython(&vm, &matches); - // See if any exception leaked out: - handle_exception(&vm, res); #[cfg(feature = "flame-it")] { main_guard.end(); if let Err(e) = write_profile(&matches) { error!("Error writing profile information: {}", e); - process::exit(1); } } + + // See if any exception leaked out: + if let Err(err) = res { + if objtype::isinstance(&err, &vm.ctx.exceptions.system_exit) { + let args = vm.get_attribute(err.clone(), "args").unwrap(); + let args = args.downcast::().expect("'args' must be a tuple"); + match args.elements.len() { + 0 => return, + 1 => match_class!(match args.elements[0].clone() { + i @ PyInt => { + use num_traits::cast::ToPrimitive; + process::exit(i.as_bigint().to_i32().unwrap()); + } + arg => { + if vm.is_none(&arg) { + return; + } + if let Ok(s) = vm.to_str(&arg) { + println!("{}", s); + } + } + }), + _ => { + if let Ok(r) = vm.to_repr(args.as_object()) { + println!("{}", r); + } + } + } + } else { + print_exception(&vm, &err); + } + process::exit(1); + } } fn parse_arguments<'a>(app: App<'a, '_>) -> ArgMatches<'a> { @@ -349,13 +381,6 @@ fn _run_string(vm: &VirtualMachine, scope: Scope, source: &str, source_path: Str vm.run_code_obj(code_obj, scope) } -fn handle_exception(vm: &VirtualMachine, result: PyResult) { - if let Err(err) = result { - print_exception(vm, &err); - process::exit(1); - } -} - fn run_command(vm: &VirtualMachine, scope: Scope, source: String) -> PyResult<()> { debug!("Running command {}", source); _run_string(vm, scope, &source, "".to_string())?; @@ -560,6 +585,10 @@ fn run_shell(vm: &VirtualMachine, scope: Scope) -> PyResult<()> { }; if let Err(exc) = result { + if objtype::isinstance(&exc, &vm.ctx.exceptions.system_exit) { + repl.save_history(&repl_history_path).unwrap(); + return Err(exc); + } print_exception(vm, &exc); } } diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index 8abc125586..e695526cf2 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -171,10 +171,9 @@ fn sys_git_info(vm: &VirtualMachine) -> PyObjectRef { ]) } -// TODO: raise a SystemExit here -fn sys_exit(code: OptionalArg, _vm: &VirtualMachine) -> PyResult<()> { - let code = code.unwrap_or(0); - std::process::exit(code) +fn sys_exit(code: OptionalArg, vm: &VirtualMachine) -> PyResult { + let code = code.unwrap_or_else(|| vm.new_int(0)); + Err(vm.new_exception_obj(vm.ctx.exceptions.system_exit.clone(), vec![code])?) } pub fn make_module(vm: &VirtualMachine, module: PyObjectRef, builtins: PyObjectRef) { diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 4858daeab7..27e8cf05e9 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -303,7 +303,7 @@ impl VirtualMachine { } #[cfg_attr(feature = "flame-it", flame("VirtualMachine"))] - fn new_exception_obj(&self, exc_type: PyClassRef, args: Vec) -> PyResult { + pub fn new_exception_obj(&self, exc_type: PyClassRef, args: Vec) -> PyResult { // TODO: add repr of args into logging? vm_trace!("New exception created: {}", exc_type.name); self.invoke(&exc_type.into_object(), args) From e304504d22778ab4adc9194a929c4873f1511ae6 Mon Sep 17 00:00:00 2001 From: Noah <33094578+coolreader18@users.noreply.github.com> Date: Fri, 4 Oct 2019 17:08:47 +0000 Subject: [PATCH 2/2] Fix some miscellaneous stuff Int division in _sre.py, float precision printf formatting --- Lib/_sre.py | 12 ++++++------ vm/src/cformat.rs | 11 +++++------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/Lib/_sre.py b/Lib/_sre.py index 644ecef3a0..e498b89978 100644 --- a/Lib/_sre.py +++ b/Lib/_sre.py @@ -1155,17 +1155,17 @@ def set_bigcharset(self, ctx): block_index = char_code >> 8 # NB: there are CODESIZE block indices per bytecode a = array.array("B") - a.fromstring(array.array(CODESIZE == 2 and "H" or "I", - [ctx.peek_code(block_index / CODESIZE)]).tostring()) + a.frombytes(array.array(CODESIZE == 2 and "H" or "I", + [ctx.peek_code(block_index // CODESIZE)]).tobytes()) block = a[block_index % CODESIZE] - ctx.skip_code(256 / CODESIZE) # skip block indices - block_value = ctx.peek_code(block * (32 / CODESIZE) + ctx.skip_code(256 // CODESIZE) # skip block indices + block_value = ctx.peek_code(block * (32 // CODESIZE) + ((char_code & 255) >> (CODESIZE == 2 and 4 or 5))) if block_value & (1 << (char_code & ((8 * CODESIZE) - 1))): return self.ok else: - ctx.skip_code(256 / CODESIZE) # skip block indices - ctx.skip_code(count * (32 / CODESIZE)) # skip blocks + ctx.skip_code(256 // CODESIZE) # skip block indices + ctx.skip_code(count * (32 // CODESIZE)) # skip blocks def unknown(self, ctx): return False diff --git a/vm/src/cformat.rs b/vm/src/cformat.rs index e67fd07154..00aa4769b8 100644 --- a/vm/src/cformat.rs +++ b/vm/src/cformat.rs @@ -225,14 +225,13 @@ impl CFormatSpec { "-" }; - // TODO: Support precision let magnitude_string = match self.format_type { CFormatType::Float(CFloatType::PointDecimal) => { - if Some(CFormatQuantity::Amount(6)) != self.precision { - return Err("Not yet implemented for %#.#f types".to_string()); - } else { - format!("{:.6}", magnitude) - } + let precision = match self.precision { + Some(CFormatQuantity::Amount(p)) => p, + _ => 6, + }; + format!("{:.*}", precision, magnitude) } CFormatType::Float(CFloatType::Exponent(_)) => { return Err("Not yet implemented for %e and %E".to_string())