Changeset 65588 in webkit for trunk/JavaScriptCore


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.
Location:
trunk/JavaScriptCore
Files:
18 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/ChangeLog

    r65571 r65588  
     12010-08-17  Gavin Barraclough  <[email protected]>
     2
     3        Reviewed by Sam Weinig.
     4
     5        Bug 44146 - Remove toDouble/toUInt32 methods from UString.
     6
     7        These methods all implement JavaScript language specific behaviour, and as such
     8        are not suited to being on a generic string object.  They are also inefficient
     9        and incorrectly used, refactor & cleanup.  Uses of these methods really divide
     10        out into two cases.
     11
     12        ToNumber:
     13        Uses of toDouble from JSString and from parseFloat are implementing ecma's
     14        ToNumber conversion from strings (see ecma-262 9.3.1), so UString::toDouble
     15        should largely just be moved out to a global jsToNumber function.  ToNumber is
     16        capable of recognizing either decimal or hexadecimal numbers, but parseFloat
     17        should only recognize decimal values.  This is currently handled by testing for
     18        hexadecimal before calling toDouble, which should unnecessary - instead we can
     19        just split out the two parts to the grammar into separate functions. Also,
     20        strtod recognizes a set of literals (nan, inf, and infinity - all with any
     21        capitalization) - which are not defined by any of the specs we are implementing.
     22        To handle this we need to perform additional work in toDouble to convert the
     23        unsupported cases of infinities back to NaNs.  Instead we should simply remove
     24        support for this literals from strtod.  This should provide a more desirable
     25        behaviour for all clients of strtod.
     26
     27        Indexed properties:
     28        Uses of the toStrictUInt32 methods are were all converting property names to
     29        indices, and all uses of toUInt32 were incorrect; in all cases we should have
     30        been calling toUInt32.  This error results in some incorrect behaviour in the
     31        DOM (accessing property "0 " of a NodeList should fail; it currently does not).
     32        Move this method onto Identifier (our canonical property name), and make it
     33        always perform a strict conversion. Add a layout test to check NodeList does
     34        convert indexed property names correctly.
     35
     36        * JavaScriptCore.exp:
     37        * runtime/Arguments.cpp:
     38        (JSC::Arguments::getOwnPropertySlot):
     39        (JSC::Arguments::getOwnPropertyDescriptor):
     40        (JSC::Arguments::put):
     41        (JSC::Arguments::deleteProperty):
     42        * runtime/Identifier.cpp:
     43        (JSC::Identifier::toUInt32):
     44        * runtime/Identifier.h:
     45        (JSC::Identifier::toUInt32):
     46        * runtime/JSArray.cpp:
     47        (JSC::JSArray::getOwnPropertySlot):
     48        (JSC::JSArray::getOwnPropertyDescriptor):
     49        (JSC::JSArray::put):
     50        (JSC::JSArray::deleteProperty):
     51        * runtime/JSArray.h:
     52        (JSC::Identifier::toArrayIndex):
     53        * runtime/JSByteArray.cpp:
     54        (JSC::JSByteArray::getOwnPropertySlot):
     55        (JSC::JSByteArray::getOwnPropertyDescriptor):
     56        (JSC::JSByteArray::put):
     57        * runtime/JSGlobalObjectFunctions.cpp:
     58        (JSC::isInfinity):
     59        (JSC::jsHexIntegerLiteral):
     60        (JSC::jsStrDecimalLiteral):
     61        (JSC::jsToNumber):
     62        (JSC::parseFloat):
     63        * runtime/JSGlobalObjectFunctions.h:
     64        * runtime/JSString.cpp:
     65        (JSC::JSString::getPrimitiveNumber):
     66        (JSC::JSString::toNumber):
     67        (JSC::JSString::getStringPropertyDescriptor):
     68        * runtime/JSString.h:
     69        (JSC::JSString::getStringPropertySlot):
     70        * runtime/ObjectPrototype.cpp:
     71        (JSC::ObjectPrototype::put):
     72        * runtime/StringObject.cpp:
     73        (JSC::StringObject::deleteProperty):
     74        * runtime/UString.cpp:
     75        * runtime/UString.h:
     76        * wtf/dtoa.cpp:
     77        (WTF::strtod):
     78
    1792010-08-17  Gavin Barraclough  <[email protected]>
    280
  • trunk/JavaScriptCore/JavaScriptCore.exp

    r65468 r65588  
    108108__ZN3JSC10Identifier4fromEPNS_9ExecStateEj
    109109__ZN3JSC10Identifier5equalEPKN3WTF10StringImplEPKc
     110__ZN3JSC10Identifier8toUInt32ERKNS_7UStringERb
    110111__ZN3JSC10JSFunction4infoE
    111112__ZN3JSC10JSFunction4nameEPNS_9ExecStateE
     
    516517__ZNK3JSC7JSValue20toThisObjectSlowCaseEPNS_9ExecStateE
    517518__ZNK3JSC7JSValue9toIntegerEPNS_9ExecStateE
    518 __ZNK3JSC7UString14toStrictUInt32EPb
    519519__ZNK3JSC7UString4utf8Eb
    520520__ZNK3JSC7UString5asciiEv
    521521__ZNK3JSC7UString6substrEjj
    522 __ZNK3JSC7UString8toUInt32EPb
    523 __ZNK3JSC7UString8toUInt32EPbb
    524522__ZNK3JSC8JSObject11hasPropertyEPNS_9ExecStateERKNS_10IdentifierE
    525523__ZNK3JSC8JSObject11hasPropertyEPNS_9ExecStateEj
  • trunk/JavaScriptCore/jit/JITStubs.cpp

    r65311 r65588  
    4646#include "JSByteArray.h"
    4747#include "JSFunction.h"
     48#include "JSGlobalObjectFunctions.h"
    4849#include "JSNotAnObject.h"
    4950#include "JSPropertyNameIterator.h"
     
    28152816    if (cell1->isString()) {
    28162817        if (src2.isInt32())
    2817             return static_cast<JSString*>(cell1)->value(stackFrame.callFrame).toDouble() == src2.asInt32();
     2818            return jsToNumber(static_cast<JSString*>(cell1)->value(stackFrame.callFrame)) == src2.asInt32();
    28182819           
    28192820        if (src2.isDouble())
    2820             return static_cast<JSString*>(cell1)->value(stackFrame.callFrame).toDouble() == src2.asDouble();
     2821            return jsToNumber(static_cast<JSString*>(cell1)->value(stackFrame.callFrame)) == src2.asDouble();
    28212822
    28222823        if (src2.isTrue())
    2823             return static_cast<JSString*>(cell1)->value(stackFrame.callFrame).toDouble() == 1.0;
     2824            return jsToNumber(static_cast<JSString*>(cell1)->value(stackFrame.callFrame)) == 1.0;
    28242825
    28252826        if (src2.isFalse())
    2826             return static_cast<JSString*>(cell1)->value(stackFrame.callFrame).toDouble() == 0.0;
     2827            return jsToNumber(static_cast<JSString*>(cell1)->value(stackFrame.callFrame)) == 0.0;
    28272828
    28282829        JSCell* cell2 = asCell(src2);
  • trunk/JavaScriptCore/runtime/Arguments.cpp

    r65305 r65588  
    158158{
    159159    bool isArrayIndex;
    160     unsigned i = toArrayIndex(propertyName.ustring(), &isArrayIndex);
     160    unsigned i = propertyName.toArrayIndex(isArrayIndex);
    161161    if (isArrayIndex && i < d->numArguments && (!d->deletedArguments || !d->deletedArguments[i])) {
    162162        if (i < d->numParameters) {
     
    183183{
    184184    bool isArrayIndex;
    185     unsigned i = toArrayIndex(propertyName.ustring(), &isArrayIndex);
     185    unsigned i = propertyName.toArrayIndex(isArrayIndex);
    186186    if (isArrayIndex && i < d->numArguments && (!d->deletedArguments || !d->deletedArguments[i])) {
    187187        if (i < d->numParameters) {
     
    234234{
    235235    bool isArrayIndex;
    236     unsigned i = toArrayIndex(propertyName.ustring(), &isArrayIndex);
     236    unsigned i = propertyName.toArrayIndex(isArrayIndex);
    237237    if (isArrayIndex && i < d->numArguments && (!d->deletedArguments || !d->deletedArguments[i])) {
    238238        if (i < d->numParameters)
     
    277277{
    278278    bool isArrayIndex;
    279     unsigned i = toArrayIndex(propertyName.ustring(), &isArrayIndex);
     279    unsigned i = propertyName.toArrayIndex(isArrayIndex);
    280280    if (isArrayIndex && i < d->numArguments) {
    281281        if (!d->deletedArguments) {
  • trunk/JavaScriptCore/runtime/Identifier.cpp

    r65104 r65588  
    169169};
    170170
     171uint32_t Identifier::toUInt32(const UString& string, bool& ok)
     172{
     173    ok = false;
     174
     175    unsigned length = string.length();
     176    const UChar* characters = string.characters();
     177
     178    // An empty string is not a number.
     179    if (!length)
     180        return 0;
     181
     182    // Get the first character, turning it into a digit.
     183    uint32_t value = characters[0] - '0';
     184    if (value > 9)
     185        return 0;
     186
     187    // Check for leading zeros. If the first characher is 0, then the
     188    // length of the string must be one - e.g. "042" is not equal to "42".
     189    if (!value && length > 1)
     190        return 0;
     191
     192    while (--length) {
     193        // Multiply value by 10, checking for overflow out of 32 bits.
     194        if (value > 0xFFFFFFFFU / 10)
     195            return 0;
     196        value *= 10;
     197
     198        // Get the next character, turning it into a digit.
     199        uint32_t newValue = *(++characters) - '0';
     200        if (newValue > 9)
     201            return 0;
     202
     203        // Add in the old value, checking for overflow out of 32 bits.
     204        newValue += value;
     205        if (newValue < value)
     206            return 0;
     207        value = newValue;
     208    }
     209
     210    ok = true;
     211    return value;
     212}
     213
    171214PassRefPtr<StringImpl> Identifier::add(JSGlobalData* globalData, const UChar* s, int length)
    172215{
  • trunk/JavaScriptCore/runtime/Identifier.h

    r65478 r65588  
    6060        static Identifier from(JSGlobalData*, int y);
    6161        static Identifier from(JSGlobalData*, double y);
    62        
     62
     63        static uint32_t toUInt32(const UString&, bool& ok);
     64        uint32_t toUInt32(bool& ok) const { return toUInt32(m_string, ok); }
     65        unsigned toArrayIndex(bool& ok) const;
     66
    6367        bool isNull() const { return m_string.isNull(); }
    6468        bool isEmpty() const { return m_string.isEmpty(); }
    65        
    66         uint32_t toUInt32(bool* ok) const { return m_string.toUInt32(ok); }
    67         uint32_t toUInt32(bool* ok, bool tolerateEmptyString) const { return m_string.toUInt32(ok, tolerateEmptyString); };
    68         uint32_t toStrictUInt32(bool* ok) const { return m_string.toStrictUInt32(ok); }
    69         double toDouble() const { return m_string.toDouble(); }
    7069       
    7170        friend bool operator==(const Identifier&, const Identifier&);
  • trunk/JavaScriptCore/runtime/JSArray.cpp

    r65311 r65588  
    274274
    275275    bool isArrayIndex;
    276     unsigned i = toArrayIndex(propertyName.ustring(), &isArrayIndex);
     276    unsigned i = propertyName.toArrayIndex(isArrayIndex);
    277277    if (isArrayIndex)
    278278        return JSArray::getOwnPropertySlot(exec, i, slot);
     
    291291   
    292292    bool isArrayIndex;
    293     unsigned i = toArrayIndex(propertyName.ustring(), &isArrayIndex);
     293    unsigned i = propertyName.toArrayIndex(isArrayIndex);
    294294    if (isArrayIndex) {
    295295        if (i >= storage->m_length)
     
    318318{
    319319    bool isArrayIndex;
    320     unsigned i = toArrayIndex(propertyName.ustring(), &isArrayIndex);
     320    unsigned i = propertyName.toArrayIndex(isArrayIndex);
    321321    if (isArrayIndex) {
    322322        put(exec, i, value);
     
    476476{
    477477    bool isArrayIndex;
    478     unsigned i = toArrayIndex(propertyName.ustring(), &isArrayIndex);
     478    unsigned i = propertyName.toArrayIndex(isArrayIndex);
    479479    if (isArrayIndex)
    480480        return deleteProperty(exec, i);
  • trunk/JavaScriptCore/runtime/JSArray.h

    r65305 r65588  
    265265    // Rule from ECMA 15.2 about what an array index is.
    266266    // Must exactly match string form of an unsigned integer, and be less than 2^32 - 1.
    267     inline unsigned toArrayIndex(const UString& string, bool* ok)
    268     {
    269         unsigned i = string.toStrictUInt32(ok);
     267    inline unsigned Identifier::toArrayIndex(bool& ok) const
     268    {
     269        unsigned i = toUInt32(ok);
    270270        if (ok && i >= 0xFFFFFFFFU)
    271             *ok = false;
     271            ok = false;
    272272        return i;
    273273    }
  • trunk/JavaScriptCore/runtime/JSByteArray.cpp

    r54022 r65588  
    6161{
    6262    bool ok;
    63     unsigned index = propertyName.toUInt32(&ok, false);
     63    unsigned index = propertyName.toUInt32(ok);
    6464    if (ok && canAccessIndex(index)) {
    6565        slot.setValue(getIndex(exec, index));
     
    7272{
    7373    bool ok;
    74     unsigned index = propertyName.toUInt32(&ok, false);
     74    unsigned index = propertyName.toUInt32(ok);
    7575    if (ok && canAccessIndex(index)) {
    7676        descriptor.setDescriptor(getIndex(exec, index), DontDelete);
     
    9292{
    9393    bool ok;
    94     unsigned index = propertyName.toUInt32(&ok, false);
     94    unsigned index = propertyName.toUInt32(ok);
    9595    if (ok) {
    9696        setIndex(exec, index, value);
  • trunk/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp

    r65305 r65588  
    277277}
    278278
     279static const int SizeOfInfinity = 8;
     280
     281static bool isInfinity(const UChar* data, const UChar* end)
     282{
     283    return (end - data) >= SizeOfInfinity
     284        && data[0] == 'I'
     285        && data[1] == 'n'
     286        && data[2] == 'f'
     287        && data[3] == 'i'
     288        && data[4] == 'n'
     289        && data[5] == 'i'
     290        && data[6] == 't'
     291        && data[7] == 'y';
     292}
     293
     294// See ecma-262 9.3.1
     295static double jsHexIntegerLiteral(const UChar*& data, const UChar* end)
     296{
     297    // Hex number.
     298    data += 2;
     299    const UChar* firstDigitPosition = data;
     300    double number = 0;
     301    while (true) {
     302        number = number * 16 + toASCIIHexValue(*data);
     303        ++data;
     304        if (data == end)
     305            break;
     306        if (!isASCIIHexDigit(*data))
     307            break;
     308    }
     309    if (number >= mantissaOverflowLowerBound)
     310        number = parseIntOverflow(firstDigitPosition, data - firstDigitPosition, 16);
     311
     312    return number;
     313}
     314
     315// See ecma-262 9.3.1
     316static double jsStrDecimalLiteral(const UChar*& data, const UChar* end)
     317{
     318    ASSERT(data < end);
     319
     320    // Copy the sting into a null-terminated byte buffer, and call strtod.
     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    char* endOfNumber;
     328    double number = WTF::strtod(byteBuffer.data(), &endOfNumber);
     329
     330    // Check if strtod found a number; if so return it.
     331    ptrdiff_t consumed = endOfNumber - byteBuffer.data();
     332    if (consumed) {
     333        data += consumed;
     334        return number;
     335    }
     336
     337    // Check for [+-]?Infinity
     338    switch (*data) {
     339    case 'I':
     340        if (isInfinity(data, end)) {
     341            data += SizeOfInfinity;
     342            return Inf;
     343        }
     344        break;
     345
     346    case '+':
     347        if (isInfinity(data + 1, end)) {
     348            data += SizeOfInfinity + 1;
     349            return Inf;
     350        }
     351        break;
     352
     353    case '-':
     354        if (isInfinity(data + 1, end)) {
     355            data += SizeOfInfinity + 1;
     356            return -Inf;
     357        }
     358        break;
     359    }
     360
     361    // Not a number.
     362    return NaN;
     363}
     364
     365// See ecma-262 9.3.1
     366double jsToNumber(const UString& s)
     367{
     368    unsigned size = s.length();
     369
     370    if (size == 1) {
     371        UChar c = s.characters()[0];
     372        if (isASCIIDigit(c))
     373            return c - '0';
     374        if (isStrWhiteSpace(c))
     375            return 0;
     376        return NaN;
     377    }
     378
     379    const UChar* data = s.characters();
     380    const UChar* end = data + size;
     381
     382    // Skip leading white space.
     383    for (; data < end; ++data) {
     384        if (!isStrWhiteSpace(*data))
     385            break;
     386    }
     387
     388    // Empty string.
     389    if (data == end)
     390        return 0.0;
     391
     392    double number;
     393    if (data[0] == '0' && data + 2 < end && (data[1] | 0x20) == 'x' && isASCIIHexDigit(data[2]))
     394        number = jsHexIntegerLiteral(data, end);
     395    else
     396        number = jsStrDecimalLiteral(data, end);
     397
     398    // Allow trailing white space.
     399    for (; data < end; ++data) {
     400        if (!isStrWhiteSpace(*data))
     401            break;
     402    }
     403    if (data != end)
     404        return NaN;
     405
     406    return number;
     407}
     408
    279409static double parseFloat(const UString& s)
    280410{
    281     // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0.
    282     // Need to skip any whitespace and then one + or - sign.
    283     int length = s.length();
     411    unsigned size = s.length();
     412
     413    if (size == 1) {
     414        UChar c = s.characters()[0];
     415        if (isASCIIDigit(c))
     416            return c - '0';
     417        return NaN;
     418    }
     419
    284420    const UChar* data = s.characters();
    285     int p = 0;
    286     while (p < length && isStrWhiteSpace(data[p]))
    287         ++p;
    288 
    289     if (p < length && (data[p] == '+' || data[p] == '-'))
    290         ++p;
    291 
    292     if (length - p >= 2 && data[p] == '0' && (data[p + 1] == 'x' || data[p + 1] == 'X'))
    293         return 0;
    294 
    295     return s.toDouble(true /*tolerant*/, false /* NaN for empty string */);
     421    const UChar* end = data + size;
     422
     423    // Skip leading white space.
     424    for (; data < end; ++data) {
     425        if (!isStrWhiteSpace(*data))
     426            break;
     427    }
     428
     429    // Empty string.
     430    if (data == end)
     431        return NaN;
     432
     433    return jsStrDecimalLiteral(data, end);
    296434}
    297435
  • trunk/JavaScriptCore/runtime/JSGlobalObjectFunctions.h

    r63120 r65588  
    5656    double parseIntOverflow(const UChar*, int length, int radix);
    5757    bool isStrWhiteSpace(UChar);
     58    double jsToNumber(const UString& s);
    5859
    5960} // namespace JSC
  • trunk/JavaScriptCore/runtime/JSString.cpp

    r65468 r65588  
    2525
    2626#include "JSGlobalObject.h"
     27#include "JSGlobalObjectFunctions.h"
    2728#include "JSObject.h"
    2829#include "Operations.h"
     
    178179{
    179180    result = this;
    180     number = value(exec).toDouble();
     181    number = jsToNumber(value(exec));
    181182    return false;
    182183}
     
    189190double JSString::toNumber(ExecState* exec) const
    190191{
    191     return value(exec).toDouble();
     192    return jsToNumber(value(exec));
    192193}
    193194
     
    241242   
    242243    bool isStrictUInt32;
    243     unsigned i = propertyName.toStrictUInt32(&isStrictUInt32);
     244    unsigned i = propertyName.toUInt32(isStrictUInt32);
    244245    if (isStrictUInt32 && i < m_length) {
    245246        descriptor.setDescriptor(getIndex(exec, i), DontDelete | ReadOnly);
  • trunk/JavaScriptCore/runtime/JSString.h

    r65305 r65588  
    565565
    566566        bool isStrictUInt32;
    567         unsigned i = propertyName.toStrictUInt32(&isStrictUInt32);
     567        unsigned i = propertyName.toUInt32(isStrictUInt32);
    568568        if (isStrictUInt32 && i < m_length) {
    569569            slot.setValue(getIndex(exec, i));
  • trunk/JavaScriptCore/runtime/ObjectPrototype.cpp

    r60762 r65588  
    6666    if (m_hasNoPropertiesWithUInt32Names) {
    6767        bool isUInt32;
    68         propertyName.toStrictUInt32(&isUInt32);
     68        propertyName.toUInt32(isUInt32);
    6969        m_hasNoPropertiesWithUInt32Names = !isUInt32;
    7070    }
  • trunk/JavaScriptCore/runtime/StringObject.cpp

    r65177 r65588  
    8181        return false;
    8282    bool isStrictUInt32;
    83     unsigned i = propertyName.toStrictUInt32(&isStrictUInt32);
     83    unsigned i = propertyName.toUInt32(isStrictUInt32);
    8484    if (isStrictUInt32 && internalValue()->canGetIndex(i))
    8585        return false;
  • 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{
  • trunk/JavaScriptCore/runtime/UString.h

    r65479 r65588  
    107107    size_t reverseFind(const UString& str, unsigned start = UINT_MAX) const
    108108        { return m_impl ? m_impl->reverseFind(str.impl(), start) : notFound; }
    109 
    110     double toDouble(bool tolerateTrailingJunk, bool tolerateEmptyString) const;
    111     double toDouble(bool tolerateTrailingJunk) const;
    112     double toDouble() const;
    113 
    114     uint32_t toUInt32(bool* ok = 0) const;
    115     uint32_t toUInt32(bool* ok, bool tolerateEmptyString) const;
    116     uint32_t toStrictUInt32(bool* ok = 0) const;
    117109
    118110    UString substr(unsigned pos = 0, unsigned len = UINT_MAX) const;
  • trunk/JavaScriptCore/wtf/dtoa.cpp

    r64706 r65588  
    8989 *    define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP,
    9090 *    FLT_RADIX, FLT_ROUNDS, and DBL_MAX.
    91  * #define INFNAN_CHECK on IEEE systems to cause strtod to check for
    92  *    Infinity and NaN (case insensitively).  On some systems (e.g.,
    93  *    some HP systems), it may be necessary to #define NAN_WORD0
    94  *    appropriately -- to the most significant word of a quiet NaN.
    95  *    (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.)
    96  *    When INFNAN_CHECK is #defined and No_Hex_NaN is not #defined,
    97  *    strtod also accepts (case insensitively) strings of the form
    98  *    NaN(x), where x is a string of hexadecimal digits and spaces;
    99  *    if there is only one string of hexadecimal digits, it is taken
    100  *    for the 52 fraction bits of the resulting NaN; if there are two
    101  *    or more strings of hex digits, the first is for the high 20 bits,
    102  *    the second and subsequent for the low 32 bits, with intervening
    103  *    white space ignored; but if this results in none of the 52
    104  *    fraction bits being on (an IEEE Infinity symbol), then NAN_WORD0
    105  *    and NAN_WORD1 are used instead.
    10691 * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that
    10792 *    avoids underflows on inputs whose result does not underflow.
     
    167152#endif
    168153
    169 #define INFNAN_CHECK
    170 #define No_Hex_NaN
    171 
    172154#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(IEEE_ARM) != 1
    173155Exactly one of IEEE_8087, IEEE_ARM or IEEE_MC68k should be defined.
     
    10411023#define n_bigtens 5
    10421024
    1043 #if defined(INFNAN_CHECK)
    1044 
    1045 #ifndef NAN_WORD0
    1046 #define NAN_WORD0 0x7ff80000
    1047 #endif
    1048 
    1049 #ifndef NAN_WORD1
    1050 #define NAN_WORD1 0
    1051 #endif
    1052 
    1053 static int match(const char** sp, const char* t)
    1054 {
    1055     int c, d;
    1056     const char* s = *sp;
    1057 
    1058     while ((d = *t++)) {
    1059         if ((c = *++s) >= 'A' && c <= 'Z')
    1060             c += 'a' - 'A';
    1061         if (c != d)
    1062             return 0;
    1063     }
    1064     *sp = s + 1;
    1065     return 1;
    1066 }
    1067 
    1068 #ifndef No_Hex_NaN
    1069 static void hexnan(U* rvp, const char** sp)
    1070 {
    1071     uint32_t c, x[2];
    1072     const char* s;
    1073     int havedig, udx0, xshift;
    1074 
    1075     x[0] = x[1] = 0;
    1076     havedig = xshift = 0;
    1077     udx0 = 1;
    1078     s = *sp;
    1079     while ((c = *(const unsigned char*)++s)) {
    1080         if (c >= '0' && c <= '9')
    1081             c -= '0';
    1082         else if (c >= 'a' && c <= 'f')
    1083             c += 10 - 'a';
    1084         else if (c >= 'A' && c <= 'F')
    1085             c += 10 - 'A';
    1086         else if (c <= ' ') {
    1087             if (udx0 && havedig) {
    1088                 udx0 = 0;
    1089                 xshift = 1;
    1090             }
    1091             continue;
    1092         } else if (/*(*/ c == ')' && havedig) {
    1093             *sp = s + 1;
    1094             break;
    1095         } else
    1096             return;    /* invalid form: don't change *sp */
    1097         havedig = 1;
    1098         if (xshift) {
    1099             xshift = 0;
    1100             x[0] = x[1];
    1101             x[1] = 0;
    1102         }
    1103         if (udx0)
    1104             x[0] = (x[0] << 4) | (x[1] >> 28);
    1105         x[1] = (x[1] << 4) | c;
    1106     }
    1107     if ((x[0] &= 0xfffff) || x[1]) {
    1108         word0(rvp) = Exp_mask | x[0];
    1109         word1(rvp) = x[1];
    1110     }
    1111 }
    1112 #endif /*No_Hex_NaN*/
    1113 #endif /* INFNAN_CHECK */
    1114 
    11151025double strtod(const char* s00, char** se)
    11161026{
     
    12371147    if (!nd) {
    12381148        if (!nz && !nz0) {
    1239 #ifdef INFNAN_CHECK
    1240             /* Check for Nan and Infinity */
    1241             switch (c) {
    1242             case 'i':
    1243             case 'I':
    1244                 if (match(&s, "nf")) {
    1245                     --s;
    1246                     if (!match(&s, "inity"))
    1247                         ++s;
    1248                     word0(&rv) = 0x7ff00000;
    1249                     word1(&rv) = 0;
    1250                     goto ret;
    1251                 }
    1252                 break;
    1253             case 'n':
    1254             case 'N':
    1255                 if (match(&s, "an")) {
    1256                     word0(&rv) = NAN_WORD0;
    1257                     word1(&rv) = NAN_WORD1;
    1258 #ifndef No_Hex_NaN
    1259                     if (*s == '(') /*)*/
    1260                         hexnan(&rv, &s);
    1261 #endif
    1262                     goto ret;
    1263                 }
    1264             }
    1265 #endif /* INFNAN_CHECK */
    12661149ret0:
    12671150            s = s00;
Note: See TracChangeset for help on using the changeset viewer.