OSDN Git Service

b26882b8d494077179260a62ee9dfae5e6f7f9af
[android-x86/external-webkit.git] / 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 "Frame.h"
38 #include "HTMLImageElement.h"
39 #include "HTMLNames.h"
40 #include "MessagePort.h"
41 #include "PlatformBridge.h"
42 #include "SVGElement.h"
43 #include "V8Binding.h"
44 #include "V8DOMMap.h"
45 #include "V8MessagePort.h"
46 #include "V8Proxy.h"
47 #include "WrapperTypeInfo.h"
48
49 #include <algorithm>
50 #include <utility>
51 #include <v8-debug.h>
52 #include <wtf/HashMap.h>
53 #include <wtf/StdLibExtras.h>
54 #include <wtf/UnusedParam.h>
55
56 namespace WebCore {
57
58 #ifndef NDEBUG
59 // Keeps track of global handles created (not JS wrappers
60 // of DOM objects). Often these global handles are source
61 // of leaks.
62 //
63 // If you want to let a C++ object hold a persistent handle
64 // to a JS object, you should register the handle here to
65 // keep track of leaks.
66 //
67 // When creating a persistent handle, call:
68 //
69 // #ifndef NDEBUG
70 //    V8GCController::registerGlobalHandle(type, host, handle);
71 // #endif
72 //
73 // When releasing the handle, call:
74 //
75 // #ifndef NDEBUG
76 //    V8GCController::unregisterGlobalHandle(type, host, handle);
77 // #endif
78 //
79 typedef HashMap<v8::Value*, GlobalHandleInfo*> GlobalHandleMap;
80
81 static GlobalHandleMap& globalHandleMap()
82 {
83     DEFINE_STATIC_LOCAL(GlobalHandleMap, staticGlobalHandleMap, ());
84     return staticGlobalHandleMap;
85 }
86
87 // The function is the place to set the break point to inspect
88 // live global handles. Leaks are often come from leaked global handles.
89 static void enumerateGlobalHandles()
90 {
91     for (GlobalHandleMap::iterator it = globalHandleMap().begin(), end = globalHandleMap().end(); it != end; ++it) {
92         GlobalHandleInfo* info = it->second;
93         UNUSED_PARAM(info);
94         v8::Value* handle = it->first;
95         UNUSED_PARAM(handle);
96     }
97 }
98
99 void V8GCController::registerGlobalHandle(GlobalHandleType type, void* host, v8::Persistent<v8::Value> handle)
100 {
101     ASSERT(!globalHandleMap().contains(*handle));
102     globalHandleMap().set(*handle, new GlobalHandleInfo(host, type));
103 }
104
105 void V8GCController::unregisterGlobalHandle(void* host, v8::Persistent<v8::Value> handle)
106 {
107     ASSERT(globalHandleMap().contains(*handle));
108     GlobalHandleInfo* info = globalHandleMap().take(*handle);
109     ASSERT(info->m_host == host);
110     delete info;
111 }
112 #endif // ifndef NDEBUG
113
114 typedef HashMap<Node*, v8::Object*> DOMNodeMap;
115 typedef HashMap<void*, v8::Object*> DOMObjectMap;
116
117 #ifndef NDEBUG
118
119 static void enumerateDOMObjectMap(DOMObjectMap& wrapperMap)
120 {
121     for (DOMObjectMap::iterator it = wrapperMap.begin(), end = wrapperMap.end(); it != end; ++it) {
122         v8::Persistent<v8::Object> wrapper(it->second);
123         WrapperTypeInfo* type = V8DOMWrapper::domWrapperType(wrapper);
124         void* object = it->first;
125         UNUSED_PARAM(type);
126         UNUSED_PARAM(object);
127     }
128 }
129
130 class DOMObjectVisitor : public DOMWrapperMap<void>::Visitor {
131 public:
132     void visitDOMWrapper(void* object, v8::Persistent<v8::Object> wrapper)
133     {
134         WrapperTypeInfo* type = V8DOMWrapper::domWrapperType(wrapper);
135         UNUSED_PARAM(type);
136         UNUSED_PARAM(object);
137     }
138 };
139
140 class EnsureWeakDOMNodeVisitor : public DOMWrapperMap<Node>::Visitor {
141 public:
142     void visitDOMWrapper(Node* object, v8::Persistent<v8::Object> wrapper)
143     {
144         UNUSED_PARAM(object);
145         ASSERT(wrapper.IsWeak());
146     }
147 };
148
149 #endif // NDEBUG
150
151 // A map from a DOM node to its JS wrapper, the wrapper
152 // is kept as a strong reference to survive GCs.
153 static DOMObjectMap& gcProtectedMap()
154 {
155     DEFINE_STATIC_LOCAL(DOMObjectMap, staticGcProtectedMap, ());
156     return staticGcProtectedMap;
157 }
158
159 void V8GCController::gcProtect(void* domObject)
160 {
161     if (!domObject)
162         return;
163     if (gcProtectedMap().contains(domObject))
164         return;
165     if (!getDOMObjectMap().contains(domObject))
166         return;
167
168     // Create a new (strong) persistent handle for the object.
169     v8::Persistent<v8::Object> wrapper = getDOMObjectMap().get(domObject);
170     if (wrapper.IsEmpty())
171         return;
172
173     gcProtectedMap().set(domObject, *v8::Persistent<v8::Object>::New(wrapper));
174 }
175
176 void V8GCController::gcUnprotect(void* domObject)
177 {
178     if (!domObject)
179         return;
180     if (!gcProtectedMap().contains(domObject))
181         return;
182
183     // Dispose the strong reference.
184     v8::Persistent<v8::Object> wrapper(gcProtectedMap().take(domObject));
185     wrapper.Dispose();
186 }
187
188 class GCPrologueVisitor : public DOMWrapperMap<void>::Visitor {
189 public:
190     void visitDOMWrapper(void* object, v8::Persistent<v8::Object> wrapper)
191     {
192         WrapperTypeInfo* typeInfo = V8DOMWrapper::domWrapperType(wrapper);  
193
194         // Additional handling of message port ensuring that entangled ports also
195         // have their wrappers entangled. This should ideally be handled when the
196         // ports are actually entangled in MessagePort::entangle, but to avoid
197         // forking MessagePort.* this is postponed to GC time. Having this postponed
198         // has the drawback that the wrappers are "entangled/unentangled" for each
199         // GC even though their entaglement most likely is still the same.
200         if (V8MessagePort::info.equals(typeInfo)) {
201             // Mark each port as in-use if it's entangled. For simplicity's sake, we assume all ports are remotely entangled,
202             // since the Chromium port implementation can't tell the difference.
203             MessagePort* port1 = static_cast<MessagePort*>(object);
204             if (port1->isEntangled() || port1->hasPendingActivity())
205                 wrapper.ClearWeak();
206         } else {
207             ActiveDOMObject* activeDOMObject = typeInfo->toActiveDOMObject(wrapper);
208             if (activeDOMObject && activeDOMObject->hasPendingActivity())
209                 wrapper.ClearWeak();
210         }
211     }
212 };
213
214 class GrouperItem {
215 public:
216     GrouperItem(uintptr_t groupId, Node* node, v8::Persistent<v8::Object> wrapper) 
217         : m_groupId(groupId)
218         , m_node(node)
219         , m_wrapper(wrapper) 
220         {
221         }
222
223     uintptr_t groupId() const { return m_groupId; }
224     Node* node() const { return m_node; }
225     v8::Persistent<v8::Object> wrapper() const { return m_wrapper; }
226
227 private:
228     uintptr_t m_groupId;
229     Node* m_node;
230     v8::Persistent<v8::Object> m_wrapper;
231 };
232
233 bool operator<(const GrouperItem& a, const GrouperItem& b)
234 {
235     return a.groupId() < b.groupId();
236 }
237
238 typedef Vector<GrouperItem> GrouperList;
239
240 class ObjectGrouperVisitor : public DOMWrapperMap<Node>::Visitor {
241 public:
242     ObjectGrouperVisitor()
243     {
244         // FIXME: grouper_.reserveCapacity(node_map.size());  ?
245     }
246
247     void visitDOMWrapper(Node* node, v8::Persistent<v8::Object> wrapper)
248     {
249
250         // If the node is in document, put it in the ownerDocument's object group.
251         //
252         // If an image element was created by JavaScript "new Image",
253         // it is not in a document. However, if the load event has not
254         // been fired (still onloading), it is treated as in the document.
255         //
256         // Otherwise, the node is put in an object group identified by the root
257         // element of the tree to which it belongs.
258         uintptr_t groupId;
259         if (node->inDocument() || (node->hasTagName(HTMLNames::imgTag) && !static_cast<HTMLImageElement*>(node)->haveFiredLoadEvent()))
260             groupId = reinterpret_cast<uintptr_t>(node->document());
261         else {
262             Node* root = node;
263             if (node->isAttributeNode()) {
264                 root = static_cast<Attr*>(node)->ownerElement();
265                 // If the attribute has no element, no need to put it in the group,
266                 // because it'll always be a group of 1.
267                 if (!root)
268                     return;
269             } else {
270                 while (root->parentNode())
271                     root = root->parentNode();
272
273                 // If the node is alone in its DOM tree (doesn't have a parent or any
274                 // children) then the group will be filtered out later anyway.
275                 if (root == node && !node->hasChildNodes() && !node->hasAttributes())
276                     return;
277             }
278             groupId = reinterpret_cast<uintptr_t>(root);
279         }
280         m_grouper.append(GrouperItem(groupId, node, wrapper));
281     }
282
283     void applyGrouping()
284     {
285         // Group by sorting by the group id.
286         std::sort(m_grouper.begin(), m_grouper.end());
287
288         // FIXME Should probably work in iterators here, but indexes were easier for my simple mind.
289         for (size_t i = 0; i < m_grouper.size(); ) {
290             // Seek to the next key (or the end of the list).
291             size_t nextKeyIndex = m_grouper.size();
292             for (size_t j = i; j < m_grouper.size(); ++j) {
293                 if (m_grouper[i].groupId() != m_grouper[j].groupId()) {
294                     nextKeyIndex = j;
295                     break;
296                 }
297             }
298
299             ASSERT(nextKeyIndex > i);
300
301             // We only care about a group if it has more than one object. If it only
302             // has one object, it has nothing else that needs to be kept alive.
303             if (nextKeyIndex - i <= 1) {
304                 i = nextKeyIndex;
305                 continue;
306             }
307
308             Vector<v8::Persistent<v8::Value> > group;
309             group.reserveCapacity(nextKeyIndex - i);
310             for (; i < nextKeyIndex; ++i) {
311                 v8::Persistent<v8::Value> wrapper = m_grouper[i].wrapper();
312                 if (!wrapper.IsEmpty())
313                     group.append(wrapper);
314                 /* FIXME: Re-enabled this code to avoid GCing these wrappers!
315                              Currently this depends on looking up the wrapper
316                              during a GC, but we don't know which isolated world
317                              we're in, so it's unclear which map to look in...
318
319                 // If the node is styled and there is a wrapper for the inline
320                 // style declaration, we need to keep that style declaration
321                 // wrapper alive as well, so we add it to the object group.
322                 if (node->isStyledElement()) {
323                   StyledElement* element = reinterpret_cast<StyledElement*>(node);
324                   CSSStyleDeclaration* style = element->inlineStyleDecl();
325                   if (style != NULL) {
326                     wrapper = getDOMObjectMap().get(style);
327                     if (!wrapper.IsEmpty())
328                       group.append(wrapper);
329                   }
330                 }
331                 */
332             }
333
334             if (group.size() > 1)
335                 v8::V8::AddObjectGroup(&group[0], group.size());
336
337             ASSERT(i == nextKeyIndex);
338         }
339     }
340
341 private:
342     GrouperList m_grouper;
343 };
344
345 // Create object groups for DOM tree nodes.
346 void V8GCController::gcPrologue()
347 {
348     v8::HandleScope scope;
349
350 #ifndef NDEBUG
351     DOMObjectVisitor domObjectVisitor;
352     visitDOMObjectsInCurrentThread(&domObjectVisitor);
353 #endif
354
355     // Run through all objects with possible pending activity making their
356     // wrappers non weak if there is pending activity.
357     GCPrologueVisitor prologueVisitor;
358     visitActiveDOMObjectsInCurrentThread(&prologueVisitor);
359
360     // Create object groups.
361     ObjectGrouperVisitor objectGrouperVisitor;
362     visitDOMNodesInCurrentThread(&objectGrouperVisitor);
363     objectGrouperVisitor.applyGrouping();
364
365     // Clean single element cache for string conversions.
366     lastStringImpl = 0;
367     lastV8String.Clear();
368 }
369
370 class GCEpilogueVisitor : public DOMWrapperMap<void>::Visitor {
371 public:
372     void visitDOMWrapper(void* object, v8::Persistent<v8::Object> wrapper)
373     {
374         WrapperTypeInfo* typeInfo = V8DOMWrapper::domWrapperType(wrapper);
375         if (V8MessagePort::info.equals(typeInfo)) {
376             MessagePort* port1 = static_cast<MessagePort*>(object);
377             // We marked this port as reachable in GCPrologueVisitor.  Undo this now since the
378             // port could be not reachable in the future if it gets disentangled (and also
379             // GCPrologueVisitor expects to see all handles marked as weak).
380             if ((!wrapper.IsWeak() && !wrapper.IsNearDeath()) || port1->hasPendingActivity())
381                 wrapper.MakeWeak(port1, &DOMDataStore::weakActiveDOMObjectCallback);
382         } else {
383             ActiveDOMObject* activeDOMObject = typeInfo->toActiveDOMObject(wrapper);
384             if (activeDOMObject && activeDOMObject->hasPendingActivity()) {
385                 ASSERT(!wrapper.IsWeak());
386                 // NOTE: To re-enable weak status of the active object we use
387                 // |object| from the map and not |activeDOMObject|. The latter
388                 // may be a different pointer (in case ActiveDOMObject is not
389                 // the main base class of the object's class) and pointer
390                 // identity is required by DOM map functions.
391                 wrapper.MakeWeak(object, &DOMDataStore::weakActiveDOMObjectCallback);
392             }
393         }
394     }
395 };
396
397 int V8GCController::workingSetEstimateMB = 0;
398
399 namespace {
400
401 int getMemoryUsageInMB()
402 {
403 #if PLATFORM(CHROMIUM) || PLATFORM(ANDROID)
404     return PlatformBridge::memoryUsageMB();
405 #else
406     return 0;
407 #endif
408 }
409
410 int getActualMemoryUsageInMB()
411 {
412 #if PLATFORM(CHROMIUM) || PLATFORM(ANDROID)
413     return PlatformBridge::actualMemoryUsageMB();
414 #else
415     return 0;
416 #endif
417 }
418
419 }  // anonymous namespace
420
421 void V8GCController::gcEpilogue()
422 {
423     v8::HandleScope scope;
424
425     // Run through all objects with pending activity making their wrappers weak
426     // again.
427     GCEpilogueVisitor epilogueVisitor;
428     visitActiveDOMObjectsInCurrentThread(&epilogueVisitor);
429
430     workingSetEstimateMB = getActualMemoryUsageInMB();
431
432 #ifndef NDEBUG
433     // Check all survivals are weak.
434     DOMObjectVisitor domObjectVisitor;
435     visitDOMObjectsInCurrentThread(&domObjectVisitor);
436
437     EnsureWeakDOMNodeVisitor weakDOMNodeVisitor;
438     visitDOMNodesInCurrentThread(&weakDOMNodeVisitor);
439
440     enumerateDOMObjectMap(gcProtectedMap());
441     enumerateGlobalHandles();
442 #endif
443 }
444
445 void V8GCController::checkMemoryUsage()
446 {
447 #if PLATFORM(CHROMIUM)
448     // These values are appropriate for Chromium only.
449     const int lowUsageMB = 256;  // If memory usage is below this threshold, do not bother forcing GC.
450     const int highUsageMB = 1024;  // If memory usage is above this threshold, force GC more aggresively.
451     const int highUsageDeltaMB = 128;  // Delta of memory usage growth (vs. last workingSetEstimateMB) to force GC when memory usage is high.
452 #elif PLATFORM(ANDROID)
453     // Query the PlatformBridge for memory thresholds as these vary device to device.
454     static const int lowUsageMB = PlatformBridge::lowMemoryUsageMB();
455     static const int highUsageMB = PlatformBridge::highMemoryUsageMB();
456     // We use a delta of -1 to ensure that when we are in a low memory situation we always trigger a GC.
457     static const int highUsageDeltaMB = -1;
458 #else
459     return;
460 #endif
461
462     int memoryUsageMB = getMemoryUsageInMB();
463     if ((memoryUsageMB > lowUsageMB && memoryUsageMB > 2 * workingSetEstimateMB) || (memoryUsageMB > highUsageMB && memoryUsageMB > workingSetEstimateMB + highUsageDeltaMB))
464         v8::V8::LowMemoryNotification();
465 }
466
467
468 }  // namespace WebCore