It should be possible to flag a cell for unconditional finalization
[WebKit-https.git] / Source / JavaScriptCore / runtime / WeakMapBase.cpp
1 /*
2  * Copyright (C) 2013, 2015 Apple 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
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "WeakMapBase.h"
28
29 #include "ExceptionHelpers.h"
30 #include "JSCInlines.h"
31
32 #include <wtf/MathExtras.h>
33
34 namespace JSC {
35
36 const ClassInfo WeakMapBase::s_info = { "WeakMapBase", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(WeakMapBase) };
37
38 WeakMapBase::WeakMapBase(VM& vm, Structure* structure)
39     : Base(vm, structure)
40 {
41     ASSERT(m_deadKeyCleaner.target() == this);
42 }
43
44 void WeakMapBase::destroy(JSCell* cell)
45 {
46     static_cast<WeakMapBase*>(cell)->~WeakMapBase();
47 }
48
49 size_t WeakMapBase::estimatedSize(JSCell* cell)
50 {
51     auto* thisObj = jsCast<WeakMapBase*>(cell);
52     return Base::estimatedSize(cell) + (thisObj->m_map.capacity() * (sizeof(JSObject*) + sizeof(WriteBarrier<Unknown>)));
53 }
54
55 void WeakMapBase::visitChildren(JSCell* cell, SlotVisitor& visitor)
56 {
57     Base::visitChildren(cell, visitor);
58     auto* thisObj = jsCast<WeakMapBase*>(cell);
59     visitor.addUnconditionalFinalizer(&thisObj->m_deadKeyCleaner);
60     visitor.addWeakReferenceHarvester(&thisObj->m_deadKeyCleaner);
61
62     // Rough approximation of the external storage needed for the hashtable.
63     // This isn't exact, but it is close enough, and proportional to the actual
64     // external memory usage.
65     visitor.reportExtraMemoryVisited(thisObj->m_map.capacity() * (sizeof(JSObject*) + sizeof(WriteBarrier<Unknown>)));
66 }
67
68 JSValue WeakMapBase::get(JSObject* key)
69 {
70     return inlineGet(key);
71 }
72
73 void WeakMapBase::set(VM& vm, JSObject* key, JSValue value)
74 {
75     // Here we force the write barrier on the key.
76     auto result = m_map.add(WriteBarrier<JSObject>(vm, this, key).get(), WriteBarrier<Unknown>());
77     result.iterator->value.set(vm, this, value);
78 }
79
80 bool WeakMapBase::remove(JSObject* key)
81 {
82     return m_map.remove(key);
83 }
84
85 bool WeakMapBase::contains(JSObject* key)
86 {
87     return m_map.contains(key);
88 }
89
90 void WeakMapBase::clear()
91 {
92     m_map.clear();
93 }
94
95 inline WeakMapBase* WeakMapBase::DeadKeyCleaner::target()
96 {
97     return bitwise_cast<WeakMapBase*>(bitwise_cast<char*>(this) - OBJECT_OFFSETOF(WeakMapBase, m_deadKeyCleaner));
98 }
99
100 void WeakMapBase::DeadKeyCleaner::visitWeakReferences(SlotVisitor& visitor)
101 {
102     WeakMapBase* map = target();
103     m_liveKeyCount = 0;
104     for (auto& pair : map->m_map) {
105         if (!Heap::isMarked(pair.key))
106             continue;
107         m_liveKeyCount++;
108         visitor.append(pair.value);
109     }
110     ASSERT(m_liveKeyCount <= map->m_map.size());
111 }
112
113 void WeakMapBase::DeadKeyCleaner::finalizeUnconditionally()
114 {
115     WeakMapBase* map = target();
116     if (m_liveKeyCount > map->m_map.size() / 2) {
117         RELEASE_ASSERT(m_liveKeyCount <= map->m_map.size());
118         int deadCount = map->m_map.size() - m_liveKeyCount;
119         if (!deadCount)
120             return;
121         Vector<JSObject*> deadEntries;
122         deadEntries.reserveCapacity(deadCount);
123         for (auto& pair : map->m_map) {
124             if (Heap::isMarked(pair.key))
125                 continue;
126             deadEntries.uncheckedAppend(pair.key);
127         }
128         for (auto& deadEntry : deadEntries)
129             map->m_map.remove(deadEntry);
130     } else {
131         MapType newMap;
132         for (auto& pair : map->m_map) {
133             if (!Heap::isMarked(pair.key))
134                 continue;
135             newMap.add(pair.key, pair.value);
136         }
137         map->m_map.swap(newMap);
138     }
139 }
140
141 }