in rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java [586:680]
public static double toNumber(String s) {
final int len = s.length();
// Skip whitespace at the start
int start = 0;
char startChar;
for (; ; ) {
if (start == len) {
// empty or contains only whitespace
return +0.0;
}
startChar = s.charAt(start);
if (!ScriptRuntime.isStrWhiteSpaceChar(startChar)) {
// found first non-whitespace character
break;
}
start++;
}
// Skip whitespace at the end
int end = len - 1;
char endChar;
while (ScriptRuntime.isStrWhiteSpaceChar(endChar = s.charAt(end))) {
end--;
}
// Do not break scripts relying on old non-compliant conversion
// (see bug #368)
// 1. makes ToNumber parse only a valid prefix in hex literals (similar to 'parseInt()')
// ToNumber('0x10 something') => 16
// 2. allows plus and minus signs for hexadecimal numbers
// ToNumber('-0x10') => -16
// 3. disables support for binary ('0b10') and octal ('0o13') literals
// ToNumber('0b1') => NaN
// ToNumber('0o5') => NaN
final Context cx = Context.getCurrentContext();
final boolean oldParsingMode = cx == null || cx.getLanguageVersion() < Context.VERSION_ES6;
// Handle non-base10 numbers
if (startChar == '0') {
if (start + 2 <= end) {
final char radixC = s.charAt(start + 1);
int radix = -1;
if (radixC == 'x' || radixC == 'X') {
radix = 16;
} else if (!oldParsingMode && (radixC == 'o' || radixC == 'O')) {
radix = 8;
} else if (!oldParsingMode && (radixC == 'b' || radixC == 'B')) {
radix = 2;
}
if (radix != -1) {
if (oldParsingMode) {
return stringPrefixToNumber(s, start + 2, radix);
}
return stringToNumber(s, start + 2, end, radix);
}
}
} else if (oldParsingMode && (startChar == '+' || startChar == '-')) {
// If in old parsing mode, check for a signed hexadecimal number
if (start + 3 <= end && s.charAt(start + 1) == '0') {
final char radixC = s.charAt(start + 2);
if (radixC == 'x' || radixC == 'X') {
double val = stringPrefixToNumber(s, start + 3, 16);
return startChar == '-' ? -val : val;
}
}
}
if (endChar == 'y') {
// check for "Infinity"
if (startChar == '+' || startChar == '-') {
start++;
}
if (start + 7 == end && s.regionMatches(start, "Infinity", 0, 8)) {
return startChar == '-' ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
}
return NaN;
}
// A base10, non-infinity number:
// just try a normal floating point conversion
String sub = s.substring(start, end + 1);
// Quick test to check string contains only valid characters because
// Double.parseDouble() can be slow and accept input we want to reject
for (int i = sub.length() - 1; i >= 0; i--) {
char c = sub.charAt(i);
if (('0' <= c && c <= '9') || c == '.' || c == 'e' || c == 'E' || c == '+' || c == '-')
continue;
return NaN;
}
try {
return Double.parseDouble(sub);
} catch (NumberFormatException ex) {
return NaN;
}
}