Ignore:
Timestamp:
Jul 12, 2010, 2:32:34 PM (15 years ago)
Author:
Darin Adler
Message:

2010-07-09 Darin Adler <Darin Adler>

Reviewed by Geoffrey Garen.

String to number coercion is not spec compliant
https://bugs.webkit.org/show_bug.cgi?id=31349

ToNumber should ignore NBSP (\u00a0)
https://bugs.webkit.org/show_bug.cgi?id=25490

  • runtime/JSGlobalObjectFunctions.cpp: (JSC::parseIntOverflow): Added a version that works on UChar.
  • runtime/JSGlobalObjectFunctions.h: Ditto.
  • runtime/UString.cpp: (JSC::isInfinity): Added helper functions. (JSC::UString::toDouble): Use isStrWhiteSpace instead of isSASCIISpace to define what we should skip. Got rid of the code that used CString and UTF8String, instead processing the UChar of the string directly, except for when we call strtod. For strtod, use our own home-grown conversion function that does not try to do any UTF-16 processing. Tidied up the logic a bit as well.

2010-07-09 Darin Adler <Darin Adler>

Reviewed by Geoffrey Garen.

String to number coercion is not spec compliant
https://bugs.webkit.org/show_bug.cgi?id=31349

  • fast/js/ToNumber-expected.txt: Updated to expect more tests to pass.
  • fast/js/parseFloat-expected.txt: Ditto.
  • fast/js/sputnik/Conformance/09_Type_Conversion/9.3_ToNumber/9.3.1_ToNumber_from_String/S9.3.1_A2-expected.txt: Ditto.
  • fast/js/sputnik/Conformance/09_Type_Conversion/9.3_ToNumber/9.3.1_ToNumber_from_String/S9.3.1_A3_T1-expected.txt: Ditto.
  • fast/js/sputnik/Conformance/09_Type_Conversion/9.3_ToNumber/9.3.1_ToNumber_from_String/S9.3.1_A3_T2-expected.txt: Ditto.
  • fast/js/sputnik/Conformance/15_Native_Objects/15.1_The_Global_Object/15.1.2/15.1.2.3_parseFloat/S15.1.2.3_A2_T10-expected.txt: Ditto.
  • fast/js/sputnik/Conformance/15_Native_Objects/15.1_The_Global_Object/15.1.2/15.1.2.3_parseFloat/S15.1.2.3_A2_T3-expected.txt: Ditto.
  • fast/js/sputnik/Conformance/15_Native_Objects/15.1_The_Global_Object/15.1.2/15.1.2.3_parseFloat/S15.1.2.3_A2_T8-expected.txt: Ditto.
  • fast/js/sputnik/Conformance/15_Native_Objects/15.1_The_Global_Object/15.1.2/15.1.2.3_parseFloat/S15.1.2.3_A2_T9-expected.txt: Ditto.
  • fast/js/sputnik/Conformance/15_Native_Objects/15.1_The_Global_Object/15.1.2/15.1.2.3_parseFloat/S15.1.2.3_A6-expected.txt: Ditto.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/runtime/UString.cpp

    r62457 r63120  
    243243}
    244244
     245static inline bool isInfinity(double number)
     246{
     247    return number == Inf || number == -Inf;
     248}
     249
     250static bool isInfinity(const UChar* data, const UChar* end)
     251{
     252    return data + 7 < end
     253        && data[0] == 'I'
     254        && data[1] == 'n'
     255        && data[2] == 'f'
     256        && data[3] == 'i'
     257        && data[4] == 'n'
     258        && data[5] == 'i'
     259        && data[6] == 't'
     260        && data[7] == 'y';
     261}
     262
    245263double UString::toDouble(bool tolerateTrailingJunk, bool tolerateEmptyString) const
    246264{
    247     if (size() == 1) {
     265    unsigned size = this->size();
     266
     267    if (size == 1) {
    248268        UChar c = data()[0];
    249269        if (isASCIIDigit(c))
    250270            return c - '0';
    251         if (isASCIISpace(c) && tolerateEmptyString)
     271        if (isStrWhiteSpace(c) && tolerateEmptyString)
    252272            return 0;
    253273        return NaN;
     
    265285    // right thing but requires UChar, not char, for its argument.
    266286
    267     CString s = UTF8String();
    268     if (s.isNull())
    269         return NaN;
    270     const char* c = s.data();
    271 
    272     // skip leading white space
    273     while (isASCIISpace(*c))
    274         c++;
    275 
    276     // empty string ?
    277     if (*c == '\0')
     287    const UChar* data = this->data();
     288    const UChar* end = data + size;
     289
     290    // Skip leading white space.
     291    for (; data < end; ++data) {
     292        if (!isStrWhiteSpace(*data))
     293            break;
     294    }
     295
     296    // Empty string.
     297    if (data == end)
    278298        return tolerateEmptyString ? 0.0 : NaN;
    279299
    280     double d;
    281 
    282     // hex number ?
    283     if (*c == '0' && (*(c + 1) == 'x' || *(c + 1) == 'X')) {
    284         const char* firstDigitPosition = c + 2;
    285         c++;
    286         d = 0.0;
    287         while (*(++c)) {
    288             if (*c >= '0' && *c <= '9')
    289                 d = d * 16.0 + *c - '0';
    290             else if ((*c >= 'A' && *c <= 'F') || (*c >= 'a' && *c <= 'f'))
    291                 d = d * 16.0 + (*c & 0xdf) - 'A' + 10.0;
    292             else
     300    double number;
     301
     302    if (data[0] == '0' && data + 2 < end && (data[1] | 0x20) == 'x' && isASCIIHexDigit(data[2])) {
     303        // Hex number.
     304        data += 2;
     305        const UChar* firstDigitPosition = data;
     306        number = 0;
     307        while (true) {
     308            number = number * 16 + toASCIIHexValue(*data);
     309            ++data;
     310            if (data == end)
    293311                break;
    294         }
    295 
    296         if (d >= mantissaOverflowLowerBound)
    297             d = parseIntOverflow(firstDigitPosition, c - firstDigitPosition, 16);
     312            if (!isASCIIHexDigit(*data))
     313                break;
     314        }
     315        if (number >= mantissaOverflowLowerBound)
     316            number = parseIntOverflow(firstDigitPosition, data - firstDigitPosition, 16);
    298317    } else {
    299         // regular number ?
    300         char* end;
    301         d = WTF::strtod(c, &end);
    302         if ((d != 0.0 || end != c) && d != Inf && d != -Inf) {
    303             c = end;
    304         } else {
    305             double sign = 1.0;
    306 
    307             if (*c == '+')
    308                 c++;
    309             else if (*c == '-') {
    310                 sign = -1.0;
    311                 c++;
    312             }
    313 
     318        // Decimal number.
     319
     320        // Put into a null-terminated byte buffer.
     321        Vector<char, 32> byteBuffer;
     322        for (const UChar* characters = data; characters < end; ++characters) {
     323            UChar character = *characters;
     324            byteBuffer.append(isASCII(character) ? character : 0);
     325        }
     326        byteBuffer.append(0);
     327
     328        char* byteBufferEnd;
     329        number = WTF::strtod(byteBuffer.data(), &byteBufferEnd);
     330        const UChar* pastNumber = data + (byteBufferEnd - byteBuffer.data());
     331
     332        if ((number || pastNumber != data) && !isInfinity(number))
     333            data = pastNumber;
     334        else {
    314335            // We used strtod() to do the conversion. However, strtod() handles
    315336            // infinite values slightly differently than JavaScript in that it
     
    317338            // whereas the ECMA spec requires that it be converted to NaN.
    318339
    319             if (c[0] == 'I' && c[1] == 'n' && c[2] == 'f' && c[3] == 'i' && c[4] == 'n' && c[5] == 'i' && c[6] == 't' && c[7] == 'y') {
    320                 d = sign * Inf;
    321                 c += 8;
    322             } else if ((d == Inf || d == -Inf) && *c != 'I' && *c != 'i')
    323                 c = end;
     340            double signedInfinity = Inf;
     341            if (data < end) {
     342                if (*data == '+')
     343                    data++;
     344                else if (*data == '-') {
     345                    signedInfinity = -Inf;
     346                    data++;
     347                }
     348            }
     349            if (isInfinity(data, end)) {
     350                number = signedInfinity;
     351                data += 8;
     352            } else if (isInfinity(number) && data < end && (*data | 0x20) != 'i')
     353                data = pastNumber;
    324354            else
    325355                return NaN;
     
    327357    }
    328358
     359    // Look for trailing junk.
    329360    if (!tolerateTrailingJunk) {
    330         // allow trailing white space
    331         while (isASCIISpace(*c))
    332             c++;
    333         if (c != s.data() + s.length())
    334             d = NaN;
    335     }
    336 
    337     return d;
     361        // Allow trailing white space.
     362        for (; data < end; ++data) {
     363            if (!isStrWhiteSpace(*data))
     364                break;
     365        }
     366        if (data != end)
     367            return NaN;
     368    }
     369
     370    return number;
    338371}
    339372
Note: See TracChangeset for help on using the changeset viewer.