Skip to content

Commit 1d86695

Browse files
authored
fix: Spec compliant parseInt (#1378)
1 parent 4fd9c29 commit 1d86695

File tree

5 files changed

+5164
-4279
lines changed

5 files changed

+5164
-4279
lines changed

std/assembly/number.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ export abstract class F32 {
295295
}
296296

297297
static parseInt(value: string, radix: i32 = 0): f32 {
298-
return <f32>strtol<i64>(value, radix);
298+
return <f32>strtol<f64>(value, radix);
299299
}
300300

301301
static parseFloat(value: string): f32 {
@@ -359,7 +359,7 @@ export abstract class F64 {
359359
}
360360

361361
static parseInt(value: string, radix: i32 = 0): f64 {
362-
return <f64>strtol<i64>(value, radix);
362+
return strtol<f64>(value, radix);
363363
}
364364

365365
static parseFloat(value: string): f64 {

std/assembly/util/string.ts

Lines changed: 62 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -652,47 +652,68 @@ export function toUpper8(c: u32): u32 {
652652
export function strtol<T>(str: string, radix: i32 = 0): T {
653653
var len = str.length;
654654
if (!len) {
655-
// @ts-ignore: cast
656-
if (isFloat<T>()) return <T>NaN;
657-
// @ts-ignore: cast
658-
return <T>0;
655+
if (isFloat<T>()) {
656+
// @ts-ignore: cast
657+
return <T>NaN;
658+
} else {
659+
// @ts-ignore: cast
660+
return <T>0;
661+
}
659662
}
660663

661664
var ptr = changetype<usize>(str) /* + HEAD -> offset */;
662665
var code = <u32>load<u16>(ptr);
663666

664-
// determine sign
665-
// @ts-ignore: cast
666-
var sign: T = 1;
667667
// trim white spaces
668668
while (isSpace(code)) {
669669
code = <u32>load<u16>(ptr += 2);
670670
--len;
671671
}
672-
if (code == CharCode.MINUS) {
672+
// determine sign
673+
// @ts-ignore
674+
var sign: T = 1;
675+
if (code == CharCode.MINUS || code == CharCode.PLUS) {
673676
if (!--len) {
674-
// @ts-ignore: cast
675-
if (isFloat<T>()) return <T>NaN;
676-
// @ts-ignore: cast
677-
return <T>0;
677+
if (isFloat<T>()) {
678+
// @ts-ignore: cast
679+
return <T>NaN;
680+
} else {
681+
// @ts-ignore: cast
682+
return <T>0;
683+
}
678684
}
679-
code = <u32>load<u16>(ptr += 2);
680-
// @ts-ignore: type
681-
sign = -1;
682-
} else if (code == CharCode.PLUS) {
683-
if (!--len) {
684-
// @ts-ignore: cast
685-
if (isFloat<T>()) return <T>NaN;
686-
// @ts-ignore: cast
687-
return <T>0;
685+
if (code == CharCode.MINUS) {
686+
// @ts-ignore: type
687+
sign = -1;
688688
}
689689
code = <u32>load<u16>(ptr += 2);
690690
}
691691

692-
// determine radix
693-
if (!radix) {
692+
// See https://tc39.es/ecma262/#sec-parseint-string-radix
693+
if (radix) {
694+
if (radix < 2 || radix > 36) {
695+
if (isFloat<T>()) {
696+
// @ts-ignore: cast
697+
return <T>NaN;
698+
} else {
699+
// @ts-ignore: cast
700+
return <T>0;
701+
}
702+
}
703+
// handle case as parseInt("0xFF", 16) by spec
704+
if (radix == 16) {
705+
if (
706+
len > 2 &&
707+
code == CharCode._0 &&
708+
(<u32>load<u16>(ptr, 2) | 32) == CharCode.x
709+
) {
710+
ptr += 4; len -= 2;
711+
}
712+
}
713+
} else {
714+
// determine radix by literal prefix
694715
if (code == CharCode._0 && len > 2) {
695-
switch (<u32>load<u16>(ptr + 2) | 32) {
716+
switch (<u32>load<u16>(ptr, 2) | 32) {
696717
case CharCode.b: {
697718
ptr += 4; len -= 2;
698719
radix = 2;
@@ -708,14 +729,9 @@ export function strtol<T>(str: string, radix: i32 = 0): T {
708729
radix = 16;
709730
break;
710731
}
711-
default: radix = 10;
712732
}
713-
} else radix = 10;
714-
} else if (radix < 2 || radix > 36) {
715-
// @ts-ignore: cast
716-
if (isFloat<T>()) return <T>NaN;
717-
// @ts-ignore: cast
718-
return <T>0;
733+
}
734+
if (!radix) radix = 10;
719735
}
720736

721737
// calculate value
@@ -729,8 +745,19 @@ export function strtol<T>(str: string, radix: i32 = 0): T {
729745
code -= CharCode.A - 10;
730746
} else if (code - CharCode.a <= <u32>(CharCode.z - CharCode.a)) {
731747
code -= CharCode.a - 10;
732-
} else break;
733-
if (code >= <u32>radix) break;
748+
}
749+
if (code >= <u32>radix) {
750+
if (!num) {
751+
if (isFloat<T>()) {
752+
// @ts-ignore: cast
753+
return <T>NaN;
754+
} else {
755+
// @ts-ignore: cast
756+
return <T>0;
757+
}
758+
}
759+
break;
760+
}
734761
// @ts-ignore: type
735762
num = num * radix + code;
736763
ptr += 2;
@@ -746,7 +773,7 @@ export function strtod(str: string): f64 {
746773
var ptr = changetype<usize>(str);
747774
var code = <u32>load<u16>(ptr);
748775

749-
var sign = 1.;
776+
var sign = 1.0;
750777
// skip white spaces
751778
while (len && isSpace(code)) {
752779
code = <u32>load<u16>(ptr += 2);
@@ -770,7 +797,7 @@ export function strtod(str: string): f64 {
770797
load<u64>(ptr, 0) == 0x690066006E0049 && // ifnI
771798
load<u64>(ptr, 8) == 0x7900740069006E // ytin
772799
) {
773-
return copysign<f64>(Infinity, sign);
800+
return Infinity * sign;
774801
}
775802
return NaN;
776803
}

0 commit comments

Comments
 (0)