Fix int type casting error with negative base value

Change base type from u32 to PyIntRef

Fixed: #1405
This commit is contained in:
Sang-Heon Jeon
2019-09-23 16:17:12 +09:00
committed by unknown
parent 00a0e455f3
commit 545e9d39df
6 changed files with 39 additions and 16 deletions

View File

@@ -167,6 +167,15 @@ with assert_raises(ValueError):
with assert_raises(ValueError):
int(' 1 ', base=37)
with assert_raises(ValueError):
int(' 1 ', base=-1)
with assert_raises(ValueError):
int(' 1 ', base=1000000000000000)
with assert_raises(ValueError):
int(' 1 ', base=-1000000000000000)
with assert_raises(TypeError):
int(base=2)

View File

@@ -135,7 +135,7 @@ impl ByteInnerNewOptions {
let mut data_bytes = vec![];
for elem in elements.unwrap() {
let v = objint::to_int(vm, &elem, 10)?;
let v = objint::to_int(vm, &elem, &BigInt::from(10))?;
if let Some(i) = v.to_u8() {
data_bytes.push(i);
} else {

View File

@@ -704,7 +704,7 @@ struct IntOptions {
#[pyarg(positional_only, optional = true)]
val_options: OptionalArg<PyObjectRef>,
#[pyarg(positional_or_keyword, optional = true)]
base: OptionalArg<u32>,
base: OptionalArg<PyIntRef>,
}
impl IntOptions {
@@ -720,9 +720,9 @@ impl IntOptions {
}
base
} else {
10
PyInt::new(10).into_ref(vm)
};
to_int(vm, &val, base)
to_int(vm, &val, base.as_bigint())
} else if let OptionalArg::Present(_) = self.base {
Err(vm.new_type_error("int() missing string argument".to_string()))
} else {
@@ -736,8 +736,14 @@ fn int_new(cls: PyClassRef, options: IntOptions, vm: &VirtualMachine) -> PyResul
}
// Casting function:
pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult<BigInt> {
if base != 0 && (base < 2 || base > 36) {
pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, base: &BigInt) -> PyResult<BigInt> {
let base_u32 = match base.to_u32() {
Some(base_u32) => base_u32,
None => {
return Err(vm.new_value_error("int() base must be >= 2 and <= 36, or 0".to_string()))
}
};
if base_u32 != 0 && (base_u32 < 2 || base_u32 > 36) {
return Err(vm.new_value_error("int() base must be >= 2 and <= 36, or 0".to_string()));
}
@@ -772,19 +778,24 @@ pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult<Big
})
}
fn str_to_int(vm: &VirtualMachine, literal: &str, mut base: u32) -> PyResult<BigInt> {
fn str_to_int(vm: &VirtualMachine, literal: &str, base: &BigInt) -> PyResult<BigInt> {
let mut buf = validate_literal(vm, literal, base)?;
let is_signed = buf.starts_with('+') || buf.starts_with('-');
let radix_range = if is_signed { 1..3 } else { 0..2 };
let radix_candidate = buf.get(radix_range.clone());
let mut base_u32 = match base.to_u32() {
Some(base_u32) => base_u32,
None => return Err(invalid_literal(vm, literal, base)),
};
// try to find base
if let Some(radix_candidate) = radix_candidate {
if let Some(matched_radix) = detect_base(&radix_candidate) {
if base != 0 && base != matched_radix {
if base_u32 != 0 && base_u32 != matched_radix {
return Err(invalid_literal(vm, literal, base));
} else {
base = matched_radix;
base_u32 = matched_radix;
}
buf.drain(radix_range);
@@ -792,18 +803,18 @@ fn str_to_int(vm: &VirtualMachine, literal: &str, mut base: u32) -> PyResult<Big
}
// base still not found, try to use default
if base == 0 {
if base_u32 == 0 {
if buf.starts_with('0') {
return Err(invalid_literal(vm, literal, base));
}
base = 10;
base_u32 = 10;
}
BigInt::from_str_radix(&buf, base).map_err(|_err| invalid_literal(vm, literal, base))
BigInt::from_str_radix(&buf, base_u32).map_err(|_err| invalid_literal(vm, literal, base))
}
fn validate_literal(vm: &VirtualMachine, literal: &str, base: u32) -> PyResult<String> {
fn validate_literal(vm: &VirtualMachine, literal: &str, base: &BigInt) -> PyResult<String> {
if literal.starts_with('_') || literal.ends_with('_') {
return Err(invalid_literal(vm, literal, base));
}
@@ -835,7 +846,7 @@ fn detect_base(literal: &str) -> Option<u32> {
}
}
fn invalid_literal(vm: &VirtualMachine, literal: &str, base: u32) -> PyObjectRef {
fn invalid_literal(vm: &VirtualMachine, literal: &str, base: &BigInt) -> PyObjectRef {
vm.new_value_error(format!(
"invalid literal for int() with base {}: '{}'",
base, literal

View File

@@ -105,7 +105,7 @@ where
}
fn get_int(vm: &VirtualMachine, arg: &PyObjectRef) -> PyResult<BigInt> {
objint::to_int(vm, arg, 10)
objint::to_int(vm, arg, &BigInt::from(10))
}
fn pack_i8(vm: &VirtualMachine, arg: &PyObjectRef, data: &mut dyn Write) -> PyResult<()> {

View File

@@ -26,6 +26,7 @@ serde = "1.0"
js-sys = "0.3"
futures = "0.1"
num-traits = "0.2"
num-bigint = { version = "0.2.3", features = ["serde"] }
[dependencies.web-sys]
version = "0.3"

View File

@@ -9,6 +9,8 @@ use rustpython_vm::py_serde;
use rustpython_vm::pyobject::{ItemProtocol, PyObjectRef, PyResult, PyValue};
use rustpython_vm::VirtualMachine;
use num_bigint::BigInt;
use crate::browser_module;
use crate::vm_class::{stored_vm_from_wasm, WASMVirtualMachine};
@@ -43,7 +45,7 @@ pub fn py_err_to_js_err(vm: &VirtualMachine, py_err: &PyObjectRef) -> JsValue {
if objtype::isinstance(&top, &vm.ctx.tuple_type()) {
let element = objsequence::get_elements_tuple(&top);
if let Some(lineno) = objint::to_int(vm, &element[1], 10)
if let Some(lineno) = objint::to_int(vm, &element[1], &BigInt::from(10))
.ok()
.and_then(|lineno| lineno.to_u32())
{