Ignore:
Timestamp:
Aug 18, 2010, 12:41:22 AM (15 years ago)
Author:
[email protected]
Message:

Bug 44146 - Remove toDouble/toUInt32 methods from UString.

Reviewed by Sam Weinig.

JavaScriptCore:

These methods all implement JavaScript language specific behaviour, and as such
are not suited to being on a generic string object. They are also inefficient
and incorrectly used, refactor & cleanup. Uses of these methods really divide
out into two cases.

ToNumber:
Uses of toDouble from JSString and from parseFloat are implementing ecma's
ToNumber conversion from strings (see ecma-262 9.3.1), so UString::toDouble
should largely just be moved out to a global jsToNumber function. ToNumber is
capable of recognizing either decimal or hexadecimal numbers, but parseFloat
should only recognize decimal values. This is currently handled by testing for
hexadecimal before calling toDouble, which should unnecessary - instead we can
just split out the two parts to the grammar into separate functions. Also,
strtod recognizes a set of literals (nan, inf, and infinity - all with any
capitalization) - which are not defined by any of the specs we are implementing.
To handle this we need to perform additional work in toDouble to convert the
unsupported cases of infinities back to NaNs. Instead we should simply remove
support for this literals from strtod. This should provide a more desirable
behaviour for all clients of strtod.

Indexed properties:
Uses of the toStrictUInt32 methods are were all converting property names to
indices, and all uses of toUInt32 were incorrect; in all cases we should have
been calling toUInt32. This error results in some incorrect behaviour in the
DOM (accessing property "0 " of a NodeList should fail; it currently does not).
Move this method onto Identifier (our canonical property name), and make it
always perform a strict conversion. Add a layout test to check NodeList does
convert indexed property names correctly.

(JSC::Arguments::getOwnPropertySlot):
(JSC::Arguments::getOwnPropertyDescriptor):
(JSC::Arguments::put):
(JSC::Arguments::deleteProperty):

  • runtime/Identifier.cpp:

(JSC::Identifier::toUInt32):

  • runtime/Identifier.h:

(JSC::Identifier::toUInt32):

  • runtime/JSArray.cpp:

(JSC::JSArray::getOwnPropertySlot):
(JSC::JSArray::getOwnPropertyDescriptor):
(JSC::JSArray::put):
(JSC::JSArray::deleteProperty):

  • runtime/JSArray.h:

(JSC::Identifier::toArrayIndex):

  • runtime/JSByteArray.cpp:

(JSC::JSByteArray::getOwnPropertySlot):
(JSC::JSByteArray::getOwnPropertyDescriptor):
(JSC::JSByteArray::put):

  • runtime/JSGlobalObjectFunctions.cpp:

(JSC::isInfinity):
(JSC::jsHexIntegerLiteral):
(JSC::jsStrDecimalLiteral):
(JSC::jsToNumber):
(JSC::parseFloat):

  • runtime/JSGlobalObjectFunctions.h:
  • runtime/JSString.cpp:

(JSC::JSString::getPrimitiveNumber):
(JSC::JSString::toNumber):
(JSC::JSString::getStringPropertyDescriptor):

  • runtime/JSString.h:

(JSC::JSString::getStringPropertySlot):

  • runtime/ObjectPrototype.cpp:

(JSC::ObjectPrototype::put):

  • runtime/StringObject.cpp:

(JSC::StringObject::deleteProperty):

  • runtime/UString.cpp:
  • runtime/UString.h:
  • wtf/dtoa.cpp:

(WTF::strtod):

WebCore:

These methods all implement JavaScript language specific behaviour, and as such
are not suited to being on a generic string object. They are also inefficient
and incorrectly used, refactor & cleanup. Uses of these methods really divide
out into two cases.

ToNumber:
Uses of toDouble from JSString and from parseFloat are implementing ecma's
ToNumber conversion from strings (see ecma-262 9.3.1), so UString::toDouble
should largely just be moved out to a global jsToNumber function. ToNumber is
capable of recognizing either decimal or hexadecimal numbers, but parseFloat
should only recognize decimal values. This is currently handled by testing for
hexadecimal before calling toDouble, which should unnecessary - instead we can
just split out the two parts to the grammar into separate functions. Also,
strtod recognizes a set of literals (nan, inf, and infinity - all with any
capitalization) - which are not defined by any of the specs we are implementing.
To handle this we need to perform additional work in toDouble to convert the
unsupported cases of infinities back to NaNs. Instead we should simply remove
support for this literals from strtod. This should provide a more desirable
behaviour for all clients of strtod.

Indexed properties:
Uses of the toStrictUInt32 methods are were all converting property names to
indices, and all uses of toUInt32 were incorrect; in all cases we should have
been calling toUInt32. This error results in some incorrect behaviour in the
DOM (accessing property "0 " of a NodeList should fail; it currently does not).
Move this method onto Identifier (our canonical property name), and make it
always perform a strict conversion. Add a layout test to check NodeList does
convert indexed property names correctly.

Test: fast/dom/NodeList/nodelist-item-with-index.html

  • WebCore.xcodeproj/project.pbxproj:
  • bindings/js/JSDOMWindowCustom.cpp:

(WebCore::JSDOMWindow::getOwnPropertySlot):
(WebCore::JSDOMWindow::getOwnPropertyDescriptor):

  • bindings/js/JSHTMLAllCollectionCustom.cpp:

(WebCore::callHTMLAllCollection):
(WebCore::JSHTMLAllCollection::item):

  • bindings/js/JSHTMLCollectionCustom.cpp:

(WebCore::callHTMLCollection):
(WebCore::JSHTMLCollection::item):

  • bindings/js/JSNodeListCustom.cpp:

(WebCore::callNodeList):

  • bindings/scripts/CodeGeneratorJS.pm:
  • bridge/runtime_array.cpp:

(JSC::RuntimeArray::getOwnPropertySlot):
(JSC::RuntimeArray::getOwnPropertyDescriptor):
(JSC::RuntimeArray::put):

LayoutTests:

Test that indexing into nodelists works correctly, particularly
wrt indices passed as strings that contain whitespace.

  • fast/dom/NodeList/nodelist-item-with-index-expected.txt: Added.
  • fast/dom/NodeList/nodelist-item-with-index.html: Added.
File:
1 edited

Legend:

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

    r65478 r65588  
    205205}
    206206
    207 static inline bool isInfinity(double number)
    208 {
    209     return number == Inf || number == -Inf;
    210 }
    211 
    212 static bool isInfinity(const UChar* data, const UChar* end)
    213 {
    214     return data + 7 < end
    215         && data[0] == 'I'
    216         && data[1] == 'n'
    217         && data[2] == 'f'
    218         && data[3] == 'i'
    219         && data[4] == 'n'
    220         && data[5] == 'i'
    221         && data[6] == 't'
    222         && data[7] == 'y';
    223 }
    224 
    225 double UString::toDouble(bool tolerateTrailingJunk, bool tolerateEmptyString) const
    226 {
    227     unsigned size = this->length();
    228 
    229     if (size == 1) {
    230         UChar c = characters()[0];
    231         if (isASCIIDigit(c))
    232             return c - '0';
    233         if (isStrWhiteSpace(c) && tolerateEmptyString)
    234             return 0;
    235         return NaN;
    236     }
    237 
    238     const UChar* data = this->characters();
    239     const UChar* end = data + size;
    240 
    241     // Skip leading white space.
    242     for (; data < end; ++data) {
    243         if (!isStrWhiteSpace(*data))
    244             break;
    245     }
    246 
    247     // Empty string.
    248     if (data == end)
    249         return tolerateEmptyString ? 0.0 : NaN;
    250 
    251     double number;
    252 
    253     if (data[0] == '0' && data + 2 < end && (data[1] | 0x20) == 'x' && isASCIIHexDigit(data[2])) {
    254         // Hex number.
    255         data += 2;
    256         const UChar* firstDigitPosition = data;
    257         number = 0;
    258         while (true) {
    259             number = number * 16 + toASCIIHexValue(*data);
    260             ++data;
    261             if (data == end)
    262                 break;
    263             if (!isASCIIHexDigit(*data))
    264                 break;
    265         }
    266         if (number >= mantissaOverflowLowerBound)
    267             number = parseIntOverflow(firstDigitPosition, data - firstDigitPosition, 16);
    268     } else {
    269         // Decimal number.
    270 
    271         // Put into a null-terminated byte buffer.
    272         Vector<char, 32> byteBuffer;
    273         for (const UChar* characters = data; characters < end; ++characters) {
    274             UChar character = *characters;
    275             byteBuffer.append(isASCII(character) ? character : 0);
    276         }
    277         byteBuffer.append(0);
    278 
    279         char* byteBufferEnd;
    280         number = WTF::strtod(byteBuffer.data(), &byteBufferEnd);
    281         const UChar* pastNumber = data + (byteBufferEnd - byteBuffer.data());
    282 
    283         if ((number || pastNumber != data) && !isInfinity(number))
    284             data = pastNumber;
    285         else {
    286             // We used strtod() to do the conversion. However, strtod() handles
    287             // infinite values slightly differently than JavaScript in that it
    288             // converts the string "inf" with any capitalization to infinity,
    289             // whereas the ECMA spec requires that it be converted to NaN.
    290 
    291             double signedInfinity = Inf;
    292             if (data < end) {
    293                 if (*data == '+')
    294                     data++;
    295                 else if (*data == '-') {
    296                     signedInfinity = -Inf;
    297                     data++;
    298                 }
    299             }
    300             if (isInfinity(data, end)) {
    301                 number = signedInfinity;
    302                 data += 8;
    303             } else if (isInfinity(number) && data < end && (*data | 0x20) != 'i')
    304                 data = pastNumber;
    305             else
    306                 return NaN;
    307         }
    308     }
    309 
    310     // Look for trailing junk.
    311     if (!tolerateTrailingJunk) {
    312         // Allow trailing white space.
    313         for (; data < end; ++data) {
    314             if (!isStrWhiteSpace(*data))
    315                 break;
    316         }
    317         if (data != end)
    318             return NaN;
    319     }
    320 
    321     return number;
    322 }
    323 
    324 double UString::toDouble(bool tolerateTrailingJunk) const
    325 {
    326     return toDouble(tolerateTrailingJunk, true);
    327 }
    328 
    329 double UString::toDouble() const
    330 {
    331     return toDouble(false, true);
    332 }
    333 
    334 uint32_t UString::toUInt32(bool* ok) const
    335 {
    336     double d = toDouble();
    337     bool b = true;
    338 
    339     if (d != static_cast<uint32_t>(d)) {
    340         b = false;
    341         d = 0;
    342     }
    343 
    344     if (ok)
    345         *ok = b;
    346 
    347     return static_cast<uint32_t>(d);
    348 }
    349 
    350 uint32_t UString::toUInt32(bool* ok, bool tolerateEmptyString) const
    351 {
    352     double d = toDouble(false, tolerateEmptyString);
    353     bool b = true;
    354 
    355     if (d != static_cast<uint32_t>(d)) {
    356         b = false;
    357         d = 0;
    358     }
    359 
    360     if (ok)
    361         *ok = b;
    362 
    363     return static_cast<uint32_t>(d);
    364 }
    365 
    366 uint32_t UString::toStrictUInt32(bool* ok) const
    367 {
    368     if (ok)
    369         *ok = false;
    370 
    371     // Empty string is not OK.
    372     unsigned len = m_impl->length();
    373     if (len == 0)
    374         return 0;
    375     const UChar* p = m_impl->characters();
    376     unsigned short c = p[0];
    377 
    378     // If the first digit is 0, only 0 itself is OK.
    379     if (c == '0') {
    380         if (len == 1 && ok)
    381             *ok = true;
    382         return 0;
    383     }
    384 
    385     // Convert to UInt32, checking for overflow.
    386     uint32_t i = 0;
    387     while (1) {
    388         // Process character, turning it into a digit.
    389         if (c < '0' || c > '9')
    390             return 0;
    391         const unsigned d = c - '0';
    392 
    393         // Multiply by 10, checking for overflow out of 32 bits.
    394         if (i > 0xFFFFFFFFU / 10)
    395             return 0;
    396         i *= 10;
    397 
    398         // Add in the digit, checking for overflow out of 32 bits.
    399         const unsigned max = 0xFFFFFFFFU - d;
    400         if (i > max)
    401             return 0;
    402         i += d;
    403 
    404         // Handle end of string.
    405         if (--len == 0) {
    406             if (ok)
    407                 *ok = true;
    408             return i;
    409         }
    410 
    411         // Get next character.
    412         c = *(++p);
    413     }
    414 }
    415 
    416207UString UString::substr(unsigned pos, unsigned len) const
    417208{
Note: See TracChangeset for help on using the changeset viewer.