Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / 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 "bindings/v8/V8GCController.h"
33
34 #include <algorithm>
35 #include "V8MutationObserver.h"
36 #include "V8Node.h"
37 #include "V8ScriptRunner.h"
38 #include "bindings/v8/RetainedDOMInfo.h"
39 #include "bindings/v8/V8AbstractEventListener.h"
40 #include "bindings/v8/V8Binding.h"
41 #include "bindings/v8/WrapperTypeInfo.h"
42 #include "core/dom/Attr.h"
43 #include "core/dom/NodeTraversal.h"
44 #include "core/dom/TemplateContentDocumentFragment.h"
45 #include "core/dom/shadow/ElementShadow.h"
46 #include "core/dom/shadow/ShadowRoot.h"
47 #include "core/html/HTMLImageElement.h"
48 #include "core/html/HTMLTemplateElement.h"
49 #include "core/svg/SVGElement.h"
50 #include "platform/TraceEvent.h"
51
52 namespace WebCore {
53
54 // FIXME: This should use opaque GC roots.
55 static void addReferencesForNodeWithEventListeners(v8::Isolate* isolate, Node* node, const v8::Persistent<v8::Object>& wrapper)
56 {
57     ASSERT(node->hasEventListeners());
58
59     EventListenerIterator iterator(node);
60     while (EventListener* listener = iterator.nextListener()) {
61         if (listener->type() != EventListener::JSEventListenerType)
62             continue;
63         V8AbstractEventListener* v8listener = static_cast<V8AbstractEventListener*>(listener);
64         if (!v8listener->hasExistingListenerObject())
65             continue;
66
67         isolate->SetReference(wrapper, v8::Persistent<v8::Value>::Cast(v8listener->existingListenerObjectPersistentHandle()));
68     }
69 }
70
71 Node* V8GCController::opaqueRootForGC(Node* node, v8::Isolate*)
72 {
73     // FIXME: Remove the special handling for image elements.
74     // The same special handling is in V8GCController::gcTree().
75     // Maybe should image elements be active DOM nodes?
76     // See https://code.google.com/p/chromium/issues/detail?id=164882
77     if (node->inDocument() || (node->hasTagName(HTMLNames::imgTag) && toHTMLImageElement(node)->hasPendingActivity()))
78         return &node->document();
79
80     if (node->isAttributeNode()) {
81         Node* ownerElement = toAttr(node)->ownerElement();
82         if (!ownerElement)
83             return node;
84         node = ownerElement;
85     }
86
87     while (Node* parent = node->parentOrShadowHostOrTemplateHostNode())
88         node = parent;
89
90     return node;
91 }
92
93 // Regarding a minor GC algorithm for DOM nodes, see this document:
94 // https://docs.google.com/a/google.com/presentation/d/1uifwVYGNYTZDoGLyCb7sXa7g49mWNMW2gaWvMN5NLk8/edit#slide=id.p
95 class MinorGCWrapperVisitor : public v8::PersistentHandleVisitor {
96 public:
97     explicit MinorGCWrapperVisitor(v8::Isolate* isolate)
98         : m_isolate(isolate)
99     { }
100
101     virtual void VisitPersistentHandle(v8::Persistent<v8::Value>* value, uint16_t classId) OVERRIDE
102     {
103         // A minor DOM GC can collect only Nodes.
104         if (classId != v8DOMNodeClassId)
105             return;
106
107         // To make minor GC cycle time bounded, we limit the number of wrappers handled
108         // by each minor GC cycle to 10000. This value was selected so that the minor
109         // GC cycle time is bounded to 20 ms in a case where the new space size
110         // is 16 MB and it is full of wrappers (which is almost the worst case).
111         // Practically speaking, as far as I crawled real web applications,
112         // the number of wrappers handled by each minor GC cycle is at most 3000.
113         // So this limit is mainly for pathological micro benchmarks.
114         const unsigned wrappersHandledByEachMinorGC = 10000;
115         if (m_nodesInNewSpace.size() >= wrappersHandledByEachMinorGC)
116             return;
117
118         // Casting to a Handle is safe here, since the Persistent doesn't get GCd
119         // during the GC prologue.
120         ASSERT((*reinterpret_cast<v8::Handle<v8::Value>*>(value))->IsObject());
121         v8::Handle<v8::Object>* wrapper = reinterpret_cast<v8::Handle<v8::Object>*>(value);
122         ASSERT(V8DOMWrapper::isDOMWrapper(*wrapper));
123         ASSERT(V8Node::hasInstance(*wrapper, m_isolate));
124         Node* node = V8Node::toNative(*wrapper);
125         // A minor DOM GC can handle only node wrappers in the main world.
126         // Note that node->wrapper().IsEmpty() returns true for nodes that
127         // do not have wrappers in the main world.
128         if (node->containsWrapper()) {
129             const WrapperTypeInfo* type = toWrapperTypeInfo(*wrapper);
130             ActiveDOMObject* activeDOMObject = type->toActiveDOMObject(*wrapper);
131             if (activeDOMObject && activeDOMObject->hasPendingActivity())
132                 return;
133             // FIXME: Remove the special handling for image elements.
134             // The same special handling is in V8GCController::opaqueRootForGC().
135             // Maybe should image elements be active DOM nodes?
136             // See https://code.google.com/p/chromium/issues/detail?id=164882
137             if (node->hasTagName(HTMLNames::imgTag) && toHTMLImageElement(node)->hasPendingActivity())
138                 return;
139             // FIXME: Remove the special handling for SVG context elements.
140             if (node->isSVGElement() && toSVGElement(node)->isContextElement())
141                 return;
142
143             m_nodesInNewSpace.append(node);
144             node->markV8CollectableDuringMinorGC();
145         }
146     }
147
148     void notifyFinished()
149     {
150         Node** nodeIterator = m_nodesInNewSpace.begin();
151         Node** nodeIteratorEnd = m_nodesInNewSpace.end();
152         for (; nodeIterator < nodeIteratorEnd; ++nodeIterator) {
153             Node* node = *nodeIterator;
154             ASSERT(node->containsWrapper());
155             if (node->isV8CollectableDuringMinorGC()) { // This branch is just for performance.
156                 gcTree(m_isolate, node);
157                 node->clearV8CollectableDuringMinorGC();
158             }
159         }
160     }
161
162 private:
163     bool traverseTree(Node* rootNode, Vector<Node*, initialNodeVectorSize>* partiallyDependentNodes)
164     {
165         // To make each minor GC time bounded, we might need to give up
166         // traversing at some point for a large DOM tree. That being said,
167         // I could not observe the need even in pathological test cases.
168         for (Node* node = rootNode; node; node = NodeTraversal::next(*node)) {
169             if (node->containsWrapper()) {
170                 if (!node->isV8CollectableDuringMinorGC()) {
171                     // This node is not in the new space of V8. This indicates that
172                     // the minor GC cannot anyway judge reachability of this DOM tree.
173                     // Thus we give up traversing the DOM tree.
174                     return false;
175                 }
176                 node->clearV8CollectableDuringMinorGC();
177                 partiallyDependentNodes->append(node);
178             }
179             if (ShadowRoot* shadowRoot = node->youngestShadowRoot()) {
180                 if (!traverseTree(shadowRoot, partiallyDependentNodes))
181                     return false;
182             } else if (node->isShadowRoot()) {
183                 if (ShadowRoot* shadowRoot = toShadowRoot(node)->olderShadowRoot()) {
184                     if (!traverseTree(shadowRoot, partiallyDependentNodes))
185                         return false;
186                 }
187             }
188             // <template> has a |content| property holding a DOM fragment which we must traverse,
189             // just like we do for the shadow trees above.
190             if (node->hasTagName(HTMLNames::templateTag)) {
191                 if (!traverseTree(toHTMLTemplateElement(node)->content(), partiallyDependentNodes))
192                     return false;
193             }
194         }
195         return true;
196     }
197
198     void gcTree(v8::Isolate* isolate, Node* startNode)
199     {
200         Vector<Node*, initialNodeVectorSize> partiallyDependentNodes;
201
202         Node* node = startNode;
203         while (Node* parent = node->parentOrShadowHostOrTemplateHostNode())
204             node = parent;
205
206         if (!traverseTree(node, &partiallyDependentNodes))
207             return;
208
209         // We completed the DOM tree traversal. All wrappers in the DOM tree are
210         // stored in partiallyDependentNodes and are expected to exist in the new space of V8.
211         // We report those wrappers to V8 as an object group.
212         Node** nodeIterator = partiallyDependentNodes.begin();
213         Node** const nodeIteratorEnd = partiallyDependentNodes.end();
214         if (nodeIterator == nodeIteratorEnd)
215             return;
216         v8::UniqueId id(reinterpret_cast<intptr_t>((*nodeIterator)->unsafePersistent().value()));
217         for (; nodeIterator != nodeIteratorEnd; ++nodeIterator) {
218             // This is safe because we know that GC won't happen before we
219             // dispose the UnsafePersistent (we're just preparing a GC). Though,
220             // we need to keep the UnsafePersistent alive until we're done with
221             // v8::Persistent.
222             UnsafePersistent<v8::Object> unsafeWrapper = (*nodeIterator)->unsafePersistent();
223             v8::Persistent<v8::Object>* wrapper = unsafeWrapper.persistent();
224             wrapper->MarkPartiallyDependent();
225             isolate->SetObjectGroupId(v8::Persistent<v8::Value>::Cast(*wrapper), id);
226         }
227     }
228
229     Vector<Node*> m_nodesInNewSpace;
230     v8::Isolate* m_isolate;
231 };
232
233 class MajorGCWrapperVisitor : public v8::PersistentHandleVisitor {
234 public:
235     explicit MajorGCWrapperVisitor(v8::Isolate* isolate, bool constructRetainedObjectInfos)
236         : m_isolate(isolate)
237         , m_liveRootGroupIdSet(false)
238         , m_constructRetainedObjectInfos(constructRetainedObjectInfos)
239     {
240     }
241
242     virtual void VisitPersistentHandle(v8::Persistent<v8::Value>* value, uint16_t classId) OVERRIDE
243     {
244         if (classId != v8DOMNodeClassId && classId != v8DOMObjectClassId)
245             return;
246
247         // Casting to a Handle is safe here, since the Persistent doesn't get GCd
248         // during the GC prologue.
249         ASSERT((*reinterpret_cast<v8::Handle<v8::Value>*>(value))->IsObject());
250         v8::Handle<v8::Object>* wrapper = reinterpret_cast<v8::Handle<v8::Object>*>(value);
251         ASSERT(V8DOMWrapper::isDOMWrapper(*wrapper));
252
253         if (value->IsIndependent())
254             return;
255
256         const WrapperTypeInfo* type = toWrapperTypeInfo(*wrapper);
257         void* object = toNative(*wrapper);
258
259         ActiveDOMObject* activeDOMObject = type->toActiveDOMObject(*wrapper);
260         if (activeDOMObject && activeDOMObject->hasPendingActivity())
261             m_isolate->SetObjectGroupId(*value, liveRootId());
262
263         if (classId == v8DOMNodeClassId) {
264             ASSERT(V8Node::hasInstance(*wrapper, m_isolate));
265             Node* node = static_cast<Node*>(object);
266             if (node->hasEventListeners())
267                 addReferencesForNodeWithEventListeners(m_isolate, node, v8::Persistent<v8::Object>::Cast(*value));
268             Node* root = V8GCController::opaqueRootForGC(node, m_isolate);
269             m_isolate->SetObjectGroupId(*value, v8::UniqueId(reinterpret_cast<intptr_t>(root)));
270             if (m_constructRetainedObjectInfos)
271                 m_groupsWhichNeedRetainerInfo.append(root);
272         } else if (classId == v8DOMObjectClassId) {
273             type->visitDOMWrapper(object, v8::Persistent<v8::Object>::Cast(*value), m_isolate);
274         } else {
275             ASSERT_NOT_REACHED();
276         }
277     }
278
279     void notifyFinished()
280     {
281         if (!m_constructRetainedObjectInfos)
282             return;
283         std::sort(m_groupsWhichNeedRetainerInfo.begin(), m_groupsWhichNeedRetainerInfo.end());
284         Node* alreadyAdded = 0;
285         v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler();
286         for (size_t i = 0; i < m_groupsWhichNeedRetainerInfo.size(); ++i) {
287             Node* root = m_groupsWhichNeedRetainerInfo[i];
288             if (root != alreadyAdded) {
289                 profiler->SetRetainedObjectInfo(v8::UniqueId(reinterpret_cast<intptr_t>(root)), new RetainedDOMInfo(root));
290                 alreadyAdded = root;
291             }
292         }
293     }
294
295 private:
296     v8::UniqueId liveRootId()
297     {
298         const v8::Persistent<v8::Value>& liveRoot = V8PerIsolateData::from(m_isolate)->ensureLiveRoot();
299         const intptr_t* idPointer = reinterpret_cast<const intptr_t*>(&liveRoot);
300         v8::UniqueId id(*idPointer);
301         if (!m_liveRootGroupIdSet) {
302             m_isolate->SetObjectGroupId(liveRoot, id);
303             m_liveRootGroupIdSet = true;
304         }
305         return id;
306     }
307
308     v8::Isolate* m_isolate;
309     Vector<Node*> m_groupsWhichNeedRetainerInfo;
310     bool m_liveRootGroupIdSet;
311     bool m_constructRetainedObjectInfos;
312 };
313
314 void V8GCController::gcPrologue(v8::GCType type, v8::GCCallbackFlags flags)
315 {
316     // FIXME: It would be nice if the GC callbacks passed the Isolate directly....
317     v8::Isolate* isolate = v8::Isolate::GetCurrent();
318     if (type == v8::kGCTypeScavenge)
319         minorGCPrologue(isolate);
320     else if (type == v8::kGCTypeMarkSweepCompact)
321         majorGCPrologue(flags & v8::kGCCallbackFlagConstructRetainedObjectInfos, isolate);
322 }
323
324 void V8GCController::minorGCPrologue(v8::Isolate* isolate)
325 {
326     TRACE_EVENT_BEGIN0("v8", "minorGC");
327     if (isMainThread()) {
328         {
329             TRACE_EVENT_SCOPED_SAMPLING_STATE("Blink", "DOMMinorGC");
330             v8::HandleScope scope(isolate);
331             MinorGCWrapperVisitor visitor(isolate);
332             v8::V8::VisitHandlesForPartialDependence(isolate, &visitor);
333             visitor.notifyFinished();
334         }
335         V8PerIsolateData::from(isolate)->setPreviousSamplingState(TRACE_EVENT_GET_SAMPLING_STATE());
336         TRACE_EVENT_SET_SAMPLING_STATE("V8", "V8MinorGC");
337     }
338 }
339
340 // Create object groups for DOM tree nodes.
341 void V8GCController::majorGCPrologue(bool constructRetainedObjectInfos, v8::Isolate* isolate)
342 {
343     v8::HandleScope scope(isolate);
344     TRACE_EVENT_BEGIN0("v8", "majorGC");
345     if (isMainThread()) {
346         {
347             TRACE_EVENT_SCOPED_SAMPLING_STATE("Blink", "DOMMajorGC");
348             MajorGCWrapperVisitor visitor(isolate, constructRetainedObjectInfos);
349             v8::V8::VisitHandlesWithClassIds(&visitor);
350             visitor.notifyFinished();
351         }
352         V8PerIsolateData::from(isolate)->setPreviousSamplingState(TRACE_EVENT_GET_SAMPLING_STATE());
353         TRACE_EVENT_SET_SAMPLING_STATE("V8", "V8MajorGC");
354     } else {
355         MajorGCWrapperVisitor visitor(isolate, constructRetainedObjectInfos);
356         v8::V8::VisitHandlesWithClassIds(&visitor);
357         visitor.notifyFinished();
358     }
359 }
360
361 void V8GCController::gcEpilogue(v8::GCType type, v8::GCCallbackFlags flags)
362 {
363     // FIXME: It would be nice if the GC callbacks passed the Isolate directly....
364     v8::Isolate* isolate = v8::Isolate::GetCurrent();
365     if (type == v8::kGCTypeScavenge)
366         minorGCEpilogue(isolate);
367     else if (type == v8::kGCTypeMarkSweepCompact)
368         majorGCEpilogue(isolate);
369
370     // Force a Blink heap garbage collection when a garbage collection
371     // was forced from V8. This is used for tests that force GCs from
372     // JavaScript to verify that objects die when expected.
373     if (flags & v8::kGCCallbackFlagForced)
374         Heap::collectGarbage(ThreadState::HeapPointersOnStack, Heap::ForcedForTesting);
375 }
376
377 void V8GCController::minorGCEpilogue(v8::Isolate* isolate)
378 {
379     TRACE_EVENT_END0("v8", "minorGC");
380     if (isMainThread())
381         TRACE_EVENT_SET_NONCONST_SAMPLING_STATE(V8PerIsolateData::from(isolate)->previousSamplingState());
382 }
383
384 void V8GCController::majorGCEpilogue(v8::Isolate* isolate)
385 {
386     v8::HandleScope scope(isolate);
387
388     TRACE_EVENT_END0("v8", "majorGC");
389     if (isMainThread())
390         TRACE_EVENT_SET_NONCONST_SAMPLING_STATE(V8PerIsolateData::from(isolate)->previousSamplingState());
391 }
392
393 void V8GCController::hintForCollectGarbage()
394 {
395     V8PerIsolateData* data = V8PerIsolateData::current();
396     if (!data->shouldCollectGarbageSoon())
397         return;
398     const int longIdlePauseInMS = 1000;
399     data->clearShouldCollectGarbageSoon();
400     v8::V8::ContextDisposedNotification();
401     v8::V8::IdleNotification(longIdlePauseInMS);
402 }
403
404 void V8GCController::collectGarbage(v8::Isolate* isolate)
405 {
406     v8::HandleScope handleScope(isolate);
407     v8::Local<v8::Context> context = v8::Context::New(isolate);
408     if (context.IsEmpty())
409         return;
410     v8::Context::Scope contextScope(context);
411     V8ScriptRunner::compileAndRunInternalScript(v8String(isolate, "if (gc) gc();"), isolate);
412 }
413
414 }  // namespace WebCore