Ignore:
Timestamp:
Feb 25, 2009, 3:44:07 PM (16 years ago)
Author:
[email protected]
Message:

JavaScriptCore:

2009-02-25 Geoffrey Garen <[email protected]>

Reviewed by Maciej Stachowiak.


Fixed <rdar://problem/6611174> REGRESSION (r36701): Unable to select
messages on hotmail (24052)


The bug was that for-in enumeration used a cached prototype chain without
validating that it was up-to-date.


This led me to refactor prototype chain caching so it was easier to work
with and harder to get wrong.


After a bit of inlining, this patch is performance-neutral on SunSpider
and the v8 benchmarks.

  • interpreter/Interpreter.cpp: (JSC::Interpreter::tryCachePutByID): (JSC::Interpreter::tryCacheGetByID):
  • jit/JITStubs.cpp: (JSC::JITStubs::tryCachePutByID): (JSC::JITStubs::tryCacheGetByID): (JSC::JITStubs::cti_op_get_by_id_proto_list): Use the new refactored goodness. See lines beginning with "-" and smile.
  • runtime/JSGlobalObject.h: (JSC::Structure::prototypeForLookup): A shout out to const.
  • runtime/JSPropertyNameIterator.h: (JSC::JSPropertyNameIterator::next): We can use a pointer comparison to see if our cached structure chain is equal to the object's structure chain, since in the case of a cache hit, we share references to the same structure chain.
  • runtime/Operations.h: (JSC::countPrototypeChainEntriesAndCheckForProxies): Use the new refactored goodness.
  • runtime/PropertyNameArray.h: (JSC::PropertyNameArray::PropertyNameArray): (JSC::PropertyNameArray::setShouldCache): (JSC::PropertyNameArray::shouldCache): Renamed "cacheable" to "shouldCache" to communicate that the client is specifying a recommendation, not a capability.


  • runtime/Structure.cpp: (JSC::Structure::Structure): No need to initialize a RefPtr. (JSC::Structure::getEnumerablePropertyNames): Moved some code into helper functions.

(JSC::Structure::prototypeChain): New centralized accessor for a prototype
chain. Revalidates on every access, since the objects in the prototype
chain may have mutated.

(JSC::Structure::isValid): Helper function for revalidating a cached
prototype chain.

(JSC::Structure::getEnumerableNamesFromPropertyTable):
(JSC::Structure::getEnumerableNamesFromClassInfoTable): Factored out of
getEnumerablePropertyNames.

  • runtime/Structure.h:
  • runtime/StructureChain.cpp: (JSC::StructureChain::StructureChain):
  • runtime/StructureChain.h: (JSC::StructureChain::create): No need for structureChainsAreEqual, since we use pointer equality now. Refactored StructureChain to make a little more sense and eliminate special cases for null prototypes.

LayoutTests:

2009-02-24 Geoffrey Garen <[email protected]>

Reviewed by Maciej Stachowiak.


Added a test for <rdar://problem/6611174> REGRESSION (r36701): Unable to
select messages on hotmail (24052)

  • fast/js/for-in-cached-expected.txt: Added.
  • fast/js/for-in-cached.html: Added.
  • fast/js/resources/for-in-cached.js: Added. (forIn):
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/runtime/Structure.cpp

    r40608 r41232  
    124124    : m_typeInfo(typeInfo)
    125125    , m_prototype(prototype)
    126     , m_cachedPrototypeChain(0)
    127     , m_previous(0)
    128     , m_nameInPrevious(0)
    129126    , m_propertyTable(0)
    130127    , m_propertyStorageCapacity(JSObject::inlineStorageCapacity)
     
    290287void Structure::getEnumerablePropertyNames(ExecState* exec, PropertyNameArray& propertyNames, JSObject* baseObject)
    291288{
    292     bool shouldCache = propertyNames.cacheable() && !(propertyNames.size() || m_isDictionary);
     289    bool shouldCache = propertyNames.shouldCache() && !(propertyNames.size() || m_isDictionary);
     290
     291    if (shouldCache && m_cachedPropertyNameArrayData) {
     292        if (m_cachedPropertyNameArrayData->cachedPrototypeChain() == prototypeChain(exec)) {
     293            propertyNames.setData(m_cachedPropertyNameArrayData);
     294            return;
     295        }
     296        clearEnumerationCache();
     297    }
     298
     299    getEnumerableNamesFromPropertyTable(propertyNames);
     300    getEnumerableNamesFromClassInfoTable(exec, baseObject->classInfo(), propertyNames);
     301
     302    if (m_prototype.isObject()) {
     303        propertyNames.setShouldCache(false); // No need for our prototypes to waste memory on caching, since they're not being enumerated directly.
     304        asObject(m_prototype)->getPropertyNames(exec, propertyNames);
     305    }
    293306
    294307    if (shouldCache) {
    295         if (m_cachedPropertyNameArrayData) {
    296             if (structureChainsAreEqual(m_cachedPropertyNameArrayData->cachedPrototypeChain(), cachedPrototypeChain())) {
    297                 propertyNames.setData(m_cachedPropertyNameArrayData);
    298                 return;
    299             }
    300         }
    301         propertyNames.setCacheable(false);
    302     }
    303 
    304     getEnumerablePropertyNamesInternal(propertyNames);
    305 
    306     // Add properties from the static hashtables of properties
    307     for (const ClassInfo* info = baseObject->classInfo(); info; info = info->parentClass) {
    308         const HashTable* table = info->propHashTable(exec);
    309         if (!table)
    310             continue;
    311         table->initializeIfNeeded(exec);
    312         ASSERT(table->table);
    313 #if ENABLE(PERFECT_HASH_SIZE)
    314         int hashSizeMask = table->hashSizeMask;
    315 #else
    316         int hashSizeMask = table->compactSize - 1;
    317 #endif
    318         const HashEntry* entry = table->table;
    319         for (int i = 0; i <= hashSizeMask; ++i, ++entry) {
    320             if (entry->key() && !(entry->attributes() & DontEnum))
    321                 propertyNames.add(entry->key());
    322         }
    323     }
    324 
    325     if (m_prototype.isObject())
    326         asObject(m_prototype)->getPropertyNames(exec, propertyNames);
    327 
    328     if (shouldCache) {
    329         if (m_cachedPropertyNameArrayData)
    330             m_cachedPropertyNameArrayData->setCachedStructure(0);
    331 
    332308        m_cachedPropertyNameArrayData = propertyNames.data();
    333 
    334         StructureChain* chain = cachedPrototypeChain();
    335         if (!chain)
    336             chain = createCachedPrototypeChain();
    337         m_cachedPropertyNameArrayData->setCachedPrototypeChain(chain);
     309        m_cachedPropertyNameArrayData->setCachedPrototypeChain(prototypeChain(exec));
    338310        m_cachedPropertyNameArrayData->setCachedStructure(this);
    339311    }
     
    535507    clearEnumerationCache();
    536508    return offset;
    537 }
    538 
    539 StructureChain* Structure::createCachedPrototypeChain()
    540 {
    541     ASSERT(typeInfo().type() == ObjectType);
    542     ASSERT(!m_cachedPrototypeChain);
    543 
    544     JSValuePtr prototype = storedPrototype();
    545     if (!prototype.isCell())
    546         return 0;
    547 
    548     RefPtr<StructureChain> chain = StructureChain::create(asObject(prototype)->structure());
    549     setCachedPrototypeChain(chain.release());
    550     return cachedPrototypeChain();
    551509}
    552510
     
    925883}
    926884
    927 void Structure::getEnumerablePropertyNamesInternal(PropertyNameArray& propertyNames)
     885void Structure::getEnumerableNamesFromPropertyTable(PropertyNameArray& propertyNames)
    928886{
    929887    materializePropertyMapIfNecessary();
     
    979937        for (size_t i = 0; i < sortedEnumerables.size(); ++i)
    980938            propertyNames.add(sortedEnumerables[i]->key);
     939    }
     940}
     941
     942void Structure::getEnumerableNamesFromClassInfoTable(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames)
     943{
     944    // Add properties from the static hashtables of properties
     945    for (; classInfo; classInfo = classInfo->parentClass) {
     946        const HashTable* table = classInfo->propHashTable(exec);
     947        if (!table)
     948            continue;
     949        table->initializeIfNeeded(exec);
     950        ASSERT(table->table);
     951#if ENABLE(PERFECT_HASH_SIZE)
     952        int hashSizeMask = table->hashSizeMask;
     953#else
     954        int hashSizeMask = table->compactSize - 1;
     955#endif
     956        const HashEntry* entry = table->table;
     957        for (int i = 0; i <= hashSizeMask; ++i, ++entry) {
     958            if (entry->key() && !(entry->attributes() & DontEnum))
     959                propertyNames.add(entry->key());
     960        }
    981961    }
    982962}
Note: See TracChangeset for help on using the changeset viewer.