[SCM] WebKit Debian packaging branch, webkit-1.2, updated. upstream/1.1.90-6072-g9a69373

oliver at apple.com oliver at apple.com
Wed Apr 7 23:28:02 UTC 2010


The following commit has been merged in the webkit-1.2 branch:
commit bd8e770a6be16888f07dc7e4f430071ad97e907c
Author: oliver at apple.com <oliver at apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Date:   Tue Nov 10 02:19:04 2009 +0000

    Can cache prototype lookups on uncacheable dictionaries.
    https://bugs.webkit.org/show_bug.cgi?id=31198
    
    Reviewed by Gavin Barraclough.
    
    Replace fromDictionaryTransition with flattenDictionaryObject and
    flattenDictionaryStructure.  This change is necessary as we need to
    guarantee that our attempt to convert away from a dictionary structure
    will definitely succeed, and in some cases this requires mutating the
    object storage itself.
    
    git-svn-id: http://svn.webkit.org/repository/webkit/trunk@50704 268f45cc-cd09-0410-ab3c-d52691b4dbfc

diff --git a/JavaScriptCore/ChangeLog b/JavaScriptCore/ChangeLog
index 5f2d59f..3b13490 100644
--- a/JavaScriptCore/ChangeLog
+++ b/JavaScriptCore/ChangeLog
@@ -1,3 +1,32 @@
+2009-11-09  Oliver Hunt  <oliver at apple.com>
+
+        Reviewed by Gavin Barraclough.
+
+        Can cache prototype lookups on uncacheable dictionaries.
+        https://bugs.webkit.org/show_bug.cgi?id=31198
+
+        Replace fromDictionaryTransition with flattenDictionaryObject and
+        flattenDictionaryStructure.  This change is necessary as we need to
+        guarantee that our attempt to convert away from a dictionary structure
+        will definitely succeed, and in some cases this requires mutating the
+        object storage itself.
+
+        * interpreter/Interpreter.cpp:
+        (JSC::Interpreter::tryCacheGetByID):
+        * jit/JITStubs.cpp:
+        (JSC::JITThunks::tryCacheGetByID):
+        (JSC::DEFINE_STUB_FUNCTION):
+        * runtime/BatchedTransitionOptimizer.h:
+        (JSC::BatchedTransitionOptimizer::~BatchedTransitionOptimizer):
+        * runtime/JSObject.h:
+        (JSC::JSObject::flattenDictionaryObject):
+        * runtime/Operations.h:
+        (JSC::normalizePrototypeChain):
+        * runtime/Structure.cpp:
+        (JSC::Structure::flattenDictionaryStructure):
+        (JSC::comparePropertyMapEntryIndices):
+        * runtime/Structure.h:
+
 2009-11-09  Laszlo Gombos  <laszlo.1.gombos at nokia.com>
 
         Not reviewed, build fix.
diff --git a/JavaScriptCore/interpreter/Interpreter.cpp b/JavaScriptCore/interpreter/Interpreter.cpp
index 229dffc..8d32342 100644
--- a/JavaScriptCore/interpreter/Interpreter.cpp
+++ b/JavaScriptCore/interpreter/Interpreter.cpp
@@ -1042,7 +1042,7 @@ NEVER_INLINE void Interpreter::tryCacheGetByID(CallFrame* callFrame, CodeBlock*
         // Since we're accessing a prototype in a loop, it's a good bet that it
         // should not be treated as a dictionary.
         if (baseObject->structure()->isDictionary())
-            baseObject->setStructure(Structure::fromDictionaryTransition(baseObject->structure()));
+            baseObject->flattenDictionaryObject();
 
         ASSERT(!baseObject->structure()->isUncacheableDictionary());
 
diff --git a/JavaScriptCore/jit/JITStubs.cpp b/JavaScriptCore/jit/JITStubs.cpp
index 7acd04c..b33319d 100644
--- a/JavaScriptCore/jit/JITStubs.cpp
+++ b/JavaScriptCore/jit/JITStubs.cpp
@@ -771,7 +771,7 @@ NEVER_INLINE void JITThunks::tryCacheGetByID(CallFrame* callFrame, CodeBlock* co
         // Since we're accessing a prototype in a loop, it's a good bet that it
         // should not be treated as a dictionary.
         if (slotBaseObject->structure()->isDictionary())
-            slotBaseObject->setStructure(Structure::fromDictionaryTransition(slotBaseObject->structure()));
+            slotBaseObject->flattenDictionaryObject();
         
         stubInfo->initGetByIdProto(structure, slotBaseObject->structure());
 
@@ -1181,7 +1181,7 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_method_check)
         // Since we're accessing a prototype in a loop, it's a good bet that it
         // should not be treated as a dictionary.
         if (slotBaseObject->structure()->isDictionary())
-            slotBaseObject->setStructure(Structure::fromDictionaryTransition(slotBaseObject->structure()));
+            slotBaseObject->flattenDictionaryObject();
 
         // The result fetched should always be the callee!
         ASSERT(result == JSValue(callee));
@@ -1334,7 +1334,7 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_proto_list)
         // Since we're accessing a prototype in a loop, it's a good bet that it
         // should not be treated as a dictionary.
         if (slotBaseObject->structure()->isDictionary())
-            slotBaseObject->setStructure(Structure::fromDictionaryTransition(slotBaseObject->structure()));
+            slotBaseObject->flattenDictionaryObject();
 
         int listIndex;
         PolymorphicAccessStructureList* prototypeStructureList = getPolymorphicAccessStructureListSlot(stubInfo, listIndex);
diff --git a/JavaScriptCore/runtime/BatchedTransitionOptimizer.h b/JavaScriptCore/runtime/BatchedTransitionOptimizer.h
index 929a5e7..74089a5 100644
--- a/JavaScriptCore/runtime/BatchedTransitionOptimizer.h
+++ b/JavaScriptCore/runtime/BatchedTransitionOptimizer.h
@@ -43,7 +43,7 @@ namespace JSC {
 
         ~BatchedTransitionOptimizer()
         {
-            m_object->setStructure(Structure::fromDictionaryTransition(m_object->structure()));
+            m_object->flattenDictionaryObject();
         }
 
     private:
diff --git a/JavaScriptCore/runtime/JSObject.h b/JavaScriptCore/runtime/JSObject.h
index 5a89c40..d8375ac 100644
--- a/JavaScriptCore/runtime/JSObject.h
+++ b/JavaScriptCore/runtime/JSObject.h
@@ -210,6 +210,11 @@ namespace JSC {
             return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags));
         }
 
+        void flattenDictionaryObject()
+        {
+            m_structure->flattenDictionaryStructure(this);
+        }
+
     protected:
         static const unsigned StructureFlags = 0;
 
diff --git a/JavaScriptCore/runtime/Operations.h b/JavaScriptCore/runtime/Operations.h
index 1aa68b3..3bfb982 100644
--- a/JavaScriptCore/runtime/Operations.h
+++ b/JavaScriptCore/runtime/Operations.h
@@ -243,7 +243,7 @@ namespace JSC {
             // Since we're accessing a prototype in a loop, it's a good bet that it
             // should not be treated as a dictionary.
             if (cell->structure()->isDictionary())
-                asObject(cell)->setStructure(Structure::fromDictionaryTransition(cell->structure()));
+                asObject(cell)->flattenDictionaryObject();
 
             ++count;
         }
@@ -265,7 +265,7 @@ namespace JSC {
             // Since we're accessing a prototype in a loop, it's a good bet that it
             // should not be treated as a dictionary.
             if (base->structure()->isDictionary())
-                asObject(base)->setStructure(Structure::fromDictionaryTransition(base->structure()));
+                asObject(base)->flattenDictionaryObject();
 
             ++count;
         }
diff --git a/JavaScriptCore/runtime/Structure.cpp b/JavaScriptCore/runtime/Structure.cpp
index 2f3331f..e4c9ac3 100644
--- a/JavaScriptCore/runtime/Structure.cpp
+++ b/JavaScriptCore/runtime/Structure.cpp
@@ -77,6 +77,8 @@ static HashSet<Structure*>& ignoreSet = *(new HashSet<Structure*>);
 static HashSet<Structure*>& liveStructureSet = *(new HashSet<Structure*>);
 #endif
 
+static int comparePropertyMapEntryIndices(const void* a, const void* b);
+
 void Structure::dumpStatistics()
 {
 #if DUMP_STRUCTURE_ID_STATISTICS
@@ -534,20 +536,47 @@ PassRefPtr<Structure> Structure::toUncacheableDictionaryTransition(Structure* st
     return toDictionaryTransition(structure, UncachedDictionaryKind);
 }
 
-PassRefPtr<Structure> Structure::fromDictionaryTransition(Structure* structure)
+PassRefPtr<Structure> Structure::flattenDictionaryStructure(JSObject* object)
 {
-    ASSERT(structure->isDictionary());
-
-    // Since dictionary Structures are not shared, and no opcodes specialize
-    // for them, we don't need to allocate a new Structure when transitioning
-    // to non-dictionary status.
-
-    // FIMXE: We can make this more efficient by canonicalizing the Structure (draining the
-    // deleted offsets vector) before transitioning from dictionary. 
-    if (!structure->m_propertyTable || !structure->m_propertyTable->deletedOffsets || structure->m_propertyTable->deletedOffsets->isEmpty())
-        structure->m_dictionaryKind = NoneDictionaryKind;
+    ASSERT(isDictionary());
+    if (isUncacheableDictionary()) {
+        ASSERT(m_propertyTable);
+        Vector<PropertyMapEntry*> sortedPropertyEntries(m_propertyTable->keyCount);
+        PropertyMapEntry** p = sortedPropertyEntries.data();
+        unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
+        for (unsigned i = 1; i <= entryCount; i++) {
+            if (m_propertyTable->entries()[i].key)
+                *p++ = &m_propertyTable->entries()[i];
+        }
+        size_t propertyCount = p - sortedPropertyEntries.data();
+        qsort(sortedPropertyEntries.data(), propertyCount, sizeof(PropertyMapEntry*), comparePropertyMapEntryIndices);
+        sortedPropertyEntries.resize(propertyCount);
+
+        // We now have the properties currently defined on this object
+        // in the order that they are expected to be in, but we need to
+        // reorder the storage, so we have to copy the current values out
+        Vector<JSValue> values(propertyCount);
+        unsigned anonymousSlotCount = m_propertyTable->anonymousSlotCount;
+        for (unsigned i = 0; i < propertyCount; i++) {
+            PropertyMapEntry* entry = sortedPropertyEntries[i];
+            values[i] = object->getDirectOffset(entry->offset);
+            // Update property table to have the new property offsets
+            entry->offset = anonymousSlotCount + i;
+            entry->index = i;
+        }
+        
+        // Copy the original property values into their final locations
+        for (unsigned i = 0; i < propertyCount; i++)
+            object->putDirectOffset(anonymousSlotCount + i, values[i]);
+
+        if (m_propertyTable->deletedOffsets) {
+            delete m_propertyTable->deletedOffsets;
+            m_propertyTable->deletedOffsets = 0;
+        }
+    }
 
-    return structure;
+    m_dictionaryKind = NoneDictionaryKind;
+    return this;
 }
 
 size_t Structure::addPropertyWithoutTransition(const Identifier& propertyName, unsigned attributes, JSCell* specificValue)
@@ -1004,7 +1033,7 @@ void Structure::rehashPropertyMapHashTable(unsigned newTableSize)
     checkConsistency();
 }
 
-static int comparePropertyMapEntryIndices(const void* a, const void* b)
+int comparePropertyMapEntryIndices(const void* a, const void* b)
 {
     unsigned ia = static_cast<PropertyMapEntry* const*>(a)[0]->index;
     unsigned ib = static_cast<PropertyMapEntry* const*>(b)[0]->index;
diff --git a/JavaScriptCore/runtime/Structure.h b/JavaScriptCore/runtime/Structure.h
index f355c53..e8356f1 100644
--- a/JavaScriptCore/runtime/Structure.h
+++ b/JavaScriptCore/runtime/Structure.h
@@ -74,7 +74,8 @@ namespace JSC {
         static PassRefPtr<Structure> getterSetterTransition(Structure*);
         static PassRefPtr<Structure> toCacheableDictionaryTransition(Structure*);
         static PassRefPtr<Structure> toUncacheableDictionaryTransition(Structure*);
-        static PassRefPtr<Structure> fromDictionaryTransition(Structure*);
+
+        PassRefPtr<Structure> flattenDictionaryStructure(JSObject*);
 
         ~Structure();
 
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 9404c67..2197fad 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,17 @@
+2009-11-09  Oliver Hunt  <oliver at apple.com>
+
+        Reviewed by Gavin Barraclough.
+
+        Can cache prototype lookups on uncacheable dictionaries.
+        https://bugs.webkit.org/show_bug.cgi?id=31198
+
+        Add tests for lookup on uncacheable prototype.
+
+        * fast/js/dictionary-prototype-caching-expected.txt: Added.
+        * fast/js/dictionary-prototype-caching.html: Added.
+        * fast/js/script-tests/dictionary-prototype-caching.js: Added.
+        (protoTest):
+
 2009-11-09  Anders Carlsson  <andersca at apple.com>
 
         Reviewed by Darin Adler and Dan Bernstein.
diff --git a/LayoutTests/fast/js/dictionary-prototype-caching-expected.txt b/LayoutTests/fast/js/dictionary-prototype-caching-expected.txt
new file mode 100644
index 0000000..b35f5e1
--- /dev/null
+++ b/LayoutTests/fast/js/dictionary-prototype-caching-expected.txt
@@ -0,0 +1,11 @@
+Test to ensure correct behaviour of prototype caching with dictionary prototypes
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS protoTest(o) is 'PASS'
+PASS protoTest(o) is undefined.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/js/dictionary-prototype-caching.html b/LayoutTests/fast/js/dictionary-prototype-caching.html
new file mode 100644
index 0000000..cc6b88f
--- /dev/null
+++ b/LayoutTests/fast/js/dictionary-prototype-caching.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<link rel="stylesheet" href="resources/js-test-style.css">
+<script src="resources/js-test-pre.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="script-tests/dictionary-prototype-caching.js"></script>
+<script src="resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/js/script-tests/dictionary-prototype-caching.js b/LayoutTests/fast/js/script-tests/dictionary-prototype-caching.js
new file mode 100644
index 0000000..68ba1c6
--- /dev/null
+++ b/LayoutTests/fast/js/script-tests/dictionary-prototype-caching.js
@@ -0,0 +1,57 @@
+description("Test to ensure correct behaviour of prototype caching with dictionary prototypes");
+
+function protoTest(o) {
+    return o.protoProp;
+}
+
+var proto = {protoProp: "PASS", propToRemove: "foo"};
+var o = { __proto__: proto };
+
+delete proto.propToRemove;
+
+// Prototype lookup caching will attempt to convert proto back to an ordinary structure
+protoTest(o);
+protoTest(o);
+protoTest(o);
+shouldBe("protoTest(o)", "'PASS'");
+delete proto.protoProp;
+proto.fakeProtoProp = "FAIL";
+shouldBeUndefined("protoTest(o)");
+
+function protoTest2(o) {
+    return o.b;
+}
+
+var proto = {a:1, b:"meh", c:2};
+var o = { __proto__: proto };
+
+delete proto.b;
+proto.d = 3;
+protoTest2(o);
+protoTest2(o);
+protoTest2(o);
+var protoKeys = [];
+for (var i in proto)
+    protoKeys.push(proto[i]);
+
+shouldBe("protoKeys", "[1,2,3]");
+
+function protoTest3(o) {
+    return o.b;
+}
+
+var proto = {a:1, b:"meh", c:2};
+var o = { __proto__: proto };
+
+delete proto.b;
+protoTest2(o);
+protoTest2(o);
+protoTest2(o);
+proto.d = 3;
+var protoKeys = [];
+for (var i in proto)
+    protoKeys.push(proto[i]);
+
+shouldBe("protoKeys", "[1,2,3]");
+
+successfullyParsed = true;

-- 
WebKit Debian packaging



More information about the Pkg-webkit-commits mailing list