Index: sdk/lib/_internal/compiler/js_lib/js_helper.dart |
diff --git a/sdk/lib/_internal/compiler/js_lib/js_helper.dart b/sdk/lib/_internal/compiler/js_lib/js_helper.dart |
index 89766d12b75c77824bc30d72c8a15331ff6f9f2d..6563192f335b05b7490c4d059f36699e8eb8b360 100644 |
--- a/sdk/lib/_internal/compiler/js_lib/js_helper.dart |
+++ b/sdk/lib/_internal/compiler/js_lib/js_helper.dart |
@@ -586,82 +586,81 @@ class Primitives { |
return JS('int', '#', hash); |
} |
- static _throwFormatException(String string) { |
- throw new FormatException(string); |
+ @NoInline() |
+ static int _parseError(String source, int handleError(String source)) { |
+ if (handleError == null) throw new FormatException(source); |
+ return handleError(source); |
} |
static int parseInt(String source, |
int radix, |
int handleError(String source)) { |
- if (handleError == null) handleError = _throwFormatException; |
- |
checkString(source); |
- var match = JS('JSExtendableArray|Null', |
- r'/^\s*[+-]?((0x[a-f0-9]+)|(\d+)|([a-z0-9]+))\s*$/i.exec(#)', |
- source); |
+ var re = JS('', r'/^\s*[+-]?((0x[a-f0-9]+)|(\d+)|([a-z0-9]+))\s*$/i'); |
+ var match = JS('JSExtendableArray|Null', '#.exec(#)', re, source); |
int digitsIndex = 1; |
int hexIndex = 2; |
int decimalIndex = 3; |
int nonDecimalHexIndex = 4; |
+ if (match == null) { |
+ // TODO(sra): It might be that the match failed due to unrecognized U+0085 |
+ // spaces. We could replace them with U+0020 spaces and try matching |
+ // again. |
Lasse Reichstein Nielsen
2015/03/06 13:06:51
If \s doesn't always include U+0085, maybe change
sra1
2015/03/06 21:38:38
In that case, JavaScript's parseInt fails too.
|
+ return _parseError(source, handleError); |
+ } |
if (radix == null) { |
- radix = 10; |
- if (match != null) { |
- if (match[hexIndex] != null) { |
- // Cannot fail because we know that the digits are all hex. |
- return JS('num', r'parseInt(#, 16)', source); |
- } |
- if (match[decimalIndex] != null) { |
- // Cannot fail because we know that the digits are all decimal. |
- return JS('num', r'parseInt(#, 10)', source); |
- } |
- return handleError(source); |
+ if (match[decimalIndex] != null) { |
+ // Cannot fail because we know that the digits are all decimal. |
+ return JS('int', r'parseInt(#, 10)', source); |
} |
- } else { |
- if (radix is! int) throw new ArgumentError("Radix is not an integer"); |
- if (radix < 2 || radix > 36) { |
- throw new RangeError("Radix $radix not in range 2..36"); |
+ if (match[hexIndex] != null) { |
+ // Cannot fail because we know that the digits are all hex. |
+ return JS('int', r'parseInt(#, 16)', source); |
} |
- if (match != null) { |
- if (radix == 10 && match[decimalIndex] != null) { |
- // Cannot fail because we know that the digits are all decimal. |
- return JS('num', r'parseInt(#, 10)', source); |
- } |
- if (radix < 10 || match[decimalIndex] == null) { |
- // We know that the characters must be ASCII as otherwise the |
- // regexp wouldn't have matched. Lowercasing by doing `| 0x20` is thus |
- // guaranteed to be a safe operation, since it preserves digits |
- // and lower-cases ASCII letters. |
- int maxCharCode; |
- if (radix <= 10) { |
- // Allow all digits less than the radix. For example 0, 1, 2 for |
- // radix 3. |
- // "0".codeUnitAt(0) + radix - 1; |
- maxCharCode = 0x30 + radix - 1; |
- } else { |
- // Letters are located after the digits in ASCII. Therefore we |
- // only check for the character code. The regexp above made already |
- // sure that the string does not contain anything but digits or |
- // letters. |
- // "a".codeUnitAt(0) + (radix - 10) - 1; |
- maxCharCode = 0x61 + radix - 10 - 1; |
- } |
- String digitsPart = match[digitsIndex]; |
- for (int i = 0; i < digitsPart.length; i++) { |
- int characterCode = digitsPart.codeUnitAt(0) | 0x20; |
- if (digitsPart.codeUnitAt(i) > maxCharCode) { |
- return handleError(source); |
- } |
- } |
+ return _parseError(source, handleError); |
+ } |
+ |
+ if (radix is! int) throw new ArgumentError("Radix is not an integer"); |
+ if (radix < 2 || radix > 36) { |
+ throw new RangeError("Radix $radix not in range 2..36"); |
Lasse Reichstein Nielsen
2015/03/06 13:06:51
You can change this one to:
throw new RangeError
sra1
2015/03/06 21:38:39
Done.
|
+ } |
+ if (radix == 10 && match[decimalIndex] != null) { |
+ // Cannot fail because we know that the digits are all decimal. |
+ return JS('int', r'parseInt(#, 10)', source); |
+ } |
+ if (radix < 10 || match[decimalIndex] == null) { |
+ // We know that the characters must be ASCII as otherwise the |
+ // regexp wouldn't have matched. Lowercasing by doing `| 0x20` is thus |
+ // guaranteed to be a safe operation, since it preserves digits |
+ // and lower-cases ASCII letters. |
Lasse Reichstein Nielsen
2015/03/06 13:06:51
How about doing parseInt first, and only scanning
sra1
2015/03/06 21:38:38
Another idea is to have a table of per-radix regex
|
+ int maxCharCode; |
+ if (radix <= 10) { |
+ // Allow all digits less than the radix. For example 0, 1, 2 for |
+ // radix 3. |
+ // "0".codeUnitAt(0) + radix - 1; |
+ maxCharCode = 0x30 + radix - 1; |
Lasse Reichstein Nielsen
2015/03/06 13:06:51
Maybe: 0x30 - 1 + radix
to constant fold (0x30-1)
sra1
2015/03/06 21:38:38
Done.
|
+ } else { |
+ // Letters are located after the digits in ASCII. Therefore we |
+ // only check for the character code. The regexp above made already |
+ // sure that the string does not contain anything but digits or |
+ // letters. |
+ // "a".codeUnitAt(0) + (radix - 10) - 1; |
+ maxCharCode = 0x61 + radix - 10 - 1; |
Lasse Reichstein Nielsen
2015/03/06 13:06:51
Similar constant folding possible.
sra1
2015/03/06 21:38:38
Done.
|
+ } |
+ assert(match[digitsIndex] is String); |
+ String digitsPart = JS('String', '#[#]', match, digitsIndex); |
+ for (int i = 0; i < digitsPart.length; i++) { |
+ int characterCode = digitsPart.codeUnitAt(i) | 0x20; |
+ if (characterCode > maxCharCode) { |
+ return _parseError(source, handleError); |
} |
} |
} |
- if (match == null) return handleError(source); |
- return JS('num', r'parseInt(#, #)', source, radix); |
+ return JS('int', r'parseInt(#, #)', source, radix); |
Lasse Reichstein Nielsen
2015/03/06 13:06:51
Make a comment that the checks above guarantee tha
sra1
2015/03/06 21:38:39
Done.
|
} |
static double parseDouble(String source, double handleError(String source)) { |
checkString(source); |
- if (handleError == null) handleError = _throwFormatException; |
// Notice that JS parseFloat accepts garbage at the end of the string. |
// Accept only: |
// - [+/-]NaN |
@@ -672,7 +671,7 @@ class Primitives { |
r'/^\s*[+-]?(?:Infinity|NaN|' |
r'(?:\.\d+|\d+(?:\.\d*)?)(?:[eE][+-]?\d+)?)\s*$/.test(#)', |
source)) { |
- return handleError(source); |
+ return _parseError(source, handleError); |
} |
var result = JS('num', r'parseFloat(#)', source); |
if (result.isNaN) { |
@@ -680,7 +679,7 @@ class Primitives { |
if (trimmed == 'NaN' || trimmed == '+NaN' || trimmed == '-NaN') { |
return result; |
} |
- return handleError(source); |
+ return _parseError(source, handleError); |
} |
return result; |
} |