Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions src/fable-library-py/src/ints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1067,9 +1067,9 @@ fn determine_radix(string: &str, style: i32, default_radix: i32) -> i32 {
// `get` returns None when the string is shorter than two bytes or the
// boundary falls inside a multi-byte character, so this never panics
match string.get(..2) {
Some("0x") | Some("0X") => 16,
Some("0b") | Some("0B") => 2,
Some("0o") | Some("0O") => 8,
Some("0x" | "0X") => 16,
Some("0b" | "0B") => 2,
Some("0o" | "0O") => 8,
_ => default_radix,
}
}
Expand All @@ -1091,15 +1091,15 @@ fn trim_whitespace(string: &str, style: i32) -> &str {
/// Removes numeric prefixes (0x, 0b, 0o) when radix is not decimal
#[inline]
fn remove_prefix(string: &str, radix: i32) -> &str {
if radix != 10 {
match string.get(..2) {
Some("0x") | Some("0X") | Some("0b") | Some("0B") | Some("0o") | Some("0O") => {
&string[2..]
}
_ => string,
}
} else {
string
if radix == 10 {
return string;
}

// `get` never panics on a multi-byte boundary; `&string[2..]` is only
// reached after matching a two-byte ASCII prefix, so byte 2 is a boundary
match string.get(..2) {
Some("0x" | "0X" | "0b" | "0B" | "0o" | "0O") => &string[2..],
_ => string,
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/fable-library-py/tests/test_ints.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ def test_uint64_create() -> None:
assert uint64(uint32(-1)) == 4294967295


# NumberStyles.Any (511) has no AllowHexSpecifier, so it reaches the prefix
# check in `determine_radix`. NumberStyles.HexNumber (515) short-circuits to
# radix 16 and reaches the prefix check in `remove_prefix` instead. Both used
# to panic on a multi-byte first character.
@pytest.mark.parametrize(
"style",
[
Expand Down
6 changes: 6 additions & 0 deletions tests/Js/Main/ConvertTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ let tests =
(fun () -> Int32.Parse("1FFFFFFFF", System.Globalization.NumberStyles.HexNumber)) |> throwsError ""
(fun () -> Int32.Parse("5foo", System.Globalization.NumberStyles.HexNumber)) |> throwsError ""
(fun () -> Int32.Parse("foo5", System.Globalization.NumberStyles.HexNumber)) |> throwsError ""
// Multi-byte UTF-8 input must fail to parse, not crash (see #4823)
(fun () -> Int32.Parse("–", System.Globalization.NumberStyles.HexNumber)) |> throwsError ""

testCase "System.Int64.Parse works" <| fun () ->
Int64.Parse("5") |> equal 5L
Expand Down Expand Up @@ -207,6 +209,10 @@ let tests =
tryParse Int32.TryParse 0 "X9TRE34" |> equal (false, 0)
tryParse Int32.TryParse 0 "9SayWhat12Huh" |> equal (false, 0)
tryParse Int32.TryParse 0 "-1" |> equal (true, -1)
// Multi-byte UTF-8 input must fail to parse, not crash (see #4823)
tryParse Int32.TryParse 0 "–" |> equal (false, 0)
tryParse Int32.TryParse 0 "é" |> equal (false, 0)
tryParse Int32.TryParse 0 "𝟙" |> equal (false, 0)

testCase "BigInt.TryParse works" <| fun () ->
tryParse bigint.TryParse 0I "4234523548923954" |> equal (true, 4234523548923954I)
Expand Down
6 changes: 6 additions & 0 deletions tests/Python/TestConvert.fs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ let ``test System.Int32.Parse with hex works`` () =
(fun () -> Int32.Parse("1FFFFFFFF", System.Globalization.NumberStyles.HexNumber)) |> throwsError ""
(fun () -> Int32.Parse("5foo", System.Globalization.NumberStyles.HexNumber)) |> throwsError ""
(fun () -> Int32.Parse("foo5", System.Globalization.NumberStyles.HexNumber)) |> throwsError ""
// Multi-byte UTF-8 input must fail to parse, not crash (see #4823)
(fun () -> Int32.Parse("–", System.Globalization.NumberStyles.HexNumber)) |> throwsError ""

[<Fact>]
let ``test System.Int64.Parse works`` () =
Expand Down Expand Up @@ -214,6 +216,10 @@ let ``test System.Int32.TryParse works`` () =
tryParse Int32.TryParse 0 "X9TRE34" |> equal (false, 0)
tryParse Int32.TryParse 0 "9SayWhat12Huh" |> equal (false, 0)
tryParse Int32.TryParse 0 "-1" |> equal (true, -1)
// Multi-byte UTF-8 input must fail to parse, not crash (see #4823)
tryParse Int32.TryParse 0 "–" |> equal (false, 0)
tryParse Int32.TryParse 0 "é" |> equal (false, 0)
tryParse Int32.TryParse 0 "𝟙" |> equal (false, 0)

[<Fact>]
let ``test BigInt.TryParse works`` () =
Expand Down
Loading