| /* |
| * Copyright (C) 2011 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #ifndef IntrusiveDOMWrapperMap_h |
| #define IntrusiveDOMWrapperMap_h |
| |
| #include "DOMDataStore.h" |
| #include "V8Node.h" |
| #include "WebCoreMemoryInstrumentation.h" |
| |
| namespace WebCore { |
| |
| template <class T, int CHUNK_SIZE, class Traits> |
| class ChunkedTable { |
| public: |
| ChunkedTable() : m_chunks(0), m_current(0), m_last(0) { } |
| |
| T* add(T element) |
| { |
| if (m_current == m_last) { |
| m_chunks = new Chunk(m_chunks); |
| m_current = m_chunks->m_entries; |
| m_last = m_current + CHUNK_SIZE; |
| } |
| ASSERT((m_chunks->m_entries <= m_current) && (m_current < m_last)); |
| T* p = m_current++; |
| *p = element; |
| return p; |
| } |
| |
| void remove(T* element) |
| { |
| ASSERT(element); |
| ASSERT(m_current > m_chunks->m_entries); |
| m_current--; |
| if (element != m_current) |
| Traits::move(element, m_current); |
| if (m_current == m_chunks->m_entries) { |
| Chunk* toDelete = m_chunks; |
| m_chunks = toDelete->m_previous; |
| m_current = m_last = m_chunks ? m_chunks->m_entries + CHUNK_SIZE : 0; |
| delete toDelete; |
| } |
| ASSERT(!m_chunks || ((m_chunks->m_entries < m_current) && (m_current <= m_last))); |
| } |
| |
| void clear() |
| { |
| if (!m_chunks) |
| return; |
| |
| clearEntries(m_chunks->m_entries, m_current); |
| Chunk* last = m_chunks; |
| while (true) { |
| Chunk* previous = last->m_previous; |
| if (!previous) |
| break; |
| delete last; |
| clearEntries(previous->m_entries, previous->m_entries + CHUNK_SIZE); |
| last = previous; |
| } |
| |
| m_chunks = last; |
| m_current = m_chunks->m_entries; |
| m_last = m_current + CHUNK_SIZE; |
| } |
| |
| void visit(DOMDataStore* store, typename Traits::Visitor* visitor) |
| { |
| if (!m_chunks) |
| return; |
| |
| visitEntries(store, m_chunks->m_entries, m_current, visitor); |
| for (Chunk* chunk = m_chunks->m_previous; chunk; chunk = chunk->m_previous) |
| visitEntries(store, chunk->m_entries, chunk->m_entries + CHUNK_SIZE, visitor); |
| } |
| |
| void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const |
| { |
| MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::Binding); |
| for (Chunk* chunk = m_chunks; chunk; chunk = chunk->m_previous) |
| info.addMember(chunk); |
| } |
| |
| private: |
| struct Chunk { |
| explicit Chunk(Chunk* previous) : m_previous(previous) { } |
| Chunk* const m_previous; |
| T m_entries[CHUNK_SIZE]; |
| }; |
| |
| static void clearEntries(T* first, T* last) |
| { |
| for (T* entry = first; entry < last; entry++) |
| Traits::clear(entry); |
| } |
| |
| static void visitEntries(DOMDataStore* store, T* first, T* last, typename Traits::Visitor* visitor) |
| { |
| for (T* entry = first; entry < last; entry++) |
| Traits::visit(store, entry, visitor); |
| } |
| |
| Chunk* m_chunks; |
| T* m_current; |
| T* m_last; |
| }; |
| |
| |
| class IntrusiveDOMWrapperMap : public AbstractWeakReferenceMap<Node, v8::Object> { |
| public: |
| IntrusiveDOMWrapperMap(v8::WeakReferenceCallback callback) |
| : AbstractWeakReferenceMap<Node, v8::Object>(callback) { } |
| |
| virtual v8::Persistent<v8::Object> get(Node* obj) |
| { |
| v8::Persistent<v8::Object>* wrapper = obj->wrapper(); |
| return wrapper ? *wrapper : v8::Persistent<v8::Object>(); |
| } |
| |
| virtual void set(Node* obj, v8::Persistent<v8::Object> wrapper) |
| { |
| ASSERT(obj); |
| ASSERT(!obj->wrapper()); |
| v8::Persistent<v8::Object>* entry = m_table.add(wrapper); |
| obj->setWrapper(entry); |
| wrapper.MakeWeak(obj, weakReferenceCallback()); |
| } |
| |
| virtual bool contains(Node* obj) |
| { |
| return obj->wrapper(); |
| } |
| |
| virtual void visit(DOMDataStore* store, Visitor* visitor) |
| { |
| m_table.visit(store, visitor); |
| } |
| |
| virtual bool removeIfPresent(Node* obj, v8::Persistent<v8::Object> value) |
| { |
| ASSERT(obj); |
| v8::Persistent<v8::Object>* entry = obj->wrapper(); |
| if (!entry) |
| return false; |
| if (*entry != value) |
| return false; |
| obj->clearWrapper(); |
| m_table.remove(entry); |
| value.Dispose(); |
| return true; |
| } |
| |
| |
| virtual void clear() |
| { |
| m_table.clear(); |
| } |
| |
| virtual void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const OVERRIDE |
| { |
| MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::Binding); |
| info.addInstrumentedMember(m_table); |
| } |
| |
| private: |
| static int const numberOfEntries = (1 << 10) - 1; |
| |
| struct ChunkedTableTraits { |
| typedef IntrusiveDOMWrapperMap::Visitor Visitor; |
| |
| static void move(v8::Persistent<v8::Object>* target, v8::Persistent<v8::Object>* source) |
| { |
| *target = *source; |
| Node* node = V8Node::toNative(*target); |
| ASSERT(node); |
| node->setWrapper(target); |
| } |
| |
| static void clear(v8::Persistent<v8::Object>* entry) |
| { |
| Node* node = V8Node::toNative(*entry); |
| ASSERT(node->wrapper() == entry); |
| |
| node->clearWrapper(); |
| entry->Dispose(); |
| } |
| |
| static void visit(DOMDataStore* store, v8::Persistent<v8::Object>* entry, Visitor* visitor) |
| { |
| Node* node = V8Node::toNative(*entry); |
| ASSERT(node->wrapper() == entry); |
| |
| visitor->visitDOMWrapper(store, node, *entry); |
| } |
| }; |
| |
| typedef ChunkedTable<v8::Persistent<v8::Object>, numberOfEntries, ChunkedTableTraits> Table; |
| Table m_table; |
| }; |
| |
| } // namespace WebCore |
| |
| #endif |