Object.prototype.toString() should use cached strings for null/undefined.
authorakling@apple.com <akling@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 25 May 2014 03:50:43 +0000 (03:50 +0000)
committerakling@apple.com <akling@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 25 May 2014 03:50:43 +0000 (03:50 +0000)
<https://webkit.org/b/133261>

Normally, when calling Object.prototype.toString() on a regular object,
we'd cache the result of the stringification on the object's structure,
making repeated calls fast.

For null and undefined, we were not as smart. We'd instead construct a
new string with either "[object Null]" or "[object Undefined]" each time.

This was exposed by Dromaeo's JS library tests, where some prototype.js
subtests generate millions of strings this way.

This patch adds two VM-permanent cached strings to the SmallStrings.
Looks like ~10% speed-up on Dromaeo/jslib-traverse-prototype.html

Reviewed by Darin Adler.

* runtime/ObjectPrototype.cpp:
(JSC::objectProtoFuncToString):
* runtime/SmallStrings.cpp:
(JSC::SmallStrings::SmallStrings):
(JSC::SmallStrings::initializeCommonStrings):
(JSC::SmallStrings::visitStrongReferences):
* runtime/SmallStrings.h:
(JSC::SmallStrings::nullObjectString):
(JSC::SmallStrings::undefinedObjectString):

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

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/ObjectPrototype.cpp
Source/JavaScriptCore/runtime/SmallStrings.cpp
Source/JavaScriptCore/runtime/SmallStrings.h

index d4888db..8e1c0a3 100644 (file)
@@ -1,3 +1,33 @@
+2014-05-24  Andreas Kling  <akling@apple.com>
+
+        Object.prototype.toString() should use cached strings for null/undefined.
+        <https://webkit.org/b/133261>
+
+        Normally, when calling Object.prototype.toString() on a regular object,
+        we'd cache the result of the stringification on the object's structure,
+        making repeated calls fast.
+
+        For null and undefined, we were not as smart. We'd instead construct a
+        new string with either "[object Null]" or "[object Undefined]" each time.
+
+        This was exposed by Dromaeo's JS library tests, where some prototype.js
+        subtests generate millions of strings this way.
+
+        This patch adds two VM-permanent cached strings to the SmallStrings.
+        Looks like ~10% speed-up on Dromaeo/jslib-traverse-prototype.html
+
+        Reviewed by Darin Adler.
+
+        * runtime/ObjectPrototype.cpp:
+        (JSC::objectProtoFuncToString):
+        * runtime/SmallStrings.cpp:
+        (JSC::SmallStrings::SmallStrings):
+        (JSC::SmallStrings::initializeCommonStrings):
+        (JSC::SmallStrings::visitStrongReferences):
+        * runtime/SmallStrings.h:
+        (JSC::SmallStrings::nullObjectString):
+        (JSC::SmallStrings::undefinedObjectString):
+
 2014-05-23  Mark Hahnenberg  <mhahnenberg@apple.com>
 
         Remove operationCallGetter
index 7daf0d6..ecd339a 100644 (file)
@@ -215,7 +215,7 @@ EncodedJSValue JSC_HOST_CALL objectProtoFuncToString(ExecState* exec)
     VM& vm = exec->vm();
     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
     if (thisValue.isUndefinedOrNull())
-        return JSValue::encode(jsNontrivialString(&vm, String(thisValue.isUndefined() ? ASCIILiteral("[object Undefined]") : ASCIILiteral("[object Null]"))));
+        return JSValue::encode(thisValue.isUndefined() ? vm.smallStrings.undefinedObjectString() : vm.smallStrings.nullObjectString());
     JSObject* thisObject = thisValue.toObject(exec);
 
     JSString* result = thisObject->structure(vm)->objectToStringValue();
index 54cef97..a4bdb0c 100644 (file)
@@ -67,6 +67,8 @@ SmallStrings::SmallStrings()
 #define JSC_COMMON_STRINGS_ATTRIBUTE_INITIALIZE(name) , m_##name(0)
     JSC_COMMON_STRINGS_EACH_NAME(JSC_COMMON_STRINGS_ATTRIBUTE_INITIALIZE)
 #undef JSC_COMMON_STRINGS_ATTRIBUTE_INITIALIZE
+    , m_nullObjectString(nullptr)
+    , m_undefinedObjectString(nullptr)
 {
     COMPILE_ASSERT(singleCharacterStringCount == sizeof(m_singleCharacterStrings) / sizeof(m_singleCharacterStrings[0]), IsNumCharactersConstInSyncWithClassUsage);
 
@@ -82,6 +84,8 @@ void SmallStrings::initializeCommonStrings(VM& vm)
 #define JSC_COMMON_STRINGS_ATTRIBUTE_INITIALIZE(name) initialize(&vm, m_##name, #name);
     JSC_COMMON_STRINGS_EACH_NAME(JSC_COMMON_STRINGS_ATTRIBUTE_INITIALIZE)
 #undef JSC_COMMON_STRINGS_ATTRIBUTE_INITIALIZE
+    initialize(&vm, m_nullObjectString, "[object Null]");
+    initialize(&vm, m_undefinedObjectString, "[object Undefined]");
 }
 
 void SmallStrings::visitStrongReferences(SlotVisitor& visitor)
@@ -92,6 +96,8 @@ void SmallStrings::visitStrongReferences(SlotVisitor& visitor)
 #define JSC_COMMON_STRINGS_ATTRIBUTE_VISIT(name) visitor.appendUnbarrieredPointer(&m_##name);
     JSC_COMMON_STRINGS_EACH_NAME(JSC_COMMON_STRINGS_ATTRIBUTE_VISIT)
 #undef JSC_COMMON_STRINGS_ATTRIBUTE_VISIT
+    visitor.appendUnbarrieredPointer(&m_nullObjectString);
+    visitor.appendUnbarrieredPointer(&m_undefinedObjectString);
 }
 
 SmallStrings::~SmallStrings()
index 0e8646a..e3d3981 100644 (file)
@@ -86,6 +86,9 @@ namespace JSC {
         JSC_COMMON_STRINGS_EACH_NAME(JSC_COMMON_STRINGS_ACCESSOR_DEFINITION)
 #undef JSC_COMMON_STRINGS_ACCESSOR_DEFINITION
 
+        JSString* nullObjectString() const { return m_nullObjectString; }
+        JSString* undefinedObjectString() const { return m_undefinedObjectString; }
+
     private:
         static const unsigned singleCharacterStringCount = maxSingleCharacterString + 1;
 
@@ -98,6 +101,8 @@ namespace JSC {
 #define JSC_COMMON_STRINGS_ATTRIBUTE_DECLARATION(name) JSString* m_##name;
         JSC_COMMON_STRINGS_EACH_NAME(JSC_COMMON_STRINGS_ATTRIBUTE_DECLARATION)
 #undef JSC_COMMON_STRINGS_ATTRIBUTE_DECLARATION
+        JSString* m_nullObjectString;
+        JSString* m_undefinedObjectString;
         JSString* m_singleCharacterStrings[singleCharacterStringCount];
         OwnPtr<SmallStringsStorage> m_storage;
     };