Skip to content

Commit 51c3f71

Browse files
Merge pull request RustPython#1406 from lntuition/fix-1405
Fix int type casting error with negative base value
2 parents 90d20fe + 545e9d3 commit 51c3f71

File tree

6 files changed

+39
-16
lines changed

6 files changed

+39
-16
lines changed

tests/snippets/ints.py

+9
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,15 @@
167167
with assert_raises(ValueError):
168168
int(' 1 ', base=37)
169169

170+
with assert_raises(ValueError):
171+
int(' 1 ', base=-1)
172+
173+
with assert_raises(ValueError):
174+
int(' 1 ', base=1000000000000000)
175+
176+
with assert_raises(ValueError):
177+
int(' 1 ', base=-1000000000000000)
178+
170179
with assert_raises(TypeError):
171180
int(base=2)
172181

vm/src/obj/objbyteinner.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ impl ByteInnerNewOptions {
135135

136136
let mut data_bytes = vec![];
137137
for elem in elements.unwrap() {
138-
let v = objint::to_int(vm, &elem, 10)?;
138+
let v = objint::to_int(vm, &elem, &BigInt::from(10))?;
139139
if let Some(i) = v.to_u8() {
140140
data_bytes.push(i);
141141
} else {

vm/src/obj/objint.rs

+24-13
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,7 @@ struct IntOptions {
704704
#[pyarg(positional_only, optional = true)]
705705
val_options: OptionalArg<PyObjectRef>,
706706
#[pyarg(positional_or_keyword, optional = true)]
707-
base: OptionalArg<u32>,
707+
base: OptionalArg<PyIntRef>,
708708
}
709709

710710
impl IntOptions {
@@ -720,9 +720,9 @@ impl IntOptions {
720720
}
721721
base
722722
} else {
723-
10
723+
PyInt::new(10).into_ref(vm)
724724
};
725-
to_int(vm, &val, base)
725+
to_int(vm, &val, base.as_bigint())
726726
} else if let OptionalArg::Present(_) = self.base {
727727
Err(vm.new_type_error("int() missing string argument".to_string()))
728728
} else {
@@ -736,8 +736,14 @@ fn int_new(cls: PyClassRef, options: IntOptions, vm: &VirtualMachine) -> PyResul
736736
}
737737

738738
// Casting function:
739-
pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult<BigInt> {
740-
if base != 0 && (base < 2 || base > 36) {
739+
pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, base: &BigInt) -> PyResult<BigInt> {
740+
let base_u32 = match base.to_u32() {
741+
Some(base_u32) => base_u32,
742+
None => {
743+
return Err(vm.new_value_error("int() base must be >= 2 and <= 36, or 0".to_string()))
744+
}
745+
};
746+
if base_u32 != 0 && (base_u32 < 2 || base_u32 > 36) {
741747
return Err(vm.new_value_error("int() base must be >= 2 and <= 36, or 0".to_string()));
742748
}
743749

@@ -772,38 +778,43 @@ pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult<Big
772778
})
773779
}
774780

775-
fn str_to_int(vm: &VirtualMachine, literal: &str, mut base: u32) -> PyResult<BigInt> {
781+
fn str_to_int(vm: &VirtualMachine, literal: &str, base: &BigInt) -> PyResult<BigInt> {
776782
let mut buf = validate_literal(vm, literal, base)?;
777783
let is_signed = buf.starts_with('+') || buf.starts_with('-');
778784
let radix_range = if is_signed { 1..3 } else { 0..2 };
779785
let radix_candidate = buf.get(radix_range.clone());
780786

787+
let mut base_u32 = match base.to_u32() {
788+
Some(base_u32) => base_u32,
789+
None => return Err(invalid_literal(vm, literal, base)),
790+
};
791+
781792
// try to find base
782793
if let Some(radix_candidate) = radix_candidate {
783794
if let Some(matched_radix) = detect_base(&radix_candidate) {
784-
if base != 0 && base != matched_radix {
795+
if base_u32 != 0 && base_u32 != matched_radix {
785796
return Err(invalid_literal(vm, literal, base));
786797
} else {
787-
base = matched_radix;
798+
base_u32 = matched_radix;
788799
}
789800

790801
buf.drain(radix_range);
791802
}
792803
}
793804

794805
// base still not found, try to use default
795-
if base == 0 {
806+
if base_u32 == 0 {
796807
if buf.starts_with('0') {
797808
return Err(invalid_literal(vm, literal, base));
798809
}
799810

800-
base = 10;
811+
base_u32 = 10;
801812
}
802813

803-
BigInt::from_str_radix(&buf, base).map_err(|_err| invalid_literal(vm, literal, base))
814+
BigInt::from_str_radix(&buf, base_u32).map_err(|_err| invalid_literal(vm, literal, base))
804815
}
805816

806-
fn validate_literal(vm: &VirtualMachine, literal: &str, base: u32) -> PyResult<String> {
817+
fn validate_literal(vm: &VirtualMachine, literal: &str, base: &BigInt) -> PyResult<String> {
807818
if literal.starts_with('_') || literal.ends_with('_') {
808819
return Err(invalid_literal(vm, literal, base));
809820
}
@@ -835,7 +846,7 @@ fn detect_base(literal: &str) -> Option<u32> {
835846
}
836847
}
837848

838-
fn invalid_literal(vm: &VirtualMachine, literal: &str, base: u32) -> PyObjectRef {
849+
fn invalid_literal(vm: &VirtualMachine, literal: &str, base: &BigInt) -> PyObjectRef {
839850
vm.new_value_error(format!(
840851
"invalid literal for int() with base {}: '{}'",
841852
base, literal

vm/src/stdlib/pystruct.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ where
105105
}
106106

107107
fn get_int(vm: &VirtualMachine, arg: &PyObjectRef) -> PyResult<BigInt> {
108-
objint::to_int(vm, arg, 10)
108+
objint::to_int(vm, arg, &BigInt::from(10))
109109
}
110110

111111
fn pack_i8(vm: &VirtualMachine, arg: &PyObjectRef, data: &mut dyn Write) -> PyResult<()> {

wasm/lib/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ serde = "1.0"
2626
js-sys = "0.3"
2727
futures = "0.1"
2828
num-traits = "0.2"
29+
num-bigint = { version = "0.2.3", features = ["serde"] }
2930

3031
[dependencies.web-sys]
3132
version = "0.3"

wasm/lib/src/convert.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ use rustpython_vm::py_serde;
99
use rustpython_vm::pyobject::{ItemProtocol, PyObjectRef, PyResult, PyValue};
1010
use rustpython_vm::VirtualMachine;
1111

12+
use num_bigint::BigInt;
13+
1214
use crate::browser_module;
1315
use crate::vm_class::{stored_vm_from_wasm, WASMVirtualMachine};
1416

@@ -43,7 +45,7 @@ pub fn py_err_to_js_err(vm: &VirtualMachine, py_err: &PyObjectRef) -> JsValue {
4345
if objtype::isinstance(&top, &vm.ctx.tuple_type()) {
4446
let element = objsequence::get_elements_tuple(&top);
4547

46-
if let Some(lineno) = objint::to_int(vm, &element[1], 10)
48+
if let Some(lineno) = objint::to_int(vm, &element[1], &BigInt::from(10))
4749
.ok()
4850
.and_then(|lineno| lineno.to_u32())
4951
{

0 commit comments

Comments
 (0)