public static double toNumber()

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;
        }
    }