Home | History | Annotate | Download | only in v8
      1 /*
      2  * Copyright (C) 2009 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #ifndef DOMDataStore_h
     32 #define DOMDataStore_h
     33 
     34 #include "DOMObjectsInclude.h"
     35 #include "V8Node.h"
     36 
     37 #include <v8.h>
     38 #include <wtf/HashMap.h>
     39 #include <wtf/MainThread.h>
     40 #include <wtf/Noncopyable.h>
     41 #include <wtf/OwnPtr.h>
     42 #include <wtf/StdLibExtras.h>
     43 #include <wtf/Threading.h>
     44 #include <wtf/ThreadSpecific.h>
     45 #include <wtf/Vector.h>
     46 
     47 namespace WebCore {
     48 
     49     class DOMData;
     50 
     51     typedef WTF::Vector<DOMDataStore*> DOMDataList;
     52 
     53     template <class T, int CHUNK_SIZE, class Traits>
     54     class ChunkedTable {
     55       public:
     56         ChunkedTable() : m_chunks(0), m_current(0), m_last(0) { }
     57 
     58         T* add(T element)
     59         {
     60             if (m_current == m_last) {
     61                 m_chunks = new Chunk(m_chunks);
     62                 m_current = m_chunks->m_entries;
     63                 m_last = m_current + CHUNK_SIZE;
     64             }
     65             ASSERT((m_chunks->m_entries <= m_current) && (m_current < m_last));
     66             T* p = m_current++;
     67             *p = element;
     68             return p;
     69         }
     70 
     71         void remove(T* element)
     72         {
     73             ASSERT(element);
     74             ASSERT(m_current > m_chunks->m_entries);
     75             m_current--;
     76             if (element != m_current)
     77                 Traits::move(element, m_current);
     78             if (m_current == m_chunks->m_entries) {
     79                 Chunk* toDelete = m_chunks;
     80                 m_chunks = toDelete->m_previous;
     81                 m_current = m_last = m_chunks ? m_chunks->m_entries + CHUNK_SIZE : 0;
     82                 delete toDelete;
     83             }
     84             ASSERT(!m_chunks || ((m_chunks->m_entries < m_current) && (m_current <= m_last)));
     85         }
     86 
     87         void clear()
     88         {
     89             if (!m_chunks)
     90                 return;
     91 
     92             clearEntries(m_chunks->m_entries, m_current);
     93             Chunk* last = m_chunks;
     94             while (true) {
     95                 Chunk* previous = last->m_previous;
     96                 if (!previous)
     97                     break;
     98                 delete last;
     99                 clearEntries(previous->m_entries, previous->m_entries + CHUNK_SIZE);
    100                 last = previous;
    101             }
    102 
    103             m_chunks = last;
    104             m_current = m_chunks->m_entries;
    105             m_last = m_current + CHUNK_SIZE;
    106         }
    107 
    108         void visit(typename Traits::Visitor* visitor)
    109         {
    110             if (!m_chunks)
    111                 return;
    112 
    113             visitEntries(m_chunks->m_entries, m_current, visitor);
    114             for (Chunk* chunk = m_chunks->m_previous; chunk; chunk = chunk->m_previous)
    115                 visitEntries(chunk->m_entries, chunk->m_entries + CHUNK_SIZE, visitor);
    116         }
    117 
    118       private:
    119         struct Chunk {
    120             explicit Chunk(Chunk* previous) : m_previous(previous) { }
    121             Chunk* const m_previous;
    122             T m_entries[CHUNK_SIZE];
    123         };
    124 
    125         static void clearEntries(T* first, T* last)
    126         {
    127             for (T* entry = first; entry < last; entry++)
    128                 Traits::clear(entry);
    129         }
    130 
    131         static void visitEntries(T* first, T* last, typename Traits::Visitor* visitor)
    132         {
    133             for (T* entry = first; entry < last; entry++)
    134                 Traits::visit(entry, visitor);
    135         }
    136 
    137         Chunk* m_chunks;
    138         T* m_current;
    139         T* m_last;
    140     };
    141 
    142     // DOMDataStore
    143     //
    144     // DOMDataStore is the backing store that holds the maps between DOM objects
    145     // and JavaScript objects.  In general, each thread can have multiple backing
    146     // stores, one per isolated world.
    147     //
    148     // This class doesn't manage the lifetime of the store.  The data store
    149     // lifetime is managed by subclasses.
    150     //
    151     class DOMDataStore : public Noncopyable {
    152     public:
    153         enum DOMWrapperMapType {
    154             DOMNodeMap,
    155             DOMObjectMap,
    156             ActiveDOMObjectMap,
    157 #if ENABLE(SVG)
    158             DOMSVGElementInstanceMap,
    159             DOMSVGObjectWithContextMap
    160 #endif
    161         };
    162 
    163         template <class KeyType>
    164         class InternalDOMWrapperMap : public DOMWrapperMap<KeyType> {
    165         public:
    166             InternalDOMWrapperMap(DOMData* domData, v8::WeakReferenceCallback callback)
    167                 : DOMWrapperMap<KeyType>(callback), m_domData(domData) { }
    168 
    169             virtual void forget(KeyType* object)
    170             {
    171                 DOMWrapperMap<KeyType>::forget(object);
    172                 forgetDelayedObject(m_domData, object);
    173             }
    174 
    175         private:
    176             DOMData* m_domData;
    177         };
    178 
    179         class IntrusiveDOMWrapperMap : public AbstractWeakReferenceMap<Node, v8::Object> {
    180         public:
    181             IntrusiveDOMWrapperMap(v8::WeakReferenceCallback callback)
    182                 : AbstractWeakReferenceMap<Node, v8::Object>(callback) { }
    183 
    184             virtual v8::Persistent<v8::Object> get(Node* obj)
    185             {
    186                 v8::Persistent<v8::Object>* wrapper = obj->wrapper();
    187                 return wrapper ? *wrapper : v8::Persistent<v8::Object>();
    188             }
    189 
    190             virtual void set(Node* obj, v8::Persistent<v8::Object> wrapper)
    191             {
    192                 ASSERT(obj);
    193                 ASSERT(!obj->wrapper());
    194                 v8::Persistent<v8::Object>* entry = m_table.add(wrapper);
    195                 obj->setWrapper(entry);
    196                 wrapper.MakeWeak(obj, weakReferenceCallback());
    197             }
    198 
    199             virtual bool contains(Node* obj)
    200             {
    201                 return obj->wrapper();
    202             }
    203 
    204             virtual void visit(Visitor* visitor)
    205             {
    206                 m_table.visit(visitor);
    207             }
    208 
    209             virtual bool removeIfPresent(Node* key, v8::Persistent<v8::Data> value);
    210 
    211             virtual void clear()
    212             {
    213                 m_table.clear();
    214             }
    215 
    216         private:
    217             static int const numberOfEntries = (1 << 10) - 1;
    218 
    219             struct ChunkedTableTraits {
    220                 typedef IntrusiveDOMWrapperMap::Visitor Visitor;
    221 
    222                 static void move(v8::Persistent<v8::Object>* target, v8::Persistent<v8::Object>* source)
    223                 {
    224                     *target = *source;
    225                     Node* node = V8Node::toNative(*target);
    226                     ASSERT(node);
    227                     node->setWrapper(target);
    228                 }
    229 
    230                 static void clear(v8::Persistent<v8::Object>* entry)
    231                 {
    232                     Node* node = V8Node::toNative(*entry);
    233                     ASSERT(node->wrapper() == entry);
    234 
    235                     node->clearWrapper();
    236                     entry->Dispose();
    237                 }
    238 
    239                 static void visit(v8::Persistent<v8::Object>* entry, Visitor* visitor)
    240                 {
    241                     Node* node = V8Node::toNative(*entry);
    242                     ASSERT(node->wrapper() == entry);
    243 
    244                     visitor->visitDOMWrapper(node, *entry);
    245                 }
    246             };
    247 
    248             typedef ChunkedTable<v8::Persistent<v8::Object>, numberOfEntries, ChunkedTableTraits> Table;
    249             Table m_table;
    250         };
    251 
    252         DOMDataStore(DOMData*);
    253         virtual ~DOMDataStore();
    254 
    255         // A list of all DOMDataStore objects.  Traversed during GC to find a thread-specific map that
    256         // contains the object - so we can schedule the object to be deleted on the thread which created it.
    257         static DOMDataList& allStores();
    258         // Mutex to protect against concurrent access of DOMDataList.
    259         static WTF::Mutex& allStoresMutex();
    260 
    261         // Helper function to avoid circular includes.
    262         static void forgetDelayedObject(DOMData*, void* object);
    263 
    264         DOMData* domData() const { return m_domData; }
    265 
    266         void* getDOMWrapperMap(DOMWrapperMapType);
    267 
    268         DOMNodeMapping& domNodeMap() { return *m_domNodeMap; }
    269         InternalDOMWrapperMap<void>& domObjectMap() { return *m_domObjectMap; }
    270         InternalDOMWrapperMap<void>& activeDomObjectMap() { return *m_activeDomObjectMap; }
    271 #if ENABLE(SVG)
    272         InternalDOMWrapperMap<SVGElementInstance>& domSvgElementInstanceMap() { return *m_domSvgElementInstanceMap; }
    273         InternalDOMWrapperMap<void>& domSvgObjectWithContextMap() { return *m_domSvgObjectWithContextMap; }
    274 #endif
    275 
    276         // Need by V8GCController.
    277         static void weakActiveDOMObjectCallback(v8::Persistent<v8::Value> v8Object, void* domObject);
    278 
    279     protected:
    280         static void weakNodeCallback(v8::Persistent<v8::Value> v8Object, void* domObject);
    281         static void weakDOMObjectCallback(v8::Persistent<v8::Value> v8Object, void* domObject);
    282 #if ENABLE(SVG)
    283         static void weakSVGElementInstanceCallback(v8::Persistent<v8::Value> v8Object, void* domObject);
    284         // SVG non-node elements may have a reference to a context node which should be notified when the element is change.
    285         static void weakSVGObjectWithContextCallback(v8::Persistent<v8::Value> v8Object, void* domObject);
    286 #endif
    287 
    288         DOMNodeMapping* m_domNodeMap;
    289         InternalDOMWrapperMap<void>* m_domObjectMap;
    290         InternalDOMWrapperMap<void>* m_activeDomObjectMap;
    291 #if ENABLE(SVG)
    292         InternalDOMWrapperMap<SVGElementInstance>* m_domSvgElementInstanceMap;
    293         InternalDOMWrapperMap<void>* m_domSvgObjectWithContextMap;
    294 #endif
    295 
    296     private:
    297         // A back-pointer to the DOMData to which we belong.
    298         DOMData* m_domData;
    299     };
    300 
    301 } // namespace WebCore
    302 
    303 #endif // DOMDataStore_h
    304