[V8] Vastly simplify V8GCController's NodeVisitor
[WebKit-https.git] / Source / WebCore / bindings / v8 / V8GCController.cpp
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 #include "config.h"
32 #include "V8GCController.h"
33
34 #include "ActiveDOMObject.h"
35 #include "Attr.h"
36 #include "DOMDataStore.h"
37 #include "DOMImplementation.h"
38 #include "HTMLImageElement.h"
39 #include "HTMLNames.h"
40 #include "MemoryUsageSupport.h"
41 #include "MessagePort.h"
42 #include "RetainedDOMInfo.h"
43 #include "RetainedObjectInfo.h"
44 #include "V8AbstractEventListener.h"
45 #include "V8Binding.h"
46 #include "V8CSSRule.h"
47 #include "V8CSSRuleList.h"
48 #include "V8CSSStyleDeclaration.h"
49 #include "V8DOMImplementation.h"
50 #include "V8MessagePort.h"
51 #include "V8RecursionScope.h"
52 #include "V8StyleSheet.h"
53 #include "V8StyleSheetList.h"
54 #include "WrapperTypeInfo.h"
55
56 #include <algorithm>
57 #include <utility>
58 #include <v8-debug.h>
59 #include <wtf/HashMap.h>
60 #include <wtf/StdLibExtras.h>
61 #include <wtf/UnusedParam.h>
62
63 #if PLATFORM(CHROMIUM)
64 #include "TraceEvent.h"
65 #endif
66
67 namespace WebCore {
68
69 #ifndef NDEBUG
70
71 class EnsureWeakDOMNodeVisitor : public DOMWrapperMap<Node>::Visitor {
72 public:
73     void visitDOMWrapper(DOMDataStore*, Node*, v8::Persistent<v8::Object> wrapper)
74     {
75         ASSERT(wrapper.IsWeak());
76     }
77 };
78
79 #endif // NDEBUG
80
81 template<typename T>
82 class ActiveDOMObjectPrologueVisitor : public DOMWrapperMap<T>::Visitor {
83 public:
84     void visitDOMWrapper(DOMDataStore*, T* object, v8::Persistent<v8::Object> wrapper)
85     {
86         WrapperTypeInfo* typeInfo = V8DOMWrapper::domWrapperType(wrapper);  
87
88         if (V8MessagePort::info.equals(typeInfo)) {
89             // Mark each port as in-use if it's entangled. For simplicity's sake,
90             // we assume all ports are remotely entangled, since the Chromium port
91             // implementation can't tell the difference.
92             MessagePort* port = reinterpret_cast<MessagePort*>(object);
93             if (port->isEntangled() || port->hasPendingActivity())
94                 wrapper.ClearWeak();
95             return;
96         }
97
98         ActiveDOMObject* activeDOMObject = typeInfo->toActiveDOMObject(wrapper);
99         if (activeDOMObject && activeDOMObject->hasPendingActivity())
100             wrapper.ClearWeak();
101     }
102 };
103
104 class ObjectVisitor : public DOMWrapperMap<void>::Visitor {
105 public:
106     void visitDOMWrapper(DOMDataStore* store, void* object, v8::Persistent<v8::Object> wrapper)
107     {
108         V8DOMWrapper::domWrapperType(wrapper)->visitDOMWrapper(store, object, wrapper);
109     }
110 };
111
112 static void addImplicitReferencesForNodeWithEventListeners(Node* node, v8::Persistent<v8::Object> wrapper)
113 {
114     ASSERT(node->hasEventListeners());
115
116     Vector<v8::Persistent<v8::Value> > listeners;
117
118     EventListenerIterator iterator(node);
119     while (EventListener* listener = iterator.nextListener()) {
120         if (listener->type() != EventListener::JSEventListenerType)
121             continue;
122         V8AbstractEventListener* v8listener = static_cast<V8AbstractEventListener*>(listener);
123         if (!v8listener->hasExistingListenerObject())
124             continue;
125         listeners.append(v8listener->existingListenerObjectPersistentHandle());
126     }
127
128     if (listeners.isEmpty())
129         return;
130
131     v8::V8::AddImplicitReferences(wrapper, listeners.data(), listeners.size());
132 }
133
134 static Node* rootForGC(Node* node)
135 {
136     if (node->inDocument() || (node->hasTagName(HTMLNames::imgTag) && static_cast<HTMLImageElement*>(node)->hasPendingActivity()))
137         return node->document();
138
139     if (node->isAttributeNode()) {
140         node = static_cast<Attr*>(node)->ownerElement();
141         if (!node)
142             return 0;
143     }
144
145     while (Node* parent = node->parentOrHostNode())
146         node = parent;
147
148     return node;
149 }
150
151 class ImplicitConnection {
152 public:
153     ImplicitConnection(Node* root, v8::Persistent<v8::Value> wrapper)
154         : m_root(root)
155         , m_wrapper(wrapper)
156     {
157     }
158
159     Node* root() const { return m_root; }
160     v8::Persistent<v8::Value> wrapper() const { return m_wrapper; }
161
162 public:
163     Node* m_root;
164     v8::Persistent<v8::Value> m_wrapper;
165 };
166
167 bool operator<(const ImplicitConnection& left, const ImplicitConnection& right)
168 {
169     return left.root() < right.root();
170 }
171
172 class NodeVisitor : public DOMWrapperMap<Node>::Visitor {
173 public:
174     void visitDOMWrapper(DOMDataStore*, Node* node, v8::Persistent<v8::Object> wrapper)
175     {
176         if (node->hasEventListeners())
177             addImplicitReferencesForNodeWithEventListeners(node, wrapper);
178
179         Node* root = rootForGC(node);
180         if (!root)
181             return;
182         m_connections.append(ImplicitConnection(root, wrapper));
183     }
184
185     void applyGrouping()
186     {
187         std::sort(m_connections.begin(), m_connections.end());
188         Vector<v8::Persistent<v8::Value> > group;
189         size_t i = 0;
190         while (i < m_connections.size()) {
191             Node* root = m_connections[i].root();
192
193             do {
194                 group.append(m_connections[i++].wrapper());
195             } while (i < m_connections.size() && root == m_connections[i].root());
196
197             if (group.size() > 1)
198                 v8::V8::AddObjectGroup(group.data(), group.size(), new RetainedDOMInfo(root));
199
200             group.shrink(0);
201         }
202     }
203
204 private:
205     Vector<ImplicitConnection> m_connections;
206 };
207
208 // Create object groups for DOM tree nodes.
209 void V8GCController::gcPrologue()
210 {
211     TRACE_EVENT_BEGIN0("v8", "GC");
212
213     v8::HandleScope scope;
214
215     ActiveDOMObjectPrologueVisitor<void> activeObjectVisitor;
216     visitActiveDOMObjects(&activeObjectVisitor);
217     ActiveDOMObjectPrologueVisitor<Node> activeNodeVisitor;
218     visitActiveDOMNodes(&activeNodeVisitor);
219
220     NodeVisitor nodeVisitor;
221     visitDOMNodes(&nodeVisitor);
222     visitActiveDOMNodes(&nodeVisitor);
223     nodeVisitor.applyGrouping();
224
225     ObjectVisitor objectVisitor;
226     visitDOMObjects(&objectVisitor);
227
228     V8PerIsolateData* data = V8PerIsolateData::current();
229     data->stringCache()->clearOnGC();
230 }
231
232 class SpecialCaseEpilogueObjectHandler {
233 public:
234     static bool process(void* object, v8::Persistent<v8::Object> wrapper, WrapperTypeInfo* typeInfo)
235     {
236         if (V8MessagePort::info.equals(typeInfo)) {
237             MessagePort* port1 = static_cast<MessagePort*>(object);
238             // We marked this port as reachable in GCPrologueVisitor.  Undo this now since the
239             // port could be not reachable in the future if it gets disentangled (and also
240             // GCPrologueVisitor expects to see all handles marked as weak).
241             if ((!wrapper.IsWeak() && !wrapper.IsNearDeath()) || port1->hasPendingActivity())
242                 wrapper.MakeWeak(port1, &DOMDataStore::weakActiveDOMObjectCallback);
243             return true;
244         }
245         return false;
246     }
247 };
248
249 class SpecialCaseEpilogueNodeHandler {
250 public:
251     static bool process(Node* object, v8::Persistent<v8::Object> wrapper, WrapperTypeInfo* typeInfo)
252     {
253         UNUSED_PARAM(object);
254         UNUSED_PARAM(wrapper);
255         UNUSED_PARAM(typeInfo);
256         return false;
257     }
258 };
259
260 template<typename T, typename S, v8::WeakReferenceCallback callback>
261 class GCEpilogueVisitor : public DOMWrapperMap<T>::Visitor {
262 public:
263     void visitDOMWrapper(DOMDataStore* store, T* object, v8::Persistent<v8::Object> wrapper)
264     {
265         WrapperTypeInfo* typeInfo = V8DOMWrapper::domWrapperType(wrapper);
266         if (!S::process(object, wrapper, typeInfo)) {
267             ActiveDOMObject* activeDOMObject = typeInfo->toActiveDOMObject(wrapper);
268             if (activeDOMObject && activeDOMObject->hasPendingActivity()) {
269                 ASSERT(!wrapper.IsWeak());
270                 // NOTE: To re-enable weak status of the active object we use
271                 // |object| from the map and not |activeDOMObject|. The latter
272                 // may be a different pointer (in case ActiveDOMObject is not
273                 // the main base class of the object's class) and pointer
274                 // identity is required by DOM map functions.
275                 wrapper.MakeWeak(object, callback);
276             }
277         }
278     }
279 };
280
281 #if PLATFORM(CHROMIUM)
282 static int workingSetEstimateMB = 0;
283
284 static Mutex& workingSetEstimateMBMutex()
285 {
286     AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex);
287     return mutex;
288 }
289 #endif
290
291 void V8GCController::gcEpilogue()
292 {
293     v8::HandleScope scope;
294
295     // Run through all objects with pending activity making their wrappers weak
296     // again.
297     GCEpilogueVisitor<void, SpecialCaseEpilogueObjectHandler, &DOMDataStore::weakActiveDOMObjectCallback> epilogueObjectVisitor;
298     visitActiveDOMObjects(&epilogueObjectVisitor);
299     GCEpilogueVisitor<Node, SpecialCaseEpilogueNodeHandler, &DOMDataStore::weakNodeCallback> epilogueNodeVisitor;
300     visitActiveDOMNodes(&epilogueNodeVisitor);
301
302 #if PLATFORM(CHROMIUM)
303     // The GC can happen on multiple threads in case of dedicated workers which run in-process.
304     {
305         MutexLocker locker(workingSetEstimateMBMutex());
306         workingSetEstimateMB = MemoryUsageSupport::actualMemoryUsageMB();
307     }
308 #endif
309
310 #ifndef NDEBUG
311     EnsureWeakDOMNodeVisitor weakDOMNodeVisitor;
312     visitDOMNodes(&weakDOMNodeVisitor);
313 #endif
314
315     TRACE_EVENT_END0("v8", "GC");
316 }
317
318 void V8GCController::checkMemoryUsage()
319 {
320 #if PLATFORM(CHROMIUM)
321     const int lowMemoryUsageMB = MemoryUsageSupport::lowMemoryUsageMB();
322     const int highMemoryUsageMB = MemoryUsageSupport::highMemoryUsageMB();
323     const int highUsageDeltaMB = MemoryUsageSupport::highUsageDeltaMB();
324     int memoryUsageMB = MemoryUsageSupport::memoryUsageMB();
325     int workingSetEstimateMBCopy;
326     {
327         MutexLocker locker(workingSetEstimateMBMutex());
328         workingSetEstimateMBCopy = workingSetEstimateMB;
329     }
330
331     if ((memoryUsageMB > lowMemoryUsageMB && memoryUsageMB > 2 * workingSetEstimateMBCopy) || (memoryUsageMB > highMemoryUsageMB && memoryUsageMB > workingSetEstimateMBCopy + highUsageDeltaMB))
332         v8::V8::LowMemoryNotification();
333 #endif
334 }
335
336 void V8GCController::hintForCollectGarbage()
337 {
338     V8PerIsolateData* data = V8PerIsolateData::current();
339     if (!data->shouldCollectGarbageSoon())
340         return;
341     const int longIdlePauseInMS = 1000;
342     data->clearShouldCollectGarbageSoon();
343     v8::V8::ContextDisposedNotification();
344     v8::V8::IdleNotification(longIdlePauseInMS);
345 }
346
347 void V8GCController::collectGarbage()
348 {
349     v8::HandleScope handleScope;
350
351     ScopedPersistent<v8::Context> context;
352
353     context.adopt(v8::Context::New());
354     if (context.isEmpty())
355         return;
356
357     {
358         v8::Context::Scope scope(context.get());
359         v8::Local<v8::String> source = v8::String::New("if (gc) gc();");
360         v8::Local<v8::String> name = v8::String::New("gc");
361         v8::Handle<v8::Script> script = v8::Script::Compile(source, name);
362         if (!script.IsEmpty()) {
363             V8RecursionScope::MicrotaskSuppression scope;
364             script->Run();
365         }
366     }
367
368     context.clear();
369 }
370
371 }  // namespace WebCore