Title: [98199] trunk
Revision
98199
Author
[email protected]
Date
2011-10-23 00:21:19 -0700 (Sun, 23 Oct 2011)

Log Message

Separated string lifetime bits from character buffer state bits
https://bugs.webkit.org/show_bug.cgi?id=70673

Source/_javascript_Core: 

Reviewed by Anders Carlsson.
        
Moved the static/immortal bit into the bottom bit of the refcount, and
moved all other bits into the high bits of the hash code.
        
This is the first step toward a new Characters/PassString class, and it
makes ref/deref slightly more efficient.

* create_hash_table:
* wtf/StringHasher.h:
(WTF::StringHasher::hash): Tweaked the string hashing function to leave
the top bits clear, so they can be used as flags.
        
Fixed some small differences between the PERL copy of this function and
the C++ copy of this function, which could have in theory caused subtle
crashes.

* wtf/text/StringImpl.cpp:
(WTF::StringImpl::sharedBuffer):
(WTF::StringImpl::createWithTerminatingNullCharacter):
* wtf/text/StringImpl.h:
(WTF::StringImpl::StringImpl):
(WTF::StringImpl::cost): Renamed s_refCountFlagShouldReportedCost to
s_didReportExtraCost, since the original name was both self-contradictory
and used as a double-negative.

(WTF::StringImpl::isIdentifier):
(WTF::StringImpl::setIsIdentifier):
(WTF::StringImpl::hasTerminatingNullCharacter):
(WTF::StringImpl::isAtomic):
(WTF::StringImpl::setIsAtomic):
(WTF::StringImpl::setHash):
(WTF::StringImpl::rawHash):
(WTF::StringImpl::hasHash):
(WTF::StringImpl::existingHash):
(WTF::StringImpl::hash):
(WTF::StringImpl::hasOneRef):
(WTF::StringImpl::ref):
(WTF::StringImpl::deref):
(WTF::StringImpl::bufferOwnership):
(WTF::StringImpl::isStatic): Moved the static/immortal bit into the bottom
bit of the refcount. Now, all lifetime information lives in the refcount
field. Moved the other bits into the hash code field.

Source/WebCore: 

Reviewed by Anders Carlsson.

* bindings/scripts/CodeGeneratorJS.pm:
(GenerateHashValue): Updated for string hashing changes in _javascript_Core.

LayoutTests: 

Reviewed by Anders Carlsson.
        
These tests depended on non-deterministic effects of hash order, so I
had to update them:

* fast/dom/prototype-inheritance-2-expected.txt: Updated for slightly
different order of discovering properties.

* fast/js/delete-syntax-expected.txt:
* fast/js/script-tests/delete-syntax.js: Updated not to depend on iteration
order, since that's not what this test is about.

* http/tests/inspector/inspector-test.js: Updated not to depend on
global function iteration order. This test setup is pretty spaghetti.
I think I avoided making it even more spaghetti.

* inspector/storage-panel-dom-storage-expected.txt: Updated for changed
iteration order.

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (98198 => 98199)


--- trunk/LayoutTests/ChangeLog	2011-10-23 05:03:19 UTC (rev 98198)
+++ trunk/LayoutTests/ChangeLog	2011-10-23 07:21:19 UTC (rev 98199)
@@ -1,3 +1,27 @@
+2011-10-21  Geoffrey Garen  <[email protected]>
+
+        Separated string lifetime bits from character buffer state bits
+        https://bugs.webkit.org/show_bug.cgi?id=70673
+
+        Reviewed by Anders Carlsson.
+        
+        These tests depended on non-deterministic effects of hash order, so I
+        had to update them:
+
+        * fast/dom/prototype-inheritance-2-expected.txt: Updated for slightly
+        different order of discovering properties.
+
+        * fast/js/delete-syntax-expected.txt:
+        * fast/js/script-tests/delete-syntax.js: Updated not to depend on iteration
+        order, since that's not what this test is about.
+
+        * http/tests/inspector/inspector-test.js: Updated not to depend on
+        global function iteration order. This test setup is pretty spaghetti.
+        I think I avoided making it even more spaghetti.
+
+        * inspector/storage-panel-dom-storage-expected.txt: Updated for changed
+        iteration order.
+
 2011-10-22  Benjamin Poulain  <[email protected]>
 
         Make relayout-nested-positioned-elements-crash.html more reliable

Modified: trunk/LayoutTests/fast/dom/prototype-inheritance-2-expected.txt (98198 => 98199)


--- trunk/LayoutTests/fast/dom/prototype-inheritance-2-expected.txt	2011-10-23 05:03:19 UTC (rev 98198)
+++ trunk/LayoutTests/fast/dom/prototype-inheritance-2-expected.txt	2011-10-23 07:21:19 UTC (rev 98199)
@@ -168,26 +168,26 @@
 PASS MediaListPrototype from inner.document.forms.testForm.0.ownerDocument.styleSheets.0.media.__proto__
 PASS MemoryInfo from inner.document.forms.testForm.0.ownerDocument.defaultView.console.memory
 PASS MemoryInfoPrototype from inner.document.forms.testForm.0.ownerDocument.defaultView.console.memory.__proto__
-PASS MimeType from inner.document.forms.testForm.0.ownerDocument.defaultView.clientInformation.mimeTypes.0
-PASS MimeTypeArray from inner.document.forms.testForm.0.ownerDocument.defaultView.clientInformation.mimeTypes
-PASS MimeTypeArrayConstructor from inner.document.forms.testForm.0.ownerDocument.defaultView.clientInformation.mimeTypes.constructor
-PASS MimeTypeArrayPrototype from inner.document.forms.testForm.0.ownerDocument.defaultView.clientInformation.mimeTypes.__proto__
-PASS MimeTypeConstructor from inner.document.forms.testForm.0.ownerDocument.defaultView.clientInformation.mimeTypes.0.constructor
-PASS MimeTypePrototype from inner.document.forms.testForm.0.ownerDocument.defaultView.clientInformation.mimeTypes.0.__proto__
+PASS MimeType from inner.document.forms.testForm.0.ownerDocument.defaultView.navigator.mimeTypes.0
+PASS MimeTypeArray from inner.document.forms.testForm.0.ownerDocument.defaultView.navigator.mimeTypes
+PASS MimeTypeArrayConstructor from inner.document.forms.testForm.0.ownerDocument.defaultView.navigator.mimeTypes.constructor
+PASS MimeTypeArrayPrototype from inner.document.forms.testForm.0.ownerDocument.defaultView.navigator.mimeTypes.__proto__
+PASS MimeTypeConstructor from inner.document.forms.testForm.0.ownerDocument.defaultView.navigator.mimeTypes.0.constructor
+PASS MimeTypePrototype from inner.document.forms.testForm.0.ownerDocument.defaultView.navigator.mimeTypes.0.__proto__
 PASS NamedNodeMap from inner.document.forms.testForm.0.parentNode.attributes
-PASS Navigator from inner.document.forms.testForm.0.ownerDocument.defaultView.clientInformation
-PASS NavigatorPrototype from inner.document.forms.testForm.0.ownerDocument.defaultView.clientInformation.__proto__
+PASS Navigator from inner.document.forms.testForm.0.ownerDocument.defaultView.navigator
+PASS NavigatorPrototype from inner.document.forms.testForm.0.ownerDocument.defaultView.navigator.__proto__
 PASS NodeList from inner.document.forms.testForm
 PASS NodeListConstructor from inner.document.forms.testForm.constructor
 PASS NodeListPrototype from inner.document.forms.testForm.__proto__
-PASS NodePrototype from inner.document.forms.testForm.0.attributes.0.__proto__.__proto__
+PASS NodePrototype from inner.document.forms.testForm.0.ownerDocument.__proto__.__proto__.__proto__
 PASS Object from inner.document.location.__proto__.__proto__
-PASS Plugin from inner.document.forms.testForm.0.ownerDocument.defaultView.clientInformation.mimeTypes.0.enabledPlugin
-PASS PluginArray from inner.document.forms.testForm.0.ownerDocument.defaultView.clientInformation.plugins
-PASS PluginArrayConstructor from inner.document.forms.testForm.0.ownerDocument.defaultView.clientInformation.plugins.constructor
-PASS PluginArrayPrototype from inner.document.forms.testForm.0.ownerDocument.defaultView.clientInformation.plugins.__proto__
-PASS PluginConstructor from inner.document.forms.testForm.0.ownerDocument.defaultView.clientInformation.plugins.0.constructor
-PASS PluginPrototype from inner.document.forms.testForm.0.ownerDocument.defaultView.clientInformation.mimeTypes.0.enabledPlugin.__proto__
+PASS Plugin from inner.document.forms.testForm.0.ownerDocument.defaultView.navigator.plugins.5
+PASS PluginArray from inner.document.forms.testForm.0.ownerDocument.defaultView.navigator.plugins
+PASS PluginArrayConstructor from inner.document.forms.testForm.0.ownerDocument.defaultView.navigator.plugins.constructor
+PASS PluginArrayPrototype from inner.document.forms.testForm.0.ownerDocument.defaultView.navigator.plugins.__proto__
+PASS PluginConstructor from inner.document.forms.testForm.0.ownerDocument.defaultView.navigator.plugins.0.constructor
+PASS PluginPrototype from inner.document.forms.testForm.0.ownerDocument.defaultView.navigator.plugins.5.__proto__
 PASS RGBColor from inner.getComputedStyle(inner.document.body).getPropertyCSSValue(fill).rgbColor
 PASS RGBColorConstructor from inner.getComputedStyle(inner.document.body).getPropertyCSSValue(fill).rgbColor.constructor
 PASS RGBColorPrototype from inner.getComputedStyle(inner.document.body).getPropertyCSSValue(fill).rgbColor.__proto__
@@ -213,9 +213,9 @@
 PASS Text from inner.document.forms.testForm.0.parentNode.lastElementChild.previousElementSibling.firstChild
 PASS TextConstructor from inner.document.forms.testForm.0.attributes.0.lastChild.constructor
 PASS TextPrototype from inner.document.forms.testForm.0.attributes.0.lastChild.__proto__
-PASS TimeRanges from inner.document.forms.testForm.0.previousElementSibling.played
-PASS TimeRangesConstructor from inner.document.forms.testForm.0.previousElementSibling.played.constructor
-PASS TimeRangesPrototype from inner.document.forms.testForm.0.previousElementSibling.played.__proto__
+PASS TimeRanges from inner.document.forms.testForm.0.previousElementSibling.seekable
+PASS TimeRangesConstructor from inner.document.forms.testForm.0.previousElementSibling.seekable.constructor
+PASS TimeRangesPrototype from inner.document.forms.testForm.0.previousElementSibling.seekable.__proto__
 PASS ValidityState from inner.document.forms.testForm.0.0.validity
 PASS ValidityStatePrototype from inner.document.forms.testForm.0.0.validity.__proto__
 PASS WebKitCSSKeyframeRule from inner.document.getElementById("dummyStyle").sheet.cssRules.6.0
@@ -225,6 +225,8 @@
 PASS WebKitCSSKeyframesRuleConstructor from inner.document.getElementById("dummyStyle").sheet.cssRules.6.constructor
 PASS WebKitCSSKeyframesRulePrototype from inner.document.getElementById("dummyStyle").sheet.cssRules.6.__proto__
 Never found Audio
+Never found AudioContext
+Never found AudioPannerNode
 Never found Blob
 Never found CDATASection
 Never found CSSRule
@@ -438,6 +440,7 @@
 Never found SharedWorker
 Never found StyleSheet
 Never found TextMetrics
+Never found WebKitCSSFilterValue
 Never found WebKitCSSMatrix
 Never found WebKitCSSTransformValue
 Never found WebKitPoint

Modified: trunk/LayoutTests/fast/js/delete-syntax-expected.txt (98198 => 98199)


--- trunk/LayoutTests/fast/js/delete-syntax-expected.txt	2011-10-23 05:03:19 UTC (rev 98198)
+++ trunk/LayoutTests/fast/js/delete-syntax-expected.txt	2011-10-23 07:21:19 UTC (rev 98199)
@@ -29,7 +29,7 @@
 PASS RegExp.prototype.compile is regExpPrototypeCompile
 PASS RegExp.prototype.exec is undefined
 PASS RegExp.prototype.test is null
-PASS String(Object.getOwnPropertyNames(Object.prototype)) is "constructor,valueOf,__lookupGetter__,toLocaleString,__defineGetter__,hasOwnProperty,propertyIsEnumerable,toString,__lookupSetter__,isPrototypeOf"
+PASS Object.getOwnPropertyNames(Object.prototype).indexOf('__defineSetter__') is -1
 PASS successfullyParsed is true
 
 TEST COMPLETE

Modified: trunk/LayoutTests/fast/js/script-tests/delete-syntax.js (98198 => 98199)


--- trunk/LayoutTests/fast/js/script-tests/delete-syntax.js	2011-10-23 05:03:19 UTC (rev 98198)
+++ trunk/LayoutTests/fast/js/script-tests/delete-syntax.js	2011-10-23 07:21:19 UTC (rev 98199)
@@ -70,7 +70,7 @@
 
 // Check that once a property is deleted its name is removed from the property name array.
 delete Object.prototype.__defineSetter__;
-shouldBe("String(Object.getOwnPropertyNames(Object.prototype))", '"constructor,valueOf,__lookupGetter__,toLocaleString,__defineGetter__,hasOwnProperty,propertyIsEnumerable,toString,__lookupSetter__,isPrototypeOf"');
+shouldBe("Object.getOwnPropertyNames(Object.prototype).indexOf('__defineSetter__')", "-1");
 
 var successfullyParsed = true;
 

Modified: trunk/LayoutTests/http/tests/inspector/inspector-test.js (98198 => 98199)


--- trunk/LayoutTests/http/tests/inspector/inspector-test.js	2011-10-23 05:03:19 UTC (rev 98198)
+++ trunk/LayoutTests/http/tests/inspector/inspector-test.js	2011-10-23 07:21:19 UTC (rev 98199)
@@ -438,9 +438,9 @@
         }
     }
 
-    var initializationFunctions = [];
+    var initializationFunctions = [ String(initialize_InspectorTest) ];
     for (var name in window) {
-        if (name.indexOf("initialize_") === 0 && typeof window[name] === "function")
+        if (name.indexOf("initialize_") === 0 && typeof window[name] === "function" && name !== "initialize_InspectorTest")
             initializationFunctions.push(window[name].toString());
     }
     var parameters = ["[" + initializationFunctions + "]", test, completeTestCallId];

Modified: trunk/LayoutTests/inspector/storage-panel-dom-storage-expected.txt (98198 => 98199)


--- trunk/LayoutTests/inspector/storage-panel-dom-storage-expected.txt	2011-10-23 05:03:19 UTC (rev 98198)
+++ trunk/LayoutTests/inspector/storage-panel-dom-storage-expected.txt	2011-10-23 07:21:19 UTC (rev 98199)
@@ -4,8 +4,8 @@
 Did show: Local storage
 Did show: Session storage
 Local storage content: 
-KeyValuea0=value0, a6=value6, a8=value8, a4=value4, a2=value2, a1=value1, resource-history{}a9=value9, a3=value3, a7=value7, a5=value5, 
+KeyValueresource-history{}a4=value4, a0=value0, a2=value2, a7=value7, a9=value9, a5=value5, a8=value8, a1=value1, a3=value3, a6=value6, 
 Session storage content: 
-KeyValueb5=value15, b4=value14, b3=value13, b6=value16, b7=value17, b0=value10, b9=value19, b8=value18, b2=value12, b1=value11, 
+KeyValueb4=value14, b3=value13, b0=value10, b9=value19, b5=value15, b6=value16, b7=value17, b8=value18, b1=value11, b2=value12, 
 DONE
 

Modified: trunk/Source/_javascript_Core/ChangeLog (98198 => 98199)


--- trunk/Source/_javascript_Core/ChangeLog	2011-10-23 05:03:19 UTC (rev 98198)
+++ trunk/Source/_javascript_Core/ChangeLog	2011-10-23 07:21:19 UTC (rev 98199)
@@ -1,3 +1,52 @@
+2011-10-21  Geoffrey Garen  <[email protected]>
+
+        Separated string lifetime bits from character buffer state bits
+        https://bugs.webkit.org/show_bug.cgi?id=70673
+
+        Reviewed by Anders Carlsson.
+        
+        Moved the static/immortal bit into the bottom bit of the refcount, and
+        moved all other bits into the high bits of the hash code.
+        
+        This is the first step toward a new Characters/PassString class, and it
+        makes ref/deref slightly more efficient.
+
+        * create_hash_table:
+        * wtf/StringHasher.h:
+        (WTF::StringHasher::hash): Tweaked the string hashing function to leave
+        the top bits clear, so they can be used as flags.
+        
+        Fixed some small differences between the PERL copy of this function and
+        the C++ copy of this function, which could have in theory caused subtle
+        crashes.
+
+        * wtf/text/StringImpl.cpp:
+        (WTF::StringImpl::sharedBuffer):
+        (WTF::StringImpl::createWithTerminatingNullCharacter):
+        * wtf/text/StringImpl.h:
+        (WTF::StringImpl::StringImpl):
+        (WTF::StringImpl::cost): Renamed s_refCountFlagShouldReportedCost to
+        s_didReportExtraCost, since the original name was both self-contradictory
+        and used as a double-negative.
+
+        (WTF::StringImpl::isIdentifier):
+        (WTF::StringImpl::setIsIdentifier):
+        (WTF::StringImpl::hasTerminatingNullCharacter):
+        (WTF::StringImpl::isAtomic):
+        (WTF::StringImpl::setIsAtomic):
+        (WTF::StringImpl::setHash):
+        (WTF::StringImpl::rawHash):
+        (WTF::StringImpl::hasHash):
+        (WTF::StringImpl::existingHash):
+        (WTF::StringImpl::hash):
+        (WTF::StringImpl::hasOneRef):
+        (WTF::StringImpl::ref):
+        (WTF::StringImpl::deref):
+        (WTF::StringImpl::bufferOwnership):
+        (WTF::StringImpl::isStatic): Moved the static/immortal bit into the bottom
+        bit of the refcount. Now, all lifetime information lives in the refcount
+        field. Moved the other bits into the hash code field.
+
 2011-10-21  Filip Pizlo  <[email protected]>
 
         DFG inlining sometimes fails to reset constant references

Modified: trunk/Source/_javascript_Core/create_hash_table (98198 => 98199)


--- trunk/Source/_javascript_Core/create_hash_table	2011-10-23 05:03:19 UTC (rev 98198)
+++ trunk/Source/_javascript_Core/create_hash_table	2011-10-23 07:21:19 UTC (rev 98199)
@@ -179,7 +179,6 @@
 
 # Paul Hsieh's SuperFastHash
 # http://www.azillionmonkeys.com/qed/hash.html
-# Ported from UString..
 sub hashValue($) {
   my @chars = split(/ */, $_[0]);
 
@@ -207,7 +206,7 @@
   }
 
   # Handle end case
-  if ($rem !=0) {
+  if ($rem != 0) {
     $hash += ord($chars[$s]);
     $hash ^= (leftShift($hash, 11)% $EXP2_32);
     $hash += $hash >> 17;
@@ -221,12 +220,16 @@
   $hash += ($hash >> 15);
   $hash = $hash% $EXP2_32;
   $hash ^= (leftShift($hash, 10)% $EXP2_32);
-  
-  # this avoids ever returning a hash code of 0, since that is used to
-  # signal "hash not computed yet", using a value that is likely to be
-  # effectively the same as 0 when the low bits are masked
-  $hash = 0x80000000  if ($hash == 0);
 
+  # Save 6 bits for StringImpl to use as flags.
+  $hash = ($hash >> 6);
+
+  # This avoids ever returning a hash code of 0, since that is used to
+  # signal "hash not computed yet". Setting the high bit maintains
+  # reasonable fidelity to a hash code of 0 because it is likely to yield
+  # exactly 0 when hash lookup masks out the high bits.
+  $hash = (0x80000000 >> 6) if ($hash == 0);
+
   return $hash;
 }
 

Modified: trunk/Source/_javascript_Core/wtf/StringHasher.h (98198 => 98199)


--- trunk/Source/_javascript_Core/wtf/StringHasher.h	2011-10-23 05:03:19 UTC (rev 98198)
+++ trunk/Source/_javascript_Core/wtf/StringHasher.h	2011-10-23 07:21:19 UTC (rev 98199)
@@ -31,8 +31,13 @@
 // Paul Hsieh's SuperFastHash
 // http://www.azillionmonkeys.com/qed/hash.html
 // char* data is interpreted as latin-encoded (zero extended to 16 bits).
+
+// NOTE: This class must stay in sync with the create_hash_table script in
+// _javascript_Core and the CodeGeneratorJS.pm script in WebCore.
 class StringHasher {
 public:
+    static const unsigned flagCount = 6; // Save 6 bits for StringImpl to use as flags.
+
     inline StringHasher()
         : m_hash(stringHashingStartValue)
         , m_hasPendingCharacter(false)
@@ -76,14 +81,16 @@
         result += result >> 15;
         result ^= result << 10;
 
-        // First bit is used in UStringImpl for m_isIdentifier.
-        result &= 0x7fffffff;
+        // Reserving the high bits for flags perserves most of the hash's value,
+        // since hash lookup typically masks out the high bits anyway.
+        result >>= flagCount;
 
         // This avoids ever returning a hash code of 0, since that is used to
-        // signal "hash not computed yet", using a value that is likely to be
-        // effectively the same as 0 when the low bits are masked.
+        // signal "hash not computed yet". Setting the high bit maintains
+        // reasonable fidelity to a hash code of 0 because it is likely to yield
+        // exactly 0 when hash lookup masks out the high bits.
         if (!result)
-            return 0x40000000;
+            result = 0x80000000 >> flagCount;
 
         return result;
     }

Modified: trunk/Source/_javascript_Core/wtf/text/StringImpl.cpp (98198 => 98199)


--- trunk/Source/_javascript_Core/wtf/text/StringImpl.cpp	2011-10-23 05:03:19 UTC (rev 98198)
+++ trunk/Source/_javascript_Core/wtf/text/StringImpl.cpp	2011-10-23 07:21:19 UTC (rev 98199)
@@ -168,7 +168,7 @@
     if (ownership == BufferOwned) {
         ASSERT(!m_sharedBuffer);
         m_sharedBuffer = SharedUChar::create(new SharableUChar(m_data)).leakRef();
-        m_refCountAndFlags = (m_refCountAndFlags & ~s_refCountMaskBufferOwnership) | BufferShared;
+        m_hashAndFlags = (m_hashAndFlags & ~s_hashMaskBufferOwnership) | BufferShared;
     }
 
     ASSERT(bufferOwnership() == BufferShared);
@@ -1156,8 +1156,7 @@
     memcpy(data, string.m_data, length * sizeof(UChar));
     data[length] = 0;
     terminatedString->m_length--;
-    terminatedString->m_hash = string.m_hash;
-    terminatedString->m_refCountAndFlags |= s_refCountFlagHasTerminatingNullCharacter;
+    terminatedString->m_hashAndFlags = (string.m_hashAndFlags & ~s_flagMask) | s_hashFlagHasTerminatingNullCharacter;
     return terminatedString.release();
 }
 

Modified: trunk/Source/_javascript_Core/wtf/text/StringImpl.h (98198 => 98199)


--- trunk/Source/_javascript_Core/wtf/text/StringImpl.h	2011-10-23 05:03:19 UTC (rev 98198)
+++ trunk/Source/_javascript_Core/wtf/text/StringImpl.h	2011-10-23 07:21:19 UTC (rev 98199)
@@ -83,13 +83,13 @@
     // Used to construct static strings, which have an special refCount that can never hit zero.
     // This means that the static string will never be destroyed, which is important because
     // static strings will be shared across threads & ref-counted in a non-threadsafe manner.
-    enum StaticStringConstructType { ConstructStaticString };
-    StringImpl(const UChar* characters, unsigned length, StaticStringConstructType)
-        : m_refCountAndFlags(s_refCountFlagStatic | s_refCountFlagIsIdentifier | BufferOwned)
+    enum ConstructStaticStringTag { ConstructStaticString };
+    StringImpl(const UChar* characters, unsigned length, ConstructStaticStringTag)
+        : m_refCount(s_refCountFlagIsStaticString)
         , m_length(length)
         , m_data(characters)
         , m_buffer(0)
-        , m_hash(0)
+        , m_hashAndFlags(s_hashFlagIsIdentifier | BufferOwned)
     {
         // Ensure that the hash is computed so that AtomicStringHash can call existingHash()
         // with impunity. The empty string is special because it is never entered into
@@ -99,11 +99,11 @@
 
     // Create a normal string with internal storage (BufferInternal)
     StringImpl(unsigned length)
-        : m_refCountAndFlags(s_refCountIncrement | s_refCountFlagShouldReportedCost | BufferInternal)
+        : m_refCount(s_refCountIncrement)
         , m_length(length)
         , m_data(reinterpret_cast<const UChar*>(this + 1))
         , m_buffer(0)
-        , m_hash(0)
+        , m_hashAndFlags(BufferInternal)
     {
         ASSERT(m_data);
         ASSERT(m_length);
@@ -111,11 +111,11 @@
 
     // Create a StringImpl adopting ownership of the provided buffer (BufferOwned)
     StringImpl(const UChar* characters, unsigned length)
-        : m_refCountAndFlags(s_refCountIncrement | s_refCountFlagShouldReportedCost | BufferOwned)
+        : m_refCount(s_refCountIncrement)
         , m_length(length)
         , m_data(characters)
         , m_buffer(0)
-        , m_hash(0)
+        , m_hashAndFlags(BufferOwned)
     {
         ASSERT(m_data);
         ASSERT(m_length);
@@ -123,11 +123,11 @@
 
     // Used to create new strings that are a substring of an existing StringImpl (BufferSubstring)
     StringImpl(const UChar* characters, unsigned length, PassRefPtr<StringImpl> base)
-        : m_refCountAndFlags(s_refCountIncrement | s_refCountFlagShouldReportedCost | BufferSubstring)
+        : m_refCount(s_refCountIncrement)
         , m_length(length)
         , m_data(characters)
         , m_substringBuffer(base.leakRef())
-        , m_hash(0)
+        , m_hashAndFlags(BufferSubstring)
     {
         ASSERT(m_data);
         ASSERT(m_length);
@@ -136,25 +136,16 @@
 
     // Used to construct new strings sharing an existing SharedUChar (BufferShared)
     StringImpl(const UChar* characters, unsigned length, PassRefPtr<SharedUChar> sharedBuffer)
-        : m_refCountAndFlags(s_refCountIncrement | s_refCountFlagShouldReportedCost | BufferShared)
+        : m_refCount(s_refCountIncrement)
         , m_length(length)
         , m_data(characters)
         , m_sharedBuffer(sharedBuffer.leakRef())
-        , m_hash(0)
+        , m_hashAndFlags(BufferShared)
     {
         ASSERT(m_data);
         ASSERT(m_length);
     }
 
-    // For use only by AtomicString's XXXTranslator helpers.
-    void setHash(unsigned hash)
-    {
-        ASSERT(!isStatic());
-        ASSERT(!m_hash);
-        ASSERT(hash == StringHasher::computeHash(m_data, m_length));
-        m_hash = hash;
-    }
-
 public:
     ~StringImpl();
 
@@ -217,7 +208,6 @@
     }
     static PassRefPtr<StringImpl> adopt(StringBuffer&);
 
-    void ref() { m_refCountAndFlags += s_refCountIncrement; }
     unsigned length() const { return m_length; }
     SharedUChar* sharedBuffer();
     const UChar* characters() const { return m_data; }
@@ -228,42 +218,96 @@
         if (bufferOwnership() == BufferSubstring)
             return m_substringBuffer->cost();
 
-        if (m_refCountAndFlags & s_refCountFlagShouldReportedCost) {
-            m_refCountAndFlags &= ~s_refCountFlagShouldReportedCost;
-            return m_length;
-        }
-        return 0;
+        if (m_hashAndFlags & s_hashFlagDidReportCost)
+            return 0;
+
+        m_hashAndFlags |= s_hashFlagDidReportCost;
+        return m_length;
     }
 
-    bool isIdentifier() const { return m_refCountAndFlags & s_refCountFlagIsIdentifier; }
+    bool isIdentifier() const { return m_hashAndFlags & s_hashFlagIsIdentifier; }
     void setIsIdentifier(bool isIdentifier)
     {
         ASSERT(!isStatic());
         if (isIdentifier)
-            m_refCountAndFlags |= s_refCountFlagIsIdentifier;
+            m_hashAndFlags |= s_hashFlagIsIdentifier;
         else
-            m_refCountAndFlags &= ~s_refCountFlagIsIdentifier;
+            m_hashAndFlags &= ~s_hashFlagIsIdentifier;
     }
 
-    bool hasTerminatingNullCharacter() const { return m_refCountAndFlags & s_refCountFlagHasTerminatingNullCharacter; }
+    bool hasTerminatingNullCharacter() const { return m_hashAndFlags & s_hashFlagHasTerminatingNullCharacter; }
 
-    bool isAtomic() const { return m_refCountAndFlags & s_refCountFlagIsAtomic; }
+    bool isAtomic() const { return m_hashAndFlags & s_hashFlagIsAtomic; }
     void setIsAtomic(bool isIdentifier)
     {
         ASSERT(!isStatic());
         if (isIdentifier)
-            m_refCountAndFlags |= s_refCountFlagIsAtomic;
+            m_hashAndFlags |= s_hashFlagIsAtomic;
         else
-            m_refCountAndFlags &= ~s_refCountFlagIsAtomic;
+            m_hashAndFlags &= ~s_hashFlagIsAtomic;
     }
 
-    unsigned hash() const { if (!m_hash) m_hash = StringHasher::computeHash(m_data, m_length); return m_hash; }
-    unsigned existingHash() const { ASSERT(m_hash); return m_hash; }
-    bool hasHash() const { return m_hash; }
+private:
+    // The high bits of 'hash' are always empty, but we prefer to store our flags
+    // in the low bits because it makes them slightly more efficient to access.
+    // So, we shift left and right when setting and getting our hash code.
+    void setHash(unsigned hash) const
+    {
+        ASSERT(!hasHash());
+        ASSERT(hash == StringHasher::computeHash(m_data, m_length)); // Multiple clients assume that StringHasher is the canonical string hash function.
+        ASSERT(!(hash & (s_flagMask << (8 * sizeof(hash) - s_flagCount)))); // Verify that enough high bits are empty.
+        
+        hash <<= s_flagCount;
+        ASSERT(!(hash & m_hashAndFlags)); // Verify that enough low bits are empty after shift.
+        ASSERT(hash); // Verify that 0 is a valid sentinel hash value.
 
-    ALWAYS_INLINE void deref() { m_refCountAndFlags -= s_refCountIncrement; if (!(m_refCountAndFlags & (s_refCountMask | s_refCountFlagStatic))) delete this; }
-    ALWAYS_INLINE bool hasOneRef() const { return (m_refCountAndFlags & (s_refCountMask | s_refCountFlagStatic)) == s_refCountIncrement; }
+        m_hashAndFlags |= hash; // Store hash with flags in low bits.
+    }
 
+    unsigned rawHash() const
+    {
+        return m_hashAndFlags >> s_flagCount;
+    }
+
+public:
+    bool hasHash() const
+    {
+        return rawHash() != 0;
+    }
+
+    unsigned existingHash() const
+    {
+        ASSERT(hasHash());
+        return rawHash();
+    }
+
+    unsigned hash() const
+    {
+        if (!hasHash())
+            setHash(StringHasher::computeHash(m_data, m_length));
+        return existingHash();
+    }
+
+    inline bool hasOneRef() const
+    {
+        return m_refCount == s_refCountIncrement;
+    }
+
+    inline void ref()
+    {
+        m_refCount += s_refCountIncrement;
+    }
+
+    inline void deref()
+    {
+        if (m_refCount == s_refCountIncrement) {
+            delete this;
+            return;
+        }
+
+        m_refCount -= s_refCountIncrement;
+    }
+
     static StringImpl* empty();
 
     static void copyChars(UChar* destination, const UChar* source, unsigned numCharacters)
@@ -351,24 +395,27 @@
 
     static PassRefPtr<StringImpl> createStrippingNullCharactersSlowCase(const UChar*, unsigned length);
     
-    BufferOwnership bufferOwnership() const { return static_cast<BufferOwnership>(m_refCountAndFlags & s_refCountMaskBufferOwnership); }
-    bool isStatic() const { return m_refCountAndFlags & s_refCountFlagStatic; }
+    BufferOwnership bufferOwnership() const { return static_cast<BufferOwnership>(m_hashAndFlags & s_hashMaskBufferOwnership); }
+    bool isStatic() const { return m_refCount & s_refCountFlagIsStaticString; }
     template <class UCharPredicate> PassRefPtr<StringImpl> stripMatchedCharacters(UCharPredicate);
     template <class UCharPredicate> PassRefPtr<StringImpl> simplifyMatchedCharactersToSpace(UCharPredicate);
 
-    // The bottom 7 bits hold flags, the top 25 bits hold the ref count.
-    // When dereferencing StringImpls we check for the ref count AND the
-    // static bit both being zero - static strings are never deleted.
-    static const unsigned s_refCountMask = 0xFFFFFF80;
-    static const unsigned s_refCountIncrement = 0x80;
-    static const unsigned s_refCountFlagStatic = 0x40;
-    static const unsigned s_refCountFlagHasTerminatingNullCharacter = 0x20;
-    static const unsigned s_refCountFlagIsAtomic = 0x10;
-    static const unsigned s_refCountFlagShouldReportedCost = 0x8;
-    static const unsigned s_refCountFlagIsIdentifier = 0x4;
-    static const unsigned s_refCountMaskBufferOwnership = 0x3;
+    // The bottom bit in the ref count indicates a static (immortal) string.
+    static const unsigned s_refCountFlagIsStaticString = 0x1;
+    static const unsigned s_refCountIncrement = 0x2; // This allows us to ref / deref without disturbing the static string flag.
 
-    unsigned m_refCountAndFlags;
+    // The bottom 6 bits in the hash are flags.
+    static const unsigned s_flagCount = 6;
+    static const unsigned s_flagMask = (1u << s_flagCount) - 1;
+    COMPILE_ASSERT(s_flagCount == StringHasher::flagCount, StringHasher_reserves_enough_bits_for_StringImpl_flags);
+
+    static const unsigned s_hashFlagHasTerminatingNullCharacter = 1u << 5;
+    static const unsigned s_hashFlagIsAtomic = 1u << 4;
+    static const unsigned s_hashFlagDidReportCost = 1u << 3;
+    static const unsigned s_hashFlagIsIdentifier = 1u << 2;
+    static const unsigned s_hashMaskBufferOwnership = 1u | (1u << 1);
+
+    unsigned m_refCount;
     unsigned m_length;
     const UChar* m_data;
     union {
@@ -376,7 +423,7 @@
         StringImpl* m_substringBuffer;
         SharedUChar* m_sharedBuffer;
     };
-    mutable unsigned m_hash;
+    mutable unsigned m_hashAndFlags;
 };
 
 bool equal(const StringImpl*, const StringImpl*);

Modified: trunk/Source/WebCore/ChangeLog (98198 => 98199)


--- trunk/Source/WebCore/ChangeLog	2011-10-23 05:03:19 UTC (rev 98198)
+++ trunk/Source/WebCore/ChangeLog	2011-10-23 07:21:19 UTC (rev 98199)
@@ -1,3 +1,13 @@
+2011-10-22  Geoffrey Garen  <[email protected]>
+
+        Separated string lifetime bits from character buffer state bits
+        https://bugs.webkit.org/show_bug.cgi?id=70673
+
+        Reviewed by Anders Carlsson.
+
+        * bindings/scripts/CodeGeneratorJS.pm:
+        (GenerateHashValue): Updated for string hashing changes in _javascript_Core.
+
 2011-10-22  Pratik Solanki  <[email protected]>
 
         HTTPBodyStream in NSURLRequest gets lost when using CFNetwork loader

Modified: trunk/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm (98198 => 98199)


--- trunk/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm	2011-10-23 05:03:19 UTC (rev 98198)
+++ trunk/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm	2011-10-23 07:21:19 UTC (rev 98199)
@@ -3038,7 +3038,8 @@
     push(@implContent, "static JSC_CONST_HASHTABLE HashTable $name = { $compactSize, $compactSizeMask, $nameEntries, 0 };\n");
 }
 
-# Internal helper
+# Paul Hsieh's SuperFastHash
+# http://www.azillionmonkeys.com/qed/hash.html
 sub GenerateHashValue
 {
     my $object = shift;
@@ -3048,16 +3049,16 @@
     # This hash is designed to work on 16-bit chunks at a time. But since the normal case
     # (above) is to hash UTF-16 characters, we just treat the 8-bit chars as if they
     # were 16-bit chunks, which should give matching results
-
+    
     my $EXP2_32 = 4294967296;
-
+    
     my $hash = 0x9e3779b9;
     my $l    = scalar @chars; #I wish this was in Ruby --- Maks
     my $rem  = $l & 1;
     $l = $l >> 1;
-
+    
     my $s = 0;
-
+    
     # Main loop
     for (; $l > 0; $l--) {
         $hash   += ord($chars[$s]);
@@ -3067,14 +3068,14 @@
         $hash += $hash >> 11;
         $hash %= $EXP2_32;
     }
-
+    
     # Handle end case
     if ($rem != 0) {
         $hash += ord($chars[$s]);
         $hash ^= (leftShift($hash, 11)% $EXP2_32);
         $hash += $hash >> 17;
     }
-
+    
     # Force "avalanching" of final 127 bits
     $hash ^= leftShift($hash, 3);
     $hash += ($hash >> 5);
@@ -3083,12 +3084,16 @@
     $hash += ($hash >> 15);
     $hash = $hash% $EXP2_32;
     $hash ^= (leftShift($hash, 10)% $EXP2_32);
-
-    # this avoids ever returning a hash code of 0, since that is used to
-    # signal "hash not computed yet", using a value that is likely to be
-    # effectively the same as 0 when the low bits are masked
-    $hash = 0x80000000 if ($hash == 0);
-
+    
+    # Save 6 bits for StringImpl to use as flags.
+    $hash = ($hash >> 6);
+    
+    # This avoids ever returning a hash code of 0, since that is used to
+    # signal "hash not computed yet". Setting the high bit maintains
+    # reasonable fidelity to a hash code of 0 because it is likely to yield
+    # exactly 0 when hash lookup masks out the high bits.
+    $hash = (0x80000000 >> 6) if ($hash == 0);
+    
     return $hash;
 }
 
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to