Skip to content

Commit c07aa7e

Browse files
committed
Fix int type casting error with negative base value
Change base type from u32 to i32 Fixed: #1405
1 parent 00a0e45 commit c07aa7e

File tree

4 files changed

+27
-15
lines changed

4 files changed

+27
-15
lines changed

tests/snippets/ints.py

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

170+
with assert_raises(ValueError):
171+
int(' 1 ', base=-1)
172+
170173
with assert_raises(TypeError):
171174
int(base=2)
172175

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

+22-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,12 @@ 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 => return Err(vm.new_value_error("int() base must be >= 2 and <= 36, or 0".to_string())),
743+
};
744+
if base_u32 != 0 && (base_u32 < 2 || base_u32 > 36) {
741745
return Err(vm.new_value_error("int() base must be >= 2 and <= 36, or 0".to_string()));
742746
}
743747

@@ -772,38 +776,43 @@ pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult<Big
772776
})
773777
}
774778

775-
fn str_to_int(vm: &VirtualMachine, literal: &str, mut base: u32) -> PyResult<BigInt> {
779+
fn str_to_int(vm: &VirtualMachine, literal: &str, base: &BigInt) -> PyResult<BigInt> {
776780
let mut buf = validate_literal(vm, literal, base)?;
777781
let is_signed = buf.starts_with('+') || buf.starts_with('-');
778782
let radix_range = if is_signed { 1..3 } else { 0..2 };
779783
let radix_candidate = buf.get(radix_range.clone());
780784

785+
let mut base_u32 = match base.to_u32() {
786+
Some(base_u32) => base_u32,
787+
None => return Err(invalid_literal(vm, literal, base)),
788+
};
789+
781790
// try to find base
782791
if let Some(radix_candidate) = radix_candidate {
783792
if let Some(matched_radix) = detect_base(&radix_candidate) {
784-
if base != 0 && base != matched_radix {
793+
if base_u32 != 0 && base_u32 != matched_radix {
785794
return Err(invalid_literal(vm, literal, base));
786795
} else {
787-
base = matched_radix;
796+
base_u32 = matched_radix;
788797
}
789798

790799
buf.drain(radix_range);
791800
}
792801
}
793802

794803
// base still not found, try to use default
795-
if base == 0 {
804+
if base_u32 == 0 {
796805
if buf.starts_with('0') {
797806
return Err(invalid_literal(vm, literal, base));
798807
}
799808

800-
base = 10;
809+
base_u32 = 10;
801810
}
802811

803-
BigInt::from_str_radix(&buf, base).map_err(|_err| invalid_literal(vm, literal, base))
812+
BigInt::from_str_radix(&buf, base_u32).map_err(|_err| invalid_literal(vm, literal, base))
804813
}
805814

806-
fn validate_literal(vm: &VirtualMachine, literal: &str, base: u32) -> PyResult<String> {
815+
fn validate_literal(vm: &VirtualMachine, literal: &str, base: &BigInt) -> PyResult<String> {
807816
if literal.starts_with('_') || literal.ends_with('_') {
808817
return Err(invalid_literal(vm, literal, base));
809818
}
@@ -835,7 +844,7 @@ fn detect_base(literal: &str) -> Option<u32> {
835844
}
836845
}
837846

838-
fn invalid_literal(vm: &VirtualMachine, literal: &str, base: u32) -> PyObjectRef {
847+
fn invalid_literal(vm: &VirtualMachine, literal: &str, base: &BigInt) -> PyObjectRef {
839848
vm.new_value_error(format!(
840849
"invalid literal for int() with base {}: '{}'",
841850
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<()> {

0 commit comments

Comments
 (0)