Reviewed by Adele.
authordarin <darin@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 8 Aug 2007 00:45:39 +0000 (00:45 +0000)
committerdarin <darin@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 8 Aug 2007 00:45:39 +0000 (00:45 +0000)
        - fix <rdar://problem/5383104> REGRESSION: XHR.responseText is null instead of empty string
          in http/tests/xmlhttprequest/zero-length-response.html

        The new code to handle out of memory conditions was turning a "" into a null string.

        * kjs/ustring.h: Removed UCharReference, which has long been obsolete and unused.
        Removed copyForWriting, which was only used for the upper/lowercasing code and for
        UCharReference.
        * kjs/ustring.cpp:
        (KJS::allocChars): Removed special case that made this fail (return 0) when passed 0.
        Instead assert that we're not passed 0. Also added an overflow check for two reasons:
        1) for sizes that aren't checked this prevents us from allocating a buffer that's too
        small, and 2) for sizes where we overflowed in the expandedSize function and returned
        overflowIndicator, it guarantees we fail.
        (KJS::reallocChars): Ditto.
        (KJS::UString::expandedSize): Return a large number, overflowIndicator, rather than 0
        for cases where we overflow.
        (KJS::UString::spliceSubstringsWithSeparators): Added a special case for empty string so
        we don't call allocChars with a length of 0.
        (KJS::UString::operator=): Added special characters for both 0 and empty string so we
        match the behavior of the constructor. This avoids calling allocChars with a length of 0
        and making a null string rather than an empty string in that case, and also matches the
        pattern used in the rest of the functions.
        (KJS::UString::operator[]): Made the return value const so code that tries to use the
        operator to modify the string will fail.

        * kjs/string_object.cpp: (KJS::StringProtoFunc::callAsFunction): Rewrote uppercasing and
        lowercasing functions so they don't need copyForWriting any more -- it wasn't really doing
        any good for optimization purposes. Instead use a Vector and releaseBuffer.

        * wtf/unicode/icu/UnicodeIcu.h: Eliminate one of the versions of toLower/toUpper -- we now
        only need the version where both a source and destination buffer is passed in, not the one
        that works in place.
        * wtf/unicode/qt4/UnicodeQt4.h: Ditto.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@24919 268f45cc-cd09-0410-ab3c-d52691b4dbfc

JavaScriptCore/ChangeLog
JavaScriptCore/kjs/string_object.cpp
JavaScriptCore/kjs/ustring.cpp
JavaScriptCore/kjs/ustring.h
JavaScriptCore/wtf/unicode/icu/UnicodeIcu.h
JavaScriptCore/wtf/unicode/qt4/UnicodeQt4.h

index 151c5c22b374d9330d34c93fac47f62d175985c6..b9acdfb31435bd4c662ff9febef31a521eeeb700 100644 (file)
@@ -1,3 +1,42 @@
+2007-08-07  Darin Adler  <darin@apple.com>
+
+        Reviewed by Adele.
+
+        - fix <rdar://problem/5383104> REGRESSION: XHR.responseText is null instead of empty string
+          in http/tests/xmlhttprequest/zero-length-response.html
+
+        The new code to handle out of memory conditions was turning a "" into a null string.
+
+        * kjs/ustring.h: Removed UCharReference, which has long been obsolete and unused.
+        Removed copyForWriting, which was only used for the upper/lowercasing code and for
+        UCharReference.
+        * kjs/ustring.cpp:
+        (KJS::allocChars): Removed special case that made this fail (return 0) when passed 0.
+        Instead assert that we're not passed 0. Also added an overflow check for two reasons:
+        1) for sizes that aren't checked this prevents us from allocating a buffer that's too
+        small, and 2) for sizes where we overflowed in the expandedSize function and returned
+        overflowIndicator, it guarantees we fail.
+        (KJS::reallocChars): Ditto.
+        (KJS::UString::expandedSize): Return a large number, overflowIndicator, rather than 0
+        for cases where we overflow.
+        (KJS::UString::spliceSubstringsWithSeparators): Added a special case for empty string so
+        we don't call allocChars with a length of 0.
+        (KJS::UString::operator=): Added special characters for both 0 and empty string so we
+        match the behavior of the constructor. This avoids calling allocChars with a length of 0
+        and making a null string rather than an empty string in that case, and also matches the
+        pattern used in the rest of the functions.
+        (KJS::UString::operator[]): Made the return value const so code that tries to use the
+        operator to modify the string will fail.
+
+        * kjs/string_object.cpp: (KJS::StringProtoFunc::callAsFunction): Rewrote uppercasing and
+        lowercasing functions so they don't need copyForWriting any more -- it wasn't really doing
+        any good for optimization purposes. Instead use a Vector and releaseBuffer.
+
+        * wtf/unicode/icu/UnicodeIcu.h: Eliminate one of the versions of toLower/toUpper -- we now
+        only need the version where both a source and destination buffer is passed in, not the one
+        that works in place.
+        * wtf/unicode/qt4/UnicodeQt4.h: Ditto.
+
 2007-08-06  Sam Weinig  <sam@webkit.org>
 
         Reviewed by Oliver.
index f20bf0caad7d7e8c7bcda93ac9e7bfd3075350e6..e66bb5b27d52b7e7f952cd015dbdaa1b63767bd8 100644 (file)
@@ -693,35 +693,45 @@ JSValue* StringProtoFunc::callAsFunction(ExecState* exec, JSObject* thisObj, con
     break;
   case ToLowerCase:
   case ToLocaleLowerCase: { // FIXME: See http://www.unicode.org/Public/UNIDATA/SpecialCasing.txt for locale-sensitive mappings that aren't implemented.
-    u = s;
-    u.copyForWriting();
-    ::UChar* dataPtr = reinterpret_cast< ::UChar*>(u.rep()->data());
-    ::UChar* destIfNeeded;
-
-    int len = Unicode::toLower(dataPtr, u.size(), destIfNeeded);
-    if (len >= 0)
-        result = jsString(UString(reinterpret_cast<UChar*>(destIfNeeded ? destIfNeeded : dataPtr), len));
-    else
-        result = jsString(s);
-
-    free(destIfNeeded);
-    break;
+    StringImp* sVal = thisObj->inherits(&StringInstance::info)
+        ? static_cast<StringInstance*>(thisObj)->internalValue()
+        : static_cast<StringImp*>(jsString(s));
+    int ssize = s.size();
+    if (!ssize)
+        return sVal;
+    Vector< ::UChar> buffer(ssize);
+    bool error;
+    int length = Unicode::toLower(buffer.data(), ssize, reinterpret_cast<const ::UChar*>(s.data()), ssize, &error);
+    if (error) {
+        buffer.resize(length);
+        length = Unicode::toLower(buffer.data(), length, reinterpret_cast<const ::UChar*>(s.data()), ssize, &error);
+        if (error)
+            return sVal;
+    }
+    if (length == ssize && memcmp(buffer.data(), s.data(), length * sizeof(UChar)) == 0)
+        return sVal;
+    return jsString(UString(reinterpret_cast<UChar*>(buffer.releaseBuffer()), length, false));
   }
   case ToUpperCase:
   case ToLocaleUpperCase: { // FIXME: See http://www.unicode.org/Public/UNIDATA/SpecialCasing.txt for locale-sensitive mappings that aren't implemented.
-    u = s;
-    u.copyForWriting();
-    ::UChar* dataPtr = reinterpret_cast< ::UChar*>(u.rep()->data());
-    ::UChar* destIfNeeded;
-
-    int len = Unicode::toUpper(dataPtr, u.size(), destIfNeeded);
-    if (len >= 0)
-        result = jsString(UString(reinterpret_cast<UChar *>(destIfNeeded ? destIfNeeded : dataPtr), len));
-    else
-        result = jsString(s);
-
-    free(destIfNeeded);
-    break;
+    StringImp* sVal = thisObj->inherits(&StringInstance::info)
+        ? static_cast<StringInstance*>(thisObj)->internalValue()
+        : static_cast<StringImp*>(jsString(s));
+    int ssize = s.size();
+    if (!ssize)
+        return sVal;
+    Vector< ::UChar> buffer(ssize);
+    bool error;
+    int length = Unicode::toUpper(buffer.data(), ssize, reinterpret_cast<const ::UChar*>(s.data()), ssize, &error);
+    if (error) {
+        buffer.resize(length);
+        length = Unicode::toUpper(buffer.data(), length, reinterpret_cast<const ::UChar*>(s.data()), ssize, &error);
+        if (error)
+            return sVal;
+    }
+    if (length == ssize && memcmp(buffer.data(), s.data(), length * sizeof(UChar)) == 0)
+        return sVal;
+    return jsString(UString(reinterpret_cast<UChar*>(buffer.releaseBuffer()), length, false));
   }
   case LocaleCompare:
     if (args.size() < 1)
index 159e9d717e3ef3704d3404633f052502a537d040..d00f130a7a4d344f1c4905c110af4532f90c5ac1 100644 (file)
@@ -2,7 +2,7 @@
 /*
  *  This file is part of the KDE libraries
  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
- *  Copyright (C) 2004 Apple Computer, Inc.
+ *  Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
  *  Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca)
  *
  *  This library is free software; you can redistribute it and/or
@@ -54,16 +54,21 @@ namespace KJS {
 extern const double NaN;
 extern const double Inf;
 
+static const size_t overflowIndicator = std::numeric_limits<size_t>::max();
+static const size_t maxUChars = std::numeric_limits<size_t>::max() / sizeof(UChar);
+
 static inline UChar* allocChars(size_t length)
 {
-    if (!length)
+    ASSERT(length);
+    if (length > maxUChars)
         return 0;
     return static_cast<UChar*>(fastMalloc(sizeof(UChar) * length));
 }
 
-static inline UChar* reallocChars(void* buffer, size_t length)
+static inline UChar* reallocChars(UChar* buffer, size_t length)
 {
-    if (!length)
+    ASSERT(length);
+    if (length > maxUChars)
         return 0;
     return static_cast<UChar*>(fastRealloc(buffer, sizeof(UChar) * length));
 }
@@ -170,27 +175,6 @@ const int normalStatBufferSize = 4096;
 static char *statBuffer = 0;
 static int statBufferSize = 0;
 
-UCharReference& UCharReference::operator=(UChar c)
-{
-  str->copyForWriting();
-  if (offset < str->rep()->len)
-    *(str->rep()->data() + offset) = c;
-  /* TODO: lengthen string ? */
-  return *this;
-}
-
-UChar& UCharReference::ref() const
-{
-  ASSERT(JSLock::lockCount() > 0);
-
-  if (offset < str->rep()->len)
-    return *(str->rep()->data() + offset);
-  else {
-    static UChar callerBetterNotModifyThis('\0');
-    return callerBetterNotModifyThis;
-  }
-}
-
 PassRefPtr<UString::Rep> UString::Rep::createCopying(const UChar *d, int l)
 {
   ASSERT(JSLock::lockCount() > 0);
@@ -363,14 +347,15 @@ unsigned UString::Rep::computeHash(const char *s)
 // put these early so they can be inlined
 inline size_t UString::expandedSize(size_t size, size_t otherSize) const
 {
-    // Do the size calculation in two parts, being careful to avoid overflow
-    static const size_t maximumAllowableSize = std::numeric_limits<size_t>::max() / sizeof(UChar);
-    if (size > maximumAllowableSize)
-        return 0;
+    // Do the size calculation in two parts, returning overflowIndicator if
+    // we overflow the maximum value that we can handle.
+
+    if (size > maxUChars)
+        return overflowIndicator;
 
     size_t expandedSize = ((size + 10) / 10 * 11) + 1;
-    if (maximumAllowableSize - expandedSize < otherSize)
-        return 0;
+    if (maxUChars - expandedSize < otherSize)
+        return overflowIndicator;
 
     return expandedSize + otherSize;
 }
@@ -693,6 +678,9 @@ UString UString::spliceSubstringsWithSeparators(const Range* substringRanges, in
   for (int i = 0; i < separatorCount; i++)
     totalLength += separators[i].size();
 
+  if (totalLength == 0)
+    return "";
+
   UChar* buffer = allocChars(totalLength);
   if (!buffer)
       return null();
@@ -903,7 +891,17 @@ void UString::globalClear()
 
 UString &UString::operator=(const char *c)
 {
-  int l = c ? static_cast<int>(strlen(c)) : 0;
+    if (!c) {
+        m_rep = &Rep::null;
+        return *this;
+    }
+
+    int l = static_cast<int>(strlen(c));
+    if (!l) {
+        m_rep = &Rep::empty;
+        return *this;
+    }
+
   UChar *d;
   if (m_rep->rc == 1 && l <= m_rep->capacity && m_rep->baseIsSelf() && m_rep->offset == 0 && m_rep->preCapacity == 0) {
     d = m_rep->buf;
@@ -936,19 +934,13 @@ bool UString::is8Bit() const
   return true;
 }
 
-UChar UString::operator[](int pos) const
+const UChar UString::operator[](int pos) const
 {
   if (pos >= size())
     return '\0';
   return data()[pos];
 }
 
-UCharReference UString::operator[](int pos)
-{
-  /* TODO: boundary check */
-  return UCharReference(this, pos);
-}
-
 double UString::toDouble(bool tolerateTrailingJunk, bool tolerateEmptyString) const
 {
   double d;
@@ -1206,20 +1198,6 @@ UString UString::substr(int pos, int len) const
   return UString(Rep::create(m_rep, pos, len));
 }
 
-void UString::copyForWriting()
-{
-  if (m_rep->rc > 1 || !m_rep->baseIsSelf()) {
-    int l = size();
-    UChar *n = allocChars(l);
-    if (!n)
-        m_rep = &Rep::null;
-    else {
-        memcpy(n, data(), l * sizeof(UChar));
-        m_rep = Rep::create(n, l);
-    }
-  }
-}
-
 bool operator==(const UString& s1, const UString& s2)
 {
   if (s1.m_rep->len != s2.m_rep->len)
index 645bb1c07292d1432c00479977d3643ed31aa6e1..fbfd6386e4677556e074523a6b6532b6d4c77bce 100644 (file)
@@ -51,7 +51,6 @@ class KJScript;
 
 namespace KJS {
 
-  class UCharReference;
   class UString;
 
   /**
@@ -79,7 +78,6 @@ namespace KJS {
     UChar(char u);
     UChar(unsigned char u);
     UChar(unsigned short u);
-    UChar(const UCharReference &c);
     /**
      * @return The higher byte of the character.
      */
@@ -102,56 +100,6 @@ namespace KJS {
   inline UChar::UChar(unsigned char u) : uc(u) { }
   inline UChar::UChar(unsigned short u) : uc(u) { }
 
-  /**
-   * @short Dynamic reference to a string character.
-   *
-   * UCharReference is the dynamic counterpart of UChar. It's used when
-   * characters retrieved via index from a UString are used in an
-   * assignment expression (and therefore can't be treated as being const):
-   * \code
-   * UString s("hello world");
-   * s[0] = 'H';
-   * \endcode
-   *
-   * If that sounds confusing your best bet is to simply forget about the
-   * existence of this class and treat is as being identical to UChar.
-   */
-  class UCharReference {
-    friend class UString;
-    UCharReference(UString *s, unsigned int off) : str(s), offset(off) { }
-  public:
-    /**
-     * Set the referenced character to c.
-     */
-    UCharReference& operator=(UChar c);
-    /**
-     * Same operator as above except the argument that it takes.
-     */
-    UCharReference& operator=(char c) { return operator=(UChar(c)); }
-    /**
-     * @return Unicode value.
-     */
-    unsigned short unicode() const { return ref().uc; }
-    /**
-     * @return Lower byte.
-     */
-    unsigned char low() const { return static_cast<unsigned char>(ref().uc); }
-    /**
-     * @return Higher byte.
-     */
-    unsigned char high() const { return static_cast<unsigned char>(ref().uc >> 8); }
-
-  private:
-    // not implemented, can only be constructed from UString
-    UCharReference();
-
-    UChar& ref() const;
-    UString *str;
-    int offset;
-  };
-
-  inline UChar::UChar(const UCharReference &c) : uc(c.unicode()) { }
-
   /**
    * @short 8 bit char based string class
    */
@@ -372,11 +320,7 @@ namespace KJS {
     /**
      * Const character at specified position.
      */
-    UChar operator[](int pos) const;
-    /**
-     * Writable reference to character at specified position.
-     */
-    UCharReference operator[](int pos);
+    const UChar operator[](int pos) const;
 
     /**
      * Attempts an conversion to a number. Apart from floating point numbers,
@@ -438,8 +382,6 @@ namespace KJS {
     Rep* rep() const { return m_rep.get(); }
     UString(PassRefPtr<Rep> r) : m_rep(r) { ASSERT(m_rep); }
 
-    void copyForWriting();
-
     size_t cost() const;
 
   private:
index 82e59deb594c00881d5a7180820d946e959c012d..4501f1a3f90cec287671451a135ce2dcc0b38845 100644 (file)
@@ -131,26 +131,6 @@ namespace WTF {
       return realLength;
     }
 
-    inline int toLower(UChar* str, int strLength, UChar*& destIfNeeded)
-    {
-      UErrorCode err = U_ZERO_ERROR;
-      int resultLength;
-      destIfNeeded = 0;
-
-      resultLength = u_strToLower(0, 0, str, strLength, "", &err);
-
-      if (resultLength <= strLength) {
-        err = U_ZERO_ERROR;
-        u_strToLower(str, resultLength, str, strLength, "", &err);
-      } else {
-        err = U_ZERO_ERROR;
-        destIfNeeded = static_cast<UChar*>(malloc(resultLength * sizeof(UChar)));
-        u_strToLower(destIfNeeded, resultLength, str, strLength, "", &err);
-      }
-
-      return U_FAILURE(err) ? -1 : resultLength;
-    }
-
     inline int toLower(UChar* result, int resultLength, const UChar* src, int srcLength, bool* error)
     {
       UErrorCode status = U_ZERO_ERROR;
@@ -169,26 +149,6 @@ namespace WTF {
       return u_toupper(c);
     }
 
-    inline int toUpper(UChar* str, int strLength, UChar*& destIfNeeded)
-    {
-      UErrorCode err = U_ZERO_ERROR;
-      int resultLength;
-      destIfNeeded = 0;
-
-      resultLength = u_strToUpper(0, 0, str, strLength, "", &err);
-
-      if (resultLength <= strLength) {
-        err = U_ZERO_ERROR;
-        u_strToUpper(str, resultLength, str, strLength, "", &err);
-      } else {
-        err = U_ZERO_ERROR;
-        destIfNeeded = (UChar*)malloc(resultLength * sizeof(UChar));
-        u_strToUpper(destIfNeeded, resultLength, str, strLength, "", &err);
-      }
-
-      return U_FAILURE(err) ? -1 : resultLength;
-    }
-
     inline int toUpper(UChar* result, int resultLength, const UChar* src, int srcLength, bool* error)
     {
       UErrorCode status = U_ZERO_ERROR;
index 44de1260bfa132c9d777c45f1031834205b08622..76dbe03b5c6d3f742c3d3df5efb0469ec27c9622 100644 (file)
@@ -175,28 +175,6 @@ namespace WTF {
 #if QT_VERSION >= 0x040300
     // FIXME: handle surrogates correctly in all methods
     
-    inline int toLower(UChar* str, int strLength, UChar*& destIfNeeded)
-    {
-        destIfNeeded = 0;
-        
-        const UChar *e = str + strLength;
-        UChar *s = str;
-        while (s < e) {
-            const QUnicodeTables::Properties *prop = QUnicodeTables::properties(*s);
-            if (prop->lowerCaseSpecial || (((*s) & 0xf800) == 0xd800)) {
-                QString qstring = QString(reinterpret_cast<QChar *>(str), strLength).toLower();
-                strLength = qstring.length();
-                destIfNeeded = static_cast<UChar*>(malloc(strLength * sizeof(UChar)));
-                memcpy(destIfNeeded, qstring.constData(), strLength * sizeof(UChar));
-                return strLength;
-            }
-            *s = *s + prop->lowerCaseDiff;
-            ++s;
-        }
-
-        return strLength;
-    }
-
     inline UChar32 toLower(UChar32 ch)
     {
       return QChar::toLower(ch);
@@ -250,28 +228,6 @@ namespace WTF {
         return (r - result) + needed;
     }
 
-    inline int toUpper(UChar* str, int strLength, UChar*& destIfNeeded)
-    {
-        destIfNeeded = 0;
-        
-        const UChar *e = str + strLength;
-        UChar *s = str;
-        while (s < e) {
-            const QUnicodeTables::Properties *prop = QUnicodeTables::properties(*s);
-            if (prop->upperCaseSpecial || (((*s) & 0xf800) == 0xd800)) {
-                QString qstring = QString(reinterpret_cast<QChar *>(str), strLength).toUpper();
-                strLength = qstring.length();
-                destIfNeeded = static_cast<UChar*>(malloc(strLength * sizeof(UChar)));
-                memcpy(destIfNeeded, qstring.constData(), strLength * sizeof(UChar));
-                return strLength;
-            }
-            *s = *s + prop->upperCaseDiff;
-            ++s;
-        }
-
-        return strLength;
-    }
-
     inline UChar32 toUpper(UChar32 ch)
     {
       return QChar::toUpper(ch);
@@ -436,16 +392,6 @@ namespace WTF {
     
 #else
 
-    inline int toLower(UChar* str, int strLength, UChar*& destIfNeeded)
-    {
-      destIfNeeded = 0;
-
-      for (int i = 0; i < strLength; ++i)
-        str[i] = QChar(str[i]).toLower().unicode();
-
-      return strLength;
-    }
-
     inline UChar32 toLower(UChar32 ch)
     {
       if (ch > 0xffff)
@@ -465,16 +411,6 @@ namespace WTF {
       return srcLength;
     }
 
-    inline int toUpper(UChar* str, int strLength, UChar*& destIfNeeded)
-    {
-      destIfNeeded = 0;
-
-      for (int i = 0; i < strLength; ++i)
-        str[i] = QChar(str[i]).toUpper().unicode();
-
-      return strLength;
-    }
-
     inline UChar32 toUpper(UChar32 ch)
     {
       if (ch > 0xffff)