Changeset 43277 in webkit


Ignore:
Timestamp:
May 5, 2009 10:23:30 PM (15 years ago)
Author:
eric@webkit.org
Message:

2009-05-05 Antony Sargent <asargent@chromium.org>

Reviewed by Dimitri Glazkov.

Switch V8EventListenerList to use HashTable<T>.
https://bugs.webkit.org/show_bug.cgi?id=25496

This avoids some tricky issues with event listener removal in the
current implementation and has slightly better performance.

No new functionality so no new tests.

  • bindings/v8/V8EventListenerList.cpp: Added V8EventListenerListIterator.
  • bindings/v8/V8EventListenerList.h: (WebCore::V8EventListenerList::size):
  • bindings/v8/WorkerContextExecutionProxy.cpp: (WebCore::WorkerContextExecutionProxy::initContextIfNeeded):
Location:
trunk/WebCore
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebCore/ChangeLog

    r43275 r43277  
     12009-05-05  Antony Sargent  <asargent@chromium.org>
     2
     3        Reviewed by Dimitri Glazkov.
     4
     5        Switch V8EventListenerList to use HashTable<T>.
     6        https://bugs.webkit.org/show_bug.cgi?id=25496
     7       
     8        This avoids some tricky issues with event listener removal in the
     9        current implementation and has slightly better performance.
     10
     11        No new functionality so no new tests.
     12
     13        * bindings/v8/V8EventListenerList.cpp: Added V8EventListenerListIterator.
     14        * bindings/v8/V8EventListenerList.h:
     15        (WebCore::V8EventListenerList::size):
     16        * bindings/v8/WorkerContextExecutionProxy.cpp:
     17        (WebCore::WorkerContextExecutionProxy::initContextIfNeeded):
     18
    1192009-05-05  Darin Fisher  <darin@chromium.org>
    220
  • trunk/WebCore/bindings/v8/V8EventListenerList.cpp

    r42838 r43277  
    3636namespace WebCore {
    3737
    38 V8EventListenerList::V8EventListenerList(const char* name)
     38V8EventListenerListIterator::V8EventListenerListIterator(V8EventListenerList* list)
     39    : m_list(list)
     40    , m_vectorIndex(0)
     41    , m_iter(list->m_table.begin())
    3942{
    40     ASSERT(strlen(name) <= maxKeyNameLength);
    41     v8::HandleScope handleScope;
     43}
    4244
    43     // Write the name into a temporary buffer, leaving 1 space at the beginning
    44     // for a prefix we'll vary between the inline and non-inline keys.
    45     char keyBuffer[maxKeyNameLength + 2] = { 0 };
    46     strncpy(keyBuffer + 1, name, maxKeyNameLength);
    47     keyBuffer[0] = '1';
    48     m_inlineKey = v8::Persistent<v8::String>::New(v8::String::New(keyBuffer));
    49     keyBuffer[0] = '2';
    50     m_nonInlineKey = v8::Persistent<v8::String>::New(v8::String::New(keyBuffer));
     45V8EventListenerListIterator::V8EventListenerListIterator(V8EventListenerList* list, bool shouldSeekToEnd)
     46    : m_list(list)
     47    , m_vectorIndex(0)
     48    , m_iter(list->m_table.begin())
     49{
     50    if (shouldSeekToEnd)
     51        seekToEnd();
     52}
     53
     54V8EventListenerListIterator::~V8EventListenerListIterator() { }
     55
     56void V8EventListenerListIterator::operator++()
     57{
     58    if (m_iter != m_list->m_table.end()) {
     59        Vector<V8EventListener*>* vector = m_iter->second;
     60        if (m_vectorIndex + 1 < vector->size()) {
     61            m_vectorIndex++;
     62            return;
     63        }
     64        m_vectorIndex = 0;
     65        ++m_iter;
     66    }
     67}
     68
     69bool V8EventListenerListIterator::operator==(const V8EventListenerListIterator& other)
     70{
     71    return other.m_iter == m_iter && other.m_vectorIndex == m_vectorIndex && other.m_list == m_list;
     72}
     73
     74bool V8EventListenerListIterator::operator!=(const V8EventListenerListIterator& other)
     75{
     76    return !operator==(other);
     77}
     78
     79V8EventListener* V8EventListenerListIterator::operator*()
     80{
     81    if (m_iter != m_list->m_table.end()) {
     82        Vector<V8EventListener*>* vector = m_iter->second;
     83        if (m_vectorIndex < vector->size())
     84            return vector->at(m_vectorIndex);
     85    }
     86    return 0;
     87}
     88
     89void V8EventListenerListIterator::seekToEnd()
     90{
     91    m_iter = m_list->m_table.end();
     92    m_vectorIndex = 0;
     93}
     94
     95
     96V8EventListenerList::V8EventListenerList()
     97{
    5198}
    5299
    53100V8EventListenerList::~V8EventListenerList()
    54101{
    55     m_inlineKey.Dispose();
    56     m_nonInlineKey.Dispose();
    57102}
    58103
    59 V8EventListenerList::iterator V8EventListenerList::begin()
     104V8EventListenerListIterator V8EventListenerList::begin()
    60105{
    61     return m_list.begin();
     106    return iterator(this);
    62107}
    63108
    64 V8EventListenerList::iterator V8EventListenerList::end()
     109V8EventListenerListIterator V8EventListenerList::end()
    65110{
    66     return m_list.end();
     111    return iterator(this, true);
    67112}
    68113
    69 v8::Handle<v8::String> V8EventListenerList::getKey(bool isInline)
     114
     115static int getKey(v8::Local<v8::Object> object)
    70116{
    71     return isInline ? m_inlineKey : m_nonInlineKey;
     117    // 0 is a sentinel value for the HashMap key, so we map it to 1.
     118    int hash = object->GetIdentityHash();
     119    if (!hash)
     120        return 1;
     121    return hash;
    72122}
    73123
    74 // See comment in .h file for this function, and update accordingly if implementation details change here.
    75124void V8EventListenerList::add(V8EventListener* listener)
    76125{
    77126    ASSERT(v8::Context::InContext());
    78     m_list.append(listener);
     127    v8::HandleScope handleScope;
    79128
    80     v8::HandleScope handleScope;
    81129    v8::Local<v8::Object> object = listener->getListenerObject();
    82     v8::Local<v8::Value> value = v8::External::Wrap(listener);
    83     object->SetHiddenValue(getKey(listener->isAttribute()), value);
     130    int key = getKey(object);
     131    Vector<V8EventListener*>* vector = m_table.get(key);
     132    if (!vector) {
     133        vector = new Vector<V8EventListener*>();
     134        m_table.set(key, vector);
     135    }
     136    vector->append(listener);
     137    m_reverseTable.set(listener, key);
    84138}
    85139
    86140void V8EventListenerList::remove(V8EventListener* listener)
    87141{
    88     ASSERT(v8::Context::InContext());
    89     v8::HandleScope handleScope;
    90     v8::Handle<v8::String> key = getKey(listener->isAttribute());
    91     for (size_t i = 0; i < m_list.size(); i++) {
    92         V8EventListener* element = m_list.at(i);
    93         if (element->isAttribute() == listener->isAttribute() && element == listener) {
    94             v8::Local<v8::Object> object = listener->getListenerObject();
     142    if (m_reverseTable.contains(listener)) {
     143        int key = m_reverseTable.get(listener);
     144        Vector<V8EventListener*>* vector = m_table.get(key);
     145        if (!vector)
     146            return;
     147        for (size_t j = 0; j < vector->size(); j++) {
     148            if (vector->at(j) == listener) {
     149                vector->remove(j);
     150                if (!vector->size())
     151                    m_table.remove(key);
    95152
    96             // FIXME(asargent) this check for hidden value being !empty is a workaround for
    97             // http://code.google.com/p/v8/issues/detail?id=300
    98             // Once the fix for that is pulled into chromium we can remove the check here.
    99             if (!object->GetHiddenValue(key).IsEmpty())
    100                 object->DeleteHiddenValue(getKey(listener->isAttribute()));
    101             m_list.remove(i);
    102             break;
     153                m_reverseTable.remove(listener);
     154                return;
     155            }
    103156        }
    104157    }
     
    107160void V8EventListenerList::clear()
    108161{
    109     ASSERT(v8::Context::InContext());
    110     v8::HandleScope handleScope;
    111     for (size_t i = 0; i < m_list.size(); i++) {
    112         V8EventListener* element = m_list.at(i);
    113         v8::Local<v8::Object> object = element->getListenerObject();
    114         object->DeleteHiddenValue(getKey(element->isAttribute()));
    115     }
    116     m_list.clear();
     162    m_table.clear();
     163    m_reverseTable.clear();
    117164}
    118165
    119 V8EventListener* V8EventListenerList::find(v8::Local<v8::Object> object, bool isInline)
     166V8EventListener* V8EventListenerList::find(v8::Local<v8::Object> object, bool isAttribute)
    120167{
    121     v8::Local<v8::Value> value = object->GetHiddenValue(getKey(isInline));
    122     if (value.IsEmpty())
     168    ASSERT(v8::Context::InContext());
     169    int key = getKey(object);
     170
     171    Vector<V8EventListener*>* vector = m_table.get(key);
     172    if (!vector)
    123173        return 0;
    124     return reinterpret_cast<V8EventListener*>(v8::External::Unwrap(value));
     174
     175    for (size_t i = 0; i < vector->size(); i++) {
     176        V8EventListener* element = vector->at(i);
     177        if (isAttribute == element->isAttribute() && object == element->getListenerObject())
     178            return element;
     179    }
     180    return 0;
    125181}
    126182
  • trunk/WebCore/bindings/v8/V8EventListenerList.h

    r42510 r43277  
    3434#include <v8.h>
    3535#include <wtf/Vector.h>
     36#include <wtf/HashMap.h>
    3637
    3738namespace WebCore {
    3839    class V8EventListener;
     40    class V8EventListenerListIterator;
    3941
    40     // This is a container for V8EventListener objects that also does some caching to speed up finding entries by v8::Object.
     42    // This is a container for V8EventListener objects that uses the identity hash of the v8::Object to
     43    // speed up lookups
    4144    class V8EventListenerList {
    4245    public:
    43         static const size_t maxKeyNameLength = 254;
     46        // Because v8::Object identity hashes are not guaranteed to be unique, we unfortunately can't just map
     47        // an int to V8EventListener. Instead we define a HashMap of int to Vector of V8EventListener
     48        // called a ListenerMultiMap.
     49        typedef Vector<V8EventListener*>* Values;
     50        struct ValuesTraits : HashTraits<Values> {
     51            static const bool needsDestruction = true;
     52        };
     53        typedef HashMap<int, Values, DefaultHash<int>::Hash, HashTraits<int>, ValuesTraits> ListenerMultiMap;
    4454
    45         // The name should be distinct from any other V8EventListenerList within the same process, and <= maxKeyNameLength characters.
    46         explicit V8EventListenerList(const char* name);
     55        V8EventListenerList();
    4756        ~V8EventListenerList();
    4857
    49         typedef Vector<V8EventListener*>::iterator iterator;
    50         V8EventListenerList::iterator begin();
     58        friend class V8EventListenerListIterator;
     59        typedef V8EventListenerListIterator iterator;       
     60
     61        iterator begin();
    5162        iterator end();
    5263
    53         // In addition to adding the listener to this list, this also caches the V8EventListener as a hidden property on its wrapped
    54         // v8 listener object, so we can quickly look it up later.
    5564        void add(V8EventListener*);
    5665        void remove(V8EventListener*);
    57         V8EventListener* find(v8::Local<v8::Object>, bool isInline);
     66        V8EventListener* find(v8::Local<v8::Object>, bool isAttribute);
    5867        void clear();
    59         size_t size() { return m_list.size(); }
     68        size_t size() { return m_table.size(); }
    6069
    6170    private:
    62         v8::Handle<v8::String> getKey(bool isInline);
    63         v8::Persistent<v8::String> m_inlineKey;
    64         v8::Persistent<v8::String> m_nonInlineKey;
    65         Vector<V8EventListener*> m_list;
     71        ListenerMultiMap m_table;
     72
     73        // we also keep a reverse mapping of V8EventListener to v8::Object identity hash,
     74        // in order to speed up removal by V8EventListener
     75        HashMap<V8EventListener*, int> m_reverseTable;
     76    };
     77
     78    class V8EventListenerListIterator {
     79    public:
     80        ~V8EventListenerListIterator();
     81        void operator++();
     82        bool operator==(const V8EventListenerListIterator&);
     83        bool operator!=(const V8EventListenerListIterator&);
     84        V8EventListener* operator*();
     85    private:
     86        friend class V8EventListenerList;
     87        explicit V8EventListenerListIterator(V8EventListenerList*);
     88        V8EventListenerListIterator(V8EventListenerList*, bool shouldSeekToEnd);
     89        void seekToEnd();
     90
     91        V8EventListenerList* m_list;
     92        V8EventListenerList::ListenerMultiMap::iterator m_iter;
     93        size_t m_vectorIndex;
    6694    };
    6795
  • trunk/WebCore/bindings/v8/WorkerContextExecutionProxy.cpp

    r42648 r43277  
    200200    globalObject->Set(implicitProtoString, jsWorkerContext);
    201201
    202     m_listeners.set(new V8EventListenerList("m_listeners"));
     202    m_listeners.set(new V8EventListenerList());
    203203}
    204204
Note: See TracChangeset for help on using the changeset viewer.